[jnr-ffi] 01/01: Imported Upstream version 1.0.10
Tim Potter
tpot-guest at moszumanska.debian.org
Mon Dec 1 05:32:27 UTC 2014
This is an automated email from the git hooks/post-receive script.
tpot-guest pushed a commit to annotated tag upstream/1.0.10
in repository jnr-ffi.
commit 7be835cc3e438ee0d621962d3c597ffbb89840b0
Author: Tim Potter <tpot at hp.com>
Date: Mon Dec 1 16:31:06 2014 +1100
Imported Upstream version 1.0.10
---
.gitignore | 11 +
.travis.yml | 4 +
LICENSE | 25 +
README.md | 52 +
libtest/Benchmark.c | 33 +
libtest/BufferTest.c | 43 +
libtest/ClosureTest.c | 132 ++
libtest/GNUmakefile | 142 ++
libtest/GlobalVariable.c | 76 +
libtest/LastErrorTest.c | 23 +
libtest/NumberTest.c | 70 +
libtest/PointerTest.c | 122 ++
libtest/ReferenceTest.c | 43 +
libtest/StringTest.c | 40 +
libtest/StructTest.c | 90 +
pom.xml | 234 +++
src/main/java/jnr/ffi/Address.java | 204 ++
src/main/java/jnr/ffi/CallingConvention.java | 36 +
src/main/java/jnr/ffi/LastError.java | 46 +
src/main/java/jnr/ffi/Library.java | 166 ++
src/main/java/jnr/ffi/LibraryLoader.java | 336 +++
src/main/java/jnr/ffi/LibraryOption.java | 58 +
src/main/java/jnr/ffi/Memory.java | 167 ++
src/main/java/jnr/ffi/NativeLong.java | 189 ++
src/main/java/jnr/ffi/NativeType.java | 75 +
src/main/java/jnr/ffi/ObjectReferenceManager.java | 129 ++
src/main/java/jnr/ffi/Platform.java | 494 +++++
src/main/java/jnr/ffi/Pointer.java | 753 +++++++
src/main/java/jnr/ffi/Runtime.java | 184 ++
src/main/java/jnr/ffi/Struct.java | 2191 ++++++++++++++++++++
src/main/java/jnr/ffi/StructLayout.java | 1866 +++++++++++++++++
src/main/java/jnr/ffi/Type.java | 65 +
src/main/java/jnr/ffi/TypeAlias.java | 40 +
src/main/java/jnr/ffi/Union.java | 37 +
src/main/java/jnr/ffi/Variable.java | 39 +
src/main/java/jnr/ffi/annotations/Clear.java | 47 +
src/main/java/jnr/ffi/annotations/Delegate.java | 17 +
src/main/java/jnr/ffi/annotations/Direct.java | 44 +
src/main/java/jnr/ffi/annotations/Encoding.java | 32 +
src/main/java/jnr/ffi/annotations/IgnoreError.java | 44 +
src/main/java/jnr/ffi/annotations/In.java | 47 +
src/main/java/jnr/ffi/annotations/LongLong.java | 37 +
.../java/jnr/ffi/annotations/NulTerminate.java | 34 +
src/main/java/jnr/ffi/annotations/Out.java | 51 +
src/main/java/jnr/ffi/annotations/Pinned.java | 40 +
src/main/java/jnr/ffi/annotations/SaveError.java | 33 +
src/main/java/jnr/ffi/annotations/StdCall.java | 30 +
.../java/jnr/ffi/annotations/Synchronized.java | 34 +
src/main/java/jnr/ffi/annotations/Transient.java | 37 +
.../java/jnr/ffi/annotations/TypeDefinition.java | 17 +
.../jnr/ffi/byref/AbstractNumberReference.java | 76 +
src/main/java/jnr/ffi/byref/AbstractReference.java | 47 +
.../java/jnr/ffi/byref/AddressByReference.java | 110 +
src/main/java/jnr/ffi/byref/ByReference.java | 90 +
src/main/java/jnr/ffi/byref/ByteByReference.java | 113 +
src/main/java/jnr/ffi/byref/DoubleByReference.java | 83 +
src/main/java/jnr/ffi/byref/FloatByReference.java | 84 +
src/main/java/jnr/ffi/byref/IntByReference.java | 113 +
.../java/jnr/ffi/byref/LongLongByReference.java | 113 +
.../java/jnr/ffi/byref/NativeLongByReference.java | 114 +
src/main/java/jnr/ffi/byref/NumberByReference.java | 154 ++
.../java/jnr/ffi/byref/PointerByReference.java | 87 +
src/main/java/jnr/ffi/byref/ShortByReference.java | 112 +
.../java/jnr/ffi/mapper/AbstractDataConverter.java | 7 +
.../jnr/ffi/mapper/AbstractFromNativeType.java | 18 +
.../ffi/mapper/AbstractSignatureTypeMapper.java | 17 +
.../java/jnr/ffi/mapper/AbstractToNativeType.java | 18 +
.../java/jnr/ffi/mapper/CachingTypeMapper.java | 101 +
.../jnr/ffi/mapper/CompositeFunctionMapper.java | 28 +
.../java/jnr/ffi/mapper/CompositeTypeMapper.java | 42 +
src/main/java/jnr/ffi/mapper/DataConverter.java | 25 +
.../java/jnr/ffi/mapper/DefaultSignatureType.java | 71 +
.../java/jnr/ffi/mapper/DefaultTypeMapper.java | 38 +
.../java/jnr/ffi/mapper/FromNativeContext.java | 42 +
.../java/jnr/ffi/mapper/FromNativeConverter.java | 48 +
src/main/java/jnr/ffi/mapper/FromNativeType.java | 18 +
src/main/java/jnr/ffi/mapper/FromNativeTypes.java | 28 +
src/main/java/jnr/ffi/mapper/FunctionMapper.java | 62 +
.../jnr/ffi/mapper/MethodParameterContext.java | 100 +
.../java/jnr/ffi/mapper/MethodResultContext.java | 53 +
src/main/java/jnr/ffi/mapper/SignatureType.java | 16 +
.../java/jnr/ffi/mapper/SignatureTypeMapper.java | 9 +
.../jnr/ffi/mapper/SignatureTypeMapperAdapter.java | 22 +
.../java/jnr/ffi/mapper/SimpleFunctionMapper.java | 19 +
src/main/java/jnr/ffi/mapper/SimpleTypeMapper.java | 25 +
src/main/java/jnr/ffi/mapper/ToNativeContext.java | 38 +
.../java/jnr/ffi/mapper/ToNativeConverter.java | 54 +
src/main/java/jnr/ffi/mapper/ToNativeType.java | 18 +
src/main/java/jnr/ffi/mapper/ToNativeTypes.java | 28 +
src/main/java/jnr/ffi/mapper/TypeMapper.java | 52 +
src/main/java/jnr/ffi/mapper/Util.java | 5 +
.../jnr/ffi/provider/AbstractArrayMemoryIO.java | 446 ++++
.../jnr/ffi/provider/AbstractBufferMemoryIO.java | 219 ++
.../java/jnr/ffi/provider/AbstractMemoryIO.java | 204 ++
.../java/jnr/ffi/provider/AbstractRuntime.java | 114 +
src/main/java/jnr/ffi/provider/BadType.java | 45 +
.../java/jnr/ffi/provider/BoundedMemoryIO.java | 305 +++
src/main/java/jnr/ffi/provider/ClosureManager.java | 9 +
.../provider/DefaultObjectReferenceManager.java | 74 +
.../java/jnr/ffi/provider/DelegatingMemoryIO.java | 25 +
src/main/java/jnr/ffi/provider/FFIProvider.java | 71 +
src/main/java/jnr/ffi/provider/FromNativeType.java | 32 +
.../jnr/ffi/provider/IdentityFunctionMapper.java | 38 +
.../jnr/ffi/provider/InAccessibleMemoryIO.java | 193 ++
src/main/java/jnr/ffi/provider/IntPointer.java | 30 +
.../java/jnr/ffi/provider/InterfaceScanner.java | 118 ++
.../java/jnr/ffi/provider/InvalidProvider.java | 36 +
src/main/java/jnr/ffi/provider/InvalidRuntime.java | 87 +
.../java/jnr/ffi/provider/InvocationSession.java | 55 +
src/main/java/jnr/ffi/provider/Invoker.java | 26 +
src/main/java/jnr/ffi/provider/LoadedLibrary.java | 26 +
src/main/java/jnr/ffi/provider/MemoryManager.java | 37 +
src/main/java/jnr/ffi/provider/NativeFunction.java | 53 +
.../jnr/ffi/provider/NativeInvocationHandler.java | 65 +
src/main/java/jnr/ffi/provider/NativeVariable.java | 15 +
src/main/java/jnr/ffi/provider/NullMemoryIO.java | 37 +
src/main/java/jnr/ffi/provider/NullTypeMapper.java | 45 +
src/main/java/jnr/ffi/provider/ParameterFlags.java | 105 +
src/main/java/jnr/ffi/provider/ParameterType.java | 19 +
src/main/java/jnr/ffi/provider/ResultType.java | 19 +
src/main/java/jnr/ffi/provider/ShareMemoryIO.java | 235 +++
src/main/java/jnr/ffi/provider/SigType.java | 53 +
src/main/java/jnr/ffi/provider/ToNativeType.java | 32 +
.../BoxedBooleanArrayParameterConverter.java | 79 +
.../BoxedByteArrayParameterConverter.java | 79 +
.../BoxedDoubleArrayParameterConverter.java | 80 +
.../BoxedFloatArrayParameterConverter.java | 80 +
.../BoxedIntegerArrayParameterConverter.java | 80 +
.../BoxedLong32ArrayParameterConverter.java | 80 +
.../BoxedLong64ArrayParameterConverter.java | 82 +
.../BoxedShortArrayParameterConverter.java | 79 +
.../converters/ByReferenceParameterConverter.java | 75 +
.../CharSequenceArrayParameterConverter.java | 131 ++
.../converters/CharSequenceParameterConverter.java | 143 ++
.../jnr/ffi/provider/converters/EnumConverter.java | 51 +
.../ffi/provider/converters/EnumSetConverter.java | 98 +
.../converters/Long32ArrayParameterConverter.java | 81 +
.../NativeLong32ArrayParameterConverter.java | 81 +
.../NativeLong64ArrayParameterConverter.java | 83 +
.../provider/converters/NativeLongConverter.java | 50 +
.../Pointer32ArrayParameterConverter.java | 86 +
.../Pointer64ArrayParameterConverter.java | 89 +
.../converters/StringBufferParameterConverter.java | 82 +
.../StringBuilderParameterConverter.java | 94 +
.../provider/converters/StringResultConverter.java | 115 +
.../jnr/ffi/provider/converters/StringUtil.java | 164 ++
.../converters/StructArrayParameterConverter.java | 112 +
.../StructByReferenceFromNativeConverter.java | 70 +
.../StructByReferenceToNativeConverter.java | 29 +
.../provider/jffi/AbstractAsmLibraryInterface.java | 27 +
.../jffi/AbstractFastNumericMethodGenerator.java | 276 +++
.../ffi/provider/jffi/AbstractX86StubCompiler.java | 169 ++
.../ffi/provider/jffi/AllocatedDirectMemoryIO.java | 73 +
.../ffi/provider/jffi/AnnotationTypeMapper.java | 104 +
.../java/jnr/ffi/provider/jffi/ArrayMemoryIO.java | 49 +
.../java/jnr/ffi/provider/jffi/AsmBuilder.java | 206 ++
.../java/jnr/ffi/provider/jffi/AsmClassLoader.java | 53 +
.../jnr/ffi/provider/jffi/AsmLibraryLoader.java | 221 ++
.../java/jnr/ffi/provider/jffi/AsmRuntime.java | 224 ++
.../AsmStructByReferenceFromNativeConverter.java | 153 ++
src/main/java/jnr/ffi/provider/jffi/AsmUtil.java | 643 ++++++
.../jnr/ffi/provider/jffi/BaseMethodGenerator.java | 127 ++
.../ffi/provider/jffi/BufferMethodGenerator.java | 197 ++
.../ffi/provider/jffi/BufferParameterStrategy.java | 87 +
.../jnr/ffi/provider/jffi/ByteBufferMemoryIO.java | 53 +
.../provider/jffi/ClosureFromNativeConverter.java | 184 ++
.../jnr/ffi/provider/jffi/ClosureTypeMapper.java | 47 +
.../java/jnr/ffi/provider/jffi/ClosureUtil.java | 77 +
.../java/jnr/ffi/provider/jffi/CodegenUtils.java | 215 ++
.../jnr/ffi/provider/jffi/ConverterMetaData.java | 107 +
.../ffi/provider/jffi/DefaultInvokerFactory.java | 736 +++++++
.../java/jnr/ffi/provider/jffi/DirectMemoryIO.java | 211 ++
.../ffi/provider/jffi/FastIntMethodGenerator.java | 141 ++
.../ffi/provider/jffi/FastLongMethodGenerator.java | 125 ++
.../provider/jffi/FastNumericMethodGenerator.java | 150 ++
.../provider/jffi/HeapBufferParameterStrategy.java | 52 +
.../jnr/ffi/provider/jffi/InvokerTypeMapper.java | 157 ++
.../java/jnr/ffi/provider/jffi/InvokerUtil.java | 230 ++
.../jnr/ffi/provider/jffi/JNIInvokeInterface.java | 30 +
.../jnr/ffi/provider/jffi/JNINativeInterface.java | 266 +++
.../java/jnr/ffi/provider/jffi/LibraryLoader.java | 27 +
.../java/jnr/ffi/provider/jffi/LocalVariable.java | 14 +
.../ffi/provider/jffi/LocalVariableAllocator.java | 32 +
.../java/jnr/ffi/provider/jffi/MemoryUtil.java | 38 +
.../jnr/ffi/provider/jffi/MethodGenerator.java | 16 +
.../ffi/provider/jffi/NativeClosureFactory.java | 220 ++
.../ffi/provider/jffi/NativeClosureManager.java | 118 ++
.../ffi/provider/jffi/NativeClosurePointer.java | 41 +
.../jnr/ffi/provider/jffi/NativeClosureProxy.java | 276 +++
.../jnr/ffi/provider/jffi/NativeFinalizer.java | 22 +
.../provider/jffi/NativeFunctionMapperContext.java | 30 +
.../java/jnr/ffi/provider/jffi/NativeLibrary.java | 141 ++
.../jnr/ffi/provider/jffi/NativeLibraryLoader.java | 54 +
.../jnr/ffi/provider/jffi/NativeMemoryManager.java | 72 +
.../java/jnr/ffi/provider/jffi/NativeRuntime.java | 224 ++
src/main/java/jnr/ffi/provider/jffi/NoTrace.java | 14 +
src/main/java/jnr/ffi/provider/jffi/NoX86.java | 14 +
.../ffi/provider/jffi/NotImplMethodGenerator.java | 19 +
.../provider/jffi/NullObjectParameterStrategy.java | 32 +
.../java/jnr/ffi/provider/jffi/NumberUtil.java | 293 +++
.../jnr/ffi/provider/jffi/ParameterStrategy.java | 22 +
.../provider/jffi/PointerParameterStrategy.java | 40 +
.../jffi/PrimitiveArrayParameterStrategy.java | 75 +
src/main/java/jnr/ffi/provider/jffi/Provider.java | 39 +
.../ffi/provider/jffi/ReflectionLibraryLoader.java | 223 ++
.../jffi/ReflectionVariableAccessorGenerator.java | 275 +++
.../jnr/ffi/provider/jffi/SimpleNativeContext.java | 25 +
.../jnr/ffi/provider/jffi/SkinnyMethodAdapter.java | 1020 +++++++++
.../StructByReferenceResultConverterFactory.java | 44 +
.../java/jnr/ffi/provider/jffi/StubCompiler.java | 119 ++
.../jnr/ffi/provider/jffi/SymbolNotFoundError.java | 26 +
.../java/jnr/ffi/provider/jffi/ToNativeOp.java | 144 ++
.../ffi/provider/jffi/TransientNativeMemory.java | 137 ++
src/main/java/jnr/ffi/provider/jffi/Types.java | 109 +
src/main/java/jnr/ffi/provider/jffi/Util.java | 13 +
.../provider/jffi/VariableAccessorGenerator.java | 222 ++
.../jnr/ffi/provider/jffi/X86Disassembler.java | 126 ++
.../jnr/ffi/provider/jffi/X86MethodGenerator.java | 329 +++
.../jnr/ffi/provider/jffi/X86_32StubCompiler.java | 372 ++++
.../jnr/ffi/provider/jffi/X86_64StubCompiler.java | 391 ++++
.../jffi/platform/arm/linux/TypeAliases.java | 51 +
.../jffi/platform/i386/darwin/TypeAliases.java | 51 +
.../jffi/platform/i386/freebsd/TypeAliases.java | 49 +
.../jffi/platform/i386/linux/TypeAliases.java | 51 +
.../jffi/platform/i386/openbsd/TypeAliases.java | 51 +
.../jffi/platform/i386/solaris/TypeAliases.java | 51 +
.../jffi/platform/i386/windows/TypeAliases.java | 51 +
.../jffi/platform/mips/linux/TypeAliases.java | 51 +
.../jffi/platform/mipsel/linux/TypeAliases.java | 51 +
.../jffi/platform/ppc/aix/TypeAliases.java | 51 +
.../jffi/platform/ppc/darwin/TypeAliases.java | 51 +
.../jffi/platform/ppc/linux/TypeAliases.java | 51 +
.../jffi/platform/s390/linux/TypeAliases.java | 51 +
.../jffi/platform/s390x/linux/TypeAliases.java | 51 +
.../jffi/platform/sparc/solaris/TypeAliases.java | 51 +
.../jffi/platform/sparcv9/solaris/TypeAliases.java | 51 +
.../jffi/platform/x86_64/darwin/TypeAliases.java | 51 +
.../jffi/platform/x86_64/freebsd/TypeAliases.java | 49 +
.../jffi/platform/x86_64/linux/TypeAliases.java | 51 +
.../jffi/platform/x86_64/openbsd/TypeAliases.java | 51 +
.../jffi/platform/x86_64/solaris/TypeAliases.java | 51 +
.../jffi/platform/x86_64/windows/TypeAliases.java | 51 +
src/main/java/jnr/ffi/types/blkcnt_t.java | 17 +
src/main/java/jnr/ffi/types/blksize_t.java | 17 +
src/main/java/jnr/ffi/types/caddr_t.java | 17 +
src/main/java/jnr/ffi/types/clock_t.java | 17 +
src/main/java/jnr/ffi/types/dev_t.java | 17 +
src/main/java/jnr/ffi/types/fsblkcnt_t.java | 17 +
src/main/java/jnr/ffi/types/fsfilcnt_t.java | 17 +
src/main/java/jnr/ffi/types/gid_t.java | 17 +
src/main/java/jnr/ffi/types/id_t.java | 17 +
src/main/java/jnr/ffi/types/in_addr_t.java | 17 +
src/main/java/jnr/ffi/types/in_port_t.java | 17 +
src/main/java/jnr/ffi/types/ino64_t.java | 17 +
src/main/java/jnr/ffi/types/ino_t.java | 17 +
src/main/java/jnr/ffi/types/int16_t.java | 17 +
src/main/java/jnr/ffi/types/int32_t.java | 17 +
src/main/java/jnr/ffi/types/int64_t.java | 17 +
src/main/java/jnr/ffi/types/int8_t.java | 17 +
src/main/java/jnr/ffi/types/intptr_t.java | 17 +
src/main/java/jnr/ffi/types/key_t.java | 17 +
src/main/java/jnr/ffi/types/mode_t.java | 17 +
src/main/java/jnr/ffi/types/nlink_t.java | 17 +
src/main/java/jnr/ffi/types/off_t.java | 17 +
src/main/java/jnr/ffi/types/pid_t.java | 17 +
src/main/java/jnr/ffi/types/rlim_t.java | 17 +
src/main/java/jnr/ffi/types/sa_family_t.java | 17 +
src/main/java/jnr/ffi/types/size_t.java | 17 +
src/main/java/jnr/ffi/types/socklen_t.java | 17 +
src/main/java/jnr/ffi/types/ssize_t.java | 17 +
src/main/java/jnr/ffi/types/swblk_t.java | 17 +
src/main/java/jnr/ffi/types/time_t.java | 17 +
src/main/java/jnr/ffi/types/u_int16_t.java | 17 +
src/main/java/jnr/ffi/types/u_int32_t.java | 17 +
src/main/java/jnr/ffi/types/u_int64_t.java | 17 +
src/main/java/jnr/ffi/types/u_int8_t.java | 17 +
src/main/java/jnr/ffi/types/uid_t.java | 17 +
src/main/java/jnr/ffi/types/uintptr_t.java | 17 +
.../jnr/ffi/util/AnnotationNameComparator.java | 23 +
src/main/java/jnr/ffi/util/Annotations.java | 68 +
src/main/java/jnr/ffi/util/BufferUtil.java | 172 ++
src/main/java/jnr/ffi/util/EnumMapper.java | 166 ++
.../ffi/util/ref/FinalizablePhantomReference.java | 43 +
.../jnr/ffi/util/ref/FinalizableReference.java | 33 +
.../ffi/util/ref/FinalizableReferenceQueue.java | 306 +++
.../jnr/ffi/util/ref/FinalizableSoftReference.java | 41 +
.../jnr/ffi/util/ref/FinalizableWeakReference.java | 41 +
.../java/jnr/ffi/util/ref/internal/Finalizer.java | 209 ++
src/test/java/jnr/ffi/ArrayTest.java | 225 ++
src/test/java/jnr/ffi/BufferTest.java | 395 ++++
src/test/java/jnr/ffi/DelegateTest.java | 766 +++++++
src/test/java/jnr/ffi/EnumTest.java | 131 ++
src/test/java/jnr/ffi/GlobalVariableTest.java | 71 +
src/test/java/jnr/ffi/InvocationTest.java | 29 +
src/test/java/jnr/ffi/LastErrorTest.java | 65 +
src/test/java/jnr/ffi/LibraryLoaderTest.java | 33 +
src/test/java/jnr/ffi/LibraryTest.java | 71 +
src/test/java/jnr/ffi/MemoryIOTest.java | 410 ++++
src/test/java/jnr/ffi/NumberTest.java | 346 ++++
.../java/jnr/ffi/ObjectReferenceManagerTest.java | 42 +
src/test/java/jnr/ffi/PointerTest.java | 407 ++++
src/test/java/jnr/ffi/ResultConverterTest.java | 161 ++
src/test/java/jnr/ffi/StringArrayTest.java | 86 +
src/test/java/jnr/ffi/StringTest.java | 115 +
src/test/java/jnr/ffi/TstUtil.java | 44 +
src/test/java/jnr/ffi/TypeDefinitionTest.java | 57 +
.../java/jnr/ffi/byref/AddressByReferenceTest.java | 98 +
.../java/jnr/ffi/byref/ByteByReferenceTest.java | 98 +
.../java/jnr/ffi/byref/IntByReferenceTest.java | 93 +
.../java/jnr/ffi/byref/PointerByReferenceTest.java | 118 ++
.../jnr/ffi/mapper/AnnotatedMappedTypeTest.java | 57 +
.../java/jnr/ffi/mapper/CachingTypeMapperTest.java | 231 +++
src/test/java/jnr/ffi/struct/AlignmentTest.java | 66 +
src/test/java/jnr/ffi/struct/ArrayTest.java | 94 +
.../java/jnr/ffi/struct/AsciiStringFieldTest.java | 91 +
src/test/java/jnr/ffi/struct/EnumTest.java | 215 ++
src/test/java/jnr/ffi/struct/PaddingTest.java | 58 +
src/test/java/jnr/ffi/struct/StructLayoutTest.java | 395 ++++
src/test/java/jnr/ffi/struct/StructureTest.java | 399 ++++
.../java/jnr/ffi/struct/UTF8StringFieldTest.java | 112 +
src/test/java/jnr/ffi/struct/UnionTest.java | 140 ++
321 files changed, 35587 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e809dfa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.orig$
+*.rej$
+*.class$
+*~
+.idea
+*.iml
+nbproject/private
+build
+dist
+target
+lib/nblibraries-private.properties
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..21f8524
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: java
+jdk:
+ - oraclejdk7
+ - openjdk6
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a3a8343
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Alternatively, you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 Lesser General Public License
+ version 3 for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..818d3c6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+jnr-ffi [](https://travis-ci.org/jnr/jnr-ffi)
+======
+
+[jnr-ffi](https://github.com/jnr/jnr-ffi) is a java library for loading native libraries without writing JNI code by hand, or using tools such as SWIG.
+
+Example
+------
+
+ package helloworld;
+
+ import jnr.ffi.LibraryLoader;
+
+ public class HelloWorld {
+ public static interface LibC {
+ int puts(String s);
+ }
+
+ public static void main(String[] args) {
+ LibC libc = LibraryLoader.create(LibC.class).load("c");
+
+ libc.puts("Hello, World");
+ }
+ }
+
+Supported Types
+------
+
+All java primitives are mapped simply to the equivalent C types.
+
+* byte - 8 bit signed integer
+* short - 16 bit signed integer
+* int - 32 bit signed integer
+* long - natural long (i.e. 32 bits wide on 32 bit systems, 64 bit wide on 64bit systems)
+* float - 32 bit float
+* double - 64 bit float
+
+The width and/or signed-ness of these basic types can be specified using one of the type alias annotations.
+ e.g.
+
+ // Use the correct width for the result from getpid(3)
+ @pid_t long getpid();
+
+ // read(2) returns a signed long result, and its length parameter is an unsigned long
+ @ssize_t long read(int fd, Pointer data, @size_t long len);
+
+
+In addition, the following java types are mapped to a C pointer
+
+* String - equivalent to "const char *"
+* Pointer - equivalent to "void *"
+* Buffer - equivalent to "void *"
+
diff --git a/libtest/Benchmark.c b/libtest/Benchmark.c
new file mode 100644
index 0000000..44de6c2
--- /dev/null
+++ b/libtest/Benchmark.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+void returnVoid() {
+
+}
+
+void returnVoidI(int arg) {
+
+}
+int returnInt() {
+ return 0;
+}
+
+int returnIntI(int arg) {
+ return arg;
+}
+
diff --git a/libtest/BufferTest.c b/libtest/BufferTest.c
new file mode 100644
index 0000000..5f8cc36
--- /dev/null
+++ b/libtest/BufferTest.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#define MEMSET(buf, value, size) do { \
+ int i; for (i = 0; i < size; ++i) buf[i] = value; \
+} while(0)
+#define MEMCPY(dst, src, size) do { \
+ int i; for (i = 0; i < size; ++i) dst[i] = src[i]; \
+} while(0)
+
+#define FILL(JTYPE, CTYPE) \
+void fill##JTYPE##Buffer(CTYPE* buf, CTYPE value, int size) { MEMSET(buf, value, size); }
+
+#define COPY(JTYPE, CTYPE) \
+void copy##JTYPE##Buffer(CTYPE* dst, CTYPE* src, int size) { MEMCPY(dst, src, size); }
+
+#define FUNC(JTYPE, CTYPE) \
+ FILL(JTYPE, CTYPE); \
+ COPY(JTYPE, CTYPE)
+
+FUNC(Byte, char);
+FUNC(Short, short);
+FUNC(Int, int);
+FUNC(Long, long long);
+FUNC(Float, float);
+FUNC(Double, double);
+
diff --git a/libtest/ClosureTest.c b/libtest/ClosureTest.c
new file mode 100644
index 0000000..ddb80aa
--- /dev/null
+++ b/libtest/ClosureTest.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+void testClosureVrV(void (*closure)(void))
+{
+ (*closure)();
+}
+char testClosureVrB(char (*closure)(void))
+{
+ return (*closure)();
+}
+short testClosureVrS(short (*closure)(void))
+{
+ return (*closure)();
+}
+int testClosureVrI(int (*closure)(void))
+{
+ return (*closure)();
+}
+long long testClosureVrL(long long (*closure)(void))
+{
+ return (*closure)();
+}
+float testClosureVrF(float (*closure)(void))
+{
+ return (*closure)();
+}
+double testClosureVrD(double (*closure)(void))
+{
+ return (*closure)();
+}
+void testClosureBrV(void (*closure)(char), char a1)
+{
+ (*closure)(a1);
+}
+void testClosureSrV(void (*closure)(short), short a1)
+{
+ (*closure)(a1);
+}
+void testClosureIrV(void (*closure)(int), int a1)
+{
+ (*closure)(a1);
+}
+void testClosureLrV(void (*closure)(long long), long long a1)
+{
+ (*closure)(a1);
+}
+void testClosureFrV(void (*closure)(float), float a1)
+{
+ (*closure)(a1);
+}
+void testClosureDrV(void (*closure)(double), double a1)
+{
+ (*closure)(a1);
+}
+
+struct StructClosureIrV {
+ void (*closure)(int);
+};
+
+void testStructClosureIrV(struct StructClosureIrV *s, int a1)
+{
+ (*s->closure)(a1);
+}
+//
+// These macros produce functions of the form:
+// testClosureBIrV(void (*closure)(char, int), char a1, int a2) {}
+//
+#define C2_(J1, J2, N1, N2) \
+void testClosure##J1##J2##rV(void (*closure)(N1, N2), N1 a1, N2 a2) \
+{ \
+ (*closure)(a1, a2); \
+}
+
+#define C2(J, N) \
+ C2_(B, J, char, N) \
+ C2_(S, J, short, N) \
+ C2_(I, J, int, N) \
+ C2_(L, J, long long, N) \
+ C2_(F, J, float, N) \
+ C2_(D, J, double, N) \
+
+
+C2(B, char);
+C2(S, short);
+C2(I, int);
+C2(L, long long);
+C2(F, float);
+C2(D, double);
+
+#define C3_(J1, J2, J3, N1, N2, N3) \
+void testClosure##J1##J2##J3##rV(void (*closure)(N1, N2, N3), N1 a1, N2 a2, N3 a3) \
+{ \
+ (*closure)(a1, a2, a3); \
+}
+
+
+#define C3(J, N) \
+ C3_(B, J, B, char, N, char) \
+ C3_(S, J, S, short, N, short) \
+ C3_(I, J, I, int, N, int) \
+ C3_(L, J, L, long long, N, long long) \
+ C3_(F, J, F, float, N, float) \
+ C3_(D, J, D, double, N, double) \
+
+C3(B, char);
+C3(S, short);
+C3(I, int);
+C3(L, long long);
+C3(F, float);
+C3(D, double);
+C3_(B, S, I, char, short, int);
+C3_(B, S, L, char, short, long long);
+C3_(L, S, B, long long, short, char);
+C3_(L, B, S, long long, char, short);
+
+
diff --git a/libtest/GNUmakefile b/libtest/GNUmakefile
new file mode 100644
index 0000000..e5b4954
--- /dev/null
+++ b/libtest/GNUmakefile
@@ -0,0 +1,142 @@
+# -*- makefile -*-
+BUILD_OS := $(strip $(shell uname -s | tr '[:upper:]' '[:lower:]'))
+OS ?= $(BUILD_OS)
+CPU = $(shell uname -m | sed -e 's/i[345678]86/i386/')
+MODEL = 32 # Default to 32bit compiles
+PLATFORM = $(CPU)-$(OS)
+ifeq ($(OS), sunos)
+ OS = solaris
+endif
+
+# Default value of $OS on Windows is Windows_NT
+ifeq ($(OS), Windows_NT)
+ # that's how we detect x64...
+ ifneq ($(findstring 64, $(BUILD_OS)),)
+ OS = win64
+ else
+ OS = win32
+ endif
+endif
+
+SRC_DIR = libtest
+BUILD_DIR ?= build
+TEST_BUILD_DIR = $(BUILD_DIR)/libtest
+# Set defaults to unix (linux/solaris/bsd)
+PREFIX = lib
+LIBEXT = so
+LIBNAME = $(PREFIX)test.$(LIBEXT)
+
+export MACOSX_DEPLOYMENT_TARGET=10.5
+
+CCACHE := $(strip $(realpath $(shell which ccache 2> /dev/null)))
+
+TEST_SRCS = $(wildcard $(SRC_DIR)/*.c)
+TEST_OBJS := $(patsubst $(SRC_DIR)/%.c, $(TEST_BUILD_DIR)/%.o, $(TEST_SRCS))
+
+#
+# Compiler/linker flags from:
+# http://weblogs.java.net/blog/kellyohair/archive/2006/01/compilation_of_1.html
+JFLAGS = -fno-omit-frame-pointer -fno-strict-aliasing
+OFLAGS = -O2 $(JFLAGS)
+WFLAGS = -W -Wall -Wno-unused -Wno-parentheses
+PICFLAGS = -fPIC
+SOFLAGS = -shared -Wl,-O1
+LDFLAGS += $(SOFLAGS)
+
+IFLAGS = -I"$(BUILD_DIR)"
+CFLAGS = $(OFLAGS) $(WFLAGS) $(IFLAGS) $(PICFLAGS) -D_REENTRANT
+
+ifeq ($(OS), win32)
+ CC += -mno-cygwin
+ LDFLAGS += -mno-cygwin -Wl,--add-stdcall-alias
+ PREFIX =
+ LIBEXT = dll
+ PICFLAGS =
+endif
+
+ifeq ($(OS),win64)
+ CC += -mno-cygwin
+ LDFLAGS += -mno-cygwin -Wl,--add-stdcall-alias
+ PREFIX =
+ LIBEXT = dll
+ PICFLAGS =
+endif
+
+ifeq ($(OS), darwin)
+ CC = gcc
+ ifneq ($(findstring $(CPU), i386 x86_64),)
+ ARCHFLAGS = -arch i386 -arch x86_64
+ endif
+ ifneq ($(findstring $(CPU), ppc powerpc),)
+ ARCHFLAGS = -arch ppc
+ endif
+
+ CFLAGS += -fno-common $(ARCHFLAGS)
+ LDFLAGS = $(ARCHFLAGS) -dynamiclib
+ LIBEXT = dylib
+ PICFLAGS =
+ SOFLAGS =
+endif
+
+ifeq ($(OS), linux)
+ SOFLAGS += -Wl,-soname,$(LIBNAME)
+endif
+
+ifeq ($(OS), solaris)
+ CC = /usr/sfw/bin/gcc -std=c99
+ LD = /usr/ccs/bin/ld
+ SOFLAGS = -shared -static-libgcc
+endif
+
+ifeq ($(OS), aix)
+ LIBEXT = a
+ SOFLAGS = -shared -static-libgcc
+ PICFLAGS += -pthread
+endif
+
+ifneq ($(findstring cygwin, $(OS)),)
+ CFLAGS += -mno-cygwin -mwin32
+ LIBEXT = dll
+# PREFIX =
+ PICFLAGS=
+endif
+ifneq ($(findstring mingw, $(OS)),)
+ LIBEXT = dll
+ PICFLAGS=
+endif
+ifeq ($(CPU), sparcv9)
+ MODEL = 64
+endif
+
+ifeq ($(CPU), amd64)
+ MODEL = 64
+endif
+
+ifeq ($(CPU), x86_64)
+ MODEL = 64
+endif
+
+# On platforms (linux, solaris) that support both 32bit and 64bit, force building for one or the other
+ifneq ($(strip $(findstring $(OS), solaris)),)
+ # Change the CC/LD instead of CFLAGS/LDFLAGS, incase other things in the flags
+ # makes the libffi build choke
+ CC += -m$(MODEL)
+ LD += -m$(MODEL)
+endif
+
+LIBTEST = $(BUILD_DIR)/$(LIBNAME)
+
+all: $(LIBTEST)
+
+$(TEST_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
+ @mkdir -p $(@D)
+ $(CCACHE) $(CC) $(CFLAGS) -c $< -o $@
+
+$(LIBTEST): $(TEST_OBJS)
+ $(CC) -o $@ $(LDFLAGS) $(TEST_OBJS) -lm
+
+clean::
+ # nothing to do - ant will delete the build dir
+
+debug::
+ @echo "SRCS=$(TEST_SRCS)"
diff --git a/libtest/GlobalVariable.c b/libtest/GlobalVariable.c
new file mode 100644
index 0000000..a3c9fd5
--- /dev/null
+++ b/libtest/GlobalVariable.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009 Wayne Meissner. All rights reserved.
+ *
+ * All rights reserved.
+ *
+ * This file is part of jnr-ffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+
+typedef int8_t s8;
+typedef uint8_t u8;
+typedef int16_t s16;
+typedef uint16_t u16;
+typedef int32_t s32;
+typedef uint32_t u32;
+typedef int64_t s64;
+typedef uint64_t u64;
+typedef signed long sL;
+typedef unsigned long uL;
+typedef float f32;
+typedef double f64;
+#if !defined(__OpenBSD__)
+typedef unsigned long ulong;
+#endif
+typedef void* pointer;
+typedef void* P;
+
+#define GVAR(T) \
+ extern T gvar_##T; \
+ T gvar_##T = (T) -1; \
+ T gvar_##T##_get() { return gvar_##T; }; \
+ void gvar_##T##_set(T v) { gvar_##T = v; }
+
+GVAR(s8);
+GVAR(u8);
+GVAR(s16);
+GVAR(u16);
+GVAR(s32);
+GVAR(u32);
+GVAR(s64);
+GVAR(u64);
+GVAR(long);
+GVAR(ulong);
+GVAR(pointer);
+
+struct gstruct {
+ long data;
+};
+
+struct gstruct gvar_gstruct = { -1 };
+
+struct gstruct*
+gvar_gstruct_get(void)
+{
+ return &gvar_gstruct;
+}
+
+void
+gvar_gstruct_set(const struct gstruct* val)
+{
+ gvar_gstruct = *val;
+}
diff --git a/libtest/LastErrorTest.c b/libtest/LastErrorTest.c
new file mode 100644
index 0000000..146e605
--- /dev/null
+++ b/libtest/LastErrorTest.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2008 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <errno.h>
+
+int setLastError(int error) {
+ errno = error;
+ return -1;
+}
diff --git a/libtest/NumberTest.c b/libtest/NumberTest.c
new file mode 100644
index 0000000..b3f4a8b
--- /dev/null
+++ b/libtest/NumberTest.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#ifndef __mips__
+# include <stdint.h>
+#endif
+
+#ifndef _STDINT_H
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+#endif
+typedef char Signed8;
+typedef short Signed16;
+typedef int Signed32;
+typedef long long Signed64;
+typedef float Float32;
+typedef double Float64;
+typedef long SignedLong;
+typedef void* pointer;
+typedef unsigned long ulong;
+
+#define ADD(T) T add_##T(T arg1, T arg2) { return arg1 + arg2; }
+#define SUB(T) T sub_##T(T arg1, T arg2) { return arg1 - arg2; }
+#define MUL(T) T mul_##T(T arg1, T arg2) { return arg1 * arg2; }
+#define DIV(T) T div_##T(T arg1, T arg2) { return arg1 / arg2; }
+#define RET(T) T ret_##T(T arg1) { return arg1; }
+typedef char* ptr;
+#define TEST(T) ADD(T) SUB(T) MUL(T) DIV(T) RET(T)
+TEST(int8_t);
+TEST(int16_t);
+TEST(int32_t);
+TEST(int64_t);
+TEST(uint8_t);
+TEST(uint16_t);
+TEST(uint32_t);
+TEST(uint64_t);
+TEST(float);
+TEST(double);
+TEST(long);
+TEST(ulong);
+TEST(Signed8);
+TEST(Signed16);
+TEST(Signed32);
+TEST(Signed64);
+TEST(SignedLong);
+TEST(Float32);
+TEST(Float64);
+RET(pointer);
diff --git a/libtest/PointerTest.c b/libtest/PointerTest.c
new file mode 100644
index 0000000..5c7e28c
--- /dev/null
+++ b/libtest/PointerTest.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifndef __mips__
+# include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+typedef void* ptr;
+typedef void* pointer;
+#ifdef _WIN32
+typedef char* caddr_t;
+#endif
+#ifndef _STDINT_H
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+#endif
+#define RET(T) T ptr_ret_##T(void* arg1, int offset) { \
+ T tmp; memcpy(&tmp, (caddr_t) arg1 + offset, sizeof(tmp)); return tmp; \
+}
+#define SET(T) void ptr_set_##T(void* arg1, int offset, T value) { \
+ memcpy((caddr_t) arg1 + offset, &value, sizeof(value)); \
+}
+#define TEST(T) SET(T) RET(T)
+
+TEST(int8_t);
+TEST(int16_t);
+TEST(int32_t);
+TEST(int64_t);
+TEST(float);
+TEST(double);
+TEST(pointer);
+
+void*
+ptr_return_array_element(void **ptrArray, int arrayIndex)
+{
+ return ptrArray[arrayIndex];
+}
+
+void
+ptr_set_array_element(void **ptrArray, int arrayIndex, void *value)
+{
+ ptrArray[arrayIndex] = value;
+}
+
+void*
+ptr_from_buffer(void* ptr)
+{
+ return ptr;
+}
+void*
+ptr_malloc(int size)
+{
+ void* ptr = malloc(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+void
+ptr_free(void* ptr)
+{
+ free(ptr);
+}
+
+#define swap(p1, p2) do { typeof(*p1) tmp__ = *p1; *p1 = *p2; *p2 = tmp__; } while (0)
+void
+ptr_reverse_l6(long* l1, long* l2, long* l3, long* l4, long* l5, long* l6)
+{
+ swap(l1, l6);
+ swap(l2, l5);
+ swap(l3, l4);
+}
+
+void
+ptr_reverse_l5(long* l1, long* l2, long* l3, long* l4, long* l5)
+{
+ swap(l1, l5);
+ swap(l2, l4);
+}
+
+
+void
+ptr_rotate_l5(long* l1, long* l2, long* l3, long* l4, long* l5)
+{
+ long tmp = *l1;
+ swap(l5, l1);
+ swap(l4, l5);
+ swap(l3, l4);
+ swap(l2, l3);
+ *l2 = tmp;
+}
+
+void
+ptr_rotate_l6(long* l1, long* l2, long* l3, long* l4, long* l5, long* l6)
+{
+ long t = *l1;
+ swap(l6, l1);
+ swap(l5, l6);
+ swap(l4, l5);
+ swap(l3, l4);
+ swap(l2, l3);
+ *l2 = t;
+}
diff --git a/libtest/ReferenceTest.c b/libtest/ReferenceTest.c
new file mode 100644
index 0000000..ae34856
--- /dev/null
+++ b/libtest/ReferenceTest.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __mips__
+# include <stdint.h>
+#endif
+
+#ifndef _STDINT_H
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+#endif
+
+#define REF(T) void ref_##T(T arg, T* result) { *result = arg; }
+#define ADD(T) void ref_add_##T(T arg1, T arg2, T* result) { *result = arg1 + arg2; }
+#define SUB(T) void ref_sub_##T(T arg1, T arg2, T* result) { *result = arg1 - arg2; }
+#define MUL(T) void ref_mul_##T(T arg1, T arg2, T* result) { *result = arg1 * arg2; }
+#define DIV(T) void ref_div_##T(T arg1, T arg2, T* result) { *result = arg1 / arg2; }
+#define TEST(T) ADD(T) SUB(T) MUL(T) DIV(T) REF(T)
+
+TEST(int8_t);
+TEST(int16_t);
+TEST(int32_t);
+TEST(int64_t);
+TEST(float);
+TEST(double);
+
diff --git a/libtest/StringTest.c b/libtest/StringTest.c
new file mode 100644
index 0000000..bd083e6
--- /dev/null
+++ b/libtest/StringTest.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+int
+string_equals(const char* s1, const char* s2)
+{
+ return strcmp(s1, s2) == 0;
+}
+
+void
+string_set(char* s1, const char* s2)
+{
+ strcpy(s1, s2);
+}
+
+void
+string_concat(char* dst, const char* src)
+{
+ char* ep = dst;
+ while (*ep)
+ ep++;
+ strcpy(ep, src);
+}
diff --git a/libtest/StructTest.c b/libtest/StructTest.c
new file mode 100644
index 0000000..62cd9bb
--- /dev/null
+++ b/libtest/StructTest.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007 Wayne Meissner. All rights reserved.
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+typedef char Signed8;
+typedef short Signed16;
+typedef int Signed32;
+typedef long long Signed64;
+typedef float Float32;
+typedef double Float64;
+typedef long SignedLong;
+
+struct test1 {
+ char b;
+ short s;
+ int i;
+ long long j;
+ SignedLong l;
+ float f;
+ double d;
+ char string[32];
+};
+
+#define T(x, type) \
+ type struct_field_##type(struct test1* t) { return t->x; } \
+ struct type##Align { char first; type value; }; \
+ type struct_align_##type(struct type##Align* a) { return a->value; }
+
+T(b, Signed8);
+T(s, Signed16);
+T(i, Signed32);
+T(j, Signed64);
+T(f, Float32);
+T(d, Float64);
+
+long struct_field_SignedLong(struct test1* t) { return t->l; }
+struct SignedLongAlign { char first; SignedLong value; };
+long struct_align_SignedLong(struct SignedLongAlign* a) { return a->value; }
+
+void
+struct_set_string(struct test1* t, char* s)
+{
+ strcpy(t->string, s);
+}
+
+struct test1*
+struct_make_struct(char b, short s, int i, long long ll, float f, double d)
+{
+ static struct test1 t;
+ memset(&t, 0, sizeof(t));
+ t.b = b;
+ t.s = s;
+ t.i = i;
+ t.j = ll;
+ t.f = f;
+ t.d = d;
+ return &t;
+}
+
+struct foo {
+ unsigned long l1,l2, l3;
+};
+
+int
+fill_struct_from_longs(unsigned long l1, unsigned long l2, struct foo* s, unsigned long l3)
+{
+ s->l1 = l1;
+ s->l2 = l2;
+ s->l3 = l3;
+
+ return 0;
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..687b1a8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,234 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>7</version>
+ </parent>
+
+ <groupId>com.github.jnr</groupId>
+ <artifactId>jnr-ffi</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0.10</version>
+ <name>jnr-ffi</name>
+ <description>A library for invoking native functions from java</description>
+ <url>http://github.com/jnr/jnr-ffi</url>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <scm>
+ <connection>scm:git:git at github.com:jnr/jnr-ffi.git</connection>
+ <developerConnection>scm:git:git at github.com:jnr/jnr-ffi.git</developerConnection>
+ <url>git at github.com:jnr/jnr-ffi.git</url>
+ <tag>1.0.10</tag>
+ </scm>
+
+ <developers>
+ <developer>
+ <id>wmeissner</id>
+ <name>Wayne Meissner</name>
+ <email>wmeissner at gmail.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.test.failure.ignore>true</maven.test.failure.ignore>
+ <maven.compiler.source>1.6</maven.compiler.source>
+ <maven.compiler.target>1.6</maven.compiler.target>
+ <github.global.server>github</github.global.server>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.jnr</groupId>
+ <artifactId>jffi</artifactId>
+ <version>1.2.7</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.jnr</groupId>
+ <artifactId>jffi</artifactId>
+ <version>1.2.7</version>
+ <scope>runtime</scope>
+ <classifier>native</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>4.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-commons</artifactId>
+ <version>4.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-analysis</artifactId>
+ <version>4.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-tree</artifactId>
+ <version>4.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-util</artifactId>
+ <version>4.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.jnr</groupId>
+ <artifactId>jnr-x86asm</artifactId>
+ <version>1.0.2</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+ <execution>
+ <phase>test-compile</phase>
+ <configuration>
+ <tasks>
+ <exec dir="${basedir}" executable="make" failonerror="true">
+ <arg line="-f libtest/GNUmakefile" />
+ <arg line="BUILD_DIR=${project.build.directory}" />
+ <arg line="CPU=${os.arch}" />
+ </exec>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.4.2</version>
+ <configuration>
+ <systemProperties>
+ <property>
+ <name>jnr.ffi.library.path</name>
+ <value>${project.build.directory}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.4.1</version>
+ <configuration>
+ <tagNameFormat>@{project.version}</tagNameFormat>
+ <arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments>
+ <!--<goals>deploy</goals>-->
+ <!--<pushChanges>false</pushChanges>-->
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.github.github</groupId>
+ <artifactId>site-maven-plugin</artifactId>
+ <version>0.6</version>
+ <configuration>
+ <message>Creating site for ${project.version}</message>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>site</goal>
+ </goals>
+ <phase>site</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9.1</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!--
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.4.4</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>sonatype-nexus-staging</serverId>
+ <nexusUrl>https://oss.sonatype.org</nexusUrl>
+ </configuration>
+ </plugin>
+ -->
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.8</version>
+ <configuration>
+ <excludePackageNames>jnr.ffi.provider:jnr.ffi.util</excludePackageNames>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
diff --git a/src/main/java/jnr/ffi/Address.java b/src/main/java/jnr/ffi/Address.java
new file mode 100644
index 0000000..4fc2db0
--- /dev/null
+++ b/src/main/java/jnr/ffi/Address.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * The {@code Address} class wraps a native address in an object. Both 32 bit and 64
+ * bit native address values are wrapped in a singular {@code Address} type.
+ *
+ * <p>This class extends {@link java.lang.Number} and implements all {@code Number} methods for
+ * converting to primitive integer types.
+ *
+ * <p>An {@code Address} instance is lighter weight than most {@link Pointer}
+ * instances, and may be used when a native address needs to be stored in java,
+ * but no other operations (such as reading/writing values) need be performed on
+ * the native memory. For most cases, a {@link Pointer} offers more flexibility
+ * and should be preferred instead.
+ */
+public final class Address extends Number implements Comparable<Address> {
+ /** A global instance of this class representing the C NULL value */
+ private static final Address NULL = new Address(0L);
+
+ /** The native numeric value of this {@code Address} */
+ private final long address;
+
+
+ /**
+ * Creates a new address representation.
+ *
+ * @param address the native address.
+ */
+ private Address(long address) {
+ this.address = address;
+ }
+
+ /**
+ * Creates a new address representation.
+ *
+ * @param address the native address.
+ */
+ public Address(final Address address) {
+ this.address = address.address;
+ }
+
+ /**
+ * Gets the native memory address represented by this {@code Address} as a {@code long} integer.
+ *
+ * @return the native memory address
+ */
+ public final long address() {
+ return address;
+ }
+
+ /**
+ * Returns the value of this {@code Address} as an {@code int}.
+ * On 64bit systems, this will result in the upper 32bits of the address being truncated.
+ *
+ * @return the numeric value of this {@code Address} after conversion to an {@code int}.
+ */
+ @Override
+ public final int intValue() {
+ return (int) address;
+ }
+
+ /**
+ * Returns the value of this {@code Address} as a {@code long}.
+ *
+ * @return the numeric value of this {@code Address} after conversion to a {@code long}.
+ */
+ @Override
+ public final long longValue() {
+ return address;
+ }
+
+ /**
+ * Returns the value of this {@code Address} as a {@code float}.
+ *
+ * This method is not particularly useful, and is here to fulfill the {@link java.lang.Number} interface contract.
+ *
+ * @return the numeric value of this {@code Address} after conversion to a {@code float}.
+ */
+ @Override
+ public final float floatValue() {
+ return (float) address;
+ }
+
+ /**
+ * Returns the value of this {@code Address} as a {@code double}.
+ *
+ * This method is not particularly useful, and is here to fulfill the {@link java.lang.Number} interface contract.
+ *
+ * @return the numeric value of this {@code Address} after conversion to a {@code double}.
+ */
+ @Override
+ public final double doubleValue() {
+ return (double) address;
+ }
+
+ /**
+ * Returns the native value of this address.
+ *
+ * @return an {@code long} value representing the native value of this address.
+ */
+ public final long nativeAddress() {
+ return address;
+ }
+
+ /**
+ * Returns a hash code for this {@code Address}.
+ *
+ * @return a hash code for this {@code Address}.
+ */
+ @Override
+ public final int hashCode() {
+ return (int)(address ^ (address >>> 32));
+ }
+
+ /**
+ * Compares this address to another address.
+ *
+ * @param obj the other address to compare to.
+ * @return {@code true} if this Address is equal to the other address, else false.
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ return ((obj instanceof Address) && address == ((Address) obj).address)
+ || (obj == null && address == 0);
+ }
+
+ /**
+ * Returns a {@code String} object representing this {@code Address} as a decimal value.
+ *
+ * @return a string representation of this {@code Address}.
+ */
+ @Override
+ public final String toString() {
+ return Long.toString(address, 10);
+ }
+
+ /**
+ * Returns a {@code String} object representing this {@code Address} as a hex value.
+ *
+ * @return a string representation of this {@code Address}.
+ */
+ public final String toHexString() {
+ return Long.toString(address, 16);
+ }
+
+ /**
+ * Compares two {@code Address} instances numerically.
+ *
+ * @param other the other Address to compare to.
+ * @return {@code 0} if {@code other} is equal to this instance, -1 if this
+ * instance is numerically less than {@code other} or 1 if this instance is
+ * numerically greater than {@code other}.
+ */
+ public final int compareTo(Address other) {
+ return address < other.address ? -1 : address > other.address ? 1 : 0;
+ }
+
+ /**
+ * Tests if this <tt>Address</tt> is equivalent to C NULL
+ *
+ * @return true if the address is 0
+ */
+ public final boolean isNull() {
+ return address == 0;
+ }
+
+ /**
+ * Returns a Address instance representing the specified {@code long} value.
+ *
+ * @param address a {@code long} value
+ * @return an {@code Address} instance representing {@code address}
+ */
+ public static Address valueOf(long address) {
+ return address == 0 ? NULL : new Address(address);
+ }
+
+ /**
+ * Returns a Address instance representing the specified {@code int} value.
+ *
+ * @param address an {@code int} value
+ * @return an {@code Address} instance representing {@code address}
+ */
+ public static Address valueOf(int address) {
+ return address == 0 ? NULL : new Address((long) address & 0xffffffffL);
+ }
+}
diff --git a/src/main/java/jnr/ffi/CallingConvention.java b/src/main/java/jnr/ffi/CallingConvention.java
new file mode 100644
index 0000000..f45f535
--- /dev/null
+++ b/src/main/java/jnr/ffi/CallingConvention.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * Native function calling conventions.
+ *
+ * <p>This is only needed on windows platforms - all platforms assume
+ * {@link #DEFAULT} as the calling convention.
+ */
+public enum CallingConvention {
+ /**
+ * The default C calling convention
+ */
+ DEFAULT,
+ /**
+ * Windows stdcall calling convention
+ */
+ STDCALL
+}
diff --git a/src/main/java/jnr/ffi/LastError.java b/src/main/java/jnr/ffi/LastError.java
new file mode 100644
index 0000000..25f8a78
--- /dev/null
+++ b/src/main/java/jnr/ffi/LastError.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * Provides access to the unix errno and windows GetLastError() value.
+ */
+public final class LastError {
+ private LastError() {}
+
+ /**
+ * Gets the value of errno from the last native call.
+ *
+ * @param runtime FFI runtime to get errno for.
+ * @return An integer containing the errno value.
+ */
+ public static int getLastError(Runtime runtime) {
+ return runtime.getLastError();
+ }
+
+ /**
+ * Sets the native errno value.
+ *
+ * @param runtime FFI runtime to set errno for.
+ * @param error The value to set errno to.
+ */
+ public static void setLastError(Runtime runtime, int error) {
+ runtime.setLastError(error);
+ }
+}
diff --git a/src/main/java/jnr/ffi/Library.java b/src/main/java/jnr/ffi/Library.java
new file mode 100644
index 0000000..0181473
--- /dev/null
+++ b/src/main/java/jnr/ffi/Library.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.provider.FFIProvider;
+import jnr.ffi.provider.LoadedLibrary;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * @deprecated Use {@link LibraryLoader} instead.
+ */
+public final class Library {
+ private static final Map<String, List<String>> customSearchPaths
+ = new ConcurrentHashMap<String, List<String>>();
+
+ /** The name of this library */
+ private final String name;
+
+ private Library(String libraryName) {
+ name = libraryName;
+ }
+
+ /**
+ * Gets the {@link Runtime} that loaded the library interface.
+ *
+ * @deprecated Use {@link Runtime#getRuntime(Object)}
+ * @param library A library implementation as returned from {@link LibraryLoader#load()}
+ * @return The runtime that loaded the library.
+ */
+ public static Runtime getRuntime(Object library) {
+ return ((LoadedLibrary) library).getRuntime();
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryName the name of the library to load
+ * @param interfaceClass the interface that describes the native library interface
+ * @return an instance of {@code interfaceclass} that will call the native methods.
+ */
+ public static <T> T loadLibrary(String libraryName, Class<T> interfaceClass) {
+ return loadLibrary(interfaceClass, libraryName);
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryNames the name of the library to load
+ * @param interfaceClass the interface that describes the native library interface
+ * @return an instance of {@code interfaceclass} that will call the native methods.
+ */
+ public static <T> T loadLibrary(Class<T> interfaceClass, String... libraryNames) {
+ final Map<LibraryOption, ?> options = Collections.emptyMap();
+ return loadLibrary(interfaceClass, options, libraryNames);
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryName the name of the library to load
+ * @param interfaceClass the interface that describes the native library interface
+ * @param libraryOptions options
+ * @return an instance of {@code interfaceclass} that will call the native methods.
+ */
+ public static <T> T loadLibrary(String libraryName, Class<T> interfaceClass,
+ Map<LibraryOption, ?> libraryOptions) {
+ return loadLibrary(interfaceClass, libraryOptions, libraryName);
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryNames the name of the library to load
+ * @param interfaceClass the interface that describes the native library interface
+ * @param libraryOptions options
+ * @return an instance of {@code interfaceclass} that will call the native methods.
+ */
+ public static <T> T loadLibrary(Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions,
+ String... libraryNames) {
+ LibraryLoader<T> loader = FFIProvider.getSystemProvider().createLibraryLoader(interfaceClass);
+
+ for (String libraryName : libraryNames) {
+ loader.library(libraryName);
+ for (String path : getLibraryPath(libraryName)) {
+ loader.search(path);
+ }
+ }
+
+ for (Map.Entry<LibraryOption, ?> option : libraryOptions.entrySet()) {
+ loader.option(option.getKey(), option.getValue());
+ }
+
+ return loader.failImmediately().load();
+ }
+
+ /**
+ * Adds a custom search path for a library
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryName the name of the library to search for
+ * @param path the path to search for the library in
+ */
+ public static synchronized void addLibraryPath(String libraryName, File path) {
+ List<String> customPaths = customSearchPaths.get(libraryName);
+ if (customPaths == null) {
+ customPaths = new CopyOnWriteArrayList<String>();
+ customSearchPaths.put(libraryName, customPaths);
+ }
+ customPaths.add(path.getAbsolutePath());
+ }
+
+ /**
+ * Gets the custom search path for a library.
+ *
+ * @deprecated see {@link LibraryLoader} for the preferred interface to loading libraries.
+ * @param libraryName The library to retrieve the path for.
+ * @return A <tt>List</tt> of <tt>String</tt> instances.
+ */
+ public static List<String> getLibraryPath(String libraryName) {
+ List<String> customPaths = customSearchPaths.get(libraryName);
+ if (customPaths != null) {
+ return customPaths;
+ }
+ return Collections.emptyList();
+ }
+
+ @Deprecated
+ public static Library getInstance(String libraryName) {
+ return new Library(libraryName);
+ }
+
+ @Deprecated
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/jnr/ffi/LibraryLoader.java b/src/main/java/jnr/ffi/LibraryLoader.java
new file mode 100644
index 0000000..1e8c9e9
--- /dev/null
+++ b/src/main/java/jnr/ffi/LibraryLoader.java
@@ -0,0 +1,336 @@
+package jnr.ffi;
+
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.FFIProvider;
+import jnr.ffi.provider.LoadedLibrary;
+import jnr.ffi.provider.NativeInvocationHandler;
+
+import java.io.File;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.*;
+
+/**
+ * Loads a native library and maps it to a java interface.
+ *
+ * <p><strong>Example usage</strong></p>
+ * <pre>
+ * {@code
+ *
+ * public interface LibC {
+ * int puts(String str);
+ * }
+ *
+ * LibC libc = LibraryLoader.create(LibC.class).load("c");
+ *
+ * libc.puts("Hello, World");
+ *
+ * }
+ * </pre></p>
+ */
+public abstract class LibraryLoader<T> {
+ private final List<String> searchPaths = new ArrayList<String>();
+ private final List<String> libraryNames = new ArrayList<String>();
+ private final List<SignatureTypeMapper> typeMappers = new ArrayList<SignatureTypeMapper>();
+ private final List<FunctionMapper> functionMappers = new ArrayList<FunctionMapper>();
+ private final Map<LibraryOption, Object> optionMap = new EnumMap<LibraryOption, Object>(LibraryOption.class);
+ private final TypeMapper.Builder typeMapperBuilder = new TypeMapper.Builder();
+ private final FunctionMapper.Builder functionMapperBuilder = new FunctionMapper.Builder();
+ private final Class<T> interfaceClass;
+ private boolean failImmediately = false;
+
+
+ /**
+ * Creates a new {@code LibraryLoader} instance.
+ *
+ * @param interfaceClass the interface that describes the native library functions
+ * @return A {@code LibraryLoader} instance.
+ */
+ public static <T> LibraryLoader<T> create(Class<T> interfaceClass) {
+ return FFIProvider.getSystemProvider().createLibraryLoader(interfaceClass);
+ }
+
+ protected LibraryLoader(Class<T> interfaceClass) {
+ this.interfaceClass = interfaceClass;
+ }
+
+ /**
+ * Adds a library to be loaded. Multiple libraries can be specified using additional calls
+ * to this method, and all libraries will be searched to resolve symbols (e.g. functions, variables).
+ *
+ * @param libraryName The name or path of library to load.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> library(String libraryName) {
+ this.libraryNames.add(libraryName);
+ return this;
+ }
+
+ /**
+ * Adds a path to search for libraries. Multiple paths can be specified using multiple calls
+ * to this method, and all paths will be searched..
+ *
+ * @param path A directory to search.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> search(String path) {
+ searchPaths.add(path);
+ return this;
+ }
+
+ /**
+ * Sets an option when loading libraries.
+ *
+ * @see LibraryOption
+ *
+ * @param option The option to set
+ * @param value The value for the option.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> option(LibraryOption option, Object value) {
+ switch (option) {
+ case TypeMapper:
+ if (value instanceof SignatureTypeMapper) {
+ mapper((SignatureTypeMapper) value);
+
+ } else if (value instanceof TypeMapper) {
+ mapper((TypeMapper) value);
+
+ } else if (value != null) {
+ throw new IllegalArgumentException("invalid TypeMapper: " + value.getClass());
+ }
+ break;
+
+ case FunctionMapper:
+ mapper((FunctionMapper) value);
+ break;
+
+ default:
+ optionMap.put(option, value);
+ }
+
+ return this;
+ }
+
+
+ /**
+ * Adds a type mapper to use when resolving method parameter and result types.
+ *
+ * Multiple type mappers can be specified by additional calls to this method, and
+ * each mapper will be tried in order until one is successful.
+ *
+ * @param typeMapper The type mapper to use.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> mapper(TypeMapper typeMapper) {
+ typeMappers.add(new SignatureTypeMapperAdapter(typeMapper));
+ return this;
+ }
+
+ /**
+ * Adds a type mapper to use when resolving method parameter and result types.
+ *
+ * Multiple type mappers can be specified by additional calls to this method, and
+ * each mapper will be tried in order until one is successful.
+ *
+ * @param typeMapper The type mapper to use.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> mapper(SignatureTypeMapper typeMapper) {
+ typeMappers.add(typeMapper);
+ return this;
+ }
+
+ /**
+ * Adds a custom java type mapping.
+ *
+ * @param javaType The java type to convert to a native type.
+ * @param toNativeConverter A {@link jnr.ffi.mapper.ToNativeConverter} that will convert from the java type to a native type.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public <J> LibraryLoader<T> map(Class<? extends J> javaType, ToNativeConverter<? extends J, ?> toNativeConverter) {
+ typeMapperBuilder.map(javaType, toNativeConverter);
+ return this;
+ }
+
+ /**
+ * Adds a custom java type mapping.
+ *
+ * @param javaType The java type to convert to a native type.
+ * @param fromNativeConverter A {@link jnr.ffi.mapper.ToNativeConverter} that will convert from the native type to a java type.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public <J> LibraryLoader<T> map(Class<? extends J> javaType, FromNativeConverter<? extends J, ?> fromNativeConverter) {
+ typeMapperBuilder.map(javaType, fromNativeConverter);
+ return this;
+ }
+
+ public <J> LibraryLoader<T> map(Class<? extends J> javaType, DataConverter<? extends J, ?> dataConverter) {
+ typeMapperBuilder.map(javaType, dataConverter);
+ return this;
+ }
+
+ /**
+ * Adds a function mapper to use when resolving symbols in this library.
+ *
+ * Multiple function mappers can be specified by additional calls to this method, and
+ * each mapper will be tried in order, until one is successful.
+ *
+ * @param typeMapper The function mapper to use.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> mapper(FunctionMapper typeMapper) {
+ optionMap.put(LibraryOption.FunctionMapper, typeMapper);
+ return this;
+ }
+
+ /**
+ * Adds a function name mapping to use when resolving symbols in this library.
+ *
+ * @param javaName The java method name.
+ * @param nativeFunction The native library symbol to map the java method name to.
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> map(String javaName, String nativeFunction) {
+ functionMapperBuilder.map(javaName, nativeFunction);
+ return this;
+ }
+
+ /**
+ * Sets the native function calling convention.
+ *
+ * <p>This is only needed on windows platforms - unless explicitly specified, all platforms assume
+ * {@link CallingConvention#DEFAULT} as the calling convention.
+ *
+ * @return The {@code LibraryLoader} instance.
+ */
+ public LibraryLoader<T> convention(CallingConvention convention) {
+ optionMap.put(LibraryOption.CallingConvention, convention);
+ return this;
+ }
+
+ /**
+ * Sets the calling convention of the library to the Windows stdcall calling convention
+ *
+ * @return This {@code LibraryLoader} instance.
+ */
+ public final LibraryLoader<T> stdcall() {
+ return convention(CallingConvention.STDCALL);
+ }
+
+ /**
+ * Turns off lazy propagation of load failures. By default, {@link jnr.ffi.LibraryLoader#load()} will not fail
+ * immediately if any libraries cannot be loaded - instead, it will create an instance of the library interface
+ * that re-throws any load errors when invoked.
+ *
+ * Calling this method will make {@link jnr.ffi.LibraryLoader#load()} throw errors immediately.
+ *
+ * @return This {@code LibraryLoader} instance.
+ */
+ public final LibraryLoader<T> failImmediately() {
+ failImmediately = true;
+ return this;
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @param libraryName The name or path of library to load.
+ * @return an implementation of the interface provided to {@link #create(Class)} that will call the native methods.
+ */
+ public T load(String libraryName) {
+ return library(libraryName).load();
+ }
+
+ /**
+ * Loads a native library and links the methods defined in {@code interfaceClass}
+ * to native methods in the library.
+ *
+ * @return an implementation of the interface provided to {@link #create(Class)} that will call the native methods.
+ */
+ public T load() {
+ if (libraryNames.isEmpty()) {
+ throw new UnsatisfiedLinkError("no library names specified");
+ }
+
+ typeMappers.add(0, new SignatureTypeMapperAdapter(typeMapperBuilder.build()));
+ optionMap.put(LibraryOption.TypeMapper, typeMappers.size() > 1 ? new CompositeTypeMapper(typeMappers) : typeMappers.get(0));
+
+ functionMappers.add(0, functionMapperBuilder.build());
+ optionMap.put(LibraryOption.FunctionMapper, functionMappers.size() > 1 ? new CompositeFunctionMapper(functionMappers) : functionMappers.get(0));
+
+ try {
+ return loadLibrary(interfaceClass, Collections.unmodifiableList(libraryNames), getSearchPaths(),
+ Collections.unmodifiableMap(optionMap));
+
+ } catch (LinkageError error) {
+ if (failImmediately) throw error;
+ return createErrorProxy(error);
+
+ } catch (Exception ex) {
+ RuntimeException re = ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex);
+ if (failImmediately) throw re;
+
+ return createErrorProxy(re);
+ }
+ }
+
+ private T createErrorProxy(final Throwable ex) {
+ return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(),
+ new Class[] { interfaceClass, LoadedLibrary.class },
+ new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ throw ex;
+ }
+ })
+ );
+ }
+
+ private Collection<String> getSearchPaths() {
+ List<String> paths = new ArrayList<String>(searchPaths);
+ paths.addAll(StaticDataHolder.USER_LIBRARY_PATH);
+
+ return Collections.unmodifiableList(paths);
+ }
+
+ /**
+ * Implemented by FFI providers to load the actual library.
+ *
+ * @param interfaceClass The java class that describes the functions to be mapped.
+ * @param libraryNames A list of libraries to load & search for symbols
+ * @param searchPaths The paths to search for libraries to be loaded
+ * @param options The options to apply when loading the library
+ * @return an instance of {@code interfaceClass} that will call the native methods.
+ */
+ protected abstract T loadLibrary(Class<T> interfaceClass, Collection<String> libraryNames,
+ Collection<String> searchPaths, Map<LibraryOption, Object> options);
+
+
+ private static final class StaticDataHolder {
+ private static final List<String> USER_LIBRARY_PATH;
+ static {
+ List<String> paths = new ArrayList<String>();
+ try {
+ paths.addAll(getPropertyPaths("jnr.ffi.library.path"));
+ paths.addAll(getPropertyPaths("jaffl.library.path"));
+ // Add JNA paths for compatibility
+ paths.addAll(getPropertyPaths("jna.library.path"));
+ paths.addAll(getPropertyPaths("java.library.path"));
+ } catch (Exception ignored) {}
+ USER_LIBRARY_PATH = Collections.unmodifiableList(new ArrayList<String>(paths));
+ }
+ }
+
+
+ private static List<String> getPropertyPaths(String propName) {
+ String value = System.getProperty(propName);
+ if (value != null) {
+ String[] paths = value.split(File.pathSeparator);
+ return new ArrayList<String>(Arrays.asList(paths));
+ }
+ return Collections.emptyList();
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/LibraryOption.java b/src/main/java/jnr/ffi/LibraryOption.java
new file mode 100644
index 0000000..cf4054d
--- /dev/null
+++ b/src/main/java/jnr/ffi/LibraryOption.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * Options that apply to a library
+ */
+public enum LibraryOption {
+ /**
+ * Function calls should save the errno/last error after the call.
+ * This option can be overridden on individual methods by use of the
+ * {@link jnr.ffi.annotations.IgnoreError} annotation.
+ */
+ SaveError,
+ /**
+ * Function calls should NOT save the errno/last error after the call.
+ * This option can be overridden on individual methods by use of the
+ * {@link jnr.ffi.annotations.SaveError} annotation.
+ */
+ IgnoreError,
+
+ /**
+ * A type mapper which maps java types to native types is present.
+ */
+ TypeMapper,
+
+ /**
+ * A function mapper which maps from java function names to native function names.
+ */
+ FunctionMapper,
+
+ /**
+ * The type of calling convention.
+ * @see CallingConvention
+ */
+ CallingConvention,
+
+ /**
+ * Load the library into memory immediately, instead of lazily loading it
+ */
+ LoadNow
+}
diff --git a/src/main/java/jnr/ffi/Memory.java b/src/main/java/jnr/ffi/Memory.java
new file mode 100644
index 0000000..d8cab4c
--- /dev/null
+++ b/src/main/java/jnr/ffi/Memory.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * A utility for allocating memory that can be passed to native functions.
+ */
+public final class Memory {
+
+ private Memory() {
+ }
+
+ /**
+ * Allocates a new block of java memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param size The size in bytes of memory to allocate.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocate(Runtime runtime, int size) {
+ return runtime.getMemoryManager().allocate(size);
+ }
+
+ /**
+ * Allocates a new block of java memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param type The native type to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocate(Runtime runtime, NativeType type) {
+ return runtime.getMemoryManager().allocate(runtime.findType(type).size());
+ }
+
+ /**
+ * Allocates a new block of java memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param type The type to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocate(Runtime runtime, Type type) {
+ return runtime.getMemoryManager().allocate(type.size());
+ }
+
+ /**
+ * Allocates a new block of java memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param type The type alias to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocate(Runtime runtime, TypeAlias type) {
+ return runtime.getMemoryManager().allocate(runtime.findType(type).size());
+ }
+
+ /**
+ * Allocates a new block of native memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param size The size in bytes of memory to allocate.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateDirect(Runtime runtime, int size) {
+ return runtime.getMemoryManager().allocateDirect(size);
+ }
+
+ /**
+ * Allocates a new block of native memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param type The native type to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateDirect(Runtime runtime, NativeType type) {
+ return runtime.getMemoryManager().allocateDirect(runtime.findType(type).size());
+ }
+
+ /**
+ * Allocates a new block of native memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param type The type alias to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateDirect(Runtime runtime, TypeAlias type) {
+ return runtime.getMemoryManager().allocateDirect(runtime.findType(type).size());
+ }
+
+ /**
+ * Allocates a new block of native memory and wraps it in a {@link Pointer}
+ * accessor.
+ *
+ * @param size The size in bytes of memory to allocate.
+ * @param clear Whether the memory contents should be cleared, or left as
+ * random data.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateDirect(Runtime runtime, int size, boolean clear) {
+ return runtime.getMemoryManager().allocateDirect(size, clear);
+ }
+
+ /**
+ * Allocates a new block of transient native memory and wraps it in a {@link Pointer}
+ * accessor. The memory returned by this method should not be passed to native methods
+ * that store the address for later use, as it may change each time it is passed to native code.
+ *
+ * @param type The native type to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateTemporary(Runtime runtime, NativeType type) {
+ return runtime.getMemoryManager().allocateTemporary(runtime.findType(type).size(), true);
+ }
+
+ /**
+ * Allocates a new block of transient native memory and wraps it in a {@link Pointer}
+ * accessor. The memory returned by this method should not be passed to native methods
+ * that store the address for later use, as it may change each time it is passed to native code.
+ *
+ * @param type The type alias to allocate memory for.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateTemporary(Runtime runtime, TypeAlias type) {
+ return runtime.getMemoryManager().allocateTemporary(runtime.findType(type).size(), true);
+ }
+
+ /**
+ * Allocates a new block of transient native memory and wraps it in a {@link Pointer}
+ * accessor. The memory returned by this method should not be passed to native methods
+ * that store the address for later use, as it may change each time it is passed to native code.
+ *
+ * @param type The native type to allocate memory for.
+ * @param clear Whether the memory contents should be cleared, or left as
+ * random data.
+ *
+ * @return a {@code Pointer} instance that can access the memory.
+ */
+ public static Pointer allocateTemporary(Runtime runtime, NativeType type, boolean clear) {
+ return runtime.getMemoryManager().allocateTemporary(runtime.findType(type).size(), clear);
+ }
+}
diff --git a/src/main/java/jnr/ffi/NativeLong.java b/src/main/java/jnr/ffi/NativeLong.java
new file mode 100644
index 0000000..b7f7114
--- /dev/null
+++ b/src/main/java/jnr/ffi/NativeLong.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * Represents a C long.
+ * <p>
+ * In C, a <tt>long</tt> can be either 32 bits or 64bits, depending on the platform.
+ * </p>
+ * Replace any function parameters which are <tt>long</tt> in the C definition with
+ * a NativeLong.
+ * </p>
+ */
+public final class NativeLong extends Number implements Comparable<NativeLong> {
+
+ private static final NativeLong ZERO = new NativeLong(0);
+ private static final NativeLong ONE = new NativeLong(1);
+ private static final NativeLong MINUS_ONE = new NativeLong(-1);
+
+ private final long value;
+
+ /**
+ * Creates a new <tt>NativeLong</tt> instance with the supplied value.
+ *
+ * @param value a long or integer.
+ */
+ public NativeLong(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Creates a new <tt>NativeLong</tt> instance with the supplied value.
+ *
+ * @param value an integer.
+ */
+ public NativeLong(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns an integer representation of this <tt>NativeLong</tt>.
+ *
+ * @return an integer value for this <tt>NativeLong</tt>.
+ */
+ @Override
+ public final int intValue() {
+ return (int) value;
+ }
+
+ /**
+ * Returns an {@code long} representation of this <tt>NativeLong</tt>.
+ *
+ * @return an {@code long} value for this <tt>NativeLong</tt>.
+ */
+ @Override
+ public final long longValue() {
+ return value;
+ }
+
+ /**
+ * Returns an {@code float} representation of this <tt>NativeLong</tt>.
+ *
+ * @return an {@code float} value for this <tt>NativeLong</tt>.
+ */
+ @Override
+ public final float floatValue() {
+ return (float) value;
+ }
+
+ /**
+ * Returns an {@code double} representation of this <tt>NativeLong</tt>.
+ *
+ * @return an {@code double} value for this <tt>NativeLong</tt>.
+ */
+ @Override
+ public final double doubleValue() {
+ return (double) value;
+ }
+
+ /**
+ * Gets a hash code for this {@code NativeLong}.
+ *
+ * @return a hash code for this {@code NativeLong}.
+ */
+ @Override
+ public final int hashCode() {
+ return (int)(value ^ (value >>> 32));
+ }
+
+ /**
+ * Compares this <tt>NativeLong</tt> to another <tt>NativeLong</tt>.
+ *
+ * @param obj the other <tt>NativeLong</tt> to compare to.
+ * @return {@code true} if this <tt>NativeLong</tt> is equal to the other
+ * <tt>NativeLong</tt>, else false.
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ return ((obj instanceof NativeLong) && value == ((NativeLong) obj).value);
+ }
+
+ /**
+ * Returns a string representation of this <tt>NativeLong</tt>.
+ *
+ * @return a string representation of this <tt>NativeLong</tt>.
+ */
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ /**
+ * Compares two {@code NativeLong} instances numerically.
+ *
+ * @param other the other NativeLong to compare to.
+ *
+ * @return {@code 0} if {@code other} is equal to this instance, -1 if this
+ * instance is numerically less than {@code other} or 1 if this instance is
+ * numerically greater than {@code other}.
+ */
+ public final int compareTo(NativeLong other) {
+ return value < other.value ? -1 : value > other.value ? 1 : 0;
+ }
+
+ /**
+ * Internal cache of common native long values
+ */
+ private static final class Cache {
+ private Cache() {}
+
+ static final NativeLong[] cache = new NativeLong[256];
+
+ static {
+ for (int i = 0; i < cache.length; ++i) {
+ cache[i] = new NativeLong(i - 128);
+ }
+ cache[128 + 0] = ZERO;
+ cache[128 + 1] = ONE;
+ cache[128 - 1] = MINUS_ONE;
+ }
+
+ }
+
+ private static NativeLong _valueOf(final long value) {
+ return value >= -128 && value <= 127
+ ? Cache.cache[128 + (int) value] : new NativeLong(value);
+ }
+
+ private static NativeLong _valueOf(final int value) {
+ return value >= -128 && value <= 127
+ ? Cache.cache[128 + value] : new NativeLong(value);
+ }
+
+ /**
+ * Returns a NativeLong instance representing the specified long value
+ *
+ * @param value a long value
+ * @return a <tt>NativeLong</tt> instance representing <tt>value</tt>
+ */
+ public static NativeLong valueOf(final long value) {
+ return value == 0 ? ZERO : value == 1 ? ONE : value == -1 ? MINUS_ONE : _valueOf(value);
+ }
+
+ /**
+ * Returns a NativeLong instance representing the specified int value
+ *
+ * @param value a 32bit integer value
+ * @return a <tt>NativeLong</tt> instance representing <tt>value</tt>
+ */
+ public static NativeLong valueOf(final int value) {
+ return value == 0 ? ZERO : value == 1 ? ONE : value == -1 ? MINUS_ONE : _valueOf(value);
+ }
+}
diff --git a/src/main/java/jnr/ffi/NativeType.java b/src/main/java/jnr/ffi/NativeType.java
new file mode 100644
index 0000000..c954139
--- /dev/null
+++ b/src/main/java/jnr/ffi/NativeType.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * NativeType defines the primitive types supported internally.
+ *
+ * Usually you will not use these types directly, and should instead use the standard
+ * types such as {@link Pointer}, {@link NativeLong}, or any of the normal java
+ * types such as {@code int}, {@code short}.
+ *
+ * All other types are defined in terms of these primitive types.
+ */
+public enum NativeType {
+ /** Void type. Only used for function return types. */
+ VOID,
+
+ /** Signed char. Equivalent to a C char or signed char type. Usually 1 byte in size. */
+ SCHAR,
+
+ /** Unsigned char. Equivalent to a C unsigned char type. Usually 1 byte in size */
+ UCHAR,
+
+ /** Signed short integer. Equivalent to a C short or signed short type. Usually 2 bytes in size. */
+ SSHORT,
+
+ /** Unsigned short integer. Equivalent to a C unsigned short type. Usually 2 bytes in size. */
+ USHORT,
+
+ /** Signed integer. Equivalent to a C int or signed int type. Usually 4 bytes in size. */
+ SINT,
+
+ /** Unsigned integer. Equivalent to a C unsigned int type. Usually 4 bytes in size. */
+ UINT,
+
+ /** Signed long integer. Equivalent to a C long or signed long type. Can be either 4 or 8 bytes in size, depending on the platform. */
+ SLONG,
+
+ /** Unsigned long integer. Equivalent to a C unsigned long type. Can be either 4 or 8 bytes in size, depending on the platform. */
+ ULONG,
+
+ /** Signed long long integer. Equivalent to a C long long or signed long long type. Usually 8 bytes in size. */
+ SLONGLONG,
+
+ /** Unsigned long long integer. Equivalent to a C unsigned long long type. Usually 8 bytes in size. */
+ ULONGLONG,
+
+ /** Single precision floating point. Equivalent to a C float type. Usually 4 bytes in size. */
+ FLOAT,
+
+ /** Double precision floating point. Equivalent to a C double type. Usually 8 bytes in size. */
+ DOUBLE,
+
+ /** Native struct type */
+ STRUCT,
+
+ /** Native memory address. Equivalent to a C void* or char* pointer type. Can be either 4 or 8 bytes in size, depending on the platform. */
+ ADDRESS
+}
diff --git a/src/main/java/jnr/ffi/ObjectReferenceManager.java b/src/main/java/jnr/ffi/ObjectReferenceManager.java
new file mode 100644
index 0000000..48d2745
--- /dev/null
+++ b/src/main/java/jnr/ffi/ObjectReferenceManager.java
@@ -0,0 +1,129 @@
+package jnr.ffi;
+
+/**
+ * Provides a mapping between java objects and unique native addresses.
+ *
+ * <p>
+ * The native addresses generated by this class do not relate to the object's true native address
+ * (since that is not supported by most java virtual machines), but is guaranteed to be unique within
+ * an ObjectReferenceManager instance.
+ * </p>
+ * <p>
+ * This would be commonly used to create a unique native pointer that can be used to retrieve an object
+ * from within a callback.
+ * e.g.
+ * <pre>
+ * {@code
+ *
+ * public interface MyLib {
+ * public static interface MyCallback {
+ * @Delegate public void call(Pointer value);
+ * }
+ *
+ * public void do_something_with_callback(MyCallback cb, Pointer cb_argument);
+ * }
+ *
+ * MyLib lib = LibraryLoader.create(MyLib.class).load("mylib");
+ * final ObjectReferenceManager referenceManager = Runtime.getRuntime(lib).newObjectReferenceManager();
+ *
+ * MyCallback cb = new MyCallback {
+ * public void call(Pointer cb_argument) {
+ * Object javaCallbackArgument = referenceManager.get(cb_argument);
+ * System.out.println("java callback parameter=" + javaCallbackArgument);
+ * }
+ * }
+ *
+ * String callbackArgument = "Hello, World";
+ * Pointer cb_argument = referenceManager.add(callback);
+ * lib.do_something_with_callback(cb, cb_argument);
+ * referenceManager.remove(cb_argument);
+ *
+ * }
+ * </pre>
+ * </p>
+ *
+ * <b>Note</b>
+ * <p>
+ * Each call to {@link #add(Object)} will return a unique native address, even for the same object, so each call to
+ * {@link #add(Object)} must be matched with a call to {@link #remove(Pointer)}.
+ * </p>
+ */
+public abstract class ObjectReferenceManager<T> {
+
+ public static <T> ObjectReferenceManager<T> newInstance(Runtime runtime) {
+ return runtime.newObjectReferenceManager();
+ }
+
+ /**
+ * Adds a mapping from a java object to a unique native address.
+ *
+ * Each call to this method is guaranteed to produce a memory address unique within the ObjectReferenceManager
+ * instance, even for the same object.
+ *
+ * <p>
+ * A strong reference to {@code object} is maintained internally, until {@link #remove(Pointer)} is called.
+ * </p>
+ *
+ * @param object The java object to generate a reference for
+ * @return A pointer representing the unique id.
+ * @deprecated use {@link #add(Object)}
+ */
+ @Deprecated
+ public Pointer newReference(T object) {
+ return add(object);
+ }
+
+ /**
+ * Removes a mapping from java object to native pointer.
+ *
+ * @param reference a native memory pointer.
+ * @deprecated use {@link #remove(Pointer)}
+ */
+ @Deprecated
+ public void freeReference(Pointer reference) {
+ remove(reference);
+ }
+
+ /**
+ * Gets the java object that is mapped to the native memory address referred to by {@code reference}.
+ *
+ * @param reference a native memory pointer.
+ * @return The java object corresponding to {@code pointer}.
+ * @deprecated use {@link #get(Pointer)}
+ */
+ @Deprecated
+ public T getObject(Pointer reference) {
+ return get(reference);
+ }
+
+ /**
+ * Adds a mapping from a java object to a unique native address.
+ *
+ * Each call to this method is guaranteed to produce a memory address unique within the ObjectReferenceManager
+ * instance, even for the same object.
+ *
+ * <p>
+ * A strong reference to {@code object} is maintained internally, until {@link #remove(Pointer)} is called.
+ * </p>
+ *
+ * @param object The java object to generate a reference for
+ * @return A pointer representing the unique id.
+ */
+ public abstract Pointer add(T object);
+
+ /**
+ * Removes a mapping from java object to native pointer.
+ *
+ * @param reference a native memory pointer.
+ * @return true if the mapping was removed.
+ */
+ public abstract boolean remove(Pointer reference);
+
+ /**
+ * Gets the java object that is mapped to the native memory address referred to by {@code reference}.
+ *
+ * @param reference a native memory pointer.
+ * @return The java object corresponding to {@code pointer}.
+ */
+ public abstract T get(Pointer reference);
+}
diff --git a/src/main/java/jnr/ffi/Platform.java b/src/main/java/jnr/ffi/Platform.java
new file mode 100644
index 0000000..b965705
--- /dev/null
+++ b/src/main/java/jnr/ffi/Platform.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public abstract class Platform {
+ private static final java.util.Locale LOCALE = java.util.Locale.ENGLISH;
+ private final OS os;
+ private final CPU cpu;
+ private final int addressSize;
+ private final int longSize;
+ protected final Pattern libPattern;
+
+ private static final class SingletonHolder {
+ static final Platform PLATFORM = determinePlatform();
+ }
+
+ /**
+ * The common names of supported operating systems.
+ */
+ public enum OS {
+ /*
+ * Note The names of the enum values are used in other parts of the
+ * code to determine where to find the native stub library. Do not rename.
+ */
+
+ /** MacOSX */
+ DARWIN,
+ /** FreeBSD */
+ FREEBSD,
+ /** NetBSD */
+ NETBSD,
+ /** OpenBSD */
+ OPENBSD,
+ /** Linux */
+ LINUX,
+ /** Solaris (and OpenSolaris) */
+ SOLARIS,
+ /** The evil borg operating system */
+ WINDOWS,
+ /** IBM AIX */
+ AIX,
+ /** IBM zOS **/
+ ZLINUX,
+ /** No idea what the operating system is */
+ UNKNOWN;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase(LOCALE);
+ }
+ }
+
+ /**
+ * The supported CPU architectures.
+ */
+ public enum CPU {
+ /*
+ * <b>Note</b> The names of the enum values are used in other parts of the
+ * code to determine where to find the native stub library. Do NOT rename.
+ */
+
+ /** 32 bit legacy Intel */
+ I386,
+
+ /** 64 bit AMD (aka EM64T/X64) */
+ X86_64,
+
+ /** 32 bit Power PC */
+ PPC,
+
+ /** 64 bit Power PC */
+ PPC64,
+
+ /** 32 bit Sun sparc */
+ SPARC,
+
+ /** 64 bit Sun sparc */
+ SPARCV9,
+
+ /** IBM zSeries S/390 */
+ S390X,
+
+ /** 32 bit MIPS (used by nestedvm) */
+ MIPS32,
+
+ /** 32 bit ARM */
+ ARM,
+
+ /**
+ * Unknown CPU architecture. A best effort will be made to infer architecture
+ * specific values such as address and long size.
+ */
+ UNKNOWN;
+
+ /**
+ * Returns a {@code String} object representing this {@code CPU} object.
+ *
+ * @return the name of the cpu architecture as a lower case {@code String}.
+ */
+ @Override
+ public String toString() {
+ return name().toLowerCase(LOCALE);
+ }
+ }
+
+ /**
+ * Determines the operating system jffi is running on
+ *
+ * @return An member of the <tt>OS</tt> enum.
+ */
+ private static OS determineOS() {
+ String osName = System.getProperty("os.name").split(" ")[0];
+ if (startsWithIgnoreCase(osName, "mac") || startsWithIgnoreCase(osName, "darwin")) {
+ return OS.DARWIN;
+ } else if (startsWithIgnoreCase(osName, "linux")) {
+ return OS.LINUX;
+ } else if (startsWithIgnoreCase(osName, "sunos") || startsWithIgnoreCase(osName, "solaris")) {
+ return OS.SOLARIS;
+ } else if (startsWithIgnoreCase(osName, "aix")) {
+ return OS.AIX;
+ } else if (startsWithIgnoreCase(osName, "openbsd")) {
+ return OS.OPENBSD;
+ } else if (startsWithIgnoreCase(osName, "freebsd")) {
+ return OS.FREEBSD;
+ } else if (startsWithIgnoreCase(osName, "windows")) {
+ return OS.WINDOWS;
+ } else {
+ return OS.UNKNOWN;
+ }
+ }
+
+ /**
+ * Determines the <tt>Platform</tt> that best describes the <tt>OS</tt>
+ *
+ * @param os The operating system.
+ * @return An instance of <tt>Platform</tt>
+ */
+ private static Platform determinePlatform(OS os) {
+ switch (os) {
+ case DARWIN:
+ return new Darwin();
+ case LINUX:
+ return new Linux();
+ case WINDOWS:
+ return new Windows();
+ case UNKNOWN:
+ return new Unsupported(os);
+ default:
+ return new Default(os);
+ }
+ }
+
+ private static Platform determinePlatform() {
+ String providerName = System.getProperty("jnr.ffi.provider");
+ try {
+ Class c = Class.forName(providerName + "$Platform");
+ return (Platform) c.newInstance();
+ } catch (ClassNotFoundException ex) {
+ return determinePlatform(determineOS());
+ } catch (IllegalAccessException ex) {
+ throw new ExceptionInInitializerError(ex);
+ } catch (InstantiationException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ private static CPU determineCPU() {
+ String archString = System.getProperty("os.arch");
+ if (equalsIgnoreCase("x86", archString) || equalsIgnoreCase("i386", archString) || equalsIgnoreCase("i86pc", archString)) {
+ return CPU.I386;
+ } else if (equalsIgnoreCase("x86_64", archString) || equalsIgnoreCase("amd64", archString)) {
+ return CPU.X86_64;
+ } else if (equalsIgnoreCase("ppc", archString) || equalsIgnoreCase("powerpc", archString)) {
+ return CPU.PPC;
+ } else if (equalsIgnoreCase("ppc64", archString) || equalsIgnoreCase("powerpc64", archString)) {
+ return CPU.PPC64;
+ } else if (equalsIgnoreCase("s390", archString) || equalsIgnoreCase("s390x", archString)) {
+ return CPU.S390X;
+ }
+
+ // Try to find by lookup up in the CPU list
+ for (CPU cpu : CPU.values()) {
+ if (equalsIgnoreCase(cpu.name(), archString)) {
+ return cpu;
+ }
+ }
+
+ return CPU.UNKNOWN;
+ }
+
+ public Platform(OS os, CPU cpu, int addressSize, int longSize, String libPattern) {
+ this.os = os;
+ this.cpu = cpu;
+ this.addressSize = addressSize;
+ this.longSize = longSize;
+ this.libPattern = Pattern.compile(libPattern);
+ }
+
+ private Platform(OS os) {
+ this.os = os;
+ this.cpu = determineCPU();
+
+ String libpattern;
+ switch (os) {
+ case WINDOWS:
+ libpattern = ".*\\.dll$";
+ break;
+ case DARWIN:
+ libpattern = "lib.*\\.(dylib|jnilib)$";
+ break;
+ default:
+ libpattern = "lib.*\\.so.*$";
+ break;
+ }
+ libPattern = Pattern.compile(libpattern);
+
+ this.addressSize = calculateAddressSize(cpu);
+ this.longSize = os == OS.WINDOWS ? 32 : addressSize;
+ }
+
+ private static int calculateAddressSize(CPU cpu) {
+ int dataModel = Integer.getInteger("sun.arch.data.model");
+ if (dataModel != 32 && dataModel != 64) {
+ switch (cpu) {
+ case I386:
+ case PPC:
+ case SPARC:
+ dataModel = 32;
+ break;
+ case X86_64:
+ case PPC64:
+ case SPARCV9:
+ case S390X:
+ dataModel = 64;
+ break;
+ default:
+ throw new ExceptionInInitializerError("Cannot determine cpu address size");
+ }
+ }
+
+ return dataModel;
+ }
+
+ /**
+ * Gets the native <tt>Platform</tt>
+ *
+ * @return The current platform.
+ */
+ public static Platform getNativePlatform() {
+ return SingletonHolder.PLATFORM;
+ }
+
+ @Deprecated
+ public static Platform getPlatform() {
+ return SingletonHolder.PLATFORM;
+ }
+
+ /**
+ * Gets the current Operating System.
+ *
+ * @return A <tt>OS</tt> value representing the current Operating System.
+ */
+ public final OS getOS() {
+ return os;
+ }
+
+ /**
+ * Gets the current processor architecture the JVM is running on.
+ *
+ * @return A <tt>CPU</tt> value representing the current processor architecture.
+ */
+ public final CPU getCPU() {
+ return cpu;
+ }
+
+ public final boolean isBSD() {
+ return os == OS.FREEBSD || os == OS.OPENBSD || os == OS.NETBSD || os == OS.DARWIN;
+ }
+ public final boolean isUnix() {
+ return os != OS.WINDOWS;
+ }
+
+ /**
+ * Gets the size of a C 'long' on the native platform.
+ *
+ * @return the size of a long in bits
+ * @deprecated Use {@link Runtime#longSize()} instead.
+ */
+ public final int longSize() {
+ return longSize;
+ }
+
+ /**
+ * Gets the size of a C address/pointer on the native platform.
+ *
+ * @return the size of a pointer in bits
+ * @deprecated Use {@link Runtime#addressSize()} instead.
+ */
+ public final int addressSize() {
+ return addressSize;
+ }
+
+ /**
+ * Gets the name of this <tt>Platform</tt>.
+ *
+ * @return The name of this platform.
+ */
+ public String getName() {
+ return cpu + "-" + os;
+ }
+
+ /**
+ * Maps from a generic library name (e.g. "c") to the platform specific library name.
+ *
+ * @param libName The library name to map
+ * @return The mapped library name.
+ */
+ public String mapLibraryName(String libName) {
+ //
+ // A specific version was requested - use as is for search
+ //
+ if (libPattern.matcher(libName).find()) {
+ return libName;
+ }
+ return System.mapLibraryName(libName);
+ }
+
+ /**
+ * Searches through a list of directories for a native library.
+ *
+ * @param libName the base name (e.g. "c") of the library to locate
+ * @param libraryPath the list of directories to search
+ * @return the path of the library
+ */
+ public String locateLibrary(String libName, List<String> libraryPath) {
+ String mappedName = mapLibraryName(libName);
+ for (String path : libraryPath) {
+ File libFile = new File(path, mappedName);
+ if (libFile.exists()) {
+ return libFile.getAbsolutePath();
+ }
+ }
+ // Default to letting the system search for it
+ return mappedName;
+ }
+ private static class Supported extends Platform {
+ public Supported(OS os) {
+ super(os);
+ }
+ }
+
+ private static class Unsupported extends Platform {
+ public Unsupported(OS os) {
+ super(os);
+ }
+ }
+
+ private static final class Default extends Supported {
+
+ public Default(OS os) {
+ super(os);
+ }
+
+ }
+ /**
+ * A {@link Platform} subclass representing the MacOS system.
+ */
+ private static final class Darwin extends Supported {
+
+ public Darwin() {
+ super(OS.DARWIN);
+ }
+
+ @Override
+ public String mapLibraryName(String libName) {
+ //
+ // A specific version was requested - use as is for search
+ //
+ if (libPattern.matcher(libName).find()) {
+ return libName;
+ }
+ return "lib" + libName + ".dylib";
+ }
+
+ @Override
+ public String getName() {
+ return "Darwin";
+ }
+
+ }
+ /**
+ * A {@link Platform} subclass representing the Linux operating system.
+ */
+ private static final class Linux extends Supported {
+
+ public Linux() {
+ super(OS.LINUX);
+ }
+
+ @Override
+ public String locateLibrary(final String libName, List<String> libraryPath) {
+ FilenameFilter filter = new FilenameFilter() {
+ Pattern p = Pattern.compile("lib" + libName + "\\.so\\.[0-9]+$");
+ String exact = "lib" + libName + ".so";
+ public boolean accept(File dir, String name) {
+ return p.matcher(name).matches() || exact.equals(name);
+ }
+ };
+
+ List<File> matches = new LinkedList<File>();
+ for (String path : libraryPath) {
+ File[] files = new File(path).listFiles(filter);
+ if (files != null && files.length > 0) {
+ matches.addAll(Arrays.asList(files));
+ }
+ }
+
+ //
+ // Search through the results and return the highest numbered version
+ // i.e. libc.so.6 is preferred over libc.so.5
+ //
+ int version = 0;
+ String bestMatch = null;
+ for (File file : matches) {
+ String path = file.getAbsolutePath();
+ if (bestMatch == null && path.endsWith(".so")) {
+ bestMatch = path;
+ version = 0;
+ } else {
+ String num = path.substring(path.lastIndexOf(".so.") + 4);
+ try {
+ if (Integer.parseInt(num) >= version) {
+ bestMatch = path;
+ }
+ } catch (NumberFormatException e) {
+ } // Just skip if not a number
+ }
+ }
+ return bestMatch != null ? bestMatch : mapLibraryName(libName);
+ }
+ @Override
+ public String mapLibraryName(String libName) {
+ // Older JDK on linux map 'c' to 'libc.so' which doesn't work
+ return "c".equals(libName) || "libc.so".equals(libName)
+ ? "libc.so.6" : super.mapLibraryName(libName);
+ }
+ }
+
+ /**
+ * A {@link Platform} subclass representing the Windows system.
+ */
+ private static class Windows extends Supported {
+
+ public Windows() {
+ super(OS.WINDOWS);
+ }
+ }
+
+ private static boolean startsWithIgnoreCase(String s1, String s2) {
+ return s1.startsWith(s2)
+ || s1.toUpperCase(LOCALE).startsWith(s2.toUpperCase(LOCALE))
+ || s1.toLowerCase(LOCALE).startsWith(s2.toLowerCase(LOCALE));
+ }
+
+ private static boolean equalsIgnoreCase(String s1, String s2) {
+ return s1.equalsIgnoreCase(s2)
+ || s1.toUpperCase(LOCALE).equals(s2.toUpperCase(LOCALE))
+ || s1.toLowerCase(LOCALE).equals(s2.toLowerCase(LOCALE));
+ }
+}
+
diff --git a/src/main/java/jnr/ffi/Pointer.java b/src/main/java/jnr/ffi/Pointer.java
new file mode 100644
index 0000000..4346db0
--- /dev/null
+++ b/src/main/java/jnr/ffi/Pointer.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2008-2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A native memory address.
+ *
+ * This class provides operations on a native memory address. Most <tt>Pointer</tt>instances will represent direct
+ * memory (that is, a fixed address in the process address space, directly accessible by native code), however,
+ * it is possible to wrap a java <tt>byte</tt> array in a <tt>Pointer</tt>instance to pass to a native function
+ * as a memory address. See {@link #isDirect()} for more information.
+ */
+abstract public class Pointer {
+ private final Runtime runtime;
+ private final long address;
+ private final boolean isDirect;
+
+ /**
+ * Wraps a native address in a {@link Pointer} instance.
+ *
+ * @param runtime the {@code Runtime} of the pointer.
+ * @param address the {@code address} to wrap in a Pointer instance.
+ *
+ * @return a {@code Pointer} instance.
+ */
+ public static Pointer wrap(Runtime runtime, long address) {
+ return runtime.getMemoryManager().newPointer(address);
+ }
+
+ /**
+ * Wraps a native address in a {@link Pointer} instance.
+ *
+ * @param runtime the {@code Runtime} of the pointer.
+ * @param address the {@code address} to wrap in a Pointer instance.
+ * @param size the size of the native memory region.
+ *
+ * @return a {@code Pointer} instance.
+ */
+ public static Pointer wrap(Runtime runtime, long address, long size) {
+ return runtime.getMemoryManager().newPointer(address, size);
+ }
+
+ /**
+ * Wraps an existing ByteBuffer in a {@link Pointer} implementation so it can
+ * be used as a parameter to native functions.
+ *
+ * <p>Wrapping a ByteBuffer is only neccessary if the native function parameter
+ * was declared as a {@code Pointer}. The if the method will always be used
+ * with {@code ByteBuffer} parameters, then the parameter type can just be declared
+ * as {@code ByteBuffer} and the conversion will be performed automatically.
+ *
+ * @param runtime the {@code Runtime} the wrapped {@code ByteBuffer} will
+ * be used with.
+ * @param buffer the {@code ByteBuffer} to wrap.
+ *
+ * @return a {@code Pointer} instance that will proxy all accesses to the ByteBuffer contents.
+ */
+ public static Pointer wrap(Runtime runtime, ByteBuffer buffer) {
+ return runtime.getMemoryManager().newPointer(buffer);
+ }
+
+ /**
+ * Wraps an integer value in an opaque {@link Pointer} instance. This is a Pointer instance that
+ * throws errors when any of the memory access methods are used, but can be otherwise used interchangeably
+ * with a real Pointer.
+ *
+ * @param runtime the {@code Runtime} of the pointer.
+ * @param address the {@code address} to wrap in a Pointer instance.
+ *
+ * @return a {@code Pointer} instance.
+ */
+ public static Pointer newIntPointer(Runtime runtime, long address) {
+ return runtime.getMemoryManager().newOpaquePointer(address);
+ }
+
+ protected Pointer(Runtime runtime, long address, boolean direct) {
+ this.runtime = runtime;
+ this.address = address;
+ isDirect = direct;
+ }
+
+ /**
+ * Indicates whether or not this memory object represents a native memory address.
+ *
+ * <p>Memory objects can be either direct (representing native memory), or
+ * non-direct (representing java heap memory).
+ *
+ * <p>Non-direct memory objects can still be passed to native functions as pointer
+ * (void *, char *, etc) parameters, but the java memory will first be copied
+ * to a temporary native memory area. The temporary memory area will then be
+ * used as the parameter value for the call. If needed, the java memory
+ * will be automatically reloaded from the temporary native memory after the
+ * native function returns.
+ * <p><b>Note:</b> the transient nature of the temporary memory allocated for
+ * non-direct memory means native functions which store the address value
+ * passed to them will fail in unpredictable ways when using non-direct memory.
+ * You will need to explicitly allocate direct memory to use those types of
+ * functions.
+ *
+ * @return true if, and only if, this memory object represents a native address.
+ */
+ public final boolean isDirect() {
+ return isDirect;
+ }
+
+ /**
+ * Gets the native address of this memory object (optional operation).
+ *
+ * @return the native address of this memory object. If this object is not
+ * a native memory address, an address of zero is returned.
+ */
+ public final long address() {
+ return address;
+ }
+
+ /**
+ * Gets the {@link Runtime} this {@code Pointer} instance belongs to.
+ *
+ * @return the {@code Runtime} instance of this {@code Pointer}.
+ */
+ public final Runtime getRuntime() {
+ return runtime;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getName());
+ sb.append(String.format("[address=%#x", address()));
+ if (size() != Long.MAX_VALUE) {
+ sb.append(String.format(" size=%d", size()));
+ }
+
+ sb.append(']');
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the size of this memory object in bytes (optional operation).
+ *
+ * @return the size of the memory area this {@code Pointer} points to. If
+ * the size is unknown, {@link java.lang.Long#MAX_VALUE} is returned}.
+ */
+ abstract public long size();
+
+ /**
+ * Indicates whether this <tt>Pointer</tt> instance is backed by an array.
+ *
+ * @return true if, and only if, this memory object is backed by an array
+ */
+ abstract public boolean hasArray();
+
+ /**
+ * Returns the array that backs this pointer.
+ *
+ * @throws {@link java.lang.UnsupportedOperationException} if this pointer does not have a backing array
+ * @return The array that backs this pointer
+ */
+ abstract public Object array();
+
+ /**
+ * Returns the offset within this pointer's backing array of the first element.
+ *
+ * @throws {@link java.lang.UnsupportedOperationException} if this pointer does not have a backing array
+ * @return The offset of the first element on the backing array
+ */
+ abstract public int arrayOffset();
+
+ /**
+ * Returns the length of this pointer's backing array that is used by this pointer.
+ *
+ * @throws {@link UnsupportedOperationException} if this pointer does not have a backing array
+ * @return The length of the backing array used
+ */
+ abstract public int arrayLength();
+
+ /**
+ * Reads an {@code byte} (8 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code byte} value at the offset.
+ */
+ abstract public byte getByte(long offset);
+
+ /**
+ * Reads a {@code short} (16 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code short} value at the offset.
+ */
+ abstract public short getShort(long offset);
+
+ /**
+ * Reads an {@code int} (32 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code int} value contained in the memory at the offset.
+ */
+ abstract public int getInt(long offset);
+
+ /**
+ * Reads a {@code long} (64 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code long} value at the offset.
+ */
+ abstract public long getLong(long offset);
+
+ /**
+ * Reads a {@code long} (64 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code long} value at the offset.
+ */
+ abstract public long getLongLong(long offset);
+
+ /**
+ * Reads a {@code float} (32 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code float} value at the offset.
+ */
+ abstract public float getFloat(long offset);
+
+ /**
+ * Reads a {@code double} (64 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code double} value at the offset.
+ */
+ abstract public double getDouble(long offset);
+
+ /**
+ * Reads a native {@code long} value at the given offset.
+ * <p>A native {@code long} can be either 32 or 64 bits in size, depending
+ * on the cpu architecture, and the C ABI in use.
+ *
+ * <p>For windows, a long is always 32 bits (4 bytes) in size, but on unix
+ * systems, a long on a 32bit system is 32 bits, and on a 64bit system, is 64 bits.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the native {@code long} value at the offset.
+ *
+ * @see NativeLong
+ */
+ abstract public long getNativeLong(long offset);
+
+ /**
+ * Reads an integer value of the given type, at the given offset.
+ *
+ * @param type Type of integer to read.
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code int} value contained in the memory at the offset.
+ */
+ abstract public long getInt(Type type, long offset);
+
+ /**
+ * Writes a {@code byte} (8 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code byte} value to be written.
+ */
+ abstract public void putByte(long offset, byte value);
+
+ /**
+ * Writes a {@code short} (16 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code short} value to be written.
+ */
+ abstract public void putShort(long offset, short value);
+
+ /**
+ * Writes an {@code int} (32 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code int} value to be written.
+ */
+ abstract public void putInt(long offset, int value);
+
+ /**
+ * Writes a {@code native long} value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code long} value to be written.
+ */
+ abstract public void putLong(long offset, long value);
+
+ /**
+ * Writes a {@code long} (64 bit) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code long} value to be written.
+ */
+ abstract public void putLongLong(long offset, long value);
+
+ /**
+ * Writes a {@code float} (32 bit, single precision) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code float} value to be written.
+ */
+ abstract public void putFloat(long offset, float value);
+
+ /**
+ * Writes a {@code double} (64 bit, double precision) value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code double} value to be written.
+ */
+ abstract public void putDouble(long offset, double value);
+
+ /**
+ * Writes a native {@code long} value at the given offset.
+ *
+ * <p>A native {@code long} can be either 32 or 64 bits in size, depending
+ * on the cpu architecture, and the C ABI in use.
+ *
+ * <p>For windows, a long is always 32 bits (4 bytes) in size, but on unix
+ * systems, a long on a 32bit system is 32 bits, and on a 64bit system, is 64 bits.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the native {@code long} value to be written.
+ */
+ abstract public void putNativeLong(long offset, long value);
+
+ /**
+ * Writes an integer of a specific type, at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code int} value to be written.
+ */
+ abstract public void putInt(Type type, long offset, long value);
+
+ /**
+ * Reads a native memory address value at the given offset.
+ * <p>A native address can be either 32 or 64 bits in size, depending
+ * on the cpu architecture.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the native address value contained in the memory at the offset
+ *
+ * @see Address
+ */
+ abstract public long getAddress(long offset);
+
+ /**
+ * Writes a native memory address value at the given offset.
+ * <p>A native address can be either 32 or 64 bits in size, depending
+ * on the cpu architecture.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value The native address value to be written.
+ *
+ * @see Address
+ */
+ abstract public void putAddress(long offset, long value);
+
+ /**
+ * Writes a native memory address value at the given offset.
+ * <p>A native address can be either 32 or 64 bits in size, depending
+ * on the cpu architecture.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value The native address value to be written.
+ *
+ * @see Address
+ */
+ abstract public void putAddress(long offset, Address value);
+
+ /**
+ * Bulk get method for multiple {@code byte} values.
+ *
+ * This method reads multiple {@code byte} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst the array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, byte[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code byte} values.
+ *
+ * This method writes multiple {@code byte} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, byte[] src, int idx, int len);
+
+ /**
+ * Bulk get method for multiple {@code short} values.
+ *
+ * This method reads multiple {@code short} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, short[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code short} values.
+ *
+ * This method writes multiple {@code short} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, short[] src, int idx, int len);
+
+ /**
+ * Bulk get method for multiple {@code int} values.
+ *
+ * This method reads multiple {@code int} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, int[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code int} values.
+ *
+ * This method writes multiple {@code int} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, int[] src, int idx, int len);
+
+ /**
+ * Bulk get method for multiple {@code long} values.
+ *
+ * This method reads multiple {@code long} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, long[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code long} values.
+ *
+ * This method writes multiple {@code long} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, long[] src, int idx, int len);
+
+ /**
+ * Bulk get method for multiple {@code float} values.
+ *
+ * This method reads multiple {@code float} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, float[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code float} values.
+ *
+ * This method writes multiple {@code float} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, float[] src, int idx, int len);
+
+ /**
+ * Bulk get method for multiple {@code double} values.
+ *
+ * This method reads multiple {@code double} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ abstract public void get(long offset, double[] dst, int idx, int len);
+
+ /**
+ * Bulk put method for multiple {@code double} values.
+ *
+ * This method writes multiple {@code double} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code dst} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ abstract public void put(long offset, double[] src, int idx, int len);
+
+ /**
+ * Reads an {@code Pointer} value at the given offset.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code Pointer} value read from memory.
+ */
+ abstract public Pointer getPointer(long offset);
+
+ /**
+ * Reads an {@code Pointer} value at the given offset.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @param size the maximum size of the memory location the returned {@code Pointer} represents.
+ * @return the {@code Pointer} value read from memory.
+ */
+ abstract public Pointer getPointer(long offset, long size);
+
+ /**
+ * Writes a {@code Pointer} value at the given offset.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param value the {@code Pointer} value to be written to memory.
+ */
+ abstract public void putPointer(long offset, Pointer value);
+
+ /**
+ * Reads an {@code String} value at the given offset.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @return the {@code String} value read from memory.
+ */
+ abstract public String getString(long offset);
+
+ /**
+ * Reads a {@code String} value at the given offset, using a specific {@code Charset}
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the value will be read.
+ * @param maxLength the maximum size of memory to search for a NUL byte.
+ * @param cs the {@code Charset} to use to decode the string.
+ * @return the {@code String} value read from memory.
+ */
+ abstract public String getString(long offset, int maxLength, Charset cs);
+
+ /**
+ * Writes a {@code String} value at the given offset, using a specific {@code Charset}
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the value will be written.
+ * @param maxLength the maximum size of memory to use to store the string.
+ * @param cs the {@code Charset} to use to decode the string.
+ */
+ abstract public void putString(long offset, String string, int maxLength, Charset cs);
+
+ /**
+ * Creates a new {@code Pointer} representing a sub-region of the memory
+ * referred to by this {@code Pointer}.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer}
+ * represents at which the new {@code Pointer} will start.
+ * @return a {@code Pointer} instance representing the new sub-region.
+ */
+ abstract public Pointer slice(long offset);
+
+ /**
+ * Creates a new {@code Pointer} representing a sub-region of the memory
+ * referred to by this {@code Pointer}.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer}
+ * represents at which the new {@code Pointer} will start.
+ * @param size the maximum size of the memory sub-region.
+ *
+ * @return a {@code Pointer} instance representing the new sub-region.
+ */
+ abstract public Pointer slice(long offset, long size);
+
+ /**
+ * Bulk data transfer from one memory location to another.
+ *
+ * @param offset the offset from the start of the memory location this {@code Pointer} represents to begin copying from.
+ * @param dst the destination memory location to transfer data to.
+ * @param dstOffset the offset from the start of the memory location the destination {@code Pointer} represents to begin copying to.
+ * @param count the number of bytes to transfer.
+ */
+ abstract public void transferTo(long offset, Pointer dst, long dstOffset, long count);
+
+ /**
+ * Bulk data transfer from one memory location to another.
+ *
+ * @param offset the offset from the start of the memory location this {@code Pointer} represents to begin copying to.
+ * @param src the destination memory location to transfer data from.
+ * @param srcOffset the offset from the start of the memory location the destination {@code Pointer} represents to begin copying from.
+ * @param count the number of bytes to transfer.
+ */
+ abstract public void transferFrom(long offset, Pointer src, long srcOffset, long count);
+
+ /**
+ * Checks that the memory region is within the bounds of this memory object
+ *
+ * @param offset the starting point within this memory region.
+ * @param length the length of the memory region in bytes
+ * @throws java.lang.IndexOutOfBoundsException
+ */
+ abstract public void checkBounds(long offset, long length);
+
+ /**
+ * Sets the value of each byte in the memory area represented by this {@code Pointer}.
+ * to a specified value.
+ *
+ * @param offset the offset from the start of the memory location this {@code Pointer} represents to begin writing to.
+ * @param size the number of bytes to set to the value.
+ * @param value the value to set each byte to.
+ */
+ abstract public void setMemory(long offset, long size, byte value);
+
+ /**
+ * Returns the location of a byte value within the memory area represented by this {@code Pointer}.
+ *
+ * @param offset the offset from the start of the memory location this {@code Pointer} represents to begin searching.
+ * @param value the {@code byte} value to locate.
+ * @return the offset from the start of the search area (i.e. relative to the offset parameter), or -1 if not found.
+ */
+ abstract public int indexOf(long offset, byte value);
+
+ /**
+ * Returns the location of a byte value within the memory area represented by this {@code Pointer}.
+ *
+ * @param offset the offset from the start of the memory location this {@code Pointer} represents to begin searching.
+ * @param value the {@code byte} value to locate.
+ * @param maxlen the maximum number of bytes to search for the desired value.
+ * @return the offset from the start of the search area (i.e. relative to the offset parameter), or -1 if not found.
+ */
+ abstract public int indexOf(long offset, byte value, int maxlen);
+
+ /**
+ * Bulk get method for multiple {@code Pointer} values.
+ *
+ * This method reads multiple {@code Pointer} values from consecutive addresses,
+ * beginning at the given offset, and stores them in an array.
+ *
+ * @param offset The offset from the start of the memory this {@code Pointer} represents at which the first value will be read.
+ * @param dst The array into which values are to be stored.
+ * @param idx the start index in the {@code dst} array to begin storing the values.
+ * @param len the number of values to be read.
+ */
+ public void get(long offset, Pointer[] dst, int idx, int len) {
+ final int pointerSize = getRuntime().addressSize();
+ for (int i = 0; i < len; i++) {
+ dst[idx + i] = getPointer(offset + (i * pointerSize));
+ }
+ }
+
+ /**
+ * Bulk put method for multiple {@code Pointer} values.
+ *
+ * This method writes multiple {@code Pointer} values to consecutive addresses,
+ * beginning at the given offset, from an array.
+ *
+ * @param offset the offset from the start of the memory this {@code Pointer} represents at which the first value will be written.
+ * @param src the array to get values from.
+ * @param idx the start index in the {@code src} array to begin reading values.
+ * @param len the number of values to be written.
+ */
+ public void put(long offset, Pointer[] src, int idx, int len) {
+ final int pointerSize = getRuntime().addressSize();
+ for (int i = 0; i < len; i++) {
+ putPointer(offset + (i * pointerSize), src[idx + i]);
+ }
+ }
+
+ public String[] getNullTerminatedStringArray(long offset) {
+
+ Pointer ptr;
+ if ((ptr = getPointer(offset)) == null) {
+ return new String[0];
+ }
+
+ final int pointerSize = getRuntime().addressSize();
+
+ List<String> array = new ArrayList<String>();
+ array.add(ptr.getString(0));
+
+ for (int off = pointerSize; (ptr = getPointer(offset + off)) != null; off += pointerSize) {
+ array.add(ptr.getString(0));
+ }
+
+ return array.toArray(new String[array.size()]);
+ }
+
+ public Pointer[] getNullTerminatedPointerArray(long offset) {
+
+ Pointer ptr;
+ if ((ptr = getPointer(offset)) == null) {
+ return new Pointer[0];
+ }
+
+ final int pointerSize = getRuntime().addressSize();
+
+ List<Pointer> array = new ArrayList<Pointer>();
+ array.add(ptr);
+
+ for (int off = pointerSize; (ptr = getPointer(offset + off)) != null; off += pointerSize) {
+ array.add(ptr);
+ }
+
+ return array.toArray(new Pointer[array.size()]);
+ }
+}
diff --git a/src/main/java/jnr/ffi/Runtime.java b/src/main/java/jnr/ffi/Runtime.java
new file mode 100644
index 0000000..d91cc2f
--- /dev/null
+++ b/src/main/java/jnr/ffi/Runtime.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.provider.FFIProvider;
+import jnr.ffi.provider.LoadedLibrary;
+import jnr.ffi.provider.MemoryManager;
+import jnr.ffi.provider.ClosureManager;
+
+import java.nio.ByteOrder;
+
+/**
+ * Access JNR runtime functionality.
+ *
+ * <p>
+ * This class is needed by many classes to correctly initialize internal data structures, and each library loaded
+ * has its own instance of this class.
+ * </p>
+ * <p>
+ * To obtain an instance of this class, use {@link #getRuntime(Object)} on a loaded library.
+ * </p>
+ * <p>
+ * Example
+ * <pre>
+ * {@code
+ *
+ * public interface LibC {
+ * public long write(int fd, Pointer data, long len);
+ * }
+ *
+ * LibC library = LibraryLoader.create(LibC.class).load("c");
+ *
+ * byte[] bytes = "Hello, World\n".getBytes("UTF-8");
+ *
+ * // Use the loaded library's Runtime to allocate memory for the string
+ * jnr.ffi.Runtime runtime = jnr.ffi.Runtime.getRuntime(library);
+ * Pointer buffer = Memory.allocateDirect(runtime, bytes.length);
+ *
+ * // Copy the java string data to the native memory, then write the contents to STDOUT
+ * buffer.put(0, bytes, 0, bytes.length);
+ * library.write(1, buffer, bytes.length);
+ * }
+ * </pre>
+ * </p>
+ */
+public abstract class Runtime {
+
+ /**
+ * Gets the global Runtime for the current FFI provider
+ *
+ * @return The system runtime
+ */
+ public static Runtime getSystemRuntime() {
+ return SingletonHolder.SYSTEM_RUNTIME;
+ }
+
+ /**
+ * Returns the runtime associated with the library instance.
+ *
+ * @param library A loaded library instance as returned from {@link LibraryLoader#load()}
+ * @return The runtime that loaded the library
+ */
+ public static Runtime getRuntime(Object library) {
+ return ((LoadedLibrary) library).getRuntime();
+ }
+
+ /** singleton holder for the default Runtime */
+ private static final class SingletonHolder {
+ public static final Runtime SYSTEM_RUNTIME = FFIProvider.getSystemProvider().getRuntime();
+ }
+
+ /**
+ * Looks up the runtime-specific type that corresponds to the pseudo-type
+ *
+ * @return A {@code Type} instance
+ */
+ public abstract Type findType(NativeType type);
+
+ /**
+ * Looks up the runtime-specific type that corresponds to the type alias
+ *
+ * @return A {@code Type} instance
+ */
+ public abstract Type findType(TypeAlias type);
+
+ /**
+ * Gets the native memory manager for this runtime
+ *
+ * @return The {@link MemoryManager} of the runtime
+ */
+ public abstract MemoryManager getMemoryManager();
+
+ /**
+ * Gets the native closure manager for this runtime
+ *
+ * @return The {@link ClosureManager} of the runtime
+ */
+ public abstract ClosureManager getClosureManager();
+
+ /**
+ * Creates a new {@code ObjectReferenceManager}
+ *
+ * @return A new {@link ObjectReferenceManager}
+ */
+ public abstract <T> ObjectReferenceManager<T> newObjectReferenceManager();
+
+ /**
+ * Gets the last native error code.
+ * <p>
+ * This returns the errno value that was set at the time of the last native
+ * function call.
+ *
+ * @return The errno value.
+ */
+ public abstract int getLastError();
+
+ /**
+ * Sets the native error code.
+ *
+ * @param error The value to set errno to.
+ */
+ public abstract void setLastError(int error);
+
+ /**
+ * Gets the address mask for this runtime
+ *
+ * @return The address mask for the runtime.
+ */
+ public abstract long addressMask();
+
+ /**
+ * Gets the size of an address (e.g. a pointer) for this runtime
+ *
+ * @return The size of an address in bytes.
+ */
+ public abstract int addressSize();
+
+ /**
+ * Gets the size of a C long integer for this runtime
+ *
+ * @return The size of a C long integer in bytes.
+ */
+ public abstract int longSize();
+
+ /**
+ * Gets the native byte order of the runtime.
+ *
+ * @return The byte order of the runtime
+ */
+ public abstract ByteOrder byteOrder();
+
+ /**
+ * Indicates whether this <tt>Runtime</tt> instance is compatible with another <tt>Runtime</tt> instance.
+ *
+ * <p>
+ * This is not the same as calling {@link #equals} - this method only indicates whether or not artifacts from the
+ * runtime (e.g. memory addresses) are compatible with artifacts from this one.
+ * </p>
+ *
+ * <p>
+ * This is mostly for internal use.
+ * </p>
+ *
+ * @param other the other runtime to test for compatibility
+ * @return true if the other runtime is compatible with this one
+ */
+ public abstract boolean isCompatible(jnr.ffi.Runtime other);
+}
diff --git a/src/main/java/jnr/ffi/Struct.java b/src/main/java/jnr/ffi/Struct.java
new file mode 100644
index 0000000..67fd74a
--- /dev/null
+++ b/src/main/java/jnr/ffi/Struct.java
@@ -0,0 +1,2191 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some of the design and code of this class is from the javolution project.
+ *
+ * Copyright (C) 2006 - Javolution (http://javolution.org/)
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software is
+ * freely granted, provided that this notice is preserved.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.provider.ParameterFlags;
+import jnr.ffi.util.EnumMapper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.nio.charset.Charset;
+
+/**
+ * Representation of C structures in java.
+ *
+ * <b>Note:</b> This class is not threadsafe.
+ */
+public abstract class Struct {
+ static final Charset ASCII = Charset.forName("ASCII");
+ static final Charset UTF8 = Charset.forName("UTF-8");
+
+ static final class Info {
+ private final Runtime runtime;
+ private jnr.ffi.Pointer memory = null;
+ Struct enclosing = null;
+ int offset = 0; // offset within enclosing Struct
+
+ int size = 0;
+ int minAlign = 1;
+ boolean isUnion = false;
+ boolean resetIndex = false;
+
+ public Info(Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ public final jnr.ffi.Pointer getMemory(int flags) {
+ return enclosing != null ? enclosing.__info.getMemory(flags) : memory != null ? memory : (memory = allocateMemory(flags));
+ }
+
+ public final jnr.ffi.Pointer getMemory() {
+ return getMemory(ParameterFlags.TRANSIENT);
+ }
+
+ final boolean isDirect() {
+ return (enclosing != null && enclosing.__info.isDirect()) || (memory != null && memory.isDirect());
+ }
+
+ final int size() {
+ return size;
+ }
+
+ final int getMinimumAlignment() {
+ return minAlign;
+ }
+
+ private jnr.ffi.Pointer allocateMemory(int flags) {
+ if (ParameterFlags.isDirect(flags)) {
+ return runtime.getMemoryManager().allocateDirect(size(), true);
+ } else {
+ return runtime.getMemoryManager().allocate(size());
+ }
+ }
+
+ public final void useMemory(jnr.ffi.Pointer io) {
+ this.memory = io;
+ }
+
+ protected final int addField(int sizeBits, int alignBits, Offset offset) {
+ this.size = Math.max(this.size, offset.intValue() + (sizeBits >> 3));
+ this.minAlign = Math.max(this.minAlign, alignBits >> 3);
+ return offset.intValue();
+ }
+
+ protected final int addField(int sizeBits, int alignBits) {
+ final int off = resetIndex ? 0 : align(this.size, alignBits >> 3);
+ this.size = Math.max(this.size, off + (sizeBits >> 3));
+ this.minAlign = Math.max(this.minAlign, alignBits >> 3);
+ return off;
+ }
+
+
+ }
+ final Info __info;
+
+ /**
+ * Creates a new <tt>Struct</tt>.
+ */
+ protected Struct(Runtime runtime) {
+ this.__info = new Info(runtime);
+ }
+
+ /**
+ * Creates a new <tt>Struct</tt>.
+ *
+ * @param isUnion if this Struct is a Union
+ */
+ Struct(Runtime runtime, final boolean isUnion) {
+ this(runtime);
+ __info.resetIndex = isUnion;
+ }
+
+ public final Runtime getRuntime() {
+ return __info.runtime;
+ }
+
+ /**
+ * Uses the specified memory address as the backing store for this structure.
+ *
+ * @param address the native memory area.
+ */
+ public final void useMemory(jnr.ffi.Pointer address) {
+ __info.useMemory(address);
+ }
+
+ public static jnr.ffi.Pointer getMemory(Struct struct) {
+ return struct.__info.getMemory(0);
+ }
+
+ public static jnr.ffi.Pointer getMemory(Struct struct, int flags) {
+ return struct.__info.getMemory(flags);
+ }
+
+ public static int size(Struct struct) {
+ return struct.__info.size();
+ }
+
+ public static int alignment(Struct struct) {
+ return struct.__info.getMinimumAlignment();
+ }
+
+ public static boolean isDirect(Struct struct) {
+ return struct.__info.isDirect();
+ }
+
+ private static int align(int offset, int align) {
+ return (offset + align - 1) & ~(align - 1);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T extends Struct> T[] arrayOf(Runtime runtime, Class<T> type, int length) {
+ try {
+ T[] array = (T[]) Array.newInstance(type, length);
+ Constructor<T> c = type.getConstructor(Runtime.class);
+ for (int i = 0; i < length; ++i) {
+ array[i] = c.newInstance(runtime);
+ }
+
+ if (array.length > 0) {
+ final int structSize = align(Struct.size(array[0]), Struct.alignment(array[0]));
+
+ jnr.ffi.Pointer memory = runtime.getMemoryManager().allocateDirect(structSize * length);
+ for (int i = 0; i < array.length; ++i) {
+ array[i].useMemory(memory.slice(structSize * i, structSize));
+ }
+ }
+
+ return array;
+ } catch (RuntimeException ex) {
+ throw ex;
+
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+
+ /**
+ * Returns a human readable {@link java.lang.String} representation of the structure.
+ *
+ * @return a <tt>String representation of this structure.
+ */
+ @Override
+ public java.lang.String toString() {
+ StringBuilder sb = new StringBuilder();
+ java.lang.reflect.Field[] fields = getClass().getDeclaredFields();
+ sb.append(getClass().getSimpleName()).append(" { \n");
+ final java.lang.String fieldPrefix = " ";
+ for (java.lang.reflect.Field field : fields) {
+ try {
+ sb.append(fieldPrefix);
+ sb.append(field.getName()).append(" = ");
+ sb.append(field.get(this).toString());
+ sb.append("\n");
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ sb.append("}\n");
+ return sb.toString();
+ }
+
+ public static final class Offset extends java.lang.Number {
+ private final int offset;
+ public Offset(int offset) {
+ this.offset = offset;
+ }
+ @Override
+ public int intValue() {
+ return offset;
+ }
+ @Override
+ public long longValue() {
+ return offset;
+ }
+ @Override
+ public float floatValue() {
+ return offset;
+ }
+ @Override
+ public double doubleValue() {
+ return offset;
+ }
+ }
+
+ /**
+ * Interface all Struct members must implement.
+ */
+ protected abstract class Member {
+ /**
+ * Gets the <tt>Struct</tt> this <tt>Member</tt> is a member of.
+ *
+ * @return a <tt>Struct</tt>.
+ */
+ abstract Struct struct();
+
+ /**
+ * Gets the memory object used to store this {@code Member}
+ *
+ * @return a {@code Pointer}
+ */
+ abstract jnr.ffi.Pointer getMemory();
+
+ /**
+ * Gets the offset within the structure for this field.
+ */
+ abstract long offset();
+ }
+
+ /**
+ * Starts an array construction session
+ */
+ protected final void arrayBegin() {
+ __info.resetIndex = false;
+ }
+
+ /**
+ * Ends an array construction session
+ */
+ protected final void arrayEnd() {
+ __info.resetIndex = __info.isUnion;
+ }
+
+ /**
+ * Creates an array of <tt>Member</tt> instances.
+ *
+ * @param <T> The type of the <tt>Member</tt> subclass to create.
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ @SuppressWarnings("unchecked")
+ protected <T extends Member> T[] array(T[] array) {
+ arrayBegin();
+ try {
+ Class<?> arrayClass = array.getClass().getComponentType();
+ Constructor<?> ctor = arrayClass.getDeclaredConstructor(new Class[] { arrayClass.getEnclosingClass() });
+ Object[] parameters = { Struct.this };
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (T) ctor.newInstance(parameters);
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Signed8</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Signed8[] array(Signed8[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Signed8();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Unsigned8</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Unsigned8[] array(Unsigned8[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Unsigned8();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Signed16</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Signed16[] array(Signed16[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Signed16();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Unsigned16</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Unsigned16[] array(Unsigned16[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Unsigned16();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Signed32</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Signed32[] array(Signed32[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Signed32();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Unsigned32</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Unsigned32[] array(Unsigned32[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Unsigned32();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Signed64</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Signed64[] array(Signed64[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Signed64();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Unsigned64</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Unsigned64[] array(Unsigned64[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Unsigned64();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>SignedLong</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final SignedLong[] array(SignedLong[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new SignedLong();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>UnsignedLong</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final UnsignedLong[] array(UnsignedLong[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new UnsignedLong();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Float</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Float[] array(Float[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Float();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Double</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Double[] array(Double[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Double();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Address</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Address[] array(Address[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Address();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ /**
+ * Creates an array of <tt>Pointer</tt> instances.
+ *
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ protected final Pointer[] array(Pointer[] array) {
+ arrayBegin();
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = new Pointer();
+ }
+ arrayEnd();
+ return array;
+ }
+
+ protected final <T extends Struct> T inner(T struct) {
+ int off = align(__info.size, struct.__info.getMinimumAlignment());
+ struct.__info.enclosing = this;
+ struct.__info.offset = off;
+ __info.size = off + struct.__info.size;
+
+ return struct;
+ }
+
+ /**
+ * Base implementation of Member
+ */
+ protected abstract class AbstractMember extends Member {
+ private final int offset;
+ protected AbstractMember(int size) {
+ this(size, size);
+ }
+ protected AbstractMember(int size, int align, Offset offset) {
+ this.offset = __info.addField(size, align, offset);
+ }
+ protected AbstractMember(int size, int align) {
+ this.offset = __info.addField(size, align);
+ }
+
+ protected AbstractMember(NativeType type) {
+ final Type t = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8);
+ }
+
+ protected AbstractMember(NativeType type, Offset offset) {
+ final Type t = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8, offset);
+ }
+
+ public final jnr.ffi.Pointer getMemory() {
+ return __info.getMemory();
+ }
+
+ /**
+ * Gets the <tt>Struct</tt> this <tt>Member</tt> is a member of.
+ *
+ * @return a <tt>Struct</tt>.
+ */
+ public final Struct struct() {
+ return Struct.this;
+ }
+
+ /**
+ * Gets the offset within the structure for this field.
+ */
+ public final long offset() {
+ return offset + __info.offset;
+ }
+ }
+
+ /**
+ * Base class for Boolean fields
+ */
+ protected abstract class AbstractBoolean extends AbstractMember {
+ protected AbstractBoolean(NativeType type) {
+ super(type);
+ }
+
+ protected AbstractBoolean(NativeType type, Offset offset) {
+ super(type, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a boolean.
+ */
+ public abstract boolean get();
+
+ /**
+ * Sets the field to a new value.
+ *
+ * @param value The new value.
+ */
+ public abstract void set(boolean value);
+
+ /**
+ * Returns a string representation of this <code>Address</code>.
+ *
+ * @return a string representation of this <code>Address</code>.
+ */
+ @Override
+ public java.lang.String toString() {
+ return java.lang.Boolean.toString(get());
+ }
+ }
+
+ /**
+ * A normal C boolean - 1 byte in size
+ */
+ public final class Boolean extends AbstractBoolean {
+ public Boolean() {
+ super(NativeType.SCHAR);
+ }
+
+ public final boolean get() {
+ return (getMemory().getByte(offset()) & 0x1) != 0;
+ }
+
+ public final void set(boolean value) {
+ getMemory().putByte(offset(), (byte) (value ? 1 : 0));
+ }
+ }
+
+ /**
+ * A Windows BOOL - 4 bytes
+ */
+ public final class WBOOL extends AbstractBoolean {
+ public WBOOL() {
+ super(NativeType.SINT);
+ }
+
+ public final boolean get() {
+ return (getMemory().getInt(offset()) & 0x1) != 0;
+ }
+
+ public final void set(boolean value) {
+ getMemory().putInt(offset(), value ? 1 : 0);
+ }
+ }
+
+ /**
+ * Base class for all Number structure fields.
+ */
+ public abstract class NumberField extends Member {
+ /**
+ * Offset from the start of the <tt>Struct</tt> memory this field is located at.
+ */
+ private final int offset;
+ protected final Type type;
+
+ protected NumberField(NativeType type) {
+ Type t = this.type = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8);
+ }
+
+ protected NumberField(NativeType type, Offset offset) {
+ Type t = this.type = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8, offset);
+ }
+
+ protected NumberField(TypeAlias type) {
+ Type t = this.type = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8);
+ }
+
+ protected NumberField(TypeAlias type, Offset offset) {
+ Type t = this.type = getRuntime().findType(type);
+ this.offset = __info.addField(t.size() * 8, t.alignment() * 8, offset);
+ }
+
+
+ public final jnr.ffi.Pointer getMemory() {
+ return __info.getMemory();
+ }
+
+
+ /**
+ * Gets the <tt>Struct</tt> this <tt>Member</tt> is in.
+ *
+ * @return a <tt>Struct</tt>.
+ */
+ public final Struct struct() {
+ return Struct.this;
+ }
+
+ /**
+ * Gets the offset within the structure for this field.
+ */
+ public final long offset() {
+ return offset + __info.offset;
+ }
+
+ /**
+ * Sets the field to a new value.
+ *
+ * @param value The new value.
+ */
+ public abstract void set(java.lang.Number value);
+
+ /**
+ * Returns an {@code float} representation of this <tt>Number</tt>.
+ *
+ * @return an {@code float} value for this <tt>Number</tt>.
+ */
+ public double doubleValue() {
+ return (double) longValue();
+ }
+
+ /**
+ * Returns an {@code float} representation of this <tt>Number</tt>.
+ *
+ * @return an {@code float} value for this <tt>Number</tt>.
+ */
+ public float floatValue() {
+ return (float) intValue();
+ }
+
+ /**
+ * Returns a {@code byte} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code byte} value for this <tt>Number</tt>.
+ */
+ public byte byteValue() {
+ return (byte) intValue();
+ }
+
+ /**
+ * Returns a {@code short} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code short} value for this <tt>Number</tt>.
+ */
+ public short shortValue() {
+ return (short) intValue();
+ }
+
+ /**
+ * Returns a {@code int} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code int} value for this <tt>Number</tt>.
+ */
+ public abstract int intValue();
+
+ /**
+ * Returns a {@code long} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code long} value for this <tt>Number</tt>.
+ */
+ public long longValue() {
+ return intValue();
+ }
+
+ /**
+ * Returns a string representation of this <code>Address</code>.
+ *
+ * @return a string representation of this <code>Address</code>.
+ */
+ @Override
+ public java.lang.String toString() {
+ return java.lang.Integer.toString(intValue(), 10);
+ }
+ }
+
+ public abstract class IntegerAlias extends NumberField {
+ IntegerAlias(TypeAlias type) {
+ super(type);
+ }
+
+ IntegerAlias(TypeAlias type, Offset offset) {
+ super(type, offset);
+ }
+
+ @Override
+ public void set(Number value) {
+ getMemory().putInt(type, offset(), value.longValue());
+ }
+
+ public void set(long value) {
+ getMemory().putInt(type, offset(), value);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get() {
+ return getMemory().getInt(type, offset());
+ }
+
+
+ @Override
+ public int intValue() {
+ return (int) get();
+ }
+
+ @Override
+ public long longValue() {
+ return get();
+ }
+ }
+
+ /**
+ * An 8 bit signed integer
+ */
+ public class Signed8 extends NumberField {
+ /**
+ * Creates a new 8 bit integer field.
+ */
+ public Signed8() {
+ super(NativeType.SCHAR);
+ }
+
+ /**
+ * Creates a new 8 bit integer field at a specific offset
+ *
+ * @param offset The offset within the memory area
+ */
+ public Signed8(Offset offset) {
+ super(NativeType.SCHAR, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a byte.
+ */
+ public final byte get() {
+ return getMemory().getByte(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 8 bit value to set.
+ */
+ public final void set(byte value) {
+ getMemory().putByte(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putByte(offset(), value.byteValue());
+ }
+
+ /**
+ * Returns a java byte representation of this field.
+ *
+ * @return a java byte value for this field.
+ */
+ @Override
+ public final byte byteValue() {
+ return get();
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue() {
+ return get();
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return get();
+ }
+ }
+
+ /**
+ * An 8 bit unsigned integer
+ */
+ public class Unsigned8 extends NumberField {
+ /**
+ * Creates a new 8 bit unsigned integer field.
+ */
+ public Unsigned8() {
+ super(NativeType.UCHAR);
+ }
+
+ /**
+ * Creates a new 8 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned8(Offset offset) {
+ super(NativeType.UCHAR, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a byte.
+ */
+ public final short get() {
+ short value = getMemory().getByte(offset());
+ return value < 0 ? (short) ((value & 0x7F) + 0x80) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 8 bit value to set.
+ */
+ public final void set(short value) {
+ getMemory().putByte(offset(), (byte) value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putByte(offset(), value.byteValue());
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue() {
+ return get();
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return get();
+ }
+ }
+
+ /**
+ * A 16 bit signed integer field.
+ */
+ public class Signed16 extends NumberField {
+ /**
+ * Creates a new 16 bit integer field.
+ */
+ public Signed16() {
+ super(NativeType.SSHORT);
+ }
+
+ /**
+ * Creates a new 16 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed16(Offset offset) {
+ super(NativeType.SSHORT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a short.
+ */
+ public final short get() {
+ return getMemory().getShort(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 16 bit value to set.
+ */
+ public final void set(short value) {
+ getMemory().putShort(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putShort(offset(), value.shortValue());
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue() {
+ return get();
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return get();
+ }
+ }
+
+ /**
+ * A 16 bit signed integer field.
+ */
+ public class Unsigned16 extends NumberField {
+ /**
+ * Creates a new 16 bit integer field.
+ */
+ public Unsigned16() {
+ super(NativeType.USHORT);
+ }
+
+ /**
+ * Creates a new 16 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned16(Offset offset) {
+ super(NativeType.USHORT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a short.
+ */
+ public final int get() {
+ int value = getMemory().getShort(offset());
+ return value < 0 ? (int)((value & 0x7FFF) + 0x8000) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 16 bit unsigned value to set.
+ */
+ public final void set(int value) {
+ getMemory().putShort(offset(), (short) value);
+ }
+
+ public void set(Number value) {
+ getMemory().putShort(offset(), value.shortValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return get();
+ }
+ }
+
+ /**
+ * A 32 bit signed integer field.
+ */
+ public class Signed32 extends NumberField {
+ /**
+ * Creates a new 32 bit integer field.
+ */
+ public Signed32() {
+ super(NativeType.SINT);
+ }
+
+ /**
+ * Creates a new 32 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed32(Offset offset) {
+ super(NativeType.SINT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a int.
+ */
+ public final int get() {
+ return getMemory().getInt(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32 bit value to set.
+ */
+ public final void set(int value) {
+ getMemory().putInt(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putInt(offset(), value.intValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return get();
+ }
+ }
+
+ /**
+ * A 32 bit signed integer field.
+ */
+ public class Unsigned32 extends NumberField {
+ /**
+ * Creates a new 32 bit integer field.
+ */
+ public Unsigned32() {
+ super(NativeType.UINT);
+ }
+
+ /**
+ * Creates a new 32 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned32(Offset offset) {
+ super(NativeType.UINT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get() {
+ long value = getMemory().getInt(offset());
+ return value < 0 ? (long)((value & 0x7FFFFFFFL) + 0x80000000L) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32 bit unsigned value to set.
+ */
+ public final void set(long value) {
+ getMemory().putInt(offset(), (int) value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putInt(offset(), value.intValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue() {
+ return get();
+ }
+ }
+
+ /**
+ * A 64 bit signed integer field.
+ */
+ public class Signed64 extends NumberField {
+ /**
+ * Creates a new 64 bit integer field.
+ */
+ public Signed64() {
+ super(NativeType.SLONGLONG);
+ }
+
+ /**
+ * Creates a new 64 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed64(Offset offset) {
+ super(NativeType.SLONGLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get() {
+ return getMemory().getLongLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 64 bit value to set.
+ */
+ public final void set(long value) {
+ getMemory().putLongLong(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putLongLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue() {
+ return get();
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.Long.toString(get());
+ }
+ }
+
+ /**
+ * A 64 bit unsigned integer field.
+ */
+ public class Unsigned64 extends NumberField {
+ /**
+ * Creates a new 64 bit integer field.
+ */
+ public Unsigned64() {
+ super(NativeType.ULONGLONG);
+ }
+
+ /**
+ * Creates a new 64 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned64(Offset offset) {
+ super(NativeType.ULONGLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get() {
+ return getMemory().getLongLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 64 bit value to set.
+ */
+ public final void set(long value) {
+ getMemory().putLongLong(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putLongLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue() {
+ return get();
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.Long.toString(get());
+ }
+ }
+
+ /**
+ * A native long integer field.
+ */
+ public class SignedLong extends NumberField {
+ /**
+ * Creates a new native long field.
+ */
+ public SignedLong() {
+ super(NativeType.SLONG);
+ }
+
+ /**
+ * Creates a new signed native long field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public SignedLong(Offset offset) {
+ super(NativeType.SLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get() {
+ return getMemory().getNativeLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32/64 bit value to set.
+ */
+ public final void set(long value) {
+ getMemory().putNativeLong(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putNativeLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue() {
+ return get();
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.Long.toString(get());
+ }
+ }
+
+ /**
+ * A native long integer field.
+ */
+ public class UnsignedLong extends NumberField {
+
+ /**
+ * Creates a new native long field.
+ */
+ public UnsignedLong() {
+ super(NativeType.ULONG);
+ }
+
+ /**
+ * Creates a new unsigned native long field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public UnsignedLong(Offset offset) {
+ super(NativeType.ULONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a int.
+ */
+ public final long get() {
+ long value = getMemory().getNativeLong(offset());
+ final long mask = getRuntime().findType(NativeType.SLONG).size() == 32 ? 0xffffffffL : 0xffffffffffffffffL;
+ return value < 0
+ ? (long) ((value & mask) + mask + 1)
+ : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32/64 bit value to set.
+ */
+ public final void set(long value) {
+ getMemory().putNativeLong(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putNativeLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue() {
+ return get();
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.Long.toString(get());
+ }
+ }
+
+ public class Float extends NumberField {
+ public Float() {
+ super(NativeType.FLOAT);
+ }
+ /**
+ * Creates a new float field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Float(Offset offset) {
+ super(NativeType.FLOAT, offset);
+ }
+
+ public final float get() {
+ return getMemory().getFloat(offset());
+ }
+ public final void set(float value) {
+ getMemory().putFloat(offset(), value);
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putFloat(offset(), value.floatValue());
+ }
+
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ @Override
+ public final double doubleValue() {
+ return get();
+ }
+
+ @Override
+ public final float floatValue() {
+ return get();
+ }
+
+ @Override
+ public final long longValue() {
+ return (long) get();
+ }
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.String.valueOf(get());
+ }
+ }
+
+ public final class Double extends NumberField {
+ public Double() {
+ super(NativeType.DOUBLE);
+ }
+ public Double(Offset offset) {
+ super(NativeType.DOUBLE, offset);
+ }
+ public final double get() {
+ return getMemory().getDouble(offset());
+ }
+ public final void set(double value) {
+ getMemory().putDouble(offset(), value);
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putDouble(offset(), value.doubleValue());
+ }
+
+ @Override
+ public final int intValue() {
+ return (int) get();
+ }
+
+ @Override
+ public final long longValue() {
+ return (long) get();
+ }
+ @Override
+ public final float floatValue() {
+ return (float) get();
+ }
+
+ @Override
+ public final double doubleValue() {
+ return get();
+ }
+ @Override
+ public final java.lang.String toString() {
+ return java.lang.String.valueOf(get());
+ }
+ }
+
+ /**
+ * Represents a native memory address.
+ */
+ public class Address extends NumberField {
+
+ /**
+ * Creates a new <tt>Address</tt> field.
+ */
+ public Address() {
+ super(NativeType.ADDRESS);
+ }
+ public Address(Offset offset) {
+ super(NativeType.ADDRESS, offset);
+ }
+
+ /**
+ * Reads an {@code Address} value from the struct.
+ *
+ * @return a {@link jnr.ffi.Address}.
+ */
+ public final jnr.ffi.Address get() {
+ return jnr.ffi.Address.valueOf(getMemory().getAddress(offset()));
+ }
+
+ /**
+ * Puts a {@link jnr.ffi.Address} value into the native memory.
+ */
+ public final void set(jnr.ffi.Address value) {
+ getMemory().putAddress(offset(), value != null ? value.nativeAddress() : 0);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putAddress(offset(), value.longValue());
+ }
+ /**
+ * Returns an integer representation of this address.
+ *
+ * @return an integer value for this address.
+ */
+ @Override
+ public final int intValue() {
+ return get().intValue();
+ }
+
+ /**
+ * Returns an {@code long} representation of this address.
+ *
+ * @return an {@code long} value for this address.
+ */
+ @Override
+ public final long longValue() {
+ return get().longValue();
+ }
+
+ /**
+ * Returns a string representation of this <code>Address</code>.
+ *
+ * @return a string representation of this <code>Address</code>.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return get().toString();
+ }
+ }
+
+ /**
+ * Represents a native memory address.
+ */
+ public class Pointer extends NumberField {
+ /**
+ * Creates a new <tt>Address</tt> field.
+ */
+ public Pointer() {
+ super(NativeType.ADDRESS);
+ }
+ public Pointer(Offset offset) {
+ super(NativeType.ADDRESS, offset);
+ }
+
+ /**
+ * Gets the {@link jnr.ffi.Pointer} value from the native memory.
+ *
+ * @return a {@link jnr.ffi.Pointer}.
+ */
+ public final jnr.ffi.Pointer get() {
+ return getMemory().getPointer(offset());
+ }
+
+ /**
+ * Gets the size of a Pointer in bits
+ *
+ * @return the size of the Pointer
+ */
+ public final int size() {
+ return getRuntime().findType(NativeType.ADDRESS).size() * 8;
+ }
+
+ /**
+ * Puts a {@link jnr.ffi.Address} value into the native memory.
+ */
+ public final void set(jnr.ffi.Pointer value) {
+ getMemory().putPointer(offset(), value);
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putAddress(offset(), value.longValue());
+ }
+ /**
+ * Returns an integer representation of this <code>Pointer</code>.
+ *
+ * @return an integer value for this <code>Pointer</code>.
+ */
+ @Override
+ public final int intValue() {
+ return (int) getMemory().getAddress(offset());
+ }
+
+ /**
+ * Returns an {@code long} representation of this <code>Pointer</code>.
+ *
+ * @return an {@code long} value for this <code>Pointer</code>.
+ */
+ @Override
+ public final long longValue() {
+ return getMemory().getAddress(offset());
+ }
+
+ /**
+ * Returns a string representation of this <code>Pointer</code>.
+ *
+ * @return a string representation of this <code>Pointer</code>.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return get().toString();
+ }
+ }
+
+ /**
+ * Base for all the Enum fields.
+ *
+ * @param <E> the type of {@link java.lang.Enum}
+ */
+ protected abstract class EnumField<E> extends NumberField {
+ protected final Class<E> enumClass;
+
+ /**
+ * Constructs a new Enum field.
+ *
+ * @param type the native type of the enum.
+ * @param enumClass the Enum class.
+ */
+ public EnumField(NativeType type, Class<E> enumClass) {
+ super(type);
+ this.enumClass = enumClass;
+ }
+
+ /**
+ * Gets a java Enum value representing the native integer value.
+ *
+ * @return a java Enum value.
+ */
+ public abstract E get();
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString() {
+ return get().toString();
+ }
+ }
+ /**
+ * An 8 bit enum field.
+ *
+ * @param <E> the {@link java.lang.Enum} to translate to/from.
+ */
+ public class Enum8<E extends java.lang.Enum<E>> extends EnumField<E> {
+ /**
+ * Creates a new 8 bit enum field.
+ *
+ * @param enumClass the class of the {@link java.lang.Enum}.
+ */
+ public Enum8(Class<E> enumClass) {
+ super(NativeType.SCHAR, enumClass);
+ }
+
+ /**
+ * Gets a java Enum value representing the native integer value.
+ *
+ * @return a java Enum value.
+ */
+ public final E get() {
+ return enumClass.cast(EnumMapper.getInstance(enumClass).valueOf(intValue()));
+ }
+
+ /**
+ * Sets the native integer value using a java Enum value.
+ *
+ * @param value the java <tt>Enum</tt> value.
+ */
+ public final void set(E value) {
+ getMemory().putByte(offset(), (byte) EnumMapper.getInstance(enumClass).intValue(value));
+ }
+
+ public void set(java.lang.Number value) {
+ getMemory().putByte(offset(), value.byteValue());
+ }
+ /**
+ * Returns an integer representation of this enum field.
+ *
+ * @return an integer value for this enum field.
+ */
+ @Override
+ public final int intValue() {
+ return getMemory().getByte(offset());
+ }
+ }
+
+ public class Enum16<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum16(Class<E> enumClass) {
+ super(NativeType.SSHORT, enumClass);
+ }
+ public final E get() {
+ return enumClass.cast(EnumMapper.getInstance(enumClass).valueOf(intValue()));
+ }
+ public final void set(E value) {
+ getMemory().putShort(offset(), (short) EnumMapper.getInstance(enumClass).intValue(value));
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putShort(offset(), value.shortValue());
+ }
+ @Override
+ public final int intValue() {
+ return getMemory().getShort(offset());
+ }
+ }
+
+ public class Enum32<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum32(Class<E> enumClass) {
+ super(NativeType.SINT, enumClass);
+ }
+ public final E get() {
+ return enumClass.cast(EnumMapper.getInstance(enumClass).valueOf(intValue()));
+ }
+ public final void set(E value) {
+ getMemory().putInt(offset(), EnumMapper.getInstance(enumClass).intValue(value));
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putInt(offset(), value.intValue());
+ }
+ @Override
+ public final int intValue() {
+ return getMemory().getInt(offset());
+ }
+ }
+
+ public class Enum64<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum64(Class<E> enumClass) {
+ super(NativeType.SLONGLONG, enumClass);
+ }
+ public final E get() {
+ return enumClass.cast(EnumMapper.getInstance(enumClass).valueOf(intValue()));
+ }
+ public final void set(E value) {
+ getMemory().putLongLong(offset(), EnumMapper.getInstance(enumClass).intValue(value));
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putLongLong(offset(), value.longValue());
+ }
+ @Override
+ public final int intValue() {
+ return (int) longValue();
+ }
+ @Override
+ public final long longValue() {
+ return getMemory().getLongLong(offset());
+ }
+ }
+
+ public class EnumLong<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public EnumLong(Class<E> enumClass) {
+ super(NativeType.SLONG, enumClass);
+ }
+
+ public final E get() {
+ return enumClass.cast(EnumMapper.getInstance(enumClass).valueOf(intValue()));
+ }
+ public final void set(E value) {
+ getMemory().putNativeLong(offset(), EnumMapper.getInstance(enumClass).intValue(value));
+ }
+ public void set(java.lang.Number value) {
+ getMemory().putNativeLong(offset(), value.longValue());
+ }
+
+ @Override
+ public final int intValue() {
+ return (int) longValue();
+ }
+
+ @Override
+ public final long longValue() {
+ return getMemory().getNativeLong(offset());
+ }
+ }
+
+ public class Enum<T extends java.lang.Enum<T>> extends Enum32<T> {
+ public Enum(Class<T> enumClass) {
+ super(enumClass);
+ }
+ }
+
+ abstract public class String extends AbstractMember {
+ protected final Charset charset;
+ protected final int length;
+
+ protected String(int size, int align, int length, Charset cs) {
+ super(size, align);
+ this.length = length;
+ this.charset = cs;
+ }
+ protected String(int size, int align, Offset offset, int length, Charset cs) {
+ super(size, align, offset);
+ this.length = length;
+ this.charset = cs;
+ }
+ public final int length() {
+ return length;
+ }
+
+ protected abstract jnr.ffi.Pointer getStringMemory();
+ public abstract java.lang.String get();
+ public abstract void set(java.lang.String value);
+
+ @Override
+ public final java.lang.String toString() {
+ return get();
+ }
+ }
+
+ public class UTFString extends String {
+ public UTFString(int length, Charset cs) {
+ super(length * 8, 8, length, cs); // FIXME: This won't work for non-ASCII
+
+ }
+ protected jnr.ffi.Pointer getStringMemory() {
+ return getMemory().slice(offset(), length());
+ }
+
+ public final java.lang.String get() {
+ return getStringMemory().getString(0, length, charset);
+ }
+
+ public final void set(java.lang.String value) {
+ getStringMemory().putString(0, value, length, charset);
+ }
+ }
+
+ public class UTF8String extends UTFString {
+ public UTF8String(int size) {
+ super(size, UTF8);
+ }
+ }
+
+ public class AsciiString extends UTFString {
+ public AsciiString(int size) {
+ super(size, ASCII);
+ }
+ }
+
+ public class UTFStringRef extends String {
+ private jnr.ffi.Pointer valueHolder;
+
+ public UTFStringRef(int length, Charset cs) {
+ super(getRuntime().findType(NativeType.ADDRESS).size() * 8, getRuntime().findType(NativeType.ADDRESS).alignment() * 8,
+ length, cs);
+ }
+
+ public UTFStringRef(Charset cs) {
+ this(Integer.MAX_VALUE, cs);
+ }
+
+ protected jnr.ffi.Pointer getStringMemory() {
+ return getMemory().getPointer(offset(), length());
+ }
+
+ public final java.lang.String get() {
+ jnr.ffi.Pointer ptr = getStringMemory();
+ return ptr != null ? ptr.getString(0, length, charset) : null;
+ }
+
+ public final void set(java.lang.String value) {
+ if (value != null) {
+ valueHolder = getRuntime().getMemoryManager().allocateDirect(length() * 4);
+ valueHolder.putString(0, value, length() * 4, charset);
+ getMemory().putPointer(offset(), valueHolder);
+
+ } else {
+ this.valueHolder = null;
+ getMemory().putAddress(offset(), 0);
+ }
+ }
+ }
+
+ public class UTF8StringRef extends UTFStringRef {
+ public UTF8StringRef(int size) {
+ super(size, UTF8);
+ }
+ public UTF8StringRef() {
+ super(Integer.MAX_VALUE, UTF8);
+ }
+ }
+
+ public class AsciiStringRef extends UTFStringRef {
+ public AsciiStringRef(int size) {
+ super(size, ASCII);
+ }
+ public AsciiStringRef() {
+ super(Integer.MAX_VALUE, ASCII);
+ }
+ }
+
+ /**
+ * Specialized padding fields for structs. Use this instead of arrays of other
+ * members for more efficient struct construction.
+ */
+ protected final class Padding extends AbstractMember {
+ public Padding(Type type, int length) {
+ super(type.size() * 8 * length, type.alignment() * 8);
+ }
+
+ public Padding(NativeType type, int length) {
+ super(getRuntime().findType(type).size() * 8 * length, getRuntime().findType(type).alignment() * 8);
+ }
+ }
+
+ protected final class Function<T> extends AbstractMember {
+ private final Class<? extends T> closureClass;
+ private T instance;
+
+ public Function(Class<? extends T> closureClass) {
+ super(NativeType.ADDRESS);
+ this.closureClass = closureClass;
+ }
+
+ public final void set(T value) {
+ getMemory().putPointer(offset(), getRuntime().getClosureManager().getClosurePointer(closureClass, instance = value));
+ }
+ }
+
+ protected final <T> Function<T> function(Class<T> closureClass) {
+ return new Function<T>(closureClass);
+ }
+
+ public final class int8_t extends IntegerAlias {
+ public int8_t() { super(TypeAlias.int8_t); }
+ public int8_t(Offset offset) { super(TypeAlias.int8_t, offset); }
+ }
+
+ public final class u_int8_t extends IntegerAlias {
+ public u_int8_t() { super(TypeAlias.u_int8_t); }
+ public u_int8_t(Offset offset) { super(TypeAlias.u_int8_t, offset); }
+ }
+
+ public final class int16_t extends IntegerAlias {
+ public int16_t() { super(TypeAlias.int16_t); }
+ public int16_t(Offset offset) { super(TypeAlias.int16_t, offset); }
+ }
+
+ public final class u_int16_t extends IntegerAlias {
+ public u_int16_t() { super(TypeAlias.u_int16_t); }
+ public u_int16_t(Offset offset) { super(TypeAlias.u_int16_t, offset); }
+ }
+
+ public final class int32_t extends IntegerAlias {
+ public int32_t() { super(TypeAlias.int32_t); }
+ public int32_t(Offset offset) { super(TypeAlias.int32_t, offset); }
+ }
+
+ public final class u_int32_t extends IntegerAlias {
+ public u_int32_t() { super(TypeAlias.u_int32_t); }
+ public u_int32_t(Offset offset) { super(TypeAlias.u_int32_t, offset); }
+ }
+
+ public final class int64_t extends IntegerAlias {
+ public int64_t() { super(TypeAlias.int64_t); }
+ public int64_t(Offset offset) { super(TypeAlias.int64_t, offset); }
+ }
+
+ public final class u_int64_t extends IntegerAlias {
+ public u_int64_t() { super(TypeAlias.u_int64_t); }
+ public u_int64_t(Offset offset) { super(TypeAlias.u_int64_t, offset); }
+ }
+
+ public final class intptr_t extends IntegerAlias {
+ public intptr_t() { super(TypeAlias.intptr_t); }
+ public intptr_t(Offset offset) { super(TypeAlias.intptr_t, offset); }
+ }
+
+ public final class uintptr_t extends IntegerAlias {
+ public uintptr_t() { super(TypeAlias.uintptr_t); }
+ public uintptr_t(Offset offset) { super(TypeAlias.uintptr_t, offset); }
+ }
+
+ public final class caddr_t extends IntegerAlias {
+ public caddr_t() { super(TypeAlias.caddr_t); }
+ public caddr_t(Offset offset) { super(TypeAlias.caddr_t, offset); }
+ }
+
+ public final class dev_t extends IntegerAlias {
+ public dev_t() { super(TypeAlias.dev_t); }
+ public dev_t(Offset offset) { super(TypeAlias.dev_t, offset); }
+ }
+
+ public final class blkcnt_t extends IntegerAlias {
+ public blkcnt_t() { super(TypeAlias.blkcnt_t); }
+ public blkcnt_t(Offset offset) { super(TypeAlias.blkcnt_t, offset); }
+ }
+
+ public final class blksize_t extends IntegerAlias {
+ public blksize_t() { super(TypeAlias.blksize_t); }
+ public blksize_t(Offset offset) { super(TypeAlias.blksize_t, offset); }
+ }
+
+ public final class gid_t extends IntegerAlias {
+ public gid_t() { super(TypeAlias.gid_t); }
+ public gid_t(Offset offset) { super(TypeAlias.gid_t, offset); }
+ }
+
+ public final class in_addr_t extends IntegerAlias {
+ public in_addr_t() { super(TypeAlias.in_addr_t); }
+ public in_addr_t(Offset offset) { super(TypeAlias.in_addr_t, offset); }
+ }
+
+ public final class in_port_t extends IntegerAlias {
+ public in_port_t() { super(TypeAlias.in_port_t); }
+ public in_port_t(Offset offset) { super(TypeAlias.in_port_t, offset); }
+ }
+
+ public final class ino_t extends IntegerAlias {
+ public ino_t() { super(TypeAlias.ino_t); }
+ public ino_t(Offset offset) { super(TypeAlias.ino_t, offset); }
+ }
+
+ public final class ino64_t extends IntegerAlias {
+ public ino64_t() { super(TypeAlias.ino64_t); }
+ public ino64_t(Offset offset) { super(TypeAlias.ino64_t, offset); }
+ }
+
+ public final class key_t extends IntegerAlias {
+ public key_t() { super(TypeAlias.key_t); }
+ public key_t(Offset offset) { super(TypeAlias.key_t, offset); }
+ }
+
+ public final class mode_t extends IntegerAlias {
+ public mode_t() { super(TypeAlias.mode_t); }
+ public mode_t(Offset offset) { super(TypeAlias.mode_t, offset); }
+ }
+
+ public final class nlink_t extends IntegerAlias {
+ public nlink_t() { super(TypeAlias.nlink_t); }
+ public nlink_t(Offset offset) { super(TypeAlias.nlink_t, offset); }
+ }
+
+ public final class id_t extends IntegerAlias {
+ public id_t() { super(TypeAlias.id_t); }
+ public id_t(Offset offset) { super(TypeAlias.id_t, offset); }
+ }
+
+ public final class pid_t extends IntegerAlias {
+ public pid_t() { super(TypeAlias.pid_t); }
+ public pid_t(Offset offset) { super(TypeAlias.pid_t, offset); }
+ }
+
+ public final class off_t extends IntegerAlias {
+ public off_t() { super(TypeAlias.off_t); }
+ public off_t(Offset offset) { super(TypeAlias.off_t, offset); }
+ }
+
+ public final class swblk_t extends IntegerAlias {
+ public swblk_t() { super(TypeAlias.swblk_t); }
+ public swblk_t(Offset offset) { super(TypeAlias.swblk_t, offset); }
+ }
+
+ public final class uid_t extends IntegerAlias {
+ public uid_t() { super(TypeAlias.uid_t); }
+ public uid_t(Offset offset) { super(TypeAlias.uid_t, offset); }
+ }
+
+ public final class clock_t extends IntegerAlias {
+ public clock_t() { super(TypeAlias.clock_t); }
+ public clock_t(Offset offset) { super(TypeAlias.clock_t, offset); }
+ }
+
+ public final class size_t extends IntegerAlias {
+ public size_t() { super(TypeAlias.size_t); }
+ public size_t(Offset offset) { super(TypeAlias.size_t, offset); }
+ }
+
+ public final class ssize_t extends IntegerAlias {
+ public ssize_t() { super(TypeAlias.ssize_t); }
+ public ssize_t(Offset offset) { super(TypeAlias.ssize_t, offset); }
+ }
+
+ public final class time_t extends IntegerAlias {
+ public time_t() { super(TypeAlias.time_t); }
+ public time_t(Offset offset) { super(TypeAlias.time_t, offset); }
+ }
+
+ public final class fsblkcnt_t extends IntegerAlias {
+ public fsblkcnt_t() { super(TypeAlias.fsblkcnt_t); }
+ public fsblkcnt_t(Offset offset) { super(TypeAlias.fsblkcnt_t, offset); }
+ }
+
+ public final class fsfilcnt_t extends IntegerAlias {
+ public fsfilcnt_t() { super(TypeAlias.fsfilcnt_t); }
+ public fsfilcnt_t(Offset offset) { super(TypeAlias.fsfilcnt_t, offset); }
+ }
+
+ public final class sa_family_t extends IntegerAlias {
+ public sa_family_t() { super(TypeAlias.sa_family_t); }
+ public sa_family_t(Offset offset) { super(TypeAlias.sa_family_t, offset); }
+ }
+
+ public final class socklen_t extends IntegerAlias {
+ public socklen_t() { super(TypeAlias.socklen_t); }
+ public socklen_t(Offset offset) { super(TypeAlias.socklen_t, offset); }
+ }
+
+ public final class rlim_t extends IntegerAlias {
+ public rlim_t() { super(TypeAlias.rlim_t); }
+ public rlim_t(Offset offset) { super(TypeAlias.rlim_t, offset); }
+ }
+}
diff --git a/src/main/java/jnr/ffi/StructLayout.java b/src/main/java/jnr/ffi/StructLayout.java
new file mode 100644
index 0000000..655958e
--- /dev/null
+++ b/src/main/java/jnr/ffi/StructLayout.java
@@ -0,0 +1,1866 @@
+package jnr.ffi;
+
+import jnr.ffi.util.EnumMapper;
+
+import java.lang.reflect.Constructor;
+import java.nio.charset.Charset;
+
+/**
+ *
+ */
+public class StructLayout extends Type {
+ static final Charset ASCII = Charset.forName("ASCII");
+ static final Charset UTF8 = Charset.forName("UTF-8");
+
+ private final Runtime runtime;
+ private final boolean isUnion = false;
+ private boolean resetIndex = false;
+ StructLayout enclosing = null;
+ int offset = 0;
+ int size = 0;
+ int alignment = 1;
+ int paddedSize = 0;
+
+ /**
+ * Creates a new <tt>StructLayout</tt>.
+ */
+ protected StructLayout(Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ protected StructLayout(Runtime runtime, int structSize) {
+ this.runtime = runtime;
+ this.size = this.paddedSize = structSize;
+ }
+
+ public final Runtime getRuntime() {
+ return this.runtime;
+ }
+
+ public final int size() {
+ return paddedSize;
+ }
+
+ public final int alignment() {
+ return alignment;
+ }
+
+ public final int offset() {
+ return offset;
+ }
+
+ public NativeType getNativeType() {
+ return NativeType.STRUCT;
+ }
+
+ /**
+ * Returns a human readable {@link java.lang.String} representation of the structure.
+ *
+ * @return a <tt>String representation of this structure.
+ */
+ @Override
+ public java.lang.String toString() {
+ StringBuilder sb = new StringBuilder();
+ java.lang.reflect.Field[] fields = getClass().getDeclaredFields();
+ sb.append(getClass().getSimpleName()).append(" { \n");
+ final java.lang.String fieldPrefix = " ";
+ for (java.lang.reflect.Field field : fields) {
+ try {
+ sb.append(fieldPrefix).append('\n');
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ sb.append("}\n");
+
+ return sb.toString();
+ }
+
+ private static int align(int offset, int alignment) {
+ return (offset + alignment - 1) & ~(alignment - 1);
+ }
+
+
+ protected final int addField(int size, int align) {
+ final int off = resetIndex ? 0 : align(this.size, align);
+ this.size = Math.max(this.size, off + size);
+ this.alignment = Math.max(this.alignment, align);
+ this.paddedSize = align(this.size, this.alignment);
+ return off;
+ }
+
+ protected final int addField(int size, int align, Offset offset) {
+ this.size = Math.max(this.size, offset.intValue() + size);
+ this.alignment = Math.max(this.alignment, align);
+ this.paddedSize = align(this.size, this.alignment);
+ return offset.intValue();
+ }
+
+
+ protected final int addField(Type t) {
+ return addField(t.size(), t.alignment());
+ }
+
+ protected final int addField(Type t, Offset offset) {
+ return addField(t.size(), t.alignment(), offset);
+ }
+
+ protected final Offset at(int offset) {
+ return new Offset(offset);
+ }
+
+ /**
+ * Interface all Struct members must implement.
+ */
+ protected abstract class Field {
+ private final int offset;
+
+ protected Field(int offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Gets the <tt>Struct</tt> this <tt>Member</tt> is a member of.
+ *
+ * @return a <tt>Struct</tt>.
+ */
+ public final StructLayout enclosing() {
+ return StructLayout.this;
+ }
+
+ /**
+ * Gets the offset within the structure for this field.
+ */
+ public final long offset() {
+ return offset + StructLayout.this.offset;
+ }
+ }
+
+ /**
+ * Starts an array construction session
+ */
+ protected final void arrayBegin() {
+ resetIndex = false;
+ }
+
+ /**
+ * Ends an array construction session
+ */
+ protected final void arrayEnd() {
+ resetIndex = isUnion;
+ }
+
+ /**
+ * Creates an array of <tt>Member</tt> instances.
+ *
+ * @param <T> The type of the <tt>Member</tt> subclass to create.
+ * @param array the array to store the instances in
+ * @return the array that was passed in
+ */
+ @SuppressWarnings("unchecked")
+ protected <T extends Field> T[] array(T[] array) {
+ arrayBegin();
+ try {
+ Class<?> arrayClass = array.getClass().getComponentType();
+ Constructor<?> ctor = arrayClass.getDeclaredConstructor(new Class[] { arrayClass.getEnclosingClass() });
+ Object[] parameters = { StructLayout.this };
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (T) ctor.newInstance(parameters);
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ arrayEnd();
+ return array;
+ }
+
+ protected final <T extends StructLayout> T inner(T structLayout) {
+ structLayout.enclosing = this;
+ structLayout.offset = align(this.size, structLayout.alignment);
+ this.size = structLayout.offset + structLayout.size;
+ this.paddedSize = align(this.size, this.alignment());
+
+ return structLayout;
+ }
+
+ protected static final class Offset extends java.lang.Number {
+ private final int offset;
+
+ public Offset(int offset) {
+ this.offset = offset;
+ }
+ @Override
+ public int intValue() {
+ return offset;
+ }
+ @Override
+ public long longValue() {
+ return offset;
+ }
+ @Override
+ public float floatValue() {
+ return offset;
+ }
+ @Override
+ public double doubleValue() {
+ return offset;
+ }
+ }
+
+ /**
+ * Base implementation of Member
+ */
+ protected abstract class AbstractField extends Field {
+ protected AbstractField(int size, int align, Offset offset) {
+ super(addField(size, align, offset));
+ }
+ protected AbstractField(int size, int align) {
+ super(addField(size, align));
+ }
+
+ protected AbstractField(NativeType type) {
+ super(addField(getRuntime().findType(type)));
+ }
+
+ protected AbstractField(Type type) {
+ super(addField(type));
+ }
+
+ protected AbstractField(NativeType type, Offset offset) {
+ super(addField(getRuntime().findType(type), offset));
+ }
+
+ protected AbstractField(Type type, Offset offset) {
+ super(addField(type, offset));
+ }
+ }
+
+
+
+ /**
+ * Base class for Boolean fields
+ */
+ protected abstract class AbstractBoolean extends AbstractField {
+ protected AbstractBoolean(NativeType type) {
+ super(type);
+ }
+
+ protected AbstractBoolean(NativeType type, Offset offset) {
+ super(type, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a boolean.
+ */
+ public abstract boolean get(jnr.ffi.Pointer ptr);
+
+ /**
+ * Sets the field to a new value.
+ *
+ * @param value The new value.
+ */
+ public abstract void set(jnr.ffi.Pointer ptr, boolean value);
+
+ /**
+ * Returns a string representation of this <code>Boolean</code>.
+ *
+ * @return a string representation of this <code>Boolean</code>.
+ */
+ public java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Boolean.toString(get(ptr));
+ }
+ }
+
+
+ /**
+ * A normal C boolean - 1 byte in size
+ */
+ protected final class Boolean extends AbstractBoolean {
+ protected Boolean() {
+ super(NativeType.SCHAR);
+ }
+
+ protected Boolean(Offset offset) {
+ super(NativeType.SCHAR, offset);
+ }
+
+ public final boolean get(jnr.ffi.Pointer ptr) {
+ return (ptr.getByte(offset()) & 0x1) != 0;
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, boolean value) {
+ ptr.putByte(offset(), (byte) (value ? 1 : 0));
+ }
+ }
+
+ /**
+ * A Windows BOOL - 4 bytes
+ */
+ protected final class WBOOL extends AbstractBoolean {
+ protected WBOOL() {
+ super(NativeType.SINT);
+ }
+
+ protected WBOOL(Offset offset) {
+ super(NativeType.SINT, offset);
+ }
+
+ public final boolean get(jnr.ffi.Pointer ptr) {
+ return (ptr.getInt(offset()) & 0x1) != 0;
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, boolean value) {
+ ptr.putInt(offset(), value ? 1 : 0);
+ }
+ }
+
+ /**
+ * Base class for all Number structure fields.
+ */
+ protected abstract class NumberField extends Field {
+ protected final Type type;
+
+ protected NumberField(NativeType nativeType) {
+ this(getRuntime().findType(nativeType));
+ }
+
+ protected NumberField(Type type) {
+ super(addField(type));
+ this.type = type;
+ }
+
+ protected NumberField(NativeType nativeType, Offset offset) {
+ this(getRuntime().findType(nativeType), offset);
+ }
+
+ protected NumberField(Type type, Offset offset) {
+ super(addField(type, offset));
+ this.type = type;
+ }
+
+
+ /**
+ * Sets the field to a new value.
+ *
+ * @param value The new value.
+ */
+ public abstract void set(jnr.ffi.Pointer ptr, java.lang.Number value);
+
+ /**
+ * Returns an {@code float} representation of this <tt>Number</tt>.
+ *
+ * @return an {@code float} value for this <tt>Number</tt>.
+ */
+ public double doubleValue(jnr.ffi.Pointer ptr) {
+ return (double) longValue(ptr);
+ }
+
+ /**
+ * Returns an {@code float} representation of this <tt>Number</tt>.
+ *
+ * @return an {@code float} value for this <tt>Number</tt>.
+ */
+ public float floatValue(jnr.ffi.Pointer ptr) {
+ return (float) intValue(ptr);
+ }
+
+ /**
+ * Returns a {@code byte} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code byte} value for this <tt>Number</tt>.
+ */
+ public byte byteValue(jnr.ffi.Pointer ptr) {
+ return (byte) intValue(ptr);
+ }
+
+ /**
+ * Returns a {@code short} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code short} value for this <tt>Number</tt>.
+ */
+ public short shortValue(jnr.ffi.Pointer ptr) {
+ return (short) intValue(ptr);
+ }
+
+ /**
+ * Returns a {@code int} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code int} value for this <tt>Number</tt>.
+ */
+ public abstract int intValue(jnr.ffi.Pointer ptr);
+
+ /**
+ * Returns a {@code long} representation of this <tt>Number</tt>.
+ *
+ * @return a {@code long} value for this <tt>Number</tt>.
+ */
+ public long longValue(jnr.ffi.Pointer ptr) {
+ return intValue(ptr);
+ }
+
+ /**
+ * Returns a string representation of this <code>Number</code>.
+ *
+ * @return a string representation of this <code>Number</code>.
+ */
+ public java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Integer.toString(intValue(ptr), 10);
+ }
+ }
+
+ public abstract class IntegerAlias extends NumberField {
+ protected IntegerAlias(TypeAlias type) {
+ super(getRuntime().findType(type));
+ }
+
+ protected IntegerAlias(TypeAlias type, Offset offset) {
+ super(getRuntime().findType(type), offset);
+ }
+
+ @Override
+ public void set(jnr.ffi.Pointer ptr, Number value) {
+ ptr.putInt(type, offset(), value.longValue());
+ }
+
+ public void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putInt(type, offset(), value);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ return ptr.getInt(type, offset());
+ }
+
+
+ @Override
+ public int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ @Override
+ public long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * An 8 bit signed integer
+ */
+ public class Signed8 extends NumberField {
+ /**
+ * Creates a new 8 bit integer field.
+ */
+ public Signed8() {
+ super(NativeType.SCHAR);
+ }
+
+ /**
+ * Creates a new 8 bit integer field at a specific offset
+ *
+ * @param offset The offset within the memory area
+ */
+ public Signed8(Offset offset) {
+ super(NativeType.SCHAR, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a byte.
+ */
+ public final byte get(jnr.ffi.Pointer ptr) {
+ return ptr.getByte(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param ptr The memory to set the value in.
+ * @param value the 8 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, byte value) {
+ ptr.putByte(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putByte(offset(), value.byteValue());
+ }
+
+ /**
+ * Returns a java byte representation of this field.
+ *
+ * @return a java byte value for this field.
+ */
+ @Override
+ public final byte byteValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * An 8 bit unsigned integer
+ */
+ public class Unsigned8 extends NumberField {
+ /**
+ * Creates a new 8 bit unsigned integer field.
+ */
+ public Unsigned8() {
+ super(NativeType.UCHAR);
+ }
+
+ /**
+ * Creates a new 8 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned8(Offset offset) {
+ super(NativeType.UCHAR, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a byte.
+ */
+ public final short get(jnr.ffi.Pointer ptr) {
+ short value = ptr.getByte(offset());
+ return value < 0 ? (short) ((value & 0x7F) + 0x80) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 8 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, short value) {
+ ptr.putByte(offset(), (byte) value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putByte(offset(), value.byteValue());
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * A 16 bit signed integer field.
+ */
+ public class Signed16 extends NumberField {
+ /**
+ * Creates a new 16 bit integer field.
+ */
+ public Signed16() {
+ super(NativeType.SSHORT);
+ }
+
+ /**
+ * Creates a new 16 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed16(Offset offset) {
+ super(NativeType.SSHORT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a short.
+ */
+ public final short get(jnr.ffi.Pointer ptr) {
+ return ptr.getShort(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 16 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, short value) {
+ ptr.putShort(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putShort(offset(), value.shortValue());
+ }
+
+ /**
+ * Returns a java short representation of this field.
+ *
+ * @return a java short value for this field.
+ */
+ @Override
+ public final short shortValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * A 16 bit signed integer field.
+ */
+ public class Unsigned16 extends NumberField {
+ /**
+ * Creates a new 16 bit integer field.
+ */
+ public Unsigned16() {
+ super(NativeType.USHORT);
+ }
+
+ /**
+ * Creates a new 16 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned16(Offset offset) {
+ super(NativeType.USHORT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a short.
+ */
+ public final int get(jnr.ffi.Pointer ptr) {
+ int value = ptr.getShort(offset());
+ return value < 0 ? (int)((value & 0x7FFF) + 0x8000) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 16 bit unsigned value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, int value) {
+ ptr.putShort(offset(), (short) value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, Number value) {
+ ptr.putShort(offset(), value.shortValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * A 32 bit signed integer field.
+ */
+ public class Signed32 extends NumberField {
+ /**
+ * Creates a new 32 bit integer field.
+ */
+ public Signed32() {
+ super(NativeType.SINT);
+ }
+
+ /**
+ * Creates a new 32 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed32(Offset offset) {
+ super(NativeType.SINT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a int.
+ */
+ public final int get(jnr.ffi.Pointer ptr) {
+ return ptr.getInt(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, int value) {
+ ptr.putInt(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putInt(offset(), value.intValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * A 32 bit signed integer field.
+ */
+ public class Unsigned32 extends NumberField {
+ /**
+ * Creates a new 32 bit integer field.
+ */
+ public Unsigned32() {
+ super(NativeType.UINT);
+ }
+
+ /**
+ * Creates a new 32 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned32(Offset offset) {
+ super(NativeType.SINT, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ long value = ptr.getInt(offset());
+ return value < 0 ? (long)((value & 0x7FFFFFFFL) + 0x80000000L) : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32 bit unsigned value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putInt(offset(), (int) value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putInt(offset(), value.intValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ /**
+ * A 64 bit signed integer field.
+ */
+ public class Signed64 extends NumberField {
+ /**
+ * Creates a new 64 bit integer field.
+ */
+ public Signed64() {
+ super(NativeType.SLONGLONG);
+ }
+
+ /**
+ * Creates a new 64 bit signed integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Signed64(Offset offset) {
+ super(NativeType.SLONGLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ return ptr.getLongLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 64 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putLongLong(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putLongLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Long.toString(get(ptr));
+ }
+ }
+
+ /**
+ * A 64 bit unsigned integer field.
+ */
+ public class Unsigned64 extends NumberField {
+ /**
+ * Creates a new 64 bit integer field.
+ */
+ public Unsigned64() {
+ super(NativeType.ULONGLONG);
+ }
+
+ /**
+ * Creates a new 64 bit unsigned integer field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Unsigned64(Offset offset) {
+ super(NativeType.ULONGLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ return ptr.getLongLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 64 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putLongLong(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putLongLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Long.toString(get(ptr));
+ }
+ }
+
+ /**
+ * A native long integer field.
+ */
+ public class SignedLong extends NumberField {
+ /**
+ * Creates a new native long field.
+ */
+ public SignedLong() {
+ super(NativeType.SLONG);
+ }
+
+ /**
+ * Creates a new signed native long field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public SignedLong(Offset offset) {
+ super(NativeType.SLONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a long.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ return ptr.getNativeLong(offset());
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32/64 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putNativeLong(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putNativeLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Long.toString(get(ptr));
+ }
+ }
+
+ /**
+ * A native long integer field.
+ */
+ public class UnsignedLong extends NumberField {
+
+ /**
+ * Creates a new native long field.
+ */
+ public UnsignedLong() {
+ super(NativeType.ULONG);
+ }
+
+ /**
+ * Creates a new unsigned native long field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public UnsignedLong(Offset offset) {
+ super(NativeType.ULONG, offset);
+ }
+
+ /**
+ * Gets the value for this field.
+ *
+ * @return a int.
+ */
+ public final long get(jnr.ffi.Pointer ptr) {
+ long value = ptr.getNativeLong(offset());
+ final long mask = getRuntime().findType(NativeType.SLONG).size() == 4 ? 0xffffffffL : 0xffffffffffffffffL;
+ return value < 0
+ ? (long) ((value & mask) + mask + 1)
+ : value;
+ }
+
+ /**
+ * Sets the value for this field.
+ *
+ * @param value the 32/64 bit value to set.
+ */
+ public final void set(jnr.ffi.Pointer ptr, long value) {
+ ptr.putNativeLong(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putNativeLong(offset(), value.longValue());
+ }
+
+ /**
+ * Returns a java int representation of this field.
+ *
+ * @return a java int value for this field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ /**
+ * Returns a java long representation of this field.
+ *
+ * @return a java long value for this field.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.Long.toString(get(ptr));
+ }
+ }
+
+ public class Float extends NumberField {
+ public Float() {
+ super(NativeType.FLOAT);
+ }
+ /**
+ * Creates a new float field at a specific offset
+ *
+ * @param offset The offset within the memory area for this field.
+ */
+ public Float(Offset offset) {
+ super(NativeType.FLOAT, offset);
+ }
+
+ public final float get(jnr.ffi.Pointer ptr) {
+ return ptr.getFloat(offset());
+ }
+ public final void set(jnr.ffi.Pointer ptr, float value) {
+ ptr.putFloat(offset(), value);
+ }
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putFloat(offset(), value.floatValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ @Override
+ public final double doubleValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ @Override
+ public final float floatValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return (long) get(ptr);
+ }
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.String.valueOf(get(ptr));
+ }
+ }
+
+ public final class Double extends NumberField {
+ public Double() {
+ super(NativeType.DOUBLE);
+ }
+ public Double(Offset offset) {
+ super(NativeType.DOUBLE, offset);
+ }
+ public final double get(jnr.ffi.Pointer ptr) {
+ return ptr.getDouble(offset());
+ }
+ public final void set(jnr.ffi.Pointer ptr, double value) {
+ ptr.putDouble(offset(), value);
+ }
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putDouble(offset(), value.doubleValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) get(ptr);
+ }
+
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return (long) get(ptr);
+ }
+ @Override
+ public final float floatValue(jnr.ffi.Pointer ptr) {
+ return (float) get(ptr);
+ }
+
+ @Override
+ public final double doubleValue(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return java.lang.String.valueOf(get(ptr));
+ }
+ }
+
+ /**
+ * Represents a native memory address.
+ */
+ public class Pointer extends NumberField {
+ /**
+ * Creates a new <tt>Address</tt> field.
+ */
+ public Pointer() {
+ super(NativeType.ADDRESS);
+ }
+
+ public Pointer(Offset offset) {
+ super(NativeType.ADDRESS, offset);
+ }
+
+ /**
+ * Gets the {@link jnr.ffi.Pointer} value from the native memory.
+ *
+ * @return a {@link jnr.ffi.Pointer}.
+ */
+ public final jnr.ffi.Pointer get(jnr.ffi.Pointer ptr) {
+ return ptr.getPointer(offset());
+ }
+
+ /**
+ * Gets the size of a Pointer in bits
+ *
+ * @return the size of the Pointer
+ */
+ public final int size() {
+ return getRuntime().findType(NativeType.ADDRESS).size();
+ }
+
+ /**
+ * Sets a {@link jnr.ffi.Pointer} value in the native memory.
+ */
+ public final void set(jnr.ffi.Pointer ptr, jnr.ffi.Pointer value) {
+ ptr.putPointer(offset(), value);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putAddress(offset(), value.longValue());
+ }
+
+ /**
+ * Returns an integer representation of this <code>Pointer</code>.
+ *
+ * @return an integer value for this <code>Pointer</code>.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) ptr.getAddress(offset());
+ }
+
+ /**
+ * Returns an {@code long} representation of this <code>Pointer</code>.
+ *
+ * @return an {@code long} value for this <code>Pointer</code>.
+ */
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return ptr.getAddress(offset());
+ }
+
+ /**
+ * Returns a string representation of this <code>Pointer</code>.
+ *
+ * @return a string representation of this <code>Pointer</code>.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return get(ptr).toString();
+ }
+ }
+
+ /**
+ * Base for all the Enum fields.
+ *
+ * @param <E> the type of {@link java.lang.Enum}
+ */
+ protected abstract class EnumField<E extends java.lang.Enum<E>> extends NumberField {
+ protected final Class<E> enumClass;
+ protected final EnumMapper enumMapper;
+
+ /**
+ * Constructs a new Enum field.
+ *
+ * @param type the native type of the enum.
+ * @param enumClass the Enum class.
+ */
+ public EnumField(NativeType type, Class<E> enumClass) {
+ super(type);
+ this.enumClass = enumClass;
+ this.enumMapper = EnumMapper.getInstance(enumClass);
+ }
+
+ /**
+ * Constructs a new Enum field.
+ *
+ * @param type the native type of the enum.
+ * @param enumClass the Enum class.
+ */
+ public EnumField(NativeType type, Class<E> enumClass, Offset offset) {
+ super(type, offset);
+ this.enumClass = enumClass;
+ this.enumMapper = EnumMapper.getInstance(enumClass);
+ }
+
+ /**
+ * Gets a java Enum value representing the native integer value.
+ *
+ * @return a java Enum value.
+ */
+ public E get(jnr.ffi.Pointer ptr) {
+ return enumClass.cast(enumMapper.valueOf(intValue(ptr)));
+ }
+
+ /**
+ * Returns a string representation of this field.
+ *
+ * @return a string representation of this field.
+ */
+ @Override
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return get(ptr).toString();
+ }
+ }
+
+ /**
+ * An 8 bit enum field.
+ *
+ * @param <E> the {@link java.lang.Enum} to translate to/from.
+ */
+ public class Enum8<E extends java.lang.Enum<E>> extends EnumField<E> {
+ /**
+ * Creates a new 8 bit enum field.
+ *
+ * @param enumClass the class of the {@link java.lang.Enum}.
+ */
+ public Enum8(Class<E> enumClass) {
+ super(NativeType.SCHAR, enumClass);
+ }
+
+ /**
+ * Creates a new 8 bit enum field.
+ *
+ * @param enumClass the class of the {@link java.lang.Enum}.
+ * @param offset the offset of the enum field.
+ */
+ public Enum8(Class<E> enumClass, Offset offset) {
+ super(NativeType.SCHAR, enumClass, offset);
+ }
+
+ /**
+ * Sets the native integer value using a java Enum value.
+ *
+ * @param value the java <tt>Enum</tt> value.
+ */
+ public final void set(jnr.ffi.Pointer ptr, E value) {
+ ptr.putByte(offset(), (byte) enumMapper.intValue(value));
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putByte(offset(), value.byteValue());
+ }
+ /**
+ * Returns an integer representation of this enum field.
+ *
+ * @return an integer value for this enum field.
+ */
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return ptr.getByte(offset());
+ }
+ }
+
+ public class Enum16<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum16(Class<E> enumClass) {
+ super(NativeType.SSHORT, enumClass);
+ }
+
+ public Enum16(Class<E> enumClass, Offset offset) {
+ super(NativeType.SSHORT, enumClass, offset);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, E value) {
+ ptr.putShort(offset(), (short) enumMapper.intValue(value));
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putShort(offset(), value.shortValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return ptr.getShort(offset());
+ }
+ }
+
+ public class Enum32<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum32(Class<E> enumClass) {
+ super(NativeType.SINT, enumClass);
+ }
+
+ public Enum32(Class<E> enumClass, Offset offset) {
+ super(NativeType.SINT, enumClass, offset);
+ }
+
+ public void set(jnr.ffi.Pointer ptr, E value) {
+ ptr.putInt(offset(), enumMapper.intValue(value));
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putInt(offset(), value.intValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return ptr.getInt(offset());
+ }
+ }
+
+ public class Enum64<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public Enum64(Class<E> enumClass) {
+ super(NativeType.SLONGLONG, enumClass);
+ }
+
+ public Enum64(Class<E> enumClass, Offset offset) {
+ super(NativeType.SLONGLONG, enumClass, offset);
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, E value) {
+ ptr.putLongLong(offset(), enumMapper.intValue(value));
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putLongLong(offset(), value.longValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) longValue(ptr);
+ }
+
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return ptr.getLongLong(offset());
+ }
+ }
+
+ public class EnumLong<E extends java.lang.Enum<E>> extends EnumField<E> {
+ public EnumLong(Class<E> enumClass) {
+ super(NativeType.SLONG, enumClass);
+ }
+
+ public EnumLong(Class<E> enumClass, Offset offset) {
+ super(NativeType.SLONG, enumClass, offset);
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, E value) {
+ ptr.putNativeLong(offset(), enumMapper.intValue(value));
+ }
+
+ public void set(jnr.ffi.Pointer ptr, java.lang.Number value) {
+ ptr.putNativeLong(offset(), value.longValue());
+ }
+
+ @Override
+ public final int intValue(jnr.ffi.Pointer ptr) {
+ return (int) longValue(ptr);
+ }
+
+ @Override
+ public final long longValue(jnr.ffi.Pointer ptr) {
+ return ptr.getNativeLong(offset());
+ }
+ }
+
+ public class Enum<T extends java.lang.Enum<T>> extends Enum32<T> {
+ public Enum(Class<T> enumClass) {
+ super(enumClass);
+ }
+
+ public Enum(Class<T> enumClass, Offset offset) {
+ super(enumClass, offset);
+ }
+ }
+
+
+ abstract public class String extends AbstractField {
+ protected final Charset charset;
+ protected final int length;
+
+ protected String(int size, int align, int length, Charset cs) {
+ super(size, align);
+ this.length = length;
+ this.charset = cs;
+ }
+
+ protected String(int size, int align, Offset offset, int length, Charset cs) {
+ super(size, align, offset);
+ this.length = length;
+ this.charset = cs;
+ }
+
+ public final int length() {
+ return length;
+ }
+
+ protected abstract jnr.ffi.Pointer getStringMemory(jnr.ffi.Pointer ptr);
+ public abstract java.lang.String get(jnr.ffi.Pointer ptr);
+ public abstract void set(jnr.ffi.Pointer ptr, java.lang.String value);
+
+ public final java.lang.String toString(jnr.ffi.Pointer ptr) {
+ return get(ptr);
+ }
+ }
+
+ public class UTFString extends String {
+ public UTFString(int length, Charset cs) {
+ super(length, 1, length, cs); // FIXME: This won't work for non-ASCII
+ }
+
+ public UTFString(int length, Charset cs, Offset offset) {
+ super(length, 1, offset, length, cs); // FIXME: This won't work for non-ASCII
+ }
+
+ protected jnr.ffi.Pointer getStringMemory(jnr.ffi.Pointer ptr) {
+ return ptr.slice(offset(), length());
+ }
+
+ public final java.lang.String get(jnr.ffi.Pointer ptr) {
+ return getStringMemory(ptr).getString(0, length, charset);
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, java.lang.String value) {
+ getStringMemory(ptr).putString(0, value, length, charset);
+ }
+ }
+
+ public class UTF8String extends UTFString {
+ public UTF8String(int size) {
+ super(size, UTF8);
+ }
+
+ public UTF8String(int size, Offset offset) {
+ super(size, UTF8, offset);
+ }
+ }
+
+ public class AsciiString extends UTFString {
+ public AsciiString(int size) {
+ super(size, ASCII);
+ }
+
+ public AsciiString(int size, Offset offset) {
+ super(size, ASCII, offset);
+ }
+ }
+
+ public class UTFStringRef extends String {
+ private jnr.ffi.Pointer valueHolder;
+
+ public UTFStringRef(int length, Charset cs) {
+ super(getRuntime().findType(NativeType.ADDRESS).size(), getRuntime().findType(NativeType.ADDRESS).alignment(),
+ length, cs);
+ }
+
+ public UTFStringRef(int length, Charset cs, Offset offset) {
+ super(getRuntime().findType(NativeType.ADDRESS).size(), getRuntime().findType(NativeType.ADDRESS).alignment(),
+ offset, length, cs);
+ }
+
+ public UTFStringRef(Charset cs) {
+ this(Integer.MAX_VALUE, cs);
+ }
+
+ protected jnr.ffi.Pointer getStringMemory(jnr.ffi.Pointer ptr) {
+ return ptr.getPointer(offset(), length());
+ }
+
+ public final java.lang.String get(jnr.ffi.Pointer ptr) {
+ jnr.ffi.Pointer memory = getStringMemory(ptr);
+ return memory != null ? memory.getString(0, length, charset) : null;
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, java.lang.String value) {
+ if (value != null) {
+ valueHolder = getRuntime().getMemoryManager().allocateDirect(length() * 4);
+ valueHolder.putString(0, value, length() * 4, charset);
+ ptr.putPointer(offset(), valueHolder);
+
+ } else {
+ this.valueHolder = null;
+ ptr.putAddress(offset(), 0);
+ }
+ }
+ }
+
+ public class UTF8StringRef extends UTFStringRef {
+ public UTF8StringRef(int size) {
+ super(size, UTF8);
+ }
+
+ public UTF8StringRef(int size, Offset offset) {
+ super(size, UTF8, offset);
+ }
+
+ public UTF8StringRef() {
+ super(Integer.MAX_VALUE, UTF8);
+ }
+ }
+
+ public class AsciiStringRef extends UTFStringRef {
+ public AsciiStringRef(int size) {
+ super(size, ASCII);
+ }
+
+ public AsciiStringRef(int size, Offset offset) {
+ super(size, ASCII, offset);
+ }
+
+ public AsciiStringRef() {
+ super(Integer.MAX_VALUE, ASCII);
+ }
+ }
+
+
+ /**
+ * Specialized padding fields for structs. Use this instead of arrays of other
+ * members for more efficient struct construction.
+ */
+ protected final class Padding extends AbstractField {
+ public Padding(Type type, int length) {
+ super(type.size() * length, type.alignment());
+ }
+
+ public Padding(Type type, int length, Offset offset) {
+ super(type.size() * length, type.alignment(), offset);
+ }
+
+ public Padding(NativeType type, int length) {
+ this(getRuntime().findType(type), length);
+ }
+
+ public Padding(NativeType type, int length, Offset offset) {
+ this(getRuntime().findType(type), length);
+ }
+ }
+
+ protected final class Function<T> extends AbstractField {
+ private final Class<? extends T> closureClass;
+ private T instance;
+
+ public Function(Class<? extends T> closureClass) {
+ super(NativeType.ADDRESS);
+ this.closureClass = closureClass;
+ }
+
+ public Function(Class<? extends T> closureClass, Offset offset) {
+ super(NativeType.ADDRESS, offset);
+ this.closureClass = closureClass;
+ }
+
+ public final void set(jnr.ffi.Pointer ptr, T value) {
+ ptr.putPointer(offset(), getRuntime().getClosureManager().getClosurePointer(closureClass, instance = value));
+ }
+ }
+
+ protected final <T> Function<T> function(Class<T> closureClass) {
+ return new Function<T>(closureClass);
+ }
+
+ protected final <T> Function<T> function(Class<T> closureClass, Offset offset) {
+ return new Function<T>(closureClass, offset);
+ }
+
+ public final class int8_t extends IntegerAlias {
+ public int8_t() { super(TypeAlias.int8_t); }
+ public int8_t(Offset offset) { super(TypeAlias.int8_t, offset); }
+ }
+
+ public final class u_int8_t extends IntegerAlias {
+ public u_int8_t() { super(TypeAlias.u_int8_t); }
+ public u_int8_t(Offset offset) { super(TypeAlias.u_int8_t, offset); }
+ }
+
+ public final class int16_t extends IntegerAlias {
+ public int16_t() { super(TypeAlias.int16_t); }
+ public int16_t(Offset offset) { super(TypeAlias.int16_t, offset); }
+ }
+
+ public final class u_int16_t extends IntegerAlias {
+ public u_int16_t() { super(TypeAlias.u_int16_t); }
+ public u_int16_t(Offset offset) { super(TypeAlias.u_int16_t, offset); }
+ }
+
+ public final class int32_t extends IntegerAlias {
+ public int32_t() { super(TypeAlias.int32_t); }
+ public int32_t(Offset offset) { super(TypeAlias.int32_t, offset); }
+ }
+
+ public final class u_int32_t extends IntegerAlias {
+ public u_int32_t() { super(TypeAlias.u_int32_t); }
+ public u_int32_t(Offset offset) { super(TypeAlias.u_int32_t, offset); }
+ }
+
+ public final class int64_t extends IntegerAlias {
+ public int64_t() { super(TypeAlias.int64_t); }
+ public int64_t(Offset offset) { super(TypeAlias.int64_t, offset); }
+ }
+
+ public final class u_int64_t extends IntegerAlias {
+ public u_int64_t() { super(TypeAlias.u_int64_t); }
+ public u_int64_t(Offset offset) { super(TypeAlias.u_int64_t, offset); }
+ }
+
+ public final class intptr_t extends IntegerAlias {
+ public intptr_t() { super(TypeAlias.intptr_t); }
+ public intptr_t(Offset offset) { super(TypeAlias.intptr_t, offset); }
+ }
+
+ public final class uintptr_t extends IntegerAlias {
+ public uintptr_t() { super(TypeAlias.uintptr_t); }
+ public uintptr_t(Offset offset) { super(TypeAlias.uintptr_t, offset); }
+ }
+
+ public final class caddr_t extends IntegerAlias {
+ public caddr_t() { super(TypeAlias.caddr_t); }
+ public caddr_t(Offset offset) { super(TypeAlias.caddr_t, offset); }
+ }
+
+ public final class dev_t extends IntegerAlias {
+ public dev_t() { super(TypeAlias.dev_t); }
+ public dev_t(Offset offset) { super(TypeAlias.dev_t, offset); }
+ }
+
+ public final class blkcnt_t extends IntegerAlias {
+ public blkcnt_t() { super(TypeAlias.blkcnt_t); }
+ public blkcnt_t(Offset offset) { super(TypeAlias.blkcnt_t, offset); }
+ }
+
+ public final class blksize_t extends IntegerAlias {
+ public blksize_t() { super(TypeAlias.blksize_t); }
+ public blksize_t(Offset offset) { super(TypeAlias.blksize_t, offset); }
+ }
+
+ public final class gid_t extends IntegerAlias {
+ public gid_t() { super(TypeAlias.gid_t); }
+ public gid_t(Offset offset) { super(TypeAlias.gid_t, offset); }
+ }
+
+ public final class in_addr_t extends IntegerAlias {
+ public in_addr_t() { super(TypeAlias.in_addr_t); }
+ public in_addr_t(Offset offset) { super(TypeAlias.in_addr_t, offset); }
+ }
+
+ public final class in_port_t extends IntegerAlias {
+ public in_port_t() { super(TypeAlias.in_port_t); }
+ public in_port_t(Offset offset) { super(TypeAlias.in_port_t, offset); }
+ }
+
+ public final class ino_t extends IntegerAlias {
+ public ino_t() { super(TypeAlias.ino_t); }
+ public ino_t(Offset offset) { super(TypeAlias.ino_t, offset); }
+ }
+
+ public final class ino64_t extends IntegerAlias {
+ public ino64_t() { super(TypeAlias.ino64_t); }
+ public ino64_t(Offset offset) { super(TypeAlias.ino64_t, offset); }
+ }
+
+ public final class key_t extends IntegerAlias {
+ public key_t() { super(TypeAlias.key_t); }
+ public key_t(Offset offset) { super(TypeAlias.key_t, offset); }
+ }
+
+ public final class mode_t extends IntegerAlias {
+ public mode_t() { super(TypeAlias.mode_t); }
+ public mode_t(Offset offset) { super(TypeAlias.mode_t, offset); }
+ }
+
+ public final class nlink_t extends IntegerAlias {
+ public nlink_t() { super(TypeAlias.nlink_t); }
+ public nlink_t(Offset offset) { super(TypeAlias.nlink_t, offset); }
+ }
+
+ public final class id_t extends IntegerAlias {
+ public id_t() { super(TypeAlias.id_t); }
+ public id_t(Offset offset) { super(TypeAlias.id_t, offset); }
+ }
+
+ public final class pid_t extends IntegerAlias {
+ public pid_t() { super(TypeAlias.pid_t); }
+ public pid_t(Offset offset) { super(TypeAlias.pid_t, offset); }
+ }
+
+ public final class off_t extends IntegerAlias {
+ public off_t() { super(TypeAlias.off_t); }
+ public off_t(Offset offset) { super(TypeAlias.off_t, offset); }
+ }
+
+ public final class swblk_t extends IntegerAlias {
+ public swblk_t() { super(TypeAlias.swblk_t); }
+ public swblk_t(Offset offset) { super(TypeAlias.swblk_t, offset); }
+ }
+
+ public final class uid_t extends IntegerAlias {
+ public uid_t() { super(TypeAlias.uid_t); }
+ public uid_t(Offset offset) { super(TypeAlias.uid_t, offset); }
+ }
+
+ public final class clock_t extends IntegerAlias {
+ public clock_t() { super(TypeAlias.clock_t); }
+ public clock_t(Offset offset) { super(TypeAlias.clock_t, offset); }
+ }
+
+ public final class size_t extends IntegerAlias {
+ public size_t() { super(TypeAlias.size_t); }
+ public size_t(Offset offset) { super(TypeAlias.size_t, offset); }
+ }
+
+ public final class ssize_t extends IntegerAlias {
+ public ssize_t() { super(TypeAlias.ssize_t); }
+ public ssize_t(Offset offset) { super(TypeAlias.ssize_t, offset); }
+ }
+
+ public final class time_t extends IntegerAlias {
+ public time_t() { super(TypeAlias.time_t); }
+ public time_t(Offset offset) { super(TypeAlias.time_t, offset); }
+ }
+
+ public final class fsblkcnt_t extends IntegerAlias {
+ public fsblkcnt_t() { super(TypeAlias.fsblkcnt_t); }
+ public fsblkcnt_t(Offset offset) { super(TypeAlias.fsblkcnt_t, offset); }
+ }
+
+ public final class fsfilcnt_t extends IntegerAlias {
+ public fsfilcnt_t() { super(TypeAlias.fsfilcnt_t); }
+ public fsfilcnt_t(Offset offset) { super(TypeAlias.fsfilcnt_t, offset); }
+ }
+
+ public final class sa_family_t extends IntegerAlias {
+ public sa_family_t() { super(TypeAlias.sa_family_t); }
+ public sa_family_t(Offset offset) { super(TypeAlias.sa_family_t, offset); }
+ }
+
+ public final class socklen_t extends IntegerAlias {
+ public socklen_t() { super(TypeAlias.socklen_t); }
+ public socklen_t(Offset offset) { super(TypeAlias.socklen_t, offset); }
+ }
+
+ public final class rlim_t extends IntegerAlias {
+ public rlim_t() { super(TypeAlias.rlim_t); }
+ public rlim_t(Offset offset) { super(TypeAlias.rlim_t, offset); }
+ }
+}
diff --git a/src/main/java/jnr/ffi/Type.java b/src/main/java/jnr/ffi/Type.java
new file mode 100644
index 0000000..cfcefbb
--- /dev/null
+++ b/src/main/java/jnr/ffi/Type.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi;
+
+/**
+ * Type is the superclass for all internal types used by jnr-ffi.
+ *
+ * <p>
+ * Use this type to access meta-data about a native type, such as its size or natural alignment.
+ * </p>
+ *
+ * <p>
+ * To obtain an instance of this class, use {@link jnr.ffi.Runtime#findType(NativeType)} or
+ * {@link jnr.ffi.Runtime#findType(TypeAlias)}.
+ * </p>
+ * <p>
+ * Example
+ * <pre>
+ * {@code
+ *
+ * Type pointerType = runtime.findType(NativeType.ADDRESS);
+ *
+ * System.out.println("The size of a pointer on this platform is " + pointerType.size());
+ * }
+ * </pre>
+ * </p>
+ */
+public abstract class Type {
+ /**
+ * The size in bytes of this type.
+ *
+ * @return An integer
+ */
+ public abstract int size();
+
+ /**
+ * The native alignment of this type, in bytes
+ *
+ * @return An integer
+ */
+ public abstract int alignment();
+
+ /**
+ * The native type of this type
+ *
+ * @return the native type of this type
+ */
+ public abstract NativeType getNativeType();
+}
diff --git a/src/main/java/jnr/ffi/TypeAlias.java b/src/main/java/jnr/ffi/TypeAlias.java
new file mode 100644
index 0000000..1d19d37
--- /dev/null
+++ b/src/main/java/jnr/ffi/TypeAlias.java
@@ -0,0 +1,40 @@
+package jnr.ffi;
+
+public enum TypeAlias {
+ int8_t,
+ u_int8_t,
+ int16_t,
+ u_int16_t,
+ int32_t,
+ u_int32_t,
+ int64_t,
+ u_int64_t,
+ intptr_t,
+ uintptr_t,
+ caddr_t,
+ dev_t,
+ blkcnt_t,
+ blksize_t,
+ gid_t,
+ in_addr_t,
+ in_port_t,
+ ino_t,
+ ino64_t,
+ key_t,
+ mode_t,
+ nlink_t,
+ id_t,
+ pid_t,
+ off_t,
+ swblk_t,
+ uid_t,
+ clock_t,
+ size_t,
+ ssize_t,
+ time_t,
+ fsblkcnt_t,
+ fsfilcnt_t,
+ sa_family_t,
+ socklen_t,
+ rlim_t,
+}
diff --git a/src/main/java/jnr/ffi/Union.java b/src/main/java/jnr/ffi/Union.java
new file mode 100644
index 0000000..693818d
--- /dev/null
+++ b/src/main/java/jnr/ffi/Union.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some of the design and code of this class is from the javolution project.
+ *
+ * Copyright (C) 2006 - Javolution (http://javolution.org/)
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software is
+ * freely granted, provided that this notice is preserved.
+ */
+
+package jnr.ffi;
+
+/**
+ * Represents a C union
+ */
+public abstract class Union extends Struct {
+ protected Union(Runtime runtime) {
+ super(runtime, true);
+ }
+}
diff --git a/src/main/java/jnr/ffi/Variable.java b/src/main/java/jnr/ffi/Variable.java
new file mode 100644
index 0000000..b82482a
--- /dev/null
+++ b/src/main/java/jnr/ffi/Variable.java
@@ -0,0 +1,39 @@
+package jnr.ffi;
+
+/**
+ * Access library global variables.
+ *
+ * <p>
+ * To access global variables, declare a method with a parameterized return type of this class.
+ * </p>
+ * <p><b>Example</b>
+ * <pre>
+ * {@code
+ *
+ * public interface MyLib {
+ * public Variable<Long> my_int_var();
+ * }
+ *
+ * MyLib lib = LibraryLoader.create(MyLib.class).load("mylib"):
+ * System.out.println("native value=" + lib.my_int_var().get())
+ *
+ * lib.my_int_var().set(0xdeadbeef);
+ * }
+ * </pre>
+ * </p>
+ */
+public interface Variable<T> {
+ /**
+ * Gets the current value of the global variable
+ *
+ * @return The value of the variable
+ */
+ public T get();
+
+ /**
+ * Sets the global variable to a value
+ *
+ * @param value The value to set the global variable to.
+ */
+ public void set(T value);
+}
diff --git a/src/main/java/jnr/ffi/annotations/Clear.java b/src/main/java/jnr/ffi/annotations/Clear.java
new file mode 100644
index 0000000..28f4adc
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Clear.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the temporary native memory allocated for an {@code @Out} paramneter
+ * should be cleared before passing to the native function.
+ *
+ * <p>By default, parameters that are annotated as {@code @Out} only do not clear
+ * the data in the temporary native memory area allocated when a java heap object
+ * is passed in as the parameter, so the memory passed to the native function is
+ * full of garbage data. After the native method returns, the native memory is
+ * copied back to java, which is usually not a problem, since the native function
+ * will have updated the memory with valid data. However, if the native function
+ * fails, the garbage data that was in the temporary native memory will be copied
+ * back to java.
+ *
+ * @see In
+ * @see Out
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.PARAMETER)
+public @interface Clear {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Delegate.java b/src/main/java/jnr/ffi/annotations/Delegate.java
new file mode 100644
index 0000000..f3a2b7a
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Delegate.java
@@ -0,0 +1,17 @@
+package jnr.ffi.annotations;
+
+import jnr.ffi.CallingConvention;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+public @interface Delegate {
+ CallingConvention convention() default CallingConvention.DEFAULT;
+}
diff --git a/src/main/java/jnr/ffi/annotations/Direct.java b/src/main/java/jnr/ffi/annotations/Direct.java
new file mode 100644
index 0000000..524a125
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Direct.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a {@link jnr.ffi.Struct}} parameter should be
+ * backed by a persistent native memory block.
+ *
+ * <p>Without the {@code @Direct} annotation, the native code will allocate a
+ * temporary native memory block for the parameter, and free it immediately after
+ * the call.
+ *
+ * <p>By using {@code @Direct}, the native memory block is permanently associated
+ * with the {@link jnr.ffi.Struct} instance, and will remain allocated
+ * for as long as the {@code Struct} instance remains strongly referenced by java code.
+ *
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.PARAMETER)
+public @interface Direct {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Encoding.java b/src/main/java/jnr/ffi/annotations/Encoding.java
new file mode 100644
index 0000000..7e82db0
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Encoding.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to specify the {@link java.nio.charset.Charset} to use for encoding/decoding a {@link String}
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE })
+public @interface Encoding {
+ String value();
+}
diff --git a/src/main/java/jnr/ffi/annotations/IgnoreError.java b/src/main/java/jnr/ffi/annotations/IgnoreError.java
new file mode 100644
index 0000000..d1c4f73
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/IgnoreError.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that the errno valud for a native function need not be saved after
+ * the function returns.
+ *
+ * <p>Due to the nature of the Java Virtual Machine, the errno value must be saved
+ * immediately after the native function is called, otherwise internal jvm operations
+ * may overwrite it before control is returned to java code.
+ *
+ * <p>Since it is not possible for jnr-ffi to infer in a generic way whether a native
+ * function has succeeded or failed, the C errno value is saved after every native
+ * function call - even for the ones that succeed. This can have a significant
+ * performance impact, so for those functions which either don't fail, or for which
+ * the errno value can be ignored, can be annotated with {@code @IgnoreError} to
+ * avoid unneccessary saving of the errno value.
+ *
+ * @see SaveError
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface IgnoreError {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/In.java b/src/main/java/jnr/ffi/annotations/In.java
new file mode 100644
index 0000000..832c952
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/In.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the parameter is an IN parameter.
+ *
+ * <p>When a java object is passed to a native function as a pointer
+ * (for example {@link jnr.ffi.Pointer}, {@link jnr.ffi.Struct}, {@link java.nio.ByteBuffer}),
+ * then a temporary native memory block is allocated, the java data is copied to
+ * the temporary memory and the address of the temporary memory is passed to the function.
+ * After the function returns, the java data is automatically updated from the
+ * contents of the native memory.
+ *
+ * <p>As this extra copying can be expensive, parameters can be annotated with {@code @In}
+ * so the data is only copied from java {@code IN} to native memory, but not copied
+ * back {@code OUT} from native memory to java memory.
+ *
+ * <p>Parameters with neither a {@code @In} nor a {@code @Out} annotation will copy both ways.
+ *
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ ElementType.PARAMETER, ElementType.METHOD })
+public @interface In {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/LongLong.java b/src/main/java/jnr/ffi/annotations/LongLong.java
new file mode 100644
index 0000000..4514533
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/LongLong.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import jnr.ffi.TypeAlias;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a long parameter should be treated as native long-long (64bit)
+ * instead of the platform-dependent long size.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.int64_t)
+public @interface LongLong {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/NulTerminate.java b/src/main/java/jnr/ffi/annotations/NulTerminate.java
new file mode 100644
index 0000000..9e75c66
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/NulTerminate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a byte array or ByteBuffer should be terminated with a zero byte
+ * before passing it to a native function.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ ElementType.PARAMETER, ElementType.METHOD })
+public @interface NulTerminate {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Out.java b/src/main/java/jnr/ffi/annotations/Out.java
new file mode 100644
index 0000000..beab9c5
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Out.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the parameter is an OUT parameter.
+ *
+ * <p>When a java object is passed to a native function as a pointer
+ * (for example {@link jnr.ffi.Pointer}, {@link jnr.ffi.Struct}, {@link java.nio.ByteBuffer}),
+ * then a temporary native memory block is allocated, the java data is copied to
+ * the temporary memory and the address of the temporary memory is passed to the function.
+ * After the function returns, the java data is automatically updated from the
+ * contents of the native memory.
+ *
+ * <p>As this extra copying can be expensive, for native functions which only
+ * write to the passed in memory block and do not use the existing contents,
+ * parameters can be annotated with {@code @Out} so there is only copied {@code OUT}
+ * from native memory to java memory after the call, and the unneccessary copy {@code IN}
+ * from java to native memory before the call can be avoided.
+ *
+ * <p>Parameters with neither a {@code @In} nor a {@code @Out} annotation will copy both ways.
+ *
+ * @see In
+ * @see Clear
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ ElementType.PARAMETER, ElementType.METHOD })
+public @interface Out {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Pinned.java b/src/main/java/jnr/ffi/annotations/Pinned.java
new file mode 100644
index 0000000..6093a82
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Pinned.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method parameter as being pinnable.
+ * <p>
+ * This means the data for the parameter is not copied to/from native memory.
+ * Instead, the JVM memory is locked and passed directly to the native code.
+ * </p>
+ * <p>
+ * <b>IMPORTANT:</b> This should not be used for functions that may block on
+ * network or filesystem access such as read(2), write(2), stat(2).
+ * </p>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.PARAMETER)
+public @interface Pinned {
+}
diff --git a/src/main/java/jnr/ffi/annotations/SaveError.java b/src/main/java/jnr/ffi/annotations/SaveError.java
new file mode 100644
index 0000000..2a63ef5
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/SaveError.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Tags a library method as requiring any error codes as returned
+ * by errno on unix, or GetLastError on windows be saved.
+ *
+ * @see IgnoreError
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface SaveError {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/StdCall.java b/src/main/java/jnr/ffi/annotations/StdCall.java
new file mode 100644
index 0000000..9c4247f
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/StdCall.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+public @interface StdCall {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Synchronized.java b/src/main/java/jnr/ffi/annotations/Synchronized.java
new file mode 100644
index 0000000..ce2e196
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Synchronized.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Indicates that a library or a library method requires all calls to be
+ * synchronized.
+ *
+ * i.e. calls from multiple threads will synchronize on a monitor object,
+ * then call the native method.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface Synchronized {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/Transient.java b/src/main/java/jnr/ffi/annotations/Transient.java
new file mode 100644
index 0000000..e7b04be
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/Transient.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the parameter is transient.
+ *
+ * This means it can be backed by a temporarily allocated native memory block,
+ * and after the method call, the native memory can be freed again.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.PARAMETER)
+public @interface Transient {
+
+}
diff --git a/src/main/java/jnr/ffi/annotations/TypeDefinition.java b/src/main/java/jnr/ffi/annotations/TypeDefinition.java
new file mode 100644
index 0000000..1488938
--- /dev/null
+++ b/src/main/java/jnr/ffi/annotations/TypeDefinition.java
@@ -0,0 +1,17 @@
+package jnr.ffi.annotations;
+
+import jnr.ffi.TypeAlias;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is used internally by jnr-ffi to define type aliases. e.g. ssize_t => long
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.ANNOTATION_TYPE })
+public @interface TypeDefinition {
+ TypeAlias alias();
+}
diff --git a/src/main/java/jnr/ffi/byref/AbstractNumberReference.java b/src/main/java/jnr/ffi/byref/AbstractNumberReference.java
new file mode 100644
index 0000000..2e634aa
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/AbstractNumberReference.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+/**
+ * An abstract class for common PrimitiveReference functionality
+ */
+abstract public class AbstractNumberReference<T extends Number> extends Number implements ByReference<T> {
+ T value;
+
+ protected AbstractNumberReference(T value) {
+ this.value = value;
+ }
+
+ protected static <T extends Number> T checkNull(T value) {
+ if (value == null) {
+ throw new NullPointerException("reference value cannot be null");
+ }
+
+ return value;
+ }
+
+ /**
+ * Gets the current value the reference points to.
+ *
+ * @return the current value.
+ */
+ public T getValue() {
+ return value;
+ }
+
+ @Override
+ public final byte byteValue() {
+ return value.byteValue();
+ }
+
+ @Override
+ public final short shortValue() {
+ return value.byteValue();
+ }
+
+ public final int intValue() {
+ return value.intValue();
+ }
+
+ @Override
+ public final long longValue() {
+ return value.longValue();
+ }
+
+ @Override
+ public final float floatValue() {
+ return value.floatValue();
+ }
+
+ @Override
+ public final double doubleValue() {
+ return value.doubleValue();
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/AbstractReference.java b/src/main/java/jnr/ffi/byref/AbstractReference.java
new file mode 100644
index 0000000..2431c22
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/AbstractReference.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+/**
+ * An abstract class for common PrimitiveReference functionality
+ */
+abstract public class AbstractReference<T> implements ByReference<T> {
+ T value;
+
+ protected AbstractReference(T value) {
+ this.value = value;
+ }
+
+ protected static <T> T checkNull(T value) {
+ if (value == null) {
+ throw new NullPointerException("reference value cannot be null");
+ }
+
+ return value;
+ }
+
+ /**
+ * Gets the current value the reference points to.
+ *
+ * @return the current value.
+ */
+ public T getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/AddressByReference.java b/src/main/java/jnr/ffi/byref/AddressByReference.java
new file mode 100644
index 0000000..c0624dc
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/AddressByReference.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Address;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * AddressByReference is used when the address of a primitive pointer value must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><pre>
+ * {@code
+ *
+ * extern void get_a(void** ap);
+ *
+ * void* foo(void) {
+ * void* a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ *
+ * }
+ * </pre>
+ *
+ * <p>Would be declared in java as
+ * <p><pre>
+ * {@code
+ *
+ * interface Lib {
+ * void get_a(@Out AddressByReference ap);
+ * }
+ *
+ * }
+ * </pre>
+ * <p>and used like this
+ *
+ * <p><blockquote><pre>
+ * AddressByReference ap = new AddressByReference();
+ * lib.get_a(ap);
+ * System.out.println("a from lib=" + a.getValue());
+ * </pre></blockquote>
+ */
+public final class AddressByReference extends AbstractReference<Address> {
+
+ /**
+ * Creates a new reference to an integer value
+ */
+ public AddressByReference() {
+ super(Address.valueOf(0));
+ }
+
+ /**
+ * Creates a new reference to an address value
+ *
+ * @param value the initial native value
+ */
+ public AddressByReference(Address value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Copies the address value to native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer memory, long offset) {
+ memory.putAddress(offset, value.nativeAddress());
+ }
+
+ /**
+ * Copies the address value from native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer memory, long offset) {
+ value = Address.valueOf(memory.getAddress(offset));
+ }
+
+ /**
+ * Gets the native size of type of reference
+ *
+ * @return The size of the native type this reference points to
+ */
+ public int nativeSize(Runtime runtime) {
+ return runtime.addressSize();
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/ByReference.java b/src/main/java/jnr/ffi/byref/ByReference.java
new file mode 100644
index 0000000..3853456
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/ByReference.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * A ByReference subclass is used when a primitive parameter must be passed
+ * by-reference.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ *
+ * extern void get_a(int * ap);
+ *
+ * int foo(void) {
+ * int a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ *
+ * interface Lib {
+ * void get_a(@Out IntByReference ap);
+ * }
+ *
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><pre>
+ *{@code
+ *
+ * IntByReference ap = new IntByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.getValue());
+ *
+ * }
+ * </pre>
+ * @param <T>
+ */
+public interface ByReference<T> {
+ /**
+ * Gets the size of the native buffer required to store the value
+ *
+ * @return the size in bytes of the native type
+ */
+ int nativeSize(Runtime runtime);
+
+ /**
+ * Copies the java value to native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer.
+ */
+ void toNative(Runtime runtime, Pointer memory, long offset);
+
+ /**
+ * Copies the java value from native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer.
+ */
+ void fromNative(Runtime runtime, Pointer memory, long offset);
+
+ T getValue();
+}
diff --git a/src/main/java/jnr/ffi/byref/ByteByReference.java b/src/main/java/jnr/ffi/byref/ByteByReference.java
new file mode 100644
index 0000000..bce6a36
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/ByteByReference.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+
+/**
+ * ByteByReference is used when the address of a primitive byte value must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(char *ap);
+ *
+ * int foo(void) {
+ * char a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out ByteByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * ByteByReference ap = new ByteByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.byteValue());
+ * </pre></blockquote>
+ */
+public final class ByteByReference extends AbstractNumberReference<Byte> {
+
+ /**
+ * Creates a new reference to a byte value initialized to zero.
+ */
+ public ByteByReference() {
+ super(Byte.valueOf((byte) 0));
+ }
+
+ /**
+ * Creates a new reference to a byte value
+ *
+ * @param value the initial native value
+ */
+ public ByteByReference(Byte value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a byte value
+ *
+ * @param value the initial native value
+ */
+ public ByteByReference(byte value) {
+ super(value);
+ }
+
+ /**
+ * Copies the Byte value to native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer buffer, long offset) {
+ buffer.putByte(offset, value);
+ }
+
+ /**
+ * Copies the Byte value from native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer buffer, long offset) {
+ this.value = buffer.getByte(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return 1;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/DoubleByReference.java b/src/main/java/jnr/ffi/byref/DoubleByReference.java
new file mode 100644
index 0000000..8de60ac
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/DoubleByReference.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ *
+ */
+public final class DoubleByReference extends AbstractNumberReference<Double> {
+ private static final Double DEFAULT = Double.valueOf(0d);
+
+ /**
+ * Creates a new reference to a double value initialized to zero.
+ */
+ public DoubleByReference() {
+ super(DEFAULT);
+ }
+
+ /**
+ * Creates a new reference to a double value
+ *
+ * @param value the initial native value
+ */
+ public DoubleByReference(Double value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a double value
+ *
+ * @param value the initial native value
+ */
+ public DoubleByReference(double value) {
+ super(value);
+ }
+
+ /**
+ * Copies the double value to native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer buffer, long offset) {
+ buffer.putDouble(offset, value);
+ }
+
+ /**
+ * Copies the double value from native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer buffer, long offset) {
+ this.value = buffer.getDouble(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return 8;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/FloatByReference.java b/src/main/java/jnr/ffi/byref/FloatByReference.java
new file mode 100644
index 0000000..8b40ee4
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/FloatByReference.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ *
+ */
+public final class FloatByReference extends AbstractNumberReference<Float> {
+ private static final Float DEFAULT = Float.valueOf(0f);
+
+ /**
+ * Creates a new reference to a short value initialized to zero.
+ */
+ public FloatByReference() {
+ super(DEFAULT);
+ }
+
+ /**
+ * Creates a new reference to a float value
+ *
+ * @param value the initial native value
+ */
+ public FloatByReference(Float value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a float value
+ *
+ * @param value the initial native value
+ */
+ public FloatByReference(float value) {
+ super(value);
+ }
+
+ /**
+ * Copies the float value to native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer buffer, long offset) {
+ buffer.putFloat(offset, value);
+ }
+
+ /**
+ * Copies the float value from native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer buffer, long offset) {
+ this.value = buffer.getFloat(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return 4;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/IntByReference.java b/src/main/java/jnr/ffi/byref/IntByReference.java
new file mode 100644
index 0000000..39d69fc
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/IntByReference.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * IntByReference is used when the address of a primitive int must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(int * ap);
+ *
+ * int foo(void) {
+ * int a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out IntByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * IntByReference ap = new IntByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.intValue());
+ * </pre></blockquote>
+ */
+public final class IntByReference extends AbstractNumberReference<Integer> {
+
+ /**
+ * Creates a new reference to an integer value initialized to zero.
+ */
+ public IntByReference() {
+ super(Integer.valueOf(0));
+ }
+
+ /**
+ * Creates a new reference to an integer value
+ *
+ * @param value the initial native value
+ */
+ public IntByReference(Integer value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to an integer value
+ *
+ * @param value the initial native value
+ */
+ public IntByReference(int value) {
+ super(value);
+ }
+
+ /**
+ * Copies the integer value to native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer buffer, long offset) {
+ buffer.putInt(offset, value);
+ }
+
+ /**
+ * Copies the integer value from native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer buffer, long offset) {
+ this.value = buffer.getInt(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference
+ *
+ * @return Integer.SIZE
+ */
+ public int nativeSize(Runtime runtime) {
+ return 4;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/LongLongByReference.java b/src/main/java/jnr/ffi/byref/LongLongByReference.java
new file mode 100644
index 0000000..d348b77
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/LongLongByReference.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * LongLongByReference is used when the address of a native long long value must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(long long * ap);
+ *
+ * long long foo(void) {
+ * long long a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out LongLongByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * LongLongByReference ap = new LongLongByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.longValue());
+ * </pre></blockquote>
+ */
+public final class LongLongByReference extends AbstractNumberReference<Long> {
+
+ /**
+ * Creates a new reference to a long long value initialized to zero.
+ */
+ public LongLongByReference() {
+ super(Long.valueOf(0));
+ }
+
+ /**
+ * Creates a new reference to a native longlong value
+ *
+ * @param value the initial native value
+ */
+ public LongLongByReference(Long value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a native longlong value
+ *
+ * @param value the initial native value
+ */
+ public LongLongByReference(long value) {
+ super(value);
+ }
+
+ /**
+ * Copies the value to native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer memory, long offset) {
+ memory.putLongLong(offset, value);
+ }
+
+ /**
+ * Copies the value from native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer memory, long offset) {
+ this.value = memory.getLongLong(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return 8;
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/NativeLongByReference.java b/src/main/java/jnr/ffi/byref/NativeLongByReference.java
new file mode 100644
index 0000000..765c5bf
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/NativeLongByReference.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.NativeLong;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * NativeLongByReference is used when the address of a primitive C long must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(long * ap);
+ *
+ * long foo(void) {
+ * long a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out NativeLongByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * NativeLongByReference ap = new NativeLongByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.longValue());
+ * </pre></blockquote>
+ */
+public final class NativeLongByReference extends AbstractNumberReference<NativeLong> {
+
+ /**
+ * Creates a new reference to a native long value initialized to zero.
+ */
+ public NativeLongByReference() {
+ super(NativeLong.valueOf(0));
+ }
+
+ /**
+ * Creates a new reference to a native long value
+ *
+ * @param value the initial native value
+ */
+ public NativeLongByReference(NativeLong value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a native long value
+ *
+ * @param value the initial native value
+ */
+ public NativeLongByReference(long value) {
+ super(NativeLong.valueOf(value));
+ }
+
+ /**
+ * Copies the long value to native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer memory, long offset) {
+ memory.putNativeLong(offset, value.longValue());
+ }
+
+ /**
+ * Copies the long value from native memory
+ *
+ * @param runtime
+ * @param memory the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer memory, long offset) {
+ this.value = NativeLong.valueOf(memory.getNativeLong(offset));
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return runtime.longSize();
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/NumberByReference.java b/src/main/java/jnr/ffi/byref/NumberByReference.java
new file mode 100644
index 0000000..9692487
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/NumberByReference.java
@@ -0,0 +1,154 @@
+package jnr.ffi.byref;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+
+/**
+ * NumberByReference is used when the address of a primitive integral value must be passed
+ * as a parameter to a function, but the exact type is system dependent.
+ *
+ * <p>For example, the following C code,
+ * <p><pre>
+ * {@code
+ *
+ * extern void get_size(ssize_t *sp);
+ *
+ * ssize_t foo(void) {
+ * ssize_t n;
+ * // pass a reference to 'n' so get_size() can fill it out
+ * get_size(&n);
+ *
+ * return n;
+ * }
+ *
+ * }
+ * </pre>
+ * <p>Would be declared in java as
+ * <p><pre>
+ * {@code
+ *
+ * interface Lib {
+ * void get_size(@Out NumberByReference ap);
+ * }
+ *
+ * }
+ * </pre>
+ * <p>and used like this
+ * <p><pre>
+ * {@code
+ *
+ * NumberByReference size = new NumberByReference(ssize_t);
+ * lib.get_size(size);
+ * System.out.printf("size from lib=%d\n", size.longValue());
+ *
+ * }
+ * </pre>
+ */
+public class NumberByReference extends AbstractNumberReference<Number> {
+ private final TypeAlias typeAlias;
+
+ public NumberByReference(TypeAlias typeAlias, Number value) {
+ super(checkNull(value));
+ this.typeAlias = typeAlias;
+ }
+
+ public NumberByReference(TypeAlias typeAlias) {
+ super(0);
+ this.typeAlias = typeAlias;
+ }
+
+ @Override
+ public int nativeSize(jnr.ffi.Runtime runtime) {
+ return runtime.findType(typeAlias).size();
+ }
+
+ @Override
+ public void fromNative(Runtime runtime, Pointer memory, long offset) {
+ switch (runtime.findType(typeAlias).getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ value = memory.getByte(offset);
+ break;
+
+ case SSHORT:
+ case USHORT:
+ value = memory.getShort(offset);
+ break;
+
+ case SINT:
+ case UINT:
+ value = memory.getInt(offset);
+ break;
+
+ case SLONG:
+ case ULONG:
+ value = memory.getLong(offset);
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ value = memory.getLongLong(offset);
+ break;
+
+ case ADDRESS:
+ value = memory.getAddress(offset);
+ break;
+
+ case FLOAT:
+ value = memory.getFloat(offset);
+ break;
+
+ case DOUBLE:
+ value = memory.getDouble(offset);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("unsupported type: " + typeAlias);
+ }
+ }
+
+ @Override
+ public void toNative(Runtime runtime, Pointer memory, long offset) {
+ switch (runtime.findType(typeAlias).getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ memory.putByte(offset, value.byteValue());
+ break;
+
+ case SSHORT:
+ case USHORT:
+ memory.putShort(offset, value.shortValue());
+ break;
+
+ case SINT:
+ case UINT:
+ memory.putInt(offset, value.intValue());
+ break;
+
+ case SLONG:
+ case ULONG:
+ memory.putLong(offset, value.longValue());
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ memory.putLongLong(offset, value.longValue());
+ break;
+
+ case ADDRESS:
+ memory.putAddress(offset, value.longValue());
+ break;
+
+ case FLOAT:
+ memory.putFloat(offset, value.floatValue());
+ break;
+
+ case DOUBLE:
+ memory.putDouble(offset, value.doubleValue());
+ break;
+
+ default:
+ throw new UnsupportedOperationException("unsupported type: " + typeAlias);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/PointerByReference.java b/src/main/java/jnr/ffi/byref/PointerByReference.java
new file mode 100644
index 0000000..5df439a
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/PointerByReference.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * AddressByReference is used when the address of a pointer must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(void** ap);
+ *
+ * void* foo(void) {
+ * void* a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out PointerByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * PointerByReference ap = new PointerByReference();
+ * lib.get_a(ap);
+ * Pointer ptr = ap.getValue();
+ * System.out.println("ptr from lib=" + a.getValue());
+ * System.out.println("ptr contents=" + ptr.getInt(0));
+ * </pre></blockquote>
+ */
+public final class PointerByReference extends AbstractReference<Pointer>{
+ /**
+ * Creates a new reference to a pointer value with a null default value.
+ */
+ public PointerByReference() {
+ super(null);
+ }
+
+ /**
+ * Creates a new reference to a pointer value
+ *
+ * @param value the initial pointer value
+ */
+ public PointerByReference(Pointer value) {
+ super(value);
+ }
+
+ public final void toNative(Runtime runtime, Pointer memory, long offset) {
+ memory.putPointer(offset, this.value);
+ }
+
+ public final void fromNative(Runtime runtime, Pointer memory, long offset) {
+ this.value = memory.getPointer(offset);
+ }
+
+ public final int nativeSize(Runtime runtime) {
+ return runtime.addressSize();
+ }
+}
diff --git a/src/main/java/jnr/ffi/byref/ShortByReference.java b/src/main/java/jnr/ffi/byref/ShortByReference.java
new file mode 100644
index 0000000..4ae4877
--- /dev/null
+++ b/src/main/java/jnr/ffi/byref/ShortByReference.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+/**
+ * ShortByReference is used when the address of a primitive short value must be passed
+ * as a parameter to a function.
+ *
+ * <p>For example, the following C code,
+ * <p><blockquote><pre>
+ * {@code
+ * extern void get_a(short * ap);
+ *
+ * short foo(void) {
+ * short a;
+ * // pass a reference to 'a' so get_a() can fill it out
+ * get_a(&a);
+ *
+ * return a;
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>Would be declared in java as
+ * <p><blockquote><pre>
+ * {@code
+ * interface Lib {
+ * void get_a(@Out ShortByReference ap);
+ * }
+ * }
+ * </pre></blockquote>
+ * <p>and used like this
+ * <p><blockquote><pre>
+ * ShortByReference ap = new ShortByReference();
+ * lib.get_a(ap);
+ * System.out.printf("a from lib=%d\n", a.getValue());
+ * </pre></blockquote>
+ */
+public final class ShortByReference extends AbstractNumberReference<Short> {
+
+ /**
+ * Creates a new reference to a short value initialized to zero.
+ */
+ public ShortByReference() {
+ super(Short.valueOf((short) 0));
+ }
+
+ /**
+ * Creates a new reference to a short value.
+ *
+ * @param value the initial native value
+ */
+ public ShortByReference(Short value) {
+ super(checkNull(value));
+ }
+
+ /**
+ * Creates a new reference to a short value.
+ *
+ * @param value the initial native value
+ */
+ public ShortByReference(short value) {
+ super(value);
+ }
+
+ /**
+ * Copies the short value to native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer
+ */
+ public void toNative(Runtime runtime, Pointer buffer, long offset) {
+ buffer.putShort(offset, value);
+ }
+
+ /**
+ * Copies the short value from native memory
+ *
+ * @param runtime
+ * @param buffer the native memory buffer.
+ */
+ public void fromNative(Runtime runtime, Pointer buffer, long offset) {
+ this.value = buffer.getShort(offset);
+ }
+
+ /**
+ * Gets the native size of type of reference in bytes.
+ *
+ * @return the size of a byte in bytes
+ */
+ public final int nativeSize(Runtime runtime) {
+ return 2;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/AbstractDataConverter.java b/src/main/java/jnr/ffi/mapper/AbstractDataConverter.java
new file mode 100644
index 0000000..a31f4bb
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/AbstractDataConverter.java
@@ -0,0 +1,7 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+abstract public class AbstractDataConverter<J, N> implements DataConverter<J, N> {
+}
diff --git a/src/main/java/jnr/ffi/mapper/AbstractFromNativeType.java b/src/main/java/jnr/ffi/mapper/AbstractFromNativeType.java
new file mode 100644
index 0000000..82e371c
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/AbstractFromNativeType.java
@@ -0,0 +1,18 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+abstract public class AbstractFromNativeType implements FromNativeType {
+ private final FromNativeConverter converter;
+
+ AbstractFromNativeType(FromNativeConverter converter) {
+ this.converter = converter;
+ }
+
+ @Override
+ public FromNativeConverter getFromNativeConverter() {
+ return converter;
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/mapper/AbstractSignatureTypeMapper.java b/src/main/java/jnr/ffi/mapper/AbstractSignatureTypeMapper.java
new file mode 100644
index 0000000..fdc586a
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/AbstractSignatureTypeMapper.java
@@ -0,0 +1,17 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+abstract public class AbstractSignatureTypeMapper implements SignatureTypeMapper {
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return null;
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/AbstractToNativeType.java b/src/main/java/jnr/ffi/mapper/AbstractToNativeType.java
new file mode 100644
index 0000000..3373ef3
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/AbstractToNativeType.java
@@ -0,0 +1,18 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+abstract public class AbstractToNativeType implements ToNativeType {
+ private final ToNativeConverter converter;
+
+ AbstractToNativeType(ToNativeConverter converter) {
+ this.converter = converter;
+ }
+
+ @Override
+ public ToNativeConverter getToNativeConverter() {
+ return converter;
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/mapper/CachingTypeMapper.java b/src/main/java/jnr/ffi/mapper/CachingTypeMapper.java
new file mode 100644
index 0000000..6bc6630
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/CachingTypeMapper.java
@@ -0,0 +1,101 @@
+package jnr.ffi.mapper;
+
+import java.util.*;
+
+/**
+ * Caches Class -> native converter lookups.
+ */
+public final class CachingTypeMapper extends AbstractSignatureTypeMapper implements SignatureTypeMapper {
+ private final SignatureTypeMapper mapper;
+ private volatile Map<SignatureType, ToNativeType> toNativeTypeMap = Collections.emptyMap();
+ private volatile Map<SignatureType, FromNativeType> fromNativeTypeMap = Collections.emptyMap();
+ private static final InvalidType UNCACHEABLE_TYPE = new InvalidType();
+ private static final InvalidType NO_TYPE = new InvalidType();
+
+ public CachingTypeMapper(SignatureTypeMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ FromNativeType fromNativeType = fromNativeTypeMap.get(type);
+
+ if (fromNativeType == UNCACHEABLE_TYPE) {
+ return mapper.getFromNativeType(type, context);
+
+ } else if (fromNativeType == NO_TYPE) {
+ return null;
+ }
+
+ return fromNativeType != null ? fromNativeType : lookupAndCacheFromNativeType(type, context);
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ ToNativeType toNativeType = toNativeTypeMap.get(type);
+ if (toNativeType == UNCACHEABLE_TYPE) {
+ return mapper.getToNativeType(type, context);
+
+ } else if (toNativeType == NO_TYPE) {
+ return null;
+ }
+
+ return toNativeType != null ? toNativeType : lookupAndCacheToNativeType(type, context);
+ }
+
+
+ private synchronized FromNativeType lookupAndCacheFromNativeType(SignatureType signature, FromNativeContext context) {
+ FromNativeType fromNativeType = fromNativeTypeMap.get(signature);
+ if (fromNativeType == null) {
+ fromNativeType = mapper.getFromNativeType(signature, context);
+ FromNativeType typeForCaching = fromNativeType;
+ if (fromNativeType == null) {
+ typeForCaching = NO_TYPE;
+
+ } else if (!fromNativeType.getClass().isAnnotationPresent(FromNativeType.Cacheable.class)) {
+ typeForCaching = UNCACHEABLE_TYPE;
+ }
+
+ Map<SignatureType, FromNativeType> m = new HashMap<SignatureType, FromNativeType>(fromNativeTypeMap.size() + 1);
+ m.putAll(fromNativeTypeMap);
+ m.put(signature, typeForCaching);
+ fromNativeTypeMap = Collections.unmodifiableMap(m);
+ }
+
+ return fromNativeType != NO_TYPE ? fromNativeType : null;
+ }
+
+ private synchronized ToNativeType lookupAndCacheToNativeType(SignatureType signature, ToNativeContext context) {
+ ToNativeType toNativeType = toNativeTypeMap.get(signature);
+ if (toNativeType == null) {
+ toNativeType = mapper.getToNativeType(signature, context);
+ ToNativeType typeForCaching = toNativeType;
+ if (toNativeType == null) {
+ typeForCaching = NO_TYPE;
+
+ } else if (!toNativeType.getClass().isAnnotationPresent(ToNativeType.Cacheable.class)) {
+ typeForCaching = UNCACHEABLE_TYPE;
+ }
+
+ Map<SignatureType, ToNativeType> m = new HashMap<SignatureType, ToNativeType>(toNativeTypeMap.size() + 1);
+ m.putAll(toNativeTypeMap);
+ m.put(signature, typeForCaching);
+ toNativeTypeMap = Collections.unmodifiableMap(m);
+ }
+
+ return toNativeType != NO_TYPE ? toNativeType : null;
+ }
+
+ private static final class InvalidType implements ToNativeType, FromNativeType {
+ @Override
+ public FromNativeConverter getFromNativeConverter() {
+ return null;
+ }
+
+ @Override
+ public ToNativeConverter getToNativeConverter() {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/mapper/CompositeFunctionMapper.java b/src/main/java/jnr/ffi/mapper/CompositeFunctionMapper.java
new file mode 100644
index 0000000..6ff3c91
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/CompositeFunctionMapper.java
@@ -0,0 +1,28 @@
+package jnr.ffi.mapper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ *
+ */
+public final class CompositeFunctionMapper implements FunctionMapper {
+ private final Collection<FunctionMapper> functionMappers;
+
+ public CompositeFunctionMapper(Collection<FunctionMapper> functionMappers) {
+ this.functionMappers = Collections.unmodifiableList(new ArrayList<FunctionMapper>(functionMappers));
+ }
+
+ @Override
+ public String mapFunctionName(String functionName, Context context) {
+ for (FunctionMapper functionMapper : functionMappers) {
+ String mappedName = functionMapper.mapFunctionName(functionName, context);
+ if (mappedName != null) {
+ return mappedName;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/CompositeTypeMapper.java b/src/main/java/jnr/ffi/mapper/CompositeTypeMapper.java
new file mode 100644
index 0000000..a6d0fbd
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/CompositeTypeMapper.java
@@ -0,0 +1,42 @@
+package jnr.ffi.mapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public final class CompositeTypeMapper implements SignatureTypeMapper {
+ private final Collection<SignatureTypeMapper> signatureTypeMappers;
+
+ public CompositeTypeMapper(SignatureTypeMapper... signatureTypeMappers) {
+ this.signatureTypeMappers = Collections.unmodifiableList(Arrays.asList(signatureTypeMappers.clone()));
+ }
+
+ public CompositeTypeMapper(Collection<SignatureTypeMapper> signatureTypeMappers) {
+ this.signatureTypeMappers = Collections.unmodifiableList(new ArrayList<SignatureTypeMapper>(signatureTypeMappers));
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ for (SignatureTypeMapper m : signatureTypeMappers) {
+ FromNativeType fromNativeType = m.getFromNativeType(type, context);
+ if (fromNativeType != null) {
+ return fromNativeType;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ for (SignatureTypeMapper m : signatureTypeMappers) {
+ ToNativeType toNativeType = m.getToNativeType(type, context);
+ if (toNativeType != null) {
+ return toNativeType;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/DataConverter.java b/src/main/java/jnr/ffi/mapper/DataConverter.java
new file mode 100644
index 0000000..87c5728
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/DataConverter.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+public interface DataConverter<J, N> extends ToNativeConverter<J, N>, FromNativeConverter<J, N> {
+}
diff --git a/src/main/java/jnr/ffi/mapper/DefaultSignatureType.java b/src/main/java/jnr/ffi/mapper/DefaultSignatureType.java
new file mode 100644
index 0000000..3e1fdda
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/DefaultSignatureType.java
@@ -0,0 +1,71 @@
+package jnr.ffi.mapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+*
+*/
+public final class DefaultSignatureType implements SignatureType {
+ private final Class declaredClass;
+ private final Collection<Annotation> annotations;
+ private final Type genericType;
+
+ public DefaultSignatureType(Class declaredClass, Collection<Annotation> annotations, Type genericType) {
+ this.declaredClass = declaredClass;
+ this.annotations = sortedAnnotationCollection(annotations);
+ this.genericType = genericType;
+ }
+
+ public Class getDeclaredType() {
+ return declaredClass;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations;
+ }
+
+ public Type getGenericType() {
+ return genericType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DefaultSignatureType signature = (DefaultSignatureType) o;
+
+ return declaredClass == signature.declaredClass
+ && genericType.equals(signature.genericType)
+ && annotations.equals(signature.annotations)
+ ;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = declaredClass.hashCode();
+ result = 31 * result + annotations.hashCode();
+ if (genericType != null) result = 31 * result + genericType.hashCode();
+ return result;
+ }
+
+ public static DefaultSignatureType create(Class type, FromNativeContext context) {
+ Type genericType = !type.isPrimitive() && context instanceof MethodResultContext
+ ? ((MethodResultContext) context).getMethod().getGenericReturnType() : type;
+ return new DefaultSignatureType(type, context.getAnnotations(), genericType);
+ }
+
+ public static DefaultSignatureType create(Class type, ToNativeContext context) {
+ Type genericType = type;
+ if (!type.isPrimitive() && context instanceof MethodParameterContext) {
+ MethodParameterContext methodParameterContext = (MethodParameterContext) context;
+ genericType = methodParameterContext.getMethod().getGenericParameterTypes()[methodParameterContext.getParameterIndex()];
+ }
+
+ return new DefaultSignatureType(type, context.getAnnotations(), genericType);
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/DefaultTypeMapper.java b/src/main/java/jnr/ffi/mapper/DefaultTypeMapper.java
new file mode 100644
index 0000000..1f5be00
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/DefaultTypeMapper.java
@@ -0,0 +1,38 @@
+package jnr.ffi.mapper;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+public final class DefaultTypeMapper implements TypeMapper {
+ private final Map<Class, ToNativeConverter> toNativeConverters;
+ private final Map<Class, FromNativeConverter> fromNativeConverters;
+
+ public DefaultTypeMapper() {
+ toNativeConverters = new LinkedHashMap<Class, ToNativeConverter>();
+ fromNativeConverters = new LinkedHashMap<Class, FromNativeConverter>();
+ }
+
+ public final void put(Class javaClass, DataConverter converter) {
+ toNativeConverters.put(javaClass, converter);
+ fromNativeConverters.put(javaClass, converter);
+ }
+
+ public final void put(Class javaClass, ToNativeConverter converter) {
+ toNativeConverters.put(javaClass, converter);
+ }
+
+ public final void put(Class javaClass, FromNativeConverter converter) {
+ fromNativeConverters.put(javaClass, converter);
+ }
+
+ public FromNativeConverter getFromNativeConverter(Class type) {
+ return fromNativeConverters.get(type);
+ }
+
+ public ToNativeConverter getToNativeConverter(Class type) {
+ return toNativeConverters.get(type);
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/FromNativeContext.java b/src/main/java/jnr/ffi/mapper/FromNativeContext.java
new file mode 100644
index 0000000..18f79bb
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/FromNativeContext.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Context for a native->java type conversion.
+ */
+public interface FromNativeContext {
+ /**
+ * Gets a sorted list of annotations
+ *
+ * @return a sorted list of annotations for this native type
+ */
+ public abstract Collection<Annotation> getAnnotations();
+
+ /**
+ * Gets the <tt>Runtime</tt> used for the conversion.
+ *
+ * @return The runtime used for the conversion.
+ */
+ public jnr.ffi.Runtime getRuntime();
+}
diff --git a/src/main/java/jnr/ffi/mapper/FromNativeConverter.java b/src/main/java/jnr/ffi/mapper/FromNativeConverter.java
new file mode 100644
index 0000000..1582c0b
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/FromNativeConverter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Converts data from a native type to a java type
+ */
+public interface FromNativeConverter<J, N> {
+ public J fromNative(N nativeValue, FromNativeContext context);
+ public Class<N> nativeType();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE, ElementType.METHOD})
+ public static @interface NoContext {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public static @interface Cacheable {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ @interface FromNative {
+ Class nativeType();
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/FromNativeType.java b/src/main/java/jnr/ffi/mapper/FromNativeType.java
new file mode 100644
index 0000000..65de0d8
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/FromNativeType.java
@@ -0,0 +1,18 @@
+package jnr.ffi.mapper;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+public interface FromNativeType {
+ public FromNativeConverter getFromNativeConverter();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public static @interface Cacheable {
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/FromNativeTypes.java b/src/main/java/jnr/ffi/mapper/FromNativeTypes.java
new file mode 100644
index 0000000..68b952e
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/FromNativeTypes.java
@@ -0,0 +1,28 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+public final class FromNativeTypes {
+
+ public static FromNativeType create(FromNativeConverter converter) {
+ if (converter == null) {
+ return null;
+ }
+ return converter.getClass().isAnnotationPresent(FromNativeConverter.Cacheable.class)
+ ? new Cacheable(converter) : new UnCacheable(converter);
+ }
+
+ @FromNativeType.Cacheable
+ static class Cacheable extends AbstractFromNativeType {
+ public Cacheable(FromNativeConverter converter) {
+ super(converter);
+ }
+ }
+
+ static class UnCacheable extends AbstractFromNativeType {
+ public UnCacheable(FromNativeConverter converter) {
+ super(converter);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/FunctionMapper.java b/src/main/java/jnr/ffi/mapper/FunctionMapper.java
new file mode 100644
index 0000000..6371c32
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/FunctionMapper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import jnr.ffi.Library;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public interface FunctionMapper {
+ public static interface Context {
+ @Deprecated
+ public abstract Library getLibrary();
+ public abstract boolean isSymbolPresent(String name);
+ public Collection<Annotation> getAnnotations();
+ }
+ public String mapFunctionName(String functionName, Context context);
+
+
+ public static final class Builder {
+ private final Map<String, String> functionNameMap = Collections.synchronizedMap(new HashMap<String, String>());
+
+ public Builder map(String javaName, String nativeFunction) {
+ functionNameMap.put(javaName, nativeFunction);
+ return this;
+ }
+
+ public FunctionMapper build() {
+ return new SimpleFunctionMapper(functionNameMap);
+ }
+ }
+
+ /**
+ * An implementation of {@link jnr.ffi.mapper.FunctionMapper} that maps 1:1 between java symbols and native functions
+ */
+ public static final FunctionMapper IDENTITY = new FunctionMapper() {
+ @Override
+ public String mapFunctionName(String functionName, Context context) {
+ return functionName;
+ }
+ };
+}
diff --git a/src/main/java/jnr/ffi/mapper/MethodParameterContext.java b/src/main/java/jnr/ffi/mapper/MethodParameterContext.java
new file mode 100644
index 0000000..f71c838
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/MethodParameterContext.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+ * Holds context for a method parameter java->native conversion.
+ */
+public final class MethodParameterContext implements ToNativeContext {
+ private final jnr.ffi.Runtime runtime;
+ private final Method method;
+ private final int parameterIndex;
+ private Collection<Annotation> annotations;
+ private Annotation[] annotationArray;
+
+ public MethodParameterContext(jnr.ffi.Runtime runtime, Method method, int parameterIndex) {
+ this.runtime = runtime;
+ this.method = method;
+ this.parameterIndex = parameterIndex;
+ }
+
+ public MethodParameterContext(jnr.ffi.Runtime runtime, Method method, int parameterIndex, Annotation[] annotationArray) {
+ this.runtime = runtime;
+ this.method = method;
+ this.parameterIndex = parameterIndex;
+ this.annotationArray = annotationArray.clone();
+ }
+
+ public MethodParameterContext(jnr.ffi.Runtime runtime, Method method, int parameterIndex, Collection<Annotation> annotations) {
+ this.runtime = runtime;
+ this.method = method;
+ this.parameterIndex = parameterIndex;
+ this.annotations = sortedAnnotationCollection(annotations);
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public int getParameterIndex() {
+ return parameterIndex;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations != null ? annotations : buildAnnotationCollection();
+ }
+
+ public jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+
+ private Collection<Annotation> buildAnnotationCollection() {
+ if (annotationArray != null) {
+ return annotations = sortedAnnotationCollection(annotationArray);
+ } else {
+ return annotations = sortedAnnotationCollection(annotationArray = method.getParameterAnnotations()[parameterIndex]);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodParameterContext that = (MethodParameterContext) o;
+
+ return parameterIndex == that.parameterIndex
+ && method.equals(that.method)
+ && getAnnotations().equals(that.getAnnotations());
+ }
+
+ @Override
+ public int hashCode() {
+ int result = method.hashCode();
+ result = 31 * result + parameterIndex;
+ result = 31 * result + getAnnotations().hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/MethodResultContext.java b/src/main/java/jnr/ffi/mapper/MethodResultContext.java
new file mode 100644
index 0000000..cb1f1bf
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/MethodResultContext.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import jnr.ffi.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+ *
+ */
+public class MethodResultContext implements FromNativeContext {
+ private final jnr.ffi.Runtime runtime;
+ private final Method method;
+ private Collection<Annotation> annotations;
+
+ public MethodResultContext(jnr.ffi.Runtime runtime, Method method) {
+ this.runtime = runtime;
+ this.method = method;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations != null ? annotations : (annotations = sortedAnnotationCollection(method.getAnnotations()));
+ }
+
+ public jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/SignatureType.java b/src/main/java/jnr/ffi/mapper/SignatureType.java
new file mode 100644
index 0000000..b9e0d42
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/SignatureType.java
@@ -0,0 +1,16 @@
+package jnr.ffi.mapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+*
+*/
+public interface SignatureType {
+ public Class getDeclaredType();
+ public Collection<Annotation> getAnnotations();
+ public java.lang.reflect.Type getGenericType();
+}
diff --git a/src/main/java/jnr/ffi/mapper/SignatureTypeMapper.java b/src/main/java/jnr/ffi/mapper/SignatureTypeMapper.java
new file mode 100644
index 0000000..971dab7
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/SignatureTypeMapper.java
@@ -0,0 +1,9 @@
+package jnr.ffi.mapper;
+
+/**
+
+*/
+public interface SignatureTypeMapper {
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context);
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context);
+}
diff --git a/src/main/java/jnr/ffi/mapper/SignatureTypeMapperAdapter.java b/src/main/java/jnr/ffi/mapper/SignatureTypeMapperAdapter.java
new file mode 100644
index 0000000..68f4cd5
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/SignatureTypeMapperAdapter.java
@@ -0,0 +1,22 @@
+package jnr.ffi.mapper;
+
+/**
+ * Adapts a {@link jnr.ffi.mapper.TypeMapper} to a SignatureTypeMapper
+ */
+public class SignatureTypeMapperAdapter implements SignatureTypeMapper {
+ private final TypeMapper typeMapper;
+
+ public SignatureTypeMapperAdapter(TypeMapper typeMapper) {
+ this.typeMapper = typeMapper;
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return FromNativeTypes.create(typeMapper.getFromNativeConverter(type.getDeclaredType()));
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return ToNativeTypes.create(typeMapper.getToNativeConverter(type.getDeclaredType()));
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/SimpleFunctionMapper.java b/src/main/java/jnr/ffi/mapper/SimpleFunctionMapper.java
new file mode 100644
index 0000000..704b35c
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/SimpleFunctionMapper.java
@@ -0,0 +1,19 @@
+package jnr.ffi.mapper;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+class SimpleFunctionMapper implements FunctionMapper {
+ private final Map<String, String> functionNameMap;
+
+ SimpleFunctionMapper(Map<String, String> map) {
+ functionNameMap = Collections.unmodifiableMap(new HashMap<String, String>(map));
+ }
+
+ public String mapFunctionName(String functionName, Context context) {
+ String nativeFunction = functionNameMap.get(functionName);
+ return nativeFunction != null ? nativeFunction : functionName;
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/mapper/SimpleTypeMapper.java b/src/main/java/jnr/ffi/mapper/SimpleTypeMapper.java
new file mode 100644
index 0000000..e2c2821
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/SimpleTypeMapper.java
@@ -0,0 +1,25 @@
+package jnr.ffi.mapper;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+final class SimpleTypeMapper implements TypeMapper {
+ private final Map<Class, ToNativeConverter<?, ?>> toNativeConverters;
+ private final Map<Class, FromNativeConverter<?, ?>> fromNativeConverters;
+
+ public SimpleTypeMapper(Map<Class, ToNativeConverter<?, ?>> toNativeConverters, Map<Class, FromNativeConverter<?, ?>> fromNativeConverters) {
+ this.toNativeConverters = Collections.unmodifiableMap(new IdentityHashMap<Class, ToNativeConverter<?, ?>>(toNativeConverters));
+ this.fromNativeConverters = Collections.unmodifiableMap(new IdentityHashMap<Class, FromNativeConverter<?, ?>>(fromNativeConverters));
+ }
+
+ @Override
+ public FromNativeConverter getFromNativeConverter(Class type) {
+ return fromNativeConverters.get(type);
+ }
+
+ @Override
+ public ToNativeConverter getToNativeConverter(Class type) {
+ return toNativeConverters.get(type);
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/ToNativeContext.java b/src/main/java/jnr/ffi/mapper/ToNativeContext.java
new file mode 100644
index 0000000..80ca03e
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/ToNativeContext.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+public interface ToNativeContext {
+ /**
+ * Gets a sorted list of annotations
+ *
+ * @return a sorted list of annotations for this native type
+ */
+ public abstract Collection<Annotation> getAnnotations();
+
+ /**
+ * Gets the <tt>Runtime</tt> used for the conversion.
+ *
+ * @return The runtime used for the conversion.
+ */
+ public jnr.ffi.Runtime getRuntime();
+}
diff --git a/src/main/java/jnr/ffi/mapper/ToNativeConverter.java b/src/main/java/jnr/ffi/mapper/ToNativeConverter.java
new file mode 100644
index 0000000..f7010df
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/ToNativeConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import jnr.ffi.Pointer;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public interface ToNativeConverter<J, N> {
+ public N toNative(J value, ToNativeContext context);
+ public Class<N> nativeType();
+
+ /**
+ * Used to reload a parameter converted to a native type via a custom {@link jnr.ffi.mapper.ToNativeConverter}
+ */
+ public static interface PostInvocation<J,N> extends ToNativeConverter<J, N> {
+ public void postInvoke(J j, N n, ToNativeContext context);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE, ElementType.METHOD})
+ public static @interface NoContext {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public static @interface Cacheable {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ @interface ToNative {
+ Class nativeType();
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/ToNativeType.java b/src/main/java/jnr/ffi/mapper/ToNativeType.java
new file mode 100644
index 0000000..cabbf98
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/ToNativeType.java
@@ -0,0 +1,18 @@
+package jnr.ffi.mapper;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+public interface ToNativeType {
+ public ToNativeConverter getToNativeConverter();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public static @interface Cacheable {
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/ToNativeTypes.java b/src/main/java/jnr/ffi/mapper/ToNativeTypes.java
new file mode 100644
index 0000000..da0e116
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/ToNativeTypes.java
@@ -0,0 +1,28 @@
+package jnr.ffi.mapper;
+
+/**
+ *
+ */
+public final class ToNativeTypes {
+
+ public static ToNativeType create(ToNativeConverter converter) {
+ if (converter == null) {
+ return null;
+ }
+ return converter.getClass().isAnnotationPresent(ToNativeConverter.Cacheable.class)
+ ? new Cacheable(converter) : new UnCacheable(converter);
+ }
+
+ @ToNativeType.Cacheable
+ static class Cacheable extends AbstractToNativeType {
+ public Cacheable(ToNativeConverter converter) {
+ super(converter);
+ }
+ }
+
+ static class UnCacheable extends AbstractToNativeType {
+ public UnCacheable(ToNativeConverter converter) {
+ super(converter);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/TypeMapper.java b/src/main/java/jnr/ffi/mapper/TypeMapper.java
new file mode 100644
index 0000000..0ec86d9
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/TypeMapper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.mapper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public interface TypeMapper {
+ public FromNativeConverter getFromNativeConverter(Class type);
+ public ToNativeConverter getToNativeConverter(Class type);
+
+ public static final class Builder {
+ private final Map<Class, ToNativeConverter<?,?>> toNativeConverterMap = new HashMap<Class, ToNativeConverter<?, ?>>();
+ private final Map<Class, FromNativeConverter<?,?>> fromNativeConverterMap = new HashMap<Class, FromNativeConverter<?,?>>();
+
+ public <T> Builder map(Class<? extends T> javaType, ToNativeConverter<? extends T, ?> toNativeConverter) {
+ toNativeConverterMap.put(javaType, toNativeConverter);
+ return this;
+ }
+
+ public <T> Builder map(Class<? extends T> javaType, FromNativeConverter<? extends T, ?> fromNativeConverter) {
+ fromNativeConverterMap.put(javaType, fromNativeConverter);
+ return this;
+ }
+
+ public <T> Builder map(Class<? extends T> javaType, DataConverter<? extends T, ?> dataConverter) {
+ toNativeConverterMap.put(javaType, dataConverter);
+ fromNativeConverterMap.put(javaType, dataConverter);
+ return this;
+ }
+
+ public TypeMapper build() {
+ return new SimpleTypeMapper(toNativeConverterMap, fromNativeConverterMap);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/mapper/Util.java b/src/main/java/jnr/ffi/mapper/Util.java
new file mode 100644
index 0000000..7779799
--- /dev/null
+++ b/src/main/java/jnr/ffi/mapper/Util.java
@@ -0,0 +1,5 @@
+package jnr.ffi.mapper;
+
+class Util {
+
+}
diff --git a/src/main/java/jnr/ffi/provider/AbstractArrayMemoryIO.java b/src/main/java/jnr/ffi/provider/AbstractArrayMemoryIO.java
new file mode 100644
index 0000000..46e574d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/AbstractArrayMemoryIO.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Runtime;
+import jnr.ffi.util.BufferUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+public abstract class AbstractArrayMemoryIO extends AbstractMemoryIO {
+ private final ArrayIO io;
+ protected final byte[] buffer;
+ protected final int offset, length;
+
+ protected AbstractArrayMemoryIO(Runtime runtime, byte[] buffer, int offset, int length) {
+ super(runtime, 0, false);
+ this.io = ArrayIO.getArrayIO(runtime);
+ this.buffer = buffer;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ protected AbstractArrayMemoryIO(Runtime runtime, byte[] buffer) {
+ this(runtime, buffer, 0, buffer.length);
+ }
+
+ protected AbstractArrayMemoryIO(Runtime runtime, int size) {
+ this(runtime, new byte[size], 0, size);
+ }
+
+ protected final ArrayIO getArrayIO() {
+ return io;
+ }
+
+ public final byte[] array() {
+ return buffer;
+ }
+ public final int offset() {
+ return offset;
+ }
+ public final int length() {
+ return length;
+ }
+
+ @Override
+ public final int arrayLength() {
+ return length;
+ }
+
+ @Override
+ public final int arrayOffset() {
+ return offset;
+ }
+
+ @Override
+ public final boolean hasArray() {
+ return true;
+ }
+
+ public final long size() {
+ return this.length;
+ }
+
+ protected final int index(long off) {
+ return this.offset + (int) off;
+ }
+
+ protected final int remaining(long offset) {
+ return length - (int) offset;
+ }
+
+ public final boolean isNull() {
+ return false;
+ }
+
+ public String getString(long offset) {
+ return BufferUtil.getString(ByteBuffer.wrap(buffer, index(offset), length - (int) offset), Charset.defaultCharset());
+ }
+
+ public String getString(long offset, int maxLength, Charset cs) {
+ return BufferUtil.getString(ByteBuffer.wrap(buffer, index(offset), Math.min(length - (int) offset, maxLength)), cs);
+ }
+
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ ByteBuffer buf = cs.encode(string);
+ int len = Math.min(maxLength - 1, Math.min(buf.remaining(), remaining(offset)));
+ buf.get(buffer, index(offset), len);
+ buffer[index(offset) + len] = (byte) 0;
+ }
+
+ public void putZeroTerminatedByteArray(long offset, byte[] src, int off, int len) {
+ System.arraycopy(src, off, buffer, index(offset), length - (int) offset);
+ buffer[index(offset) + len] = (byte) 0;
+ }
+
+ public final byte getByte(long offset) {
+ return (byte) (buffer[index(offset)] & 0xff);
+ }
+
+ public final short getShort(long offset) {
+ return io.getInt16(buffer, index(offset));
+ }
+
+ public final int getInt(long offset) {
+ return io.getInt32(buffer, index(offset));
+ }
+
+ public final long getLongLong(long offset) {
+ return io.getInt64(buffer, index(offset));
+ }
+
+ @Override
+ public final long getAddress(long offset) {
+ return io.getAddress(buffer, index(offset));
+ }
+
+ public final float getFloat(long offset) {
+ return io.getFloat32(buffer, index(offset));
+ }
+
+ public final double getDouble(long offset) {
+ return io.getFloat64(buffer, index(offset));
+ }
+
+ public final void putByte(long offset, byte value) {
+ buffer[index(offset)] = value;
+ }
+
+ public final void putShort(long offset, short value) {
+ io.putInt16(buffer, index(offset), value);
+ }
+
+ public final void putInt(long offset, int value) {
+ io.putInt32(buffer, index(offset), value);
+ }
+
+ public final void putLongLong(long offset, long value) {
+ io.putInt64(buffer, index(offset), value);
+ }
+
+ @Override
+ public final void putAddress(long offset, long value) {
+ io.putAddress(buffer, index(offset), value);
+ }
+
+ public final void putFloat(long offset, float value) {
+ io.putFloat32(buffer, index(offset), value);
+ }
+
+ public final void putDouble(long offset, double value) {
+ io.putFloat64(buffer, index(offset), value);
+ }
+
+ public final void get(long offset, byte[] dst, int off, int len) {
+ System.arraycopy(buffer, index(offset), dst, off, len);
+ }
+
+ public final void put(long offset, byte[] src, int off, int len) {
+ System.arraycopy(src, off, buffer, index(offset), len);
+ }
+
+ public final void get(long offset, short[] dst, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ dst[off + i] = io.getInt16(buffer, begin + (i << 1));
+ }
+ }
+
+ public final void put(long offset, short[] src, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ io.putInt16(buffer, begin + (i << 1), src[off + i]);
+ }
+ }
+
+ public final void get(long offset, int[] dst, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ dst[off + i] = io.getInt32(buffer, begin + (i << 2));
+ }
+ }
+
+ public final void put(long offset, int[] src, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ io.putInt32(buffer, begin + (i << 2), src[off + i]);
+ }
+ }
+
+ public final void get(long offset, long[] dst, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ dst[off + i] = io.getInt64(buffer, begin + (i << 3));
+ }
+ }
+
+ public final void put(long offset, long[] src, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ io.putInt64(buffer, begin + (i << 3), src[off + i]);
+ }
+ }
+
+ public final void get(long offset, float[] dst, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ dst[off + i] = io.getFloat32(buffer, begin + (i << 2));
+ }
+ }
+
+ public final void put(long offset, float[] src, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ io.putFloat32(buffer, begin + (i << 2), src[off + i]);
+ }
+ }
+
+ public final void get(long offset, double[] dst, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ dst[off + i] = io.getFloat64(buffer, begin + (i << 3));
+ }
+ }
+
+ public final void put(long offset, double[] src, int off, int len) {
+ int begin = index(offset);
+ for (int i = 0; i < len; ++i) {
+ io.putFloat64(buffer, begin + (i << 3), src[off + i]);
+ }
+ }
+
+ @Override
+ public final int indexOf(long offset, byte value) {
+ int off = index(offset);
+ for (int i = 0; i < length; ++i) {
+ if (buffer[off + i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public final int indexOf(long offset, byte value, int maxlen) {
+ int off = index(offset);
+ for (int i = 0; i < Math.min(length, maxlen); ++i) {
+ if (buffer[off + i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public final void setMemory(long offset, long size, byte value) {
+ Arrays.fill(buffer, index(offset), (int) size, value);
+ }
+
+ public final void clear() {
+ Arrays.fill(buffer, offset, length, (byte) 0);
+ }
+
+ protected static abstract class ArrayIO {
+
+ public static ArrayIO getArrayIO(Runtime runtime) {
+ if (runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)) {
+ return runtime.addressSize() == 8
+ ? BE64ArrayIO.INSTANCE : BE32ArrayIO.INSTANCE;
+ } else {
+ return runtime.addressSize() == 8
+ ? LE64ArrayIO.INSTANCE : LE32ArrayIO.INSTANCE;
+ }
+ }
+
+ public abstract short getInt16(byte[] buffer, int offset);
+ public abstract int getInt32(byte[] buffer, int offset);
+ public abstract long getInt64(byte[] buffer, int offset);
+ public abstract long getAddress(byte[] buffer, int offset);
+
+ public abstract void putInt16(byte[] buffer, int offset, int value);
+ public abstract void putInt32(byte[] buffer, int offset, int value);
+ public abstract void putInt64(byte[] buffer, int offset, long value);
+ public abstract void putAddress(byte[] buffer, int offset, long value);
+
+
+
+ public final float getFloat32(byte[] buffer, int offset) {
+ return Float.intBitsToFloat(getInt32(buffer, offset));
+ }
+ public final void putFloat32(byte[] buffer, int offset, float value) {
+ putInt32(buffer, offset, Float.floatToRawIntBits(value));
+ }
+ public final double getFloat64(byte[] buffer, int offset) {
+ return Double.longBitsToDouble(getInt64(buffer, offset));
+ }
+ public final void putFloat64(byte[] buffer, int offset, double value) {
+ putInt64(buffer, offset, Double.doubleToRawLongBits(value));
+ }
+ }
+
+ private static abstract class LittleEndianArrayIO extends ArrayIO {
+ public final short getInt16(byte[] array, int offset) {
+ return (short) ((array[offset] & 0xff) | ((array[offset + 1] & 0xff) << 8));
+ }
+ public final int getInt32(byte[] array, int offset) {
+ return ((array[offset + 0] & 0xff) << 0)
+ | ((array[offset + 1] & 0xff) << 8)
+ | ((array[offset + 2] & 0xff) << 16)
+ | ((array[offset + 3] & 0xff) << 24);
+ }
+ public final long getInt64(byte[] array, int offset) {
+ return (((long)array[offset + 0] & 0xff) << 0)
+ | (((long)array[offset + 1] & 0xff) << 8)
+ | (((long)array[offset + 2] & 0xff) << 16)
+ | (((long)array[offset + 3] & 0xff) << 24)
+ | (((long)array[offset + 4] & 0xff) << 32)
+ | (((long)array[offset + 5] & 0xff) << 40)
+ | (((long)array[offset + 6] & 0xff) << 48)
+ | (((long)array[offset + 7] & 0xff) << 56);
+ }
+ public final void putInt16(byte[] buffer, int offset, int value) {
+ buffer[offset + 0] = (byte) (value >> 0);
+ buffer[offset + 1] = (byte) (value >> 8);
+ }
+ public final void putInt32(byte[] buffer, int offset, int value) {
+ buffer[offset + 0] = (byte) (value >> 0);
+ buffer[offset + 1] = (byte) (value >> 8);
+ buffer[offset + 2] = (byte) (value >> 16);
+ buffer[offset + 3] = (byte) (value >> 24);
+ }
+ public final void putInt64(byte[] buffer, int offset, long value) {
+ buffer[offset + 0] = (byte) (value >> 0);
+ buffer[offset + 1] = (byte) (value >> 8);
+ buffer[offset + 2] = (byte) (value >> 16);
+ buffer[offset + 3] = (byte) (value >> 24);
+ buffer[offset + 4] = (byte) (value >> 32);
+ buffer[offset + 5] = (byte) (value >> 40);
+ buffer[offset + 6] = (byte) (value >> 48);
+ buffer[offset + 7] = (byte) (value >> 56);
+ }
+ }
+
+ private static abstract class BigEndianArrayIO extends ArrayIO {
+ public short getInt16(byte[] array, int offset) {
+ return (short) (((array[offset + 0] & 0xff) << 8)
+ | (array[offset + 1] & 0xff));
+ }
+ public int getInt32(byte[] array, int offset) {
+ return ((array[offset + 0] & 0xff) << 24)
+ | ((array[offset + 1] & 0xff) << 16)
+ | ((array[offset + 2] & 0xff) << 8)
+ | ((array[offset + 3] & 0xff) << 0);
+ }
+ public long getInt64(byte[] array, int offset) {
+ return (((long)array[offset + 0] & 0xff) << 56)
+ | (((long)array[offset + 1] & 0xff) << 48)
+ | (((long)array[offset + 2] & 0xff) << 40)
+ | (((long)array[offset + 3] & 0xff) << 32)
+ | (((long)array[offset + 4] & 0xff) << 24)
+ | (((long)array[offset + 5] & 0xff) << 16)
+ | (((long)array[offset + 6] & 0xff) << 8)
+ | (((long)array[offset + 7] & 0xff) << 0);
+ }
+ public final void putInt16(byte[] buffer, int offset, int value) {
+ buffer[offset + 0] = (byte) (value >> 8);
+ buffer[offset + 1] = (byte) (value >> 0);
+ }
+ public final void putInt32(byte[] buffer, int offset, int value) {
+ buffer[offset + 0] = (byte) (value >> 24);
+ buffer[offset + 1] = (byte) (value >> 16);
+ buffer[offset + 2] = (byte) (value >> 8);
+ buffer[offset + 3] = (byte) (value >> 0);
+ }
+ public final void putInt64(byte[] buffer, int offset, long value) {
+ buffer[offset + 0] = (byte) (value >> 56);
+ buffer[offset + 1] = (byte) (value >> 48);
+ buffer[offset + 2] = (byte) (value >> 40);
+ buffer[offset + 3] = (byte) (value >> 32);
+ buffer[offset + 4] = (byte) (value >> 24);
+ buffer[offset + 5] = (byte) (value >> 16);
+ buffer[offset + 6] = (byte) (value >> 8);
+ buffer[offset + 7] = (byte) (value >> 0);
+ }
+ }
+
+ private static final class LE32ArrayIO extends LittleEndianArrayIO {
+ public static final ArrayIO INSTANCE = new LE32ArrayIO();
+
+ public final long getAddress(byte[] buffer, int offset) {
+ return (long) getInt32(buffer, offset) & 0xffffffffL;
+ }
+ public final void putAddress(byte[] buffer, int offset, long value) {
+ putInt32(buffer, offset, (int) value);
+ }
+ }
+
+ private static final class LE64ArrayIO extends LittleEndianArrayIO {
+ public static final ArrayIO INSTANCE = new LE64ArrayIO();
+
+ public final long getAddress(byte[] buffer, int offset) {
+ return getInt64(buffer, offset);
+ }
+ public final void putAddress(byte[] buffer, int offset, long value) {
+ putInt64(buffer, offset, value);
+ }
+ }
+
+ private static final class BE32ArrayIO extends BigEndianArrayIO {
+ public static final ArrayIO INSTANCE = new BE32ArrayIO();
+
+ public final long getAddress(byte[] buffer, int offset) {
+ return (long) getInt32(buffer, offset) & 0xffffffffL;
+ }
+ public final void putAddress(byte[] buffer, int offset, long value) {
+ putInt32(buffer, offset, (int) value);
+ }
+ }
+
+ private static final class BE64ArrayIO extends BigEndianArrayIO {
+ public static final ArrayIO INSTANCE = new BE64ArrayIO();
+
+ public final long getAddress(byte[] buffer, int offset) {
+ return getInt64(buffer, offset);
+ }
+ public final void putAddress(byte[] buffer, int offset, long value) {
+ putInt64(buffer, offset, value);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/AbstractBufferMemoryIO.java b/src/main/java/jnr/ffi/provider/AbstractBufferMemoryIO.java
new file mode 100644
index 0000000..a7b19e8
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/AbstractBufferMemoryIO.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Runtime;
+import jnr.ffi.util.BufferUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/**
+ *
+ */
+abstract public class AbstractBufferMemoryIO extends AbstractMemoryIO {
+
+ protected final ByteBuffer buffer;
+
+ public AbstractBufferMemoryIO(Runtime runtime, ByteBuffer buffer, long address) {
+ super(runtime, address, buffer.isDirect());
+ this.buffer = buffer;
+ }
+
+
+ public long size() {
+ return buffer.remaining();
+ }
+
+ public final ByteBuffer getByteBuffer() {
+ return buffer;
+ }
+
+ @Override
+ public int arrayLength() {
+ return getByteBuffer().remaining();
+ }
+
+ @Override
+ public int arrayOffset() {
+ return getByteBuffer().arrayOffset();
+ }
+
+ @Override
+ public Object array() {
+ return getByteBuffer().array();
+ }
+
+ @Override
+ public boolean hasArray() {
+ return getByteBuffer().hasArray();
+ }
+
+ public byte getByte(long offset) {
+ return buffer.get((int) offset);
+ }
+
+ public short getShort(long offset) {
+ return buffer.getShort((int) offset);
+ }
+
+ public int getInt(long offset) {
+ return buffer.getInt((int) offset);
+ }
+
+ public long getLongLong(long offset) {
+ return buffer.getLong((int) offset);
+ }
+
+ public float getFloat(long offset) {
+ return buffer.getFloat((int) offset);
+ }
+
+ public double getDouble(long offset) {
+ return buffer.getDouble((int) offset);
+ }
+
+ public void putByte(long offset, byte value) {
+ buffer.put((int) offset, value);
+ }
+
+ public void putShort(long offset, short value) {
+ buffer.putShort((int) offset, value);
+ }
+
+ public void putInt(long offset, int value) {
+ buffer.putInt((int) offset, value);
+ }
+
+ public void putLongLong(long offset, long value) {
+ buffer.putLong((int) offset, value);
+ }
+
+ public void putFloat(long offset, float value) {
+ buffer.putFloat((int) offset, value);
+ }
+
+ public void putDouble(long offset, double value) {
+ buffer.putDouble((int) offset, value);
+ }
+
+ public String getString(long offset, int size) {
+ return BufferUtil.getString(BufferUtil.slice(buffer, (int) offset), Charset.defaultCharset());
+ }
+
+ public void putString(long offset, String string) {
+ BufferUtil.putString(BufferUtil.slice(buffer, (int) offset), Charset.defaultCharset(), string);
+ }
+
+ @Override
+ public void get(long offset, byte[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len).get(dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, short[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Short.SIZE / 8).asShortBuffer().get(dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, int[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Integer.SIZE / 8).asIntBuffer().get(dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, long[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Long.SIZE / 8).asLongBuffer().get(dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, float[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Float.SIZE / 8).asFloatBuffer().get(dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, double[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Double.SIZE / 8).asDoubleBuffer().get(dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, byte[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len).put(dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, short[] dst, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Short.SIZE / 8).asShortBuffer().put(dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, int[] src, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Integer.SIZE / 8).asIntBuffer().put(src, off, len);
+ }
+
+ @Override
+ public void put(long offset, long[] src, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Long.SIZE / 8).asLongBuffer().put(src, off, len);
+ }
+
+ @Override
+ public void put(long offset, float[] src, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Float.SIZE / 8).asFloatBuffer().put(src, off, len);
+ }
+
+ @Override
+ public void put(long offset, double[] src, int off, int len) {
+ BufferUtil.slice(buffer, (int) offset, len * Double.SIZE / 8).asDoubleBuffer().put(src, off, len);
+ }
+
+ @Override
+ public String getString(long offset) {
+ return BufferUtil.getString(BufferUtil.slice(buffer, (int) offset), Charset.defaultCharset());
+ }
+
+
+ @Override
+ public String getString(long offset, int maxLength, Charset cs) {
+ return BufferUtil.getString(BufferUtil.slice(buffer, (int) offset, maxLength),
+ cs);
+ }
+
+ @Override
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ BufferUtil.putString(BufferUtil.slice(buffer, (int) offset, maxLength), cs, string);
+ }
+
+ @Override
+ public int indexOf(long offset, byte value, int maxlen) {
+ for (; offset > -1; ++offset) {
+ if (buffer.get((int) offset) == value) {
+ return (int) offset;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void setMemory(long offset, long size, byte value) {
+ for (int i = 0; i < size; ++i) {
+ buffer.put((int) offset + i, value);
+ }
+ }
+
+
+}
diff --git a/src/main/java/jnr/ffi/provider/AbstractMemoryIO.java b/src/main/java/jnr/ffi/provider/AbstractMemoryIO.java
new file mode 100644
index 0000000..a9f8e02
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/AbstractMemoryIO.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Address;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.Type;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base implementations of some MemoryIO operations.
+ */
+abstract public class AbstractMemoryIO extends Pointer {
+
+ protected static void checkBounds(long size, long off, long len) {
+ if ((off | len | (off + len) | (size - (off + len))) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ protected AbstractMemoryIO(Runtime runtime, long address, boolean isDirect) {
+ super(runtime, address, isDirect);
+ }
+
+ public int indexOf(long offset, byte value) {
+ return indexOf(offset, value, Integer.MAX_VALUE);
+ }
+
+ public long getAddress(long offset) {
+ return getRuntime().addressSize() == 4 ? getInt(offset) : getLongLong(offset);
+ }
+
+ public void putAddress(long offset, long value) {
+ if (getRuntime().addressSize() == 4) {
+ putInt(offset, (int) value);
+ } else {
+ putLongLong(offset, value);
+ }
+ }
+
+ public void checkBounds(long offset, long size) {
+ // No bounds checking by default
+ }
+
+ public void putAddress(long offset, Address value) {
+ if (getRuntime().addressSize() == 4) {
+ putInt(offset, value.intValue());
+ } else {
+ putLongLong(offset, value.longValue());
+ }
+ }
+
+ public final long getNativeLong(long offset) {
+ return getRuntime().longSize() == 4 ? getInt(offset) : getLongLong(offset);
+ }
+
+ public void putNativeLong(long offset, long value) {
+ if (getRuntime().longSize() == 4) {
+ putInt(offset, (int) value);
+ } else {
+ putLongLong(offset, value);
+ }
+ }
+
+ public long getLong(long offset) {
+ return getRuntime().longSize() == 4 ? getInt(offset) : getLongLong(offset);
+ }
+
+ public void putLong(long offset, long value) {
+ if (getRuntime().longSize() == 4) {
+ putInt(offset, (int) value);
+ } else {
+ putLongLong(offset, value);
+ }
+ }
+
+ @Override
+ public void putInt(Type type, long offset, long value) {
+ switch (type.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ putByte(offset, (byte) value);
+ break;
+
+ case SSHORT:
+ case USHORT:
+ putShort(offset, (short) value);
+ break;
+
+ case SINT:
+ case UINT:
+ putInt(offset, (int) value);
+ break;
+
+ case SLONG:
+ case ULONG:
+ putNativeLong(offset, value);
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ putLongLong(offset, value);
+ break;
+ default:
+ throw new IllegalArgumentException("unsupported integer type: " + type.getNativeType());
+ }
+ }
+
+ @Override
+ public long getInt(Type type, long offset) {
+ switch (type.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ return getByte(offset);
+
+ case SSHORT:
+ case USHORT:
+ return getShort(offset);
+
+ case SINT:
+ case UINT:
+ return getInt(offset);
+
+ case SLONG:
+ case ULONG:
+ return getNativeLong(offset);
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return getLongLong(offset);
+
+ default:
+ throw new IllegalArgumentException("unsupported integer type: " + type.getNativeType());
+ }
+ }
+
+ public AbstractMemoryIO slice(long offset) {
+ return new ShareMemoryIO(this, offset);
+ }
+
+ public AbstractMemoryIO slice(long offset, long size) {
+ return new BoundedMemoryIO(this, offset, size);
+ }
+
+
+ public void transferTo(long offset, Pointer other, long otherOffset, long count) {
+ Pointer dst = other instanceof DelegatingMemoryIO ? ((DelegatingMemoryIO) other).getDelegatedMemoryIO() : other;
+
+ dst.checkBounds(otherOffset, count);
+
+ if (dst instanceof AbstractArrayMemoryIO) {
+ AbstractArrayMemoryIO aio = (AbstractArrayMemoryIO) dst;
+ get(offset, aio.array(), aio.offset() + (int) otherOffset, (int) count);
+
+ } else if (dst instanceof AbstractBufferMemoryIO && ((AbstractBufferMemoryIO) dst).getByteBuffer().hasArray()) {
+ ByteBuffer buf = ((AbstractBufferMemoryIO) dst).getByteBuffer();
+ get(offset, buf.array(), buf.arrayOffset() + buf.position() + (int) otherOffset, (int) count);
+
+ } else {
+ for (long i = 0; i < count; ++i) {
+ other.putByte(otherOffset + i, getByte(offset + i));
+ }
+ }
+ }
+
+ public void transferFrom(long offset, Pointer other, long otherOffset, long count) {
+ Pointer src = other instanceof DelegatingMemoryIO ? ((DelegatingMemoryIO) other).getDelegatedMemoryIO() : other;
+
+ src.checkBounds(otherOffset, count);
+
+ if (src instanceof AbstractArrayMemoryIO) {
+ AbstractArrayMemoryIO aio = (AbstractArrayMemoryIO) src;
+ put(offset, aio.array(), aio.offset() + (int) otherOffset, (int) count);
+
+ } else if (src instanceof AbstractBufferMemoryIO && ((AbstractBufferMemoryIO) src).getByteBuffer().hasArray()) {
+ ByteBuffer buf = ((AbstractBufferMemoryIO) src).getByteBuffer();
+ put(offset, buf.array(), buf.arrayOffset() + buf.position() + (int) otherOffset, (int) count);
+
+ } else {
+ // Do a byte-at-a-time copy
+ for (long i = 0; i < count; ++i) {
+ putByte(offset + i, other.getByte(otherOffset + i));
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/AbstractRuntime.java b/src/main/java/jnr/ffi/provider/AbstractRuntime.java
new file mode 100644
index 0000000..501462a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/AbstractRuntime.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.Runtime;
+import jnr.ffi.Type;
+
+import java.nio.ByteOrder;
+import java.util.EnumMap;
+import java.util.EnumSet;
+
+/**
+ *
+ */
+public abstract class AbstractRuntime extends Runtime {
+ private final Type[] types;
+ private final long addressMask;
+ private final int addressSize;
+ private final int longSize;
+ private final ByteOrder byteOrder;
+
+ public AbstractRuntime(ByteOrder byteOrder, EnumMap<NativeType, Type> typeMap) {
+ this.byteOrder = byteOrder;
+
+ EnumSet<NativeType> nativeTypes = EnumSet.allOf(NativeType.class);
+ types = new Type[nativeTypes.size()];
+ for (NativeType t : nativeTypes) {
+ types[t.ordinal()] = typeMap.containsKey(t) ? typeMap.get(t) : new BadType(t.toString());
+ }
+
+ this.addressSize = types[NativeType.ADDRESS.ordinal()].size();
+ this.longSize = types[NativeType.SLONG.ordinal()].size();
+ this.addressMask = addressSize == 4 ? 0xffffffffL : 0xffffffffffffffffL;
+ }
+
+
+ /** Looks up the runtime-specific that corresponds to the pseudo-type */
+ public final Type findType(NativeType type) {
+ return types[type.ordinal()];
+ }
+
+ /**
+ * Gets the native memory manager instance for this runtime
+ *
+ * @return a {@link MemoryManager}
+ */
+ public abstract MemoryManager getMemoryManager();
+
+ /**
+ * Gets the last native error code.
+ * <p>
+ * This returns the errno value that was set at the time of the last native
+ * function call.
+ *
+ * @return The errno value.
+ */
+ public abstract int getLastError();
+
+ /**
+ * Sets the native error code.
+ *
+ * @param error The value to set errno to.
+ */
+ public abstract void setLastError(int error);
+
+ /** Gets the address mask for this runtime */
+ public final long addressMask() {
+ return addressMask;
+ }
+
+ /**
+ * Gets the size of an address (e.g. a pointer) for this runtime
+ *
+ * @return The size of an address in bytes.
+ */
+ public final int addressSize() {
+ return addressSize;
+ }
+
+ /**
+ * Gets the size of a C long integer for this runtime
+ *
+ * @return The size of a C long integer in bytes.
+ */
+ public final int longSize() {
+ return longSize;
+ }
+
+ /**
+ * Retrieves this runtime's native byte order.
+ *
+ * @return this runtime's byte order
+ */
+ public final ByteOrder byteOrder() {
+ return byteOrder;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/BadType.java b/src/main/java/jnr/ffi/provider/BadType.java
new file mode 100644
index 0000000..e1b9fae
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/BadType.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+
+/**
+ *
+ */
+public final class BadType extends jnr.ffi.Type {
+
+ private final String typeName;
+
+ public BadType(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public final int alignment() {
+ throw new RuntimeException("invalid type: " + typeName);
+ }
+
+ public final int size() {
+ throw new RuntimeException("invalid type: " + typeName);
+ }
+
+ public NativeType getNativeType() {
+ throw new RuntimeException("invalid type: " + typeName);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/BoundedMemoryIO.java b/src/main/java/jnr/ffi/provider/BoundedMemoryIO.java
new file mode 100644
index 0000000..a108884
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/BoundedMemoryIO.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Address;
+import jnr.ffi.Pointer;
+
+import java.nio.charset.Charset;
+
+
+public final class BoundedMemoryIO extends AbstractMemoryIO implements DelegatingMemoryIO {
+
+ private final long base, size;
+ private final Pointer io;
+
+ public BoundedMemoryIO(Pointer parent, long offset, long size) {
+ super(parent.getRuntime(), parent.address() != 0L ? parent.address() + offset : 0L, parent.isDirect());
+ this.io = parent;
+ this.base = offset;
+ this.size = size;
+ }
+
+ public long size() {
+ return this.size;
+ }
+
+ @Override
+ public final boolean hasArray() {
+ return io.hasArray();
+ }
+
+ @Override
+ public final Object array() {
+ return io.array();
+ }
+
+ @Override
+ public final int arrayOffset() {
+ return io.arrayOffset() + (int) base;
+ }
+
+ @Override
+ public final int arrayLength() {
+ return (int) size;
+ }
+
+ @Override
+ public void checkBounds(long offset, long length) {
+ checkBounds(this.size, offset, length);
+ getDelegatedMemoryIO().checkBounds(base + offset, length);
+ }
+
+ public Pointer getDelegatedMemoryIO() {
+ return io;
+ }
+
+ @Override
+ public int hashCode() {
+ return getDelegatedMemoryIO().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof BoundedMemoryIO && io.equals(((BoundedMemoryIO) obj).io) &&
+ ((BoundedMemoryIO) obj).base == base && ((BoundedMemoryIO) obj).size == size)
+ || io.equals(obj);
+ }
+
+ @Override
+ public byte getByte(long offset) {
+ checkBounds(size, offset, 1);
+ return io.getByte(base + offset);
+ }
+
+ @Override
+ public short getShort(long offset) {
+ checkBounds(size, offset, 2);
+ return io.getShort(base + offset);
+ }
+
+ @Override
+ public int getInt(long offset) {
+ checkBounds(size, offset, 4);
+ return io.getInt(base + offset);
+ }
+
+ @Override
+ public long getLongLong(long offset) {
+ checkBounds(size, offset, 8);
+ return io.getLongLong(base + offset);
+ }
+
+ @Override
+ public float getFloat(long offset) {
+ checkBounds(size, offset, 4);
+ return io.getFloat(base + offset);
+ }
+
+ @Override
+ public double getDouble(long offset) {
+ checkBounds(size, offset, 8);
+ return io.getDouble(base + offset);
+ }
+
+ public Pointer getPointer(long offset) {
+ checkBounds(size, offset, getRuntime().addressSize());
+ return io.getPointer(base + offset);
+ }
+
+ public Pointer getPointer(long offset, long size) {
+ checkBounds(this.size, base + offset, getRuntime().addressSize());
+ return io.getPointer(base + offset, size);
+ }
+
+ @Override
+ public void putByte(long offset, byte value) {
+ checkBounds(size, offset, 1);
+ io.putByte(base + offset, value);
+ }
+
+ @Override
+ public void putShort(long offset, short value) {
+ checkBounds(size, offset, 2);
+ io.putShort(base + offset, value);
+ }
+
+ @Override
+ public void putInt(long offset, int value) {
+ checkBounds(size, offset, 4);
+ io.putInt(base + offset, value);
+ }
+
+ @Override
+ public void putLongLong(long offset, long value) {
+ checkBounds(size, offset, 8);
+ io.putLongLong(base + offset, value);
+ }
+
+ @Override
+ public void putFloat(long offset, float value) {
+ checkBounds(size, offset, 4);
+ io.putFloat(base + offset, value);
+ }
+
+ @Override
+ public void putDouble(long offset, double value) {
+ checkBounds(size, offset, 8);
+ io.putDouble(base + offset, value);
+ }
+ public void putPointer(long offset, Pointer value) {
+ checkBounds(size, offset, getRuntime().addressSize());
+ io.putPointer(base + offset, value);
+ }
+ @Override
+ public void get(long offset, byte[] dst, int off, int len) {
+ checkBounds(size, offset, len);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, byte[] dst, int off, int len) {
+ checkBounds(size, offset, len);
+ io.put(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, short[] dst, int off, int len) {
+ checkBounds(size, offset, len * Short.SIZE / 8);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, short[] dst, int off, int len) {
+ checkBounds(size, offset, len * Short.SIZE / 8);
+ io.put(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, int[] dst, int off, int len) {
+ checkBounds(size, offset, len * Integer.SIZE / 8);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, int[] src, int off, int len) {
+ checkBounds(size, offset, len * Integer.SIZE / 8);
+ io.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, long[] dst, int off, int len) {
+ checkBounds(size, offset, len * Long.SIZE / 8);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, long[] src, int off, int len) {
+ checkBounds(size, offset, len * Long.SIZE / 8);
+ io.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, float[] dst, int off, int len) {
+ checkBounds(size, offset, len * Float.SIZE / 8);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, float[] src, int off, int len) {
+ checkBounds(size, offset, len * Float.SIZE / 8);
+ io.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, double[] dst, int off, int len) {
+ checkBounds(size, offset, len * Double.SIZE / 8);
+ io.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, double[] src, int off, int len) {
+ checkBounds(size, offset, len * Double.SIZE / 8);
+ io.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public long getAddress(long offset) {
+ checkBounds(size, offset, getRuntime().addressSize());
+ return io.getAddress(base + offset);
+ }
+
+ @Override
+ public String getString(long offset, int maxLength, Charset cs) {
+ checkBounds(size, offset, maxLength);
+ return io.getString(base + offset, maxLength, cs);
+ }
+
+ @Override
+ public String getString(long offset) {
+ return io.getString(base + offset, (int) size, Charset.defaultCharset());
+ }
+
+ @Override
+ public void putAddress(long offset, long value) {
+ checkBounds(size, offset, getRuntime().addressSize());
+ io.putAddress(base + offset, value);
+ }
+
+ @Override
+ public void putAddress(long offset, Address value) {
+ checkBounds(size, offset, getRuntime().addressSize());
+ io.putAddress(base + offset, value);
+ }
+
+ @Override
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ checkBounds(size, offset, maxLength);
+ io.putString(base + offset, string, maxLength, cs);
+ }
+
+ @Override
+ public int indexOf(long offset, byte value) {
+ return io.indexOf(base + offset, value, (int) size);
+ }
+
+ @Override
+ public int indexOf(long offset, byte value, int maxlen) {
+ checkBounds(size, offset, maxlen);
+ return io.indexOf(base + offset, value, maxlen);
+ }
+
+ @Override
+ public void setMemory(long offset, long size, byte value) {
+ checkBounds(this.size, base + offset, size);
+ io.setMemory(base + offset, size, value);
+ }
+
+ @Override
+ public void transferFrom(long offset, Pointer other, long otherOffset, long count) {
+ checkBounds(this.size, base + offset, count);
+ getDelegatedMemoryIO().transferFrom(offset, other, otherOffset, count);
+ }
+
+ @Override
+ public void transferTo(long offset, Pointer other, long otherOffset, long count) {
+ checkBounds(this.size, base + offset, count);
+ getDelegatedMemoryIO().transferTo(offset, other, otherOffset, count);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ClosureManager.java b/src/main/java/jnr/ffi/provider/ClosureManager.java
new file mode 100644
index 0000000..e814b66
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ClosureManager.java
@@ -0,0 +1,9 @@
+package jnr.ffi.provider;
+
+/**
+ *
+ */
+public interface ClosureManager {
+ public abstract <T> T newClosure(Class<? extends T> closureClass, T instance);
+ public abstract <T> jnr.ffi.Pointer getClosurePointer(Class<? extends T> closureClass, T instance);
+}
diff --git a/src/main/java/jnr/ffi/provider/DefaultObjectReferenceManager.java b/src/main/java/jnr/ffi/provider/DefaultObjectReferenceManager.java
new file mode 100644
index 0000000..b3bec86
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/DefaultObjectReferenceManager.java
@@ -0,0 +1,74 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.ObjectReferenceManager;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ */
+public final class DefaultObjectReferenceManager extends ObjectReferenceManager {
+ private final Runtime runtime;
+ private final ConcurrentMap<Long, ObjectReference> references = new ConcurrentHashMap<Long, ObjectReference>();
+
+ public DefaultObjectReferenceManager(Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ public Pointer add(Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("reference to null value not allowed");
+ }
+
+ long nextId = id(obj);
+
+ ObjectReference ptr;
+ while (references.putIfAbsent(nextId, ptr = new ObjectReference(runtime, nextId, obj)) != null) {
+ // A collision on the identity hash is extremely rare, but possible, so probe for a vacant slot
+ ++nextId;
+ }
+
+ return ptr;
+ }
+
+ public boolean remove(Pointer reference) {
+ ObjectReference entry = references.remove(reference.address());
+ return entry != null;
+ }
+
+ public Object get(Pointer reference) {
+ ObjectReference ptr = references.get(reference.address());
+ return ptr != null ? ptr.referent : null;
+ }
+
+ private long id(Object obj) {
+ return ((0xcafebabeL << 32) | (System.identityHashCode(obj) & 0xffffffffL)) & runtime.addressMask();
+ }
+
+ private static final class ObjectReference extends InAccessibleMemoryIO {
+ private final Object referent;
+
+ public ObjectReference(jnr.ffi.Runtime runtime, long address, Object referent) {
+ super(runtime, address, true);
+ this.referent = referent;
+ }
+
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) address();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Pointer && ((Pointer) obj).address() == address();
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/DelegatingMemoryIO.java b/src/main/java/jnr/ffi/provider/DelegatingMemoryIO.java
new file mode 100644
index 0000000..4443d9d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/DelegatingMemoryIO.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Pointer;
+
+public interface DelegatingMemoryIO {
+ public Pointer getDelegatedMemoryIO();
+}
diff --git a/src/main/java/jnr/ffi/provider/FFIProvider.java b/src/main/java/jnr/ffi/provider/FFIProvider.java
new file mode 100644
index 0000000..de70bec
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/FFIProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008-2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.*;
+
+/**
+ * This class defines the facilities a JNR FFI provider must provide.
+ *
+ * <strong>You most likely do NOT want to use this class directly</strong>
+ */
+public abstract class FFIProvider {
+ /**
+ * Gets an instance of <tt>FFIProvider</tt>
+ *
+ * @return an instance of <tt>FFIProvider</tt>
+ */
+ public static FFIProvider getSystemProvider() {
+ return SystemProviderSingletonHolder.INSTANCE;
+ }
+
+ protected FFIProvider() {}
+
+ /** Gets the default <tt>Runtime</tt> for this provider */
+ public abstract jnr.ffi.Runtime getRuntime();
+
+ /**
+ * Creates a new {@link LibraryLoader} instance
+ */
+ public abstract <T> LibraryLoader<T> createLibraryLoader(Class<T> interfaceClass);
+
+ private static final class SystemProviderSingletonHolder {
+ private static final FFIProvider INSTANCE = getInstance();
+
+ static FFIProvider getInstance() {
+ String providerName = System.getProperty("jnr.ffi.provider");
+ if (providerName == null) {
+ Package pkg = FFIProvider.class.getPackage();
+ String pkgName = pkg != null && pkg.getName() != null ? pkg.getName() : "jnr.ffi.provider";
+ providerName = pkgName + ".jffi.Provider";
+ }
+
+ try {
+ return (FFIProvider) Class.forName(providerName).newInstance();
+
+ } catch (Throwable ex) {
+ return newInvalidProvider("could not load FFI provider " + providerName, ex);
+ }
+ }
+ }
+
+ private static FFIProvider newInvalidProvider(String message, Throwable cause) {
+ return new InvalidProvider(message, cause);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/FromNativeType.java b/src/main/java/jnr/ffi/provider/FromNativeType.java
new file mode 100644
index 0000000..9f718fd
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/FromNativeType.java
@@ -0,0 +1,32 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ *
+ */
+public class FromNativeType extends SigType implements jnr.ffi.mapper.FromNativeType {
+ private final FromNativeConverter fromNativeConverter;
+ private final FromNativeContext fromNativeContext;
+
+ public FromNativeType(Class javaType, NativeType nativeType, Collection<Annotation> annotations,
+ FromNativeConverter fromNativeConverter, FromNativeContext fromNativeContext) {
+ super(javaType, nativeType, annotations, fromNativeConverter != null ? fromNativeConverter.nativeType() : javaType);
+ this.fromNativeConverter = fromNativeConverter;
+ this.fromNativeContext = fromNativeContext;
+ }
+
+ @Override
+ public FromNativeConverter getFromNativeConverter() {
+ return fromNativeConverter;
+ }
+
+ public FromNativeContext getFromNativeContext() {
+ return fromNativeContext;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/IdentityFunctionMapper.java b/src/main/java/jnr/ffi/provider/IdentityFunctionMapper.java
new file mode 100644
index 0000000..54ebbf8
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/IdentityFunctionMapper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.mapper.FunctionMapper;
+
+/**
+ * An implementation of {@link FunctionMapper} that just returns the same name as input
+ */
+public class IdentityFunctionMapper implements FunctionMapper {
+ private static final class SingletonHolder {
+ public static final FunctionMapper INSTANCE = new IdentityFunctionMapper();
+ }
+
+ public static FunctionMapper getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ public String mapFunctionName(String functionName, Context context) {
+ return functionName;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/InAccessibleMemoryIO.java b/src/main/java/jnr/ffi/provider/InAccessibleMemoryIO.java
new file mode 100644
index 0000000..eec3837
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/InAccessibleMemoryIO.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+
+import java.nio.charset.Charset;
+
+abstract public class InAccessibleMemoryIO extends AbstractMemoryIO {
+ private static final String msg = "attempted access to inaccessible memory";
+
+ protected InAccessibleMemoryIO(Runtime runtime, long address, boolean isDirect) {
+ super(runtime, address, isDirect);
+ }
+
+ protected RuntimeException error() {
+ return new IndexOutOfBoundsException(msg);
+ }
+
+
+ @Override
+ public boolean hasArray() {
+ return false;
+ }
+
+ @Override
+ public Object array() {
+ return null;
+ }
+
+ @Override
+ public int arrayOffset() {
+ return 0;
+ }
+
+ @Override
+ public int arrayLength() {
+ return 0;
+ }
+
+ public final byte getByte(long offset) {
+ throw error();
+ }
+
+ public final short getShort(long offset) {
+ throw error();
+ }
+
+ public final int getInt(long offset) {
+ throw error();
+ }
+
+ public final long getLong(long offset) {
+ throw error();
+ }
+
+ public final long getLongLong(long offset) {
+ throw error();
+ }
+
+ public final float getFloat(long offset) {
+ throw error();
+ }
+
+ public final double getDouble(long offset) {
+ throw error();
+ }
+
+ public final void putByte(long offset, byte value) {
+ throw error();
+ }
+
+ public final void putShort(long offset, short value) {
+ throw error();
+ }
+
+ public final void putInt(long offset, int value) {
+ throw error();
+ }
+
+ public final void putLong(long offset, long value) {
+ throw error();
+ }
+
+ public final void putLongLong(long offset, long value) {
+ throw error();
+ }
+
+ public final void putFloat(long offset, float value) {
+ throw error();
+ }
+
+ public final void putDouble(long offset, double value) {
+ throw error();
+ }
+
+ public final void get(long offset, byte[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, byte[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void get(long offset, short[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, short[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void get(long offset, int[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, int[] src, int off, int len) {
+ throw error();
+ }
+
+ public final void get(long offset, long[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, long[] src, int off, int len) {
+ throw error();
+ }
+
+ public final void get(long offset, float[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, float[] src, int off, int len) {
+ throw error();
+ }
+
+ public final void get(long offset, double[] dst, int off, int len) {
+ throw error();
+ }
+
+ public final void put(long offset, double[] src, int off, int len) {
+ throw error();
+ }
+
+ public final Pointer getPointer(long offset, long size) {
+ throw error();
+ }
+
+ public final Pointer getPointer(long offset) {
+ throw error();
+ }
+
+ public final void putPointer(long offset, Pointer value) {
+ throw error();
+ }
+
+ public String getString(long offset) {
+ throw error();
+ }
+
+ public String getString(long offset, int maxLength, Charset cs) {
+ throw error();
+ }
+
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ throw error();
+ }
+
+ public final int indexOf(long offset, byte value, int maxlen) {
+ throw error();
+ }
+
+ public final void setMemory(long offset, long size, byte value) {
+ throw error();
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/IntPointer.java b/src/main/java/jnr/ffi/provider/IntPointer.java
new file mode 100644
index 0000000..8a19e02
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/IntPointer.java
@@ -0,0 +1,30 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.Pointer;
+
+/**
+ *
+ */
+public final class IntPointer extends InAccessibleMemoryIO {
+ public IntPointer(jnr.ffi.Runtime runtime, long address) {
+ super(runtime, address, true);
+ }
+
+ public IntPointer(jnr.ffi.Runtime runtime, int address) {
+ super(runtime, address & 0xffffffffL, true);
+ }
+
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) address();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Pointer && ((Pointer) obj).address() == address();
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/InterfaceScanner.java b/src/main/java/jnr/ffi/provider/InterfaceScanner.java
new file mode 100644
index 0000000..58e4110
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/InterfaceScanner.java
@@ -0,0 +1,118 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.CallingConvention;
+import jnr.ffi.Variable;
+import jnr.ffi.annotations.StdCall;
+import jnr.ffi.mapper.SignatureTypeMapper;
+
+import java.lang.reflect.Method;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class InterfaceScanner {
+ private final Class interfaceClass;
+ private final SignatureTypeMapper typeMapper;
+ private final CallingConvention callingConvention;
+ private final Method[] methods;
+
+ public InterfaceScanner(Class interfaceClass, SignatureTypeMapper typeMapper, CallingConvention callingConvention) {
+ this.interfaceClass = interfaceClass;
+ this.typeMapper = typeMapper;
+ this.methods = interfaceClass.getMethods();
+ this.callingConvention = interfaceClass.isAnnotationPresent(StdCall.class) ? CallingConvention.STDCALL : callingConvention;
+ }
+
+ public Collection<NativeFunction> functions() {
+ return new AbstractCollection<NativeFunction>() {
+ @Override
+ public Iterator<NativeFunction> iterator() {
+ return new FunctionsIterator(methods);
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+ };
+ }
+
+ public Collection<NativeVariable> variables() {
+ return new AbstractCollection<NativeVariable>() {
+ @Override
+ public Iterator<NativeVariable> iterator() {
+ return new VariablesIterator(methods);
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+ };
+ }
+
+ private final class FunctionsIterator implements Iterator<NativeFunction> {
+ private final java.lang.reflect.Method[] methods;
+ private int nextIndex;
+
+ private FunctionsIterator(Method[] methods) {
+ this.methods = methods;
+ this.nextIndex = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ for (; nextIndex < methods.length; nextIndex++) {
+ if (!Variable.class.isAssignableFrom(methods[nextIndex].getReturnType())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public NativeFunction next() {
+ // Allow individual methods to set the calling convention to stdcall
+ CallingConvention callingConvention = methods[nextIndex].isAnnotationPresent(StdCall.class)
+ ? CallingConvention.STDCALL : InterfaceScanner.this.callingConvention;
+ return new NativeFunction(methods[nextIndex++], callingConvention);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private final class VariablesIterator implements Iterator<NativeVariable> {
+ private final java.lang.reflect.Method[] methods;
+ private int nextIndex;
+
+ private VariablesIterator(Method[] methods) {
+ this.methods = methods;
+ this.nextIndex = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ for (; nextIndex < methods.length; nextIndex++) {
+ if (Variable.class == methods[nextIndex].getReturnType()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public NativeVariable next() {
+ return new NativeVariable(methods[nextIndex++]);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/InvalidProvider.java b/src/main/java/jnr/ffi/provider/InvalidProvider.java
new file mode 100644
index 0000000..2da77e1
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/InvalidProvider.java
@@ -0,0 +1,36 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+
+import java.util.Collection;
+import java.util.Map;
+
+final class InvalidProvider extends FFIProvider {
+ private final String message;
+ private final Throwable cause;
+ private final jnr.ffi.Runtime runtime;
+
+ InvalidProvider(String message, Throwable cause) {
+ this.message = message;
+ this.cause = cause;
+ this.runtime = new InvalidRuntime(message, cause);
+ }
+
+ @Override
+ public Runtime getRuntime() {
+ return runtime;
+ }
+
+ @Override
+ public <T> LibraryLoader<T> createLibraryLoader(Class<T> interfaceClass) {
+ return new LibraryLoader<T>(interfaceClass) {
+ @Override
+ protected T loadLibrary(Class<T> interfaceClass, Collection<String> libraryNames, Collection<String> searchPaths, Map<LibraryOption, Object> options) {
+ UnsatisfiedLinkError error = new UnsatisfiedLinkError(message);
+ error.initCause(cause);
+ throw error;
+ }
+ };
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/InvalidRuntime.java b/src/main/java/jnr/ffi/provider/InvalidRuntime.java
new file mode 100644
index 0000000..7182db3
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/InvalidRuntime.java
@@ -0,0 +1,87 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.provider.MemoryManager;
+import jnr.ffi.provider.ClosureManager;
+
+import java.nio.ByteOrder;
+
+/**
+ * A {@link jnr.ffi.Runtime} subclass that throws exceptions for all methods
+ */
+class InvalidRuntime extends jnr.ffi.Runtime {
+ private final String message;
+ private final Throwable cause;
+
+ InvalidRuntime(String message, Throwable cause) {
+ this.message = message;
+ this.cause = cause;
+ }
+
+ @Override
+ public Type findType(NativeType type) {
+ throw newLoadError();
+ }
+
+ @Override
+ public Type findType(TypeAlias type) {
+ throw newLoadError();
+ }
+
+ @Override
+ public MemoryManager getMemoryManager() {
+ throw newLoadError();
+ }
+
+ @Override
+ public ClosureManager getClosureManager() {
+ throw newLoadError();
+ }
+
+ @Override
+ public ObjectReferenceManager newObjectReferenceManager() {
+ throw newLoadError();
+ }
+
+ @Override
+ public int getLastError() {
+ throw newLoadError();
+ }
+
+ @Override
+ public void setLastError(int error) {
+ throw newLoadError();
+ }
+
+ @Override
+ public long addressMask() {
+ throw newLoadError();
+ }
+
+ @Override
+ public int addressSize() {
+ throw newLoadError();
+ }
+
+ @Override
+ public int longSize() {
+ throw newLoadError();
+ }
+
+ @Override
+ public ByteOrder byteOrder() {
+ throw newLoadError();
+ }
+
+ @Override
+ public boolean isCompatible(Runtime other) {
+ throw newLoadError();
+ }
+
+ private UnsatisfiedLinkError newLoadError() {
+ UnsatisfiedLinkError error = new UnsatisfiedLinkError(message);
+ error.initCause(cause);
+ throw error;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/InvocationSession.java b/src/main/java/jnr/ffi/provider/InvocationSession.java
new file mode 100644
index 0000000..49d7800
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/InvocationSession.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import java.util.ArrayList;
+
+/**
+ * Holds information for each invocation of a native function
+ */
+public class InvocationSession {
+ private ArrayList<PostInvoke> list;
+ private ArrayList<Object> liveObjects;
+ public InvocationSession() {
+
+ }
+ public static interface PostInvoke {
+ void postInvoke();
+ }
+ public void finish() {
+ if (list != null) for (PostInvoke p : list) {
+ try {
+ p.postInvoke();
+ } catch (Throwable t) {}
+ }
+ }
+ public void addPostInvoke(PostInvoke postInvoke) {
+ if (list == null) {
+ list = new ArrayList<PostInvoke>();
+ }
+ list.add(postInvoke);
+ }
+
+ public void keepAlive(Object obj) {
+ if (liveObjects == null) {
+ liveObjects = new ArrayList<Object>();
+ }
+ liveObjects.add(obj);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/Invoker.java b/src/main/java/jnr/ffi/provider/Invoker.java
new file mode 100644
index 0000000..dd214ea
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/Invoker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+/**
+ * Interface for any invocable function
+ */
+public interface Invoker {
+ Object invoke(Object self, Object[] parameters);
+}
diff --git a/src/main/java/jnr/ffi/provider/LoadedLibrary.java b/src/main/java/jnr/ffi/provider/LoadedLibrary.java
new file mode 100644
index 0000000..9d90c05
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/LoadedLibrary.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+/**
+ * All loaded libraries must implement this interface
+ */
+public interface LoadedLibrary {
+ public jnr.ffi.Runtime getRuntime();
+}
diff --git a/src/main/java/jnr/ffi/provider/MemoryManager.java b/src/main/java/jnr/ffi/provider/MemoryManager.java
new file mode 100644
index 0000000..4e05468
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/MemoryManager.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Pointer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Manages access to various types of java and native memory.
+ */
+public interface MemoryManager {
+ public abstract Pointer allocate(int size);
+ public abstract Pointer allocateDirect(int size);
+ public abstract Pointer allocateDirect(int size, boolean clear);
+ public abstract Pointer allocateTemporary(int size, boolean clear);
+ public abstract Pointer newPointer(ByteBuffer buffer);
+ public abstract Pointer newPointer(long address);
+ public abstract Pointer newPointer(long address, long size);
+ public abstract Pointer newOpaquePointer(long address);
+}
diff --git a/src/main/java/jnr/ffi/provider/NativeFunction.java b/src/main/java/jnr/ffi/provider/NativeFunction.java
new file mode 100644
index 0000000..04b7415
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/NativeFunction.java
@@ -0,0 +1,53 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.CallingConvention;
+import jnr.ffi.annotations.IgnoreError;
+import jnr.ffi.annotations.SaveError;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public final class NativeFunction {
+ private final Method method;
+ private final Collection<Annotation> annotations;
+ private final boolean saveError;
+ private final CallingConvention callingConvention;
+
+ public NativeFunction(Method method, CallingConvention callingConvention) {
+ this.method = method;
+ this.annotations = Collections.unmodifiableCollection(Arrays.asList(method.getAnnotations()));
+ boolean saveError = true;
+ for (Annotation a : annotations) {
+ if (a instanceof IgnoreError) {
+ saveError = false;
+ } else if (a instanceof SaveError) {
+ saveError = true;
+ }
+ }
+ this.saveError = saveError;
+ this.callingConvention = callingConvention;
+ }
+
+ public Collection<Annotation> annotations() {
+ return annotations;
+ }
+
+ public CallingConvention convention() {
+ return callingConvention;
+ }
+
+ public String name() {
+ return method.getName();
+ }
+
+ public boolean isErrnoRequired() {
+ return saveError;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/NativeInvocationHandler.java b/src/main/java/jnr/ffi/provider/NativeInvocationHandler.java
new file mode 100644
index 0000000..d2ec90d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/NativeInvocationHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008-2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * InvocationHandler used to map invocations on a java interface to the correct native function.
+ */
+public class NativeInvocationHandler implements InvocationHandler {
+ private volatile Map<Method, Invoker> fastLookupTable;
+ private final Map<Method, Invoker> invokerMap;
+
+ /**
+ * Creates a new InvocationHandler instance.
+ *
+ * @param invokers A map of method invokers
+ *
+ */
+ public NativeInvocationHandler(Map<Method, Invoker> invokers) {
+ this.invokerMap = invokers;
+ this.fastLookupTable = Collections.emptyMap();
+ }
+
+ public Object invoke(Object self, Method method, Object[] argArray) throws Throwable {
+ Invoker invoker = fastLookupTable.get(method);
+ return invoker != null ? invoker.invoke(self, argArray) : lookupAndCacheInvoker(method).invoke(self, argArray);
+ }
+
+ private synchronized Invoker lookupAndCacheInvoker(Method method) {
+ Invoker invoker;
+ if ((invoker = fastLookupTable.get(method)) != null) {
+ return invoker;
+ }
+
+ Map<Method, Invoker> map = new IdentityHashMap<Method, Invoker>(fastLookupTable);
+ map.put(method, invoker = invokerMap.get(method));
+ if (invoker == null) {
+ throw new UnsatisfiedLinkError("no invoker for native method " + method.getName());
+ }
+
+ fastLookupTable = map;
+ return invoker;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/jnr/ffi/provider/NativeVariable.java b/src/main/java/jnr/ffi/provider/NativeVariable.java
new file mode 100644
index 0000000..4e09978
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/NativeVariable.java
@@ -0,0 +1,15 @@
+package jnr.ffi.provider;
+
+import java.lang.reflect.Method;
+
+public class NativeVariable {
+ private final Method method;
+
+ public NativeVariable(Method method) {
+ this.method = method;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/NullMemoryIO.java b/src/main/java/jnr/ffi/provider/NullMemoryIO.java
new file mode 100644
index 0000000..72be606
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/NullMemoryIO.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Runtime;
+
+public final class NullMemoryIO extends InAccessibleMemoryIO {
+ private static final String msg = "attempted access to a NULL memory address";
+
+ public NullMemoryIO(Runtime runtime) {
+ super(runtime, 0, true);
+ }
+
+ protected final NullPointerException error() {
+ return new NullPointerException(msg);
+ }
+
+ public long size() {
+ return Long.MAX_VALUE;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/NullTypeMapper.java b/src/main/java/jnr/ffi/provider/NullTypeMapper.java
new file mode 100644
index 0000000..1a4158f
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/NullTypeMapper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.mapper.*;
+
+/**
+ * An instance of {@link TypeMapper} which always returns null
+ */
+public class NullTypeMapper extends AbstractSignatureTypeMapper implements TypeMapper, SignatureTypeMapper {
+
+ public FromNativeConverter getFromNativeConverter(Class type) {
+ return null;
+ }
+
+ public ToNativeConverter getToNativeConverter(Class type) {
+ return null;
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return null;
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ParameterFlags.java b/src/main/java/jnr/ffi/provider/ParameterFlags.java
new file mode 100644
index 0000000..9b8c754
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ParameterFlags.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.annotations.*;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ *
+ */
+public final class ParameterFlags {
+ private ParameterFlags() {}
+ /** Contents of the parameter memory will be copied from native memory back to java */
+ public static final int OUT = 0x01;
+
+ /** Contents of the parameter memory will be copied from from java to native memory */
+ public static final int IN = 0x02;
+
+ /** The java array memory should be pinned by the JVM during the function call */
+ public static final int PINNED = 0x04;
+
+ /** The contents of the java array should have a zero byte appended */
+ public static final int NULTERMINATE = 0x08;
+
+ /** When allocating memory for the parameter, a temporary memory block can be used */
+ public static final int TRANSIENT = 0x10;
+
+ /** When allocating memory for the parameter, allocate a persistent memory block */
+ public static final int DIRECT = 0x20;
+
+ public static int parse(Annotation annotation) {
+ int flags = 0;
+ flags |= annotation instanceof Out ? OUT : 0;
+ flags |= annotation instanceof In ? IN : 0;
+ flags |= annotation instanceof Transient ? TRANSIENT : 0;
+ flags |= annotation instanceof Direct ? DIRECT : 0;
+ flags |= annotation instanceof Pinned ? PINNED : 0;
+ flags |= annotation instanceof NulTerminate ? NULTERMINATE : 0;
+
+ return flags;
+ }
+
+ public static int parse(Annotation[] annotations) {
+ int flags = 0;
+ for (Annotation a : annotations) {
+ flags |= parse(a);
+ }
+ return flags;
+ }
+
+ public static int parse(Collection<Annotation> annotations) {
+ int flags = 0;
+ for (Annotation a : annotations) {
+ flags |= parse(a);
+ }
+ return flags;
+ }
+
+ /**
+ * Checks if the annotation is a recognised parameter flag.
+ *
+ * @param annotation the annotation to check.
+ * @return <tt>true</tt> if the annotation is a parameter flag
+ */
+ public static boolean isFlag(Annotation annotation) {
+ return parse(annotation) != 0;
+ }
+
+ public static boolean isPinned(int flags) {
+ return (flags & PINNED) != 0;
+ }
+ public static boolean isTransient(int flags) {
+ return (flags & TRANSIENT) != 0;
+ }
+ public static boolean isDirect(int flags) {
+ return (flags & DIRECT) != 0;
+ }
+ public static boolean isNulTerminate(int flags) {
+ return (flags & NULTERMINATE) != 0;
+ }
+ public static boolean isOut(int flags) {
+ return (flags & (OUT | IN)) != IN;
+ }
+ public static boolean isIn(int flags) {
+ return (flags & (OUT | IN)) != OUT;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ParameterType.java b/src/main/java/jnr/ffi/provider/ParameterType.java
new file mode 100644
index 0000000..e69af7a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ParameterType.java
@@ -0,0 +1,19 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ *
+ */
+public class ParameterType extends ToNativeType {
+
+ public ParameterType(Class javaType, NativeType nativeType, Collection<Annotation> annotations,
+ ToNativeConverter toNativeConverter, ToNativeContext toNativeContext) {
+ super(javaType, nativeType, annotations, toNativeConverter, toNativeContext);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ResultType.java b/src/main/java/jnr/ffi/provider/ResultType.java
new file mode 100644
index 0000000..d551638
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ResultType.java
@@ -0,0 +1,19 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ *
+ */
+public class ResultType extends FromNativeType {
+
+ public ResultType(Class javaType, NativeType nativeType, Collection<Annotation> annotations,
+ FromNativeConverter fromNativeConverter, FromNativeContext fromNativeContext) {
+ super(javaType, nativeType, annotations, fromNativeConverter, fromNativeContext);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ShareMemoryIO.java b/src/main/java/jnr/ffi/provider/ShareMemoryIO.java
new file mode 100644
index 0000000..54e8474
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ShareMemoryIO.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider;
+
+import jnr.ffi.Pointer;
+
+import java.nio.charset.Charset;
+
+/**
+ *
+ */
+public class ShareMemoryIO extends AbstractMemoryIO implements DelegatingMemoryIO {
+
+ private final Pointer ptr;
+ private final long base;
+
+ public ShareMemoryIO(Pointer parent, long offset) {
+ super(parent.getRuntime(), parent.address() != 0L ? parent.address() + offset : 0L, parent.isDirect());
+ this.ptr = parent;
+ this.base = offset;
+ }
+
+ public long size() {
+ return ptr.size() - base;
+ }
+
+ @Override
+ public final boolean hasArray() {
+ return ptr.hasArray();
+ }
+
+ @Override
+ public final Object array() {
+ return ptr.array();
+ }
+
+ @Override
+ public final int arrayOffset() {
+ return ptr.arrayOffset() + (int) base;
+ }
+
+ @Override
+ public final int arrayLength() {
+ return ptr.arrayLength() - (int) base;
+ }
+
+ public final Pointer getDelegatedMemoryIO() {
+ return ptr;
+ }
+
+ @Override
+ public byte getByte(long offset) {
+ return ptr.getByte(base + offset);
+ }
+
+ @Override
+ public short getShort(long offset) {
+ return ptr.getShort(base + offset);
+ }
+
+ @Override
+ public int getInt(long offset) {
+ return ptr.getInt(base + offset);
+ }
+
+ @Override
+ public long getLong(long offset) {
+ return ptr.getLong(base + offset);
+ }
+
+ @Override
+ public long getLongLong(long offset) {
+ return ptr.getLongLong(base + offset);
+ }
+
+ @Override
+ public float getFloat(long offset) {
+ return ptr.getFloat(base + offset);
+ }
+
+ @Override
+ public double getDouble(long offset) {
+ return ptr.getDouble(base + offset);
+ }
+
+ public Pointer getPointer(long offset) {
+ return ptr.getPointer(base + offset);
+ }
+
+ public Pointer getPointer(long offset, long size) {
+ return ptr.getPointer(base + offset, size);
+ }
+
+ @Override
+ public String getString(long offset) {
+ return ptr.getString(base + offset);
+ }
+
+
+ @Override
+ public String getString(long offset, int maxLength, Charset cs) {
+ return ptr.getString(base + offset, maxLength, cs);
+ }
+
+
+ @Override
+ public void putByte(long offset, byte value) {
+ ptr.putByte(base + offset, value);
+ }
+
+ @Override
+ public void putShort(long offset, short value) {
+ ptr.putShort(base + offset, value);
+ }
+
+ @Override
+ public void putInt(long offset, int value) {
+ ptr.putInt(base + offset, value);
+ }
+
+ @Override
+ public void putLong(long offset, long value) {
+ ptr.putLong(base + offset, value);
+ }
+
+ @Override
+ public void putLongLong(long offset, long value) {
+ ptr.putLongLong(base + offset, value);
+ }
+
+ @Override
+ public void putFloat(long offset, float value) {
+ ptr.putFloat(base + offset, value);
+ }
+
+ @Override
+ public void putDouble(long offset, double value) {
+ ptr.putDouble(base + offset, value);
+ }
+
+ public void putPointer(long offset, Pointer value) {
+ ptr.putPointer(base + offset, value);
+ }
+
+ @Override
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ ptr.putString(base + offset, string, maxLength, cs);
+ }
+
+ @Override
+ public void get(long offset, byte[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, byte[] dst, int off, int len) {
+ ptr.put(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, short[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, short[] dst, int off, int len) {
+ ptr.put(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void get(long offset, int[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, int[] src, int off, int len) {
+ ptr.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, long[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, long[] src, int off, int len) {
+ ptr.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, float[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, float[] src, int off, int len) {
+ ptr.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public void get(long offset, double[] dst, int off, int len) {
+ ptr.get(base + offset, dst, off, len);
+ }
+
+ @Override
+ public void put(long offset, double[] src, int off, int len) {
+ ptr.put(base + offset, src, off, len);
+ }
+
+ @Override
+ public int indexOf(long offset, byte value, int maxlen) {
+ return ptr.indexOf(base + offset, value, maxlen);
+ }
+
+ @Override
+ public void setMemory(long offset, long size, byte value) {
+ ptr.setMemory(base + offset, size, value);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/SigType.java b/src/main/java/jnr/ffi/provider/SigType.java
new file mode 100644
index 0000000..365f39a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/SigType.java
@@ -0,0 +1,53 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.mapper.SignatureType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ *
+ */
+abstract public class SigType implements SignatureType {
+ private final Class javaType, convertedType;
+ private final Collection<Annotation> annotations;
+ private final NativeType nativeType;
+
+ public SigType(Class javaType, NativeType nativeType, Collection<Annotation> annotations, Class convertedType) {
+ this.javaType = javaType;
+ this.annotations = annotations;
+ this.convertedType = convertedType;
+ this.nativeType = nativeType;
+ }
+
+ public final Class getDeclaredType() {
+ return javaType;
+ }
+
+ public final Class effectiveJavaType() {
+ return convertedType;
+ }
+
+ public final Collection<Annotation> annotations() {
+ return annotations;
+ }
+
+ public final Collection<Annotation> getAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public Type getGenericType() {
+ return getDeclaredType();
+ }
+
+ public final String toString() {
+ return String.format("declared: %s, effective: %s, native: %s", getDeclaredType(), effectiveJavaType(), getNativeType());
+ }
+
+ public NativeType getNativeType() {
+ return nativeType;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/ToNativeType.java b/src/main/java/jnr/ffi/provider/ToNativeType.java
new file mode 100644
index 0000000..e186f2c
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/ToNativeType.java
@@ -0,0 +1,32 @@
+package jnr.ffi.provider;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ *
+ */
+public class ToNativeType extends SigType implements jnr.ffi.mapper.ToNativeType {
+ private final ToNativeConverter toNativeConverter;
+ private final ToNativeContext toNativeContext;
+
+ public ToNativeType(Class javaType, NativeType nativeType, Collection<Annotation> annotations,
+ ToNativeConverter toNativeConverter, ToNativeContext toNativeContext) {
+ super(javaType, nativeType, annotations, toNativeConverter != null ? toNativeConverter.nativeType() : javaType);
+ this.toNativeConverter = toNativeConverter;
+ this.toNativeContext = toNativeContext;
+ }
+
+ @Override
+ public final ToNativeConverter getToNativeConverter() {
+ return toNativeConverter;
+ }
+
+ public ToNativeContext getToNativeContext() {
+ return toNativeContext;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedBooleanArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedBooleanArrayParameterConverter.java
new file mode 100644
index 0000000..333259d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedBooleanArrayParameterConverter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Boolean[] array to a primitive boolean[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedBooleanArrayParameterConverter implements ToNativeConverter<Boolean[], boolean[]> {
+ private static final ToNativeConverter<Boolean[], boolean[]> IN = new BoxedBooleanArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Boolean[], boolean[]> OUT = new BoxedBooleanArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Boolean[], boolean[]> INOUT = new BoxedBooleanArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Boolean[], boolean[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public BoxedBooleanArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public boolean[] toNative(Boolean[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ boolean[] primitive = new boolean[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : false;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedBooleanArrayParameterConverter implements PostInvocation<Boolean[], boolean[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Boolean[] array, boolean[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<boolean[]> nativeType() {
+ return boolean[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedByteArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedByteArrayParameterConverter.java
new file mode 100644
index 0000000..dec8381
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedByteArrayParameterConverter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Byte[] array to a byte[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedByteArrayParameterConverter implements ToNativeConverter<Byte[], byte[]> {
+ private static final ToNativeConverter<Byte[], byte[]> IN = new BoxedByteArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Byte[], byte[]> OUT = new BoxedByteArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Byte[], byte[]> INOUT = new BoxedByteArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Byte[], byte[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ BoxedByteArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public byte[] toNative(Byte[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ byte[] primitive = new byte[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedByteArrayParameterConverter implements PostInvocation<Byte[], byte[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Byte[] array, byte[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<byte[]> nativeType() {
+ return byte[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedDoubleArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedDoubleArrayParameterConverter.java
new file mode 100644
index 0000000..124c134
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedDoubleArrayParameterConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Double[] array to a double[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedDoubleArrayParameterConverter implements ToNativeConverter<Double[], double[]> {
+ private static final ToNativeConverter<Double[], double[]> IN = new BoxedDoubleArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Double[], double[]> OUT = new BoxedDoubleArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Double[], double[]> INOUT = new BoxedDoubleArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Double[], double[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ BoxedDoubleArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public double[] toNative(Double[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ double[] primitive = new double[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedDoubleArrayParameterConverter implements PostInvocation<Double[], double[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Double[] array, double[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<double[]> nativeType() {
+ return double[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedFloatArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedFloatArrayParameterConverter.java
new file mode 100644
index 0000000..cb3c45c
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedFloatArrayParameterConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Float[] array to a float[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedFloatArrayParameterConverter implements ToNativeConverter<Float[], float[]> {
+ private static final ToNativeConverter<Float[], float[]> IN = new BoxedFloatArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Float[], float[]> OUT = new BoxedFloatArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Float[], float[]> INOUT = new BoxedFloatArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Float[], float[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ BoxedFloatArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public float[] toNative(Float[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ float[] primitive = new float[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedFloatArrayParameterConverter implements PostInvocation<Float[], float[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Float[] array, float[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<float[]> nativeType() {
+ return float[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedIntegerArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedIntegerArrayParameterConverter.java
new file mode 100644
index 0000000..e571e6a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedIntegerArrayParameterConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Integer[] array to a primitive int[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedIntegerArrayParameterConverter implements ToNativeConverter<Integer[], int[]> {
+ private static final ToNativeConverter<Integer[], int[]> IN = new BoxedIntegerArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Integer[], int[]> OUT = new BoxedIntegerArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Integer[], int[]> INOUT = new BoxedIntegerArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Integer[], int[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public BoxedIntegerArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public int[] toNative(Integer[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ int[] primitive = new int[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedIntegerArrayParameterConverter implements PostInvocation<Integer[], int[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Integer[] array, int[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<int[]> nativeType() {
+ return int[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedLong32ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedLong32ArrayParameterConverter.java
new file mode 100644
index 0000000..f285344
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedLong32ArrayParameterConverter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Long[] array to a primitive int[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedLong32ArrayParameterConverter implements ToNativeConverter<Long[], int[]> {
+ private static final ToNativeConverter<Long[], int[]> IN = new BoxedLong32ArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Long[], int[]> OUT = new BoxedLong32ArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Long[], int[]> INOUT = new BoxedLong32ArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Long[], int[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public BoxedLong32ArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public int[] toNative(Long[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ int[] primitive = new int[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i].intValue() : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedLong32ArrayParameterConverter implements PostInvocation<Long[], int[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Long[] array, int[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = (long) primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<int[]> nativeType() {
+ return int[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedLong64ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedLong64ArrayParameterConverter.java
new file mode 100644
index 0000000..4ec67b8
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedLong64ArrayParameterConverter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Long[] array to a primitive 64bit long[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedLong64ArrayParameterConverter implements ToNativeConverter<Long[], long[]> {
+ private static final ToNativeConverter<Long[], long[]> IN = new BoxedLong64ArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Long[], long[]> OUT = new BoxedLong64ArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Long[], long[]> INOUT = new BoxedLong64ArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Long[], long[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public BoxedLong64ArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public long[] toNative(Long[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ long[] primitive = new long[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedLong64ArrayParameterConverter implements PostInvocation<Long[], long[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Long[] array, long[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ @LongLong
+ public Class<long[]> nativeType() {
+ return long[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/BoxedShortArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/BoxedShortArrayParameterConverter.java
new file mode 100644
index 0000000..aabd28b
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/BoxedShortArrayParameterConverter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Short[] array to a primitive short[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class BoxedShortArrayParameterConverter implements ToNativeConverter<Short[], short[]> {
+ private static final ToNativeConverter<Short[], short[]> IN = new BoxedShortArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<Short[], short[]> OUT = new BoxedShortArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<Short[], short[]> INOUT = new BoxedShortArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+ private final int parameterFlags;
+
+ public static ToNativeConverter<Short[], short[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public BoxedShortArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public short[] toNative(Short[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ short[] primitive = new short[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i] : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends BoxedShortArrayParameterConverter implements PostInvocation<Short[], short[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Short[] array, short[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<short[]> nativeType() {
+ return short[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/ByReferenceParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/ByReferenceParameterConverter.java
new file mode 100644
index 0000000..8ecc7b1
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/ByReferenceParameterConverter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.byref.ByReference;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ *
+ */
+ at ToNativeConverter.Cacheable
+public class ByReferenceParameterConverter implements ToNativeConverter<ByReference, Pointer> {
+ private static final ToNativeConverter<ByReference, Pointer> IN = new ByReferenceParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<ByReference, Pointer> OUT = new ByReferenceParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<ByReference, Pointer> INOUT = new ByReferenceParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+ private final int parameterFlags;
+
+ private ByReferenceParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ public static ToNativeConverter<ByReference, Pointer> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ public Pointer toNative(ByReference value, ToNativeContext context) {
+ if (value == null) {
+ return null;
+ }
+
+ Pointer memory = Memory.allocate(context.getRuntime(), value.nativeSize(context.getRuntime()));
+ if (ParameterFlags.isIn(parameterFlags)) {
+ value.toNative(context.getRuntime(), memory, 0);
+ }
+
+ return memory;
+ }
+
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ public static final class Out extends ByReferenceParameterConverter implements ToNativeConverter.PostInvocation<ByReference, Pointer> {
+ public Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ public void postInvoke(ByReference byReference, Pointer pointer, ToNativeContext context) {
+ if (byReference != null && pointer != null) {
+ byReference.fromNative(context.getRuntime(), pointer, 0);
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/CharSequenceArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/CharSequenceArrayParameterConverter.java
new file mode 100644
index 0000000..4da6787
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/CharSequenceArrayParameterConverter.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.InAccessibleMemoryIO;
+import jnr.ffi.provider.ParameterFlags;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Converts a CharSequence[] array to a Pointer parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class CharSequenceArrayParameterConverter implements ToNativeConverter<CharSequence[], Pointer> {
+ private final jnr.ffi.Runtime runtime;
+ private final int parameterFlags;
+
+ public static ToNativeConverter<CharSequence[], Pointer> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return !ParameterFlags.isOut(parameterFlags)
+ ? new CharSequenceArrayParameterConverter(toNativeContext.getRuntime(), parameterFlags)
+ : new CharSequenceArrayParameterConverter.Out(toNativeContext.getRuntime(), parameterFlags);
+ }
+
+ CharSequenceArrayParameterConverter(jnr.ffi.Runtime runtime, int parameterFlags) {
+ this.runtime = runtime;
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public Pointer toNative(CharSequence[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+
+ StringArray stringArray = StringArray.allocate(runtime, array.length + 1);
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ stringArray.put(i, array[i]);
+ }
+ }
+
+ return stringArray;
+ }
+
+ public static final class Out extends CharSequenceArrayParameterConverter implements PostInvocation<CharSequence[], Pointer> {
+ Out(jnr.ffi.Runtime runtime, int parameterFlags) {
+ super(runtime, parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(CharSequence[] array, Pointer primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ StringArray stringArray = (StringArray) primitive;
+ for (int i = 0; i < array.length; i++) {
+ array[i] = stringArray.get(i);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ private final static class StringArray extends InAccessibleMemoryIO {
+ private final Pointer memory;
+ private List<Pointer> stringMemory;
+ private final Charset charset = Charset.defaultCharset();
+
+ private StringArray(Runtime runtime, Pointer memory, int capacity) {
+ super(runtime, memory.address(), memory.isDirect());
+ this.memory = memory;
+ this.stringMemory = new ArrayList<Pointer>(capacity);
+ }
+
+ String get(int idx) {
+ Pointer ptr = memory.getPointer(idx * getRuntime().addressSize());
+ return ptr != null ? ptr.getString(0) : null;
+ }
+
+ void put(int idx, CharSequence str) {
+ if (str == null) {
+ memory.putAddress(idx * getRuntime().addressSize(), 0L);
+ } else {
+ ByteBuffer buf = charset.encode(CharBuffer.wrap(str));
+ Pointer ptr = Memory.allocateDirect(getRuntime(), buf.remaining() + 4, true);
+ ptr.put(0, buf.array(), 0, buf.remaining());
+ stringMemory.add(idx, ptr);
+ memory.putPointer(idx * getRuntime().addressSize(), ptr);
+ }
+ }
+
+ @Override
+ public long size() {
+ return memory.size();
+ }
+
+ static StringArray allocate(Runtime runtime, int capacity) {
+ Pointer memory = Memory.allocateDirect(runtime, capacity * runtime.addressSize());
+ return new StringArray(runtime, memory, capacity);
+ }
+
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/CharSequenceParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/CharSequenceParameterConverter.java
new file mode 100644
index 0000000..bb6d7a6
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/CharSequenceParameterConverter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.annotations.Encoding;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.NulTerminate;
+import jnr.ffi.mapper.MethodParameterContext;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static jnr.ffi.provider.converters.StringUtil.getEncoder;
+import static jnr.ffi.provider.converters.StringUtil.throwException;
+
+/**
+ * Converts a CharSequence (e.g. String) to a primitive ByteBuffer array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class CharSequenceParameterConverter implements ToNativeConverter<CharSequence, ByteBuffer> {
+ private static final ToNativeConverter<CharSequence, ByteBuffer> DEFAULT = new CharSequenceParameterConverter(Charset.defaultCharset());
+ private final ThreadLocal<Reference<CharsetEncoder>> localEncoder = new ThreadLocal<Reference<CharsetEncoder>>();
+
+ private final Charset charset;
+
+
+ public static ToNativeConverter<CharSequence, ByteBuffer> getInstance(Charset charset, ToNativeContext toNativeContext) {
+ return Charset.defaultCharset().equals(charset) ? DEFAULT : new CharSequenceParameterConverter(charset);
+ }
+
+ public static ToNativeConverter<CharSequence, ByteBuffer> getInstance(ToNativeContext toNativeContext) {
+ Charset charset = Charset.defaultCharset();
+
+ if (toNativeContext instanceof MethodParameterContext) {
+ // See if the interface class has a global @Encoding declaration
+ Charset cs = getEncodingCharset(Arrays.asList(((MethodParameterContext) toNativeContext).getMethod().getDeclaringClass().getAnnotations()));
+ if (cs != null) {
+ charset = cs;
+ }
+
+ // Allow each method to override the default
+ cs = getEncodingCharset(Arrays.asList(((MethodParameterContext) toNativeContext).getMethod().getAnnotations()));
+ if (cs != null) {
+ charset = cs;
+ }
+ }
+
+ // Override on a per-parameter basis
+ Charset cs = getEncodingCharset(toNativeContext.getAnnotations());
+ if (cs != null) {
+ charset = cs;
+ }
+
+ return getInstance(charset, toNativeContext);
+ }
+
+ private static Charset getEncodingCharset(Collection<Annotation> annotations) {
+ for (Annotation a : annotations) {
+ if (a instanceof Encoding) {
+ return Charset.forName(((Encoding) a).value());
+ }
+ }
+
+ return null;
+ }
+
+ private CharSequenceParameterConverter(Charset charset) {
+ this.charset = charset;
+ }
+
+ @Override
+ public ByteBuffer toNative(CharSequence string, ToNativeContext context) {
+ if (string == null) {
+ return null;
+ }
+
+ CharsetEncoder encoder = getEncoder(charset, localEncoder);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[(int) (string.length() * encoder.averageBytesPerChar()) + 4]);
+ CharBuffer charBuffer = CharBuffer.wrap(string);
+
+ encoder.reset();
+ while (charBuffer.hasRemaining()) {
+ CoderResult result = encoder.encode(charBuffer, byteBuffer, true);
+
+ if (result.isUnderflow() && (result = encoder.flush(byteBuffer)).isUnderflow()) {
+ break;
+
+ } else if (result.isOverflow()) {
+ // Output buffer is full; expand and continue encoding
+ byteBuffer = grow(byteBuffer);
+
+ } else {
+ throwException(result);
+ }
+ }
+
+ // ensure native memory is NUL terminated (assume max wchar_t 4 byte termination needed)
+ if (byteBuffer.remaining() <= 4) byteBuffer = grow(byteBuffer);
+ byteBuffer.position(byteBuffer.position() + 4);
+
+ byteBuffer.flip();
+
+ return byteBuffer;
+ }
+
+ private static ByteBuffer grow(ByteBuffer oldBuffer) {
+ ByteBuffer buf = ByteBuffer.wrap(new byte[oldBuffer.capacity() * 2]);
+ oldBuffer.flip();
+ buf.put(oldBuffer);
+ return buf;
+ }
+
+ @Override
+ @In
+ @NulTerminate
+ public Class<ByteBuffer> nativeType() {
+ return ByteBuffer.class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/EnumConverter.java b/src/main/java/jnr/ffi/provider/converters/EnumConverter.java
new file mode 100644
index 0000000..ecba661
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/EnumConverter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.*;
+import jnr.ffi.util.EnumMapper;
+
+
+ at ToNativeConverter.NoContext
+ at FromNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+ at FromNativeConverter.Cacheable
+public final class EnumConverter implements DataConverter<Enum, Integer> {
+ private final EnumMapper mapper;
+
+ public static EnumConverter getInstance(Class<? extends Enum> enumClass) {
+ return new EnumConverter(enumClass);
+ }
+
+ private EnumConverter(Class<? extends Enum> enumClass) {
+ this.mapper = EnumMapper.getInstance(enumClass);
+ }
+
+ public Enum fromNative(Integer nativeValue, FromNativeContext context) {
+ return mapper.valueOf(nativeValue);
+ }
+
+ public Integer toNative(Enum value, ToNativeContext context) {
+ return mapper.integerValue(value);
+ }
+
+ public Class<Integer> nativeType() {
+ return Integer.class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/EnumSetConverter.java b/src/main/java/jnr/ffi/provider/converters/EnumSetConverter.java
new file mode 100644
index 0000000..ac5e112
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/EnumSetConverter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.*;
+import jnr.ffi.util.EnumMapper;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.EnumSet;
+import java.util.Set;
+
+ at FromNativeConverter.Cacheable
+ at ToNativeConverter.Cacheable
+public final class EnumSetConverter implements DataConverter<Set<? extends Enum>, Integer> {
+ private final Class<? extends Enum> enumClass;
+ private final EnumMapper enumMapper;
+ private final EnumSet<? extends Enum> allValues;
+
+ private EnumSetConverter(Class<? extends Enum> enumClass) {
+ this.enumClass = enumClass;
+ this.enumMapper = EnumMapper.getInstance(enumClass);
+ this.allValues = EnumSet.allOf(enumClass);
+ }
+
+ public static ToNativeConverter<Set<? extends Enum>, Integer> getToNativeConverter(SignatureType type, ToNativeContext toNativeContext) {
+ return getInstance(type.getGenericType());
+ }
+
+ public static FromNativeConverter<Set<? extends Enum>, Integer> getFromNativeConverter(SignatureType type, FromNativeContext fromNativeContext) {
+ return getInstance(type.getGenericType());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static EnumSetConverter getInstance(Type parameterizedType) {
+ if (!(parameterizedType instanceof ParameterizedType)) {
+ return null;
+ }
+
+ if (((ParameterizedType) parameterizedType).getActualTypeArguments().length < 1) {
+ return null;
+ }
+
+ Type enumType = ((ParameterizedType) parameterizedType).getActualTypeArguments()[0];
+ if (!(enumType instanceof Class) || !Enum.class.isAssignableFrom((Class) enumType)) {
+ return null;
+ }
+
+ return new EnumSetConverter(((Class) enumType).asSubclass(Enum.class));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Set fromNative(Integer nativeValue, FromNativeContext context) {
+ EnumSet enums = EnumSet.noneOf(enumClass);
+ for (Enum e : allValues) {
+ int enumValue = enumMapper.intValue(e);
+ if ((nativeValue & enumValue) == enumValue) {
+ enums.add(e);
+ }
+ }
+
+ return enums;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Integer toNative(Set<? extends Enum> value, ToNativeContext context) {
+ int intValue = 0;
+ for (Enum e : value) {
+ intValue |= enumMapper.intValue(e);
+ }
+
+ return intValue;
+ }
+
+ @Override
+ public Class<Integer> nativeType() {
+ return Integer.class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/Long32ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/Long32ArrayParameterConverter.java
new file mode 100644
index 0000000..372a5a0
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/Long32ArrayParameterConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a long[] array to a primitive int[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class Long32ArrayParameterConverter implements ToNativeConverter<long[], int[]> {
+ private static final Long32ArrayParameterConverter IN = new Long32ArrayParameterConverter(ParameterFlags.IN);
+ private static final Long32ArrayParameterConverter OUT = new Long32ArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final Long32ArrayParameterConverter INOUT = new Long32ArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<long[], int[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ private Long32ArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public int[] toNative(long[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+
+ int[] primitive = new int[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = (int) array[i];
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends Long32ArrayParameterConverter implements PostInvocation<long[], int[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(long[] array, int[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = primitive[i];
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<int[]> nativeType() {
+ return int[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/NativeLong32ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/NativeLong32ArrayParameterConverter.java
new file mode 100644
index 0000000..fcbacfe
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/NativeLong32ArrayParameterConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.NativeLong;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a NativeLong[] array to a primitive int[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class NativeLong32ArrayParameterConverter implements ToNativeConverter<NativeLong[], int[]> {
+ private static final ToNativeConverter<NativeLong[], int[]> IN = new NativeLong32ArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<NativeLong[], int[]> OUT = new NativeLong32ArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<NativeLong[], int[]> INOUT = new NativeLong32ArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<NativeLong[], int[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ NativeLong32ArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public int[] toNative(NativeLong[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ int[] primitive = new int[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i].intValue() : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends NativeLong32ArrayParameterConverter implements PostInvocation<NativeLong[], int[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(NativeLong[] array, int[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = NativeLong.valueOf(primitive[i]);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Class<int[]> nativeType() {
+ return int[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/NativeLong64ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/NativeLong64ArrayParameterConverter.java
new file mode 100644
index 0000000..1d60cd7
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/NativeLong64ArrayParameterConverter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.NativeLong;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a NativeLong[] array to a primitive long[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class NativeLong64ArrayParameterConverter implements ToNativeConverter<NativeLong[], long[]> {
+ private static final ToNativeConverter<NativeLong[], long[]> IN = new NativeLong64ArrayParameterConverter(ParameterFlags.IN);
+ private static final ToNativeConverter<NativeLong[], long[]> OUT = new NativeLong64ArrayParameterConverter.Out(ParameterFlags.OUT);
+ private static final ToNativeConverter<NativeLong[], long[]> INOUT = new NativeLong64ArrayParameterConverter.Out(ParameterFlags.IN | ParameterFlags.OUT);
+
+ private final int parameterFlags;
+
+ public static ToNativeConverter<NativeLong[], long[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return ParameterFlags.isOut(parameterFlags) ? ParameterFlags.isIn(parameterFlags) ? INOUT : OUT : IN;
+ }
+
+ private NativeLong64ArrayParameterConverter(int parameterFlags) {
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public long[] toNative(NativeLong[] array, ToNativeContext context) {
+ if (array == null) {
+ return null;
+ }
+ long[] primitive = new long[array.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < array.length; i++) {
+ primitive[i] = array[i] != null ? array[i].intValue() : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends NativeLong64ArrayParameterConverter implements PostInvocation<NativeLong[], long[]> {
+ Out(int parameterFlags) {
+ super(parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(NativeLong[] array, long[] primitive, ToNativeContext context) {
+ if (array != null && primitive != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = NativeLong.valueOf(primitive[i]);
+ }
+ }
+ }
+ }
+
+ @Override
+ @LongLong
+ public Class<long[]> nativeType() {
+ return long[].class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/NativeLongConverter.java b/src/main/java/jnr/ffi/provider/converters/NativeLongConverter.java
new file mode 100644
index 0000000..692a1b6
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/NativeLongConverter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.NativeLong;
+
+/**
+ * Parameter and return type support for the old NativeLong type
+ */
+import jnr.ffi.mapper.*;
+
+ at ToNativeConverter.NoContext
+ at FromNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+ at FromNativeConverter.Cacheable
+public final class NativeLongConverter extends AbstractDataConverter<NativeLong, Long> {
+ private static final DataConverter INSTANCE = new NativeLongConverter();
+
+ public static DataConverter<NativeLong, Long> getInstance() {
+ return INSTANCE;
+ }
+
+ public Class<Long> nativeType() {
+ return Long.class;
+ }
+
+ public Long toNative(NativeLong value, ToNativeContext toNativeContext) {
+ return value.longValue();
+ }
+
+ public NativeLong fromNative(Long value, FromNativeContext fromNativeContext) {
+ return NativeLong.valueOf(value);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/Pointer32ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/Pointer32ArrayParameterConverter.java
new file mode 100644
index 0000000..30b6192
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/Pointer32ArrayParameterConverter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.*;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.MemoryManager;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Pointer[] array to a int[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class Pointer32ArrayParameterConverter implements ToNativeConverter<Pointer[], int[]> {
+ protected final jnr.ffi.Runtime runtime;
+ protected final int parameterFlags;
+
+ public static ToNativeConverter<Pointer[], int[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return !ParameterFlags.isOut(parameterFlags)
+ ? new Pointer32ArrayParameterConverter(toNativeContext.getRuntime(), parameterFlags)
+ : new Pointer32ArrayParameterConverter.Out(toNativeContext.getRuntime(), parameterFlags);
+ }
+
+ Pointer32ArrayParameterConverter(jnr.ffi.Runtime runtime, int parameterFlags) {
+ this.runtime = runtime;
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ public Class<int[]> nativeType() {
+ return int[].class;
+ }
+
+ @Override
+ public int[] toNative(Pointer[] pointers, ToNativeContext context) {
+ if (pointers == null) {
+ return null;
+ }
+ int[] primitive = new int[pointers.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < pointers.length; i++) {
+ if (pointers[i] != null && !pointers[i].isDirect()) {
+ throw new IllegalArgumentException("invalid pointer in array at index " + i);
+ }
+ primitive[i] = pointers[i] != null ? (int) pointers[i].address() : 0;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends Pointer32ArrayParameterConverter implements ToNativeConverter.PostInvocation<Pointer[], int[]> {
+ public Out(jnr.ffi.Runtime runtime, int parameterFlags) {
+ super(runtime, parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Pointer[] pointers, int[] primitive, ToNativeContext context) {
+ if (pointers != null && primitive != null && ParameterFlags.isOut(parameterFlags)) {
+ MemoryManager mm = runtime.getMemoryManager();
+ for (int i = 0; i < pointers.length; i++) {
+ pointers[i] = mm.newPointer(primitive[i]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/Pointer64ArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/Pointer64ArrayParameterConverter.java
new file mode 100644
index 0000000..8da9688
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/Pointer64ArrayParameterConverter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.MemoryManager;
+import jnr.ffi.provider.ParameterFlags;
+
+/**
+ * Converts a Pointer[] array to a long[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class Pointer64ArrayParameterConverter implements ToNativeConverter<Pointer[], long[]> {
+ protected final jnr.ffi.Runtime runtime;
+ protected final int parameterFlags;
+
+ public static ToNativeConverter<Pointer[], long[]> getInstance(ToNativeContext toNativeContext) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return !ParameterFlags.isOut(parameterFlags)
+ ? new Pointer64ArrayParameterConverter(toNativeContext.getRuntime(), parameterFlags)
+ : new Pointer64ArrayParameterConverter.Out(toNativeContext.getRuntime(), parameterFlags);
+ }
+
+ Pointer64ArrayParameterConverter(jnr.ffi.Runtime runtime, int parameterFlags) {
+ this.runtime = runtime;
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ @LongLong
+ public Class<long[]> nativeType() {
+ return long[].class;
+ }
+
+ @Override
+ public long[] toNative(Pointer[] pointers, ToNativeContext context) {
+ if (pointers == null) {
+ return null;
+ }
+ long[] primitive = new long[pointers.length];
+ if (ParameterFlags.isIn(parameterFlags)) {
+ for (int i = 0; i < pointers.length; i++) {
+ if (pointers[i] != null && !pointers[i].isDirect()) {
+ throw new IllegalArgumentException("invalid pointer in array at index " + i);
+ }
+ primitive[i] = pointers[i] != null ? pointers[i].address() : 0L;
+ }
+ }
+
+ return primitive;
+ }
+
+ public static final class Out extends Pointer64ArrayParameterConverter implements ToNativeConverter.PostInvocation<Pointer[], long[]> {
+ Out(jnr.ffi.Runtime runtime, int parameterFlags) {
+ super(runtime, parameterFlags);
+ }
+
+ @Override
+ public void postInvoke(Pointer[] pointers, long[] primitive, ToNativeContext context) {
+ if (pointers != null && primitive != null) {
+ MemoryManager mm = runtime.getMemoryManager();
+ for (int i = 0; i < pointers.length; i++) {
+ pointers[i] = mm.newPointer(primitive[i]);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StringBufferParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/StringBufferParameterConverter.java
new file mode 100644
index 0000000..a69f3ca
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StringBufferParameterConverter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+import jnr.ffi.util.BufferUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class StringBufferParameterConverter implements ToNativeConverter<StringBuffer, ByteBuffer>, ToNativeConverter.PostInvocation<StringBuffer, ByteBuffer> {
+ private final Charset charset;
+ private final int parameterFlags;
+
+ private StringBufferParameterConverter(Charset charset, int parameterFlags) {
+ this.charset = charset;
+ this.parameterFlags = parameterFlags;
+ }
+
+ public Class<ByteBuffer> nativeType() {
+ return ByteBuffer.class;
+ }
+
+ public static StringBufferParameterConverter getInstance(int parameterFlags, ToNativeContext toNativeContext) {
+ return new StringBufferParameterConverter(Charset.defaultCharset(), parameterFlags);
+ }
+
+ public static StringBufferParameterConverter getInstance(Charset charset, int parameterFlags, ToNativeContext toNativeContext) {
+ return new StringBufferParameterConverter(charset, parameterFlags);
+ }
+
+ public ByteBuffer toNative(StringBuffer parameter, ToNativeContext context) {
+ if (parameter == null) {
+ return null;
+
+ } else {
+ ByteBuffer buf = ParameterFlags.isIn(parameterFlags)
+ ? charset.encode(CharBuffer.wrap(parameter))
+ : ByteBuffer.allocate(parameter.capacity() + 1);
+
+ if ((ParameterFlags.isOut(parameterFlags) && buf.capacity() < parameter.capacity() + 1) || !buf.hasArray()) {
+ byte[] array = new byte[parameter.capacity() + 1];
+ buf.get(array, 0, buf.remaining());
+ return ByteBuffer.wrap(array);
+
+ } else {
+ return buf;
+ }
+ }
+ }
+
+ public void postInvoke(StringBuffer stringBuffer, ByteBuffer buf, ToNativeContext context) {
+ //
+ // Copy the string back out if its an OUT parameter
+ //
+ if (ParameterFlags.isOut(parameterFlags) && stringBuffer != null && buf != null) {
+ buf.limit(buf.capacity());
+ stringBuffer.delete(0, stringBuffer.length()).append(BufferUtil.getCharSequence(buf, charset));
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StringBuilderParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/StringBuilderParameterConverter.java
new file mode 100644
index 0000000..b1603f7
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StringBuilderParameterConverter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+import jnr.ffi.util.BufferUtil;
+
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.*;
+
+import static jnr.ffi.provider.converters.StringUtil.*;
+
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class StringBuilderParameterConverter implements ToNativeConverter<StringBuilder, ByteBuffer>, ToNativeConverter.PostInvocation<StringBuilder, ByteBuffer> {
+ private final ThreadLocal<Reference<CharsetEncoder>> localEncoder = new ThreadLocal<Reference<CharsetEncoder>>();
+ private final ThreadLocal<Reference<CharsetDecoder>> localDecoder = new ThreadLocal<Reference<CharsetDecoder>>();
+ private final Charset charset;
+ private final int parameterFlags;
+ private final int terminatorWidth;
+
+ private StringBuilderParameterConverter(Charset charset, int parameterFlags) {
+ this.charset = charset;
+ this.parameterFlags = parameterFlags;
+ this.terminatorWidth = terminatorWidth(charset);
+ }
+
+ public Class<ByteBuffer> nativeType() {
+ return ByteBuffer.class;
+ }
+
+ public static StringBuilderParameterConverter getInstance(int parameterFlags, ToNativeContext toNativeContext) {
+ return new StringBuilderParameterConverter(getCharset(toNativeContext), parameterFlags);
+ }
+
+ public static StringBuilderParameterConverter getInstance(Charset charset, int parameterFlags, ToNativeContext toNativeContext) {
+ return new StringBuilderParameterConverter(charset, parameterFlags);
+ }
+
+ public ByteBuffer toNative(StringBuilder parameter, ToNativeContext context) {
+ if (parameter == null) {
+ return null;
+
+ } else {
+ CharsetEncoder encoder = getEncoder(charset, localEncoder);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[parameter.capacity() * (int) Math.ceil(encoder.maxBytesPerChar()) + 4]);
+
+ if (ParameterFlags.isIn(parameterFlags)) {
+ byteBuffer.mark();
+ encoder.reset();
+ CoderResult result = encoder.encode(CharBuffer.wrap(parameter), byteBuffer, true);
+ if (result.isUnderflow()) result = encoder.flush(byteBuffer);
+ if (result.isError()) throwException(result);
+ byteBuffer.reset();
+ }
+
+ return byteBuffer;
+ }
+ }
+
+ public void postInvoke(StringBuilder stringBuilder, ByteBuffer buf, ToNativeContext context) {
+ //
+ // Copy the string back out if its an OUT parameter
+ //
+ if (ParameterFlags.isOut(parameterFlags) && stringBuilder != null && buf != null) {
+ buf.limit(stringLength(buf, terminatorWidth));
+ try {
+ stringBuilder.delete(0, stringBuilder.length()).append(getDecoder(charset, localDecoder).reset().decode(buf));
+ } catch (CharacterCodingException cce) {
+ throw new RuntimeException(cce);
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StringResultConverter.java b/src/main/java/jnr/ffi/provider/converters/StringResultConverter.java
new file mode 100644
index 0000000..ba8e182
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StringResultConverter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.Encoding;
+import jnr.ffi.mapper.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static jnr.ffi.provider.converters.StringUtil.getDecoder;
+
+/**
+ * Converts a native pointer result into a java String
+ */
+ at FromNativeConverter.NoContext
+ at FromNativeConverter.Cacheable
+public class StringResultConverter implements FromNativeConverter<String, Pointer> {
+ private static final FromNativeConverter<String, Pointer> DEFAULT = new StringResultConverter(Charset.defaultCharset());
+ private final ThreadLocal<Reference<CharsetDecoder>> localDecoder = new ThreadLocal<Reference<CharsetDecoder>>();
+ private final Charset charset;
+ private final int terminatorWidth;
+
+ private StringResultConverter(Charset charset) {
+ this.charset = charset;
+ this.terminatorWidth = StringUtil.terminatorWidth(charset);
+ }
+
+ public static FromNativeConverter<String, Pointer> getInstance(Charset cs) {
+ return Charset.defaultCharset().equals(cs) ? DEFAULT : new StringResultConverter(cs);
+ }
+
+ public static FromNativeConverter<String, Pointer> getInstance(FromNativeContext fromNativeContext) {
+ Charset charset = Charset.defaultCharset();
+
+ if (fromNativeContext instanceof MethodResultContext) {
+ // See if the interface class has a global @Encoding declaration
+ Encoding e = getEncoding(Arrays.asList(((MethodResultContext) fromNativeContext).getMethod().getDeclaringClass().getAnnotations()));
+ if (e != null) {
+ charset = Charset.forName(e.value());
+ }
+ }
+
+ // Allow each method to override the default
+ Encoding e = getEncoding(fromNativeContext.getAnnotations());
+ if (e != null) {
+ charset = Charset.forName(e.value());
+ }
+
+ return getInstance(charset);
+ }
+
+ @Override
+ public String fromNative(Pointer pointer, FromNativeContext context) {
+ if (pointer == null) {
+ return null;
+ }
+
+ Search: for (int idx = 0; ; ) {
+ idx += pointer.indexOf(idx, (byte) 0);
+ for (int tcount = 1; tcount < terminatorWidth; tcount++) {
+ if (pointer.getByte(idx + tcount) != 0) {
+ idx += tcount;
+ continue Search;
+ }
+ }
+
+ byte[] bytes = new byte[idx];
+ pointer.get(0, bytes, 0, bytes.length);
+ try {
+ return getDecoder(charset, localDecoder).reset().decode(ByteBuffer.wrap(bytes)).toString();
+ } catch (CharacterCodingException cce) {
+ throw new RuntimeException(cce);
+ }
+ }
+ }
+
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ private static Encoding getEncoding(Collection<Annotation> annotations) {
+ for (Annotation a : annotations) {
+ if (a instanceof Encoding) {
+ return (Encoding) a;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StringUtil.java b/src/main/java/jnr/ffi/provider/converters/StringUtil.java
new file mode 100644
index 0000000..7dfa461
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StringUtil.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.annotations.Encoding;
+import jnr.ffi.mapper.MethodParameterContext;
+import jnr.ffi.mapper.ToNativeContext;
+
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.nio.ByteBuffer;
+import java.nio.charset.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+final class StringUtil {
+ private StringUtil() {}
+
+ static CharsetEncoder getEncoder(Charset charset, ThreadLocal<Reference<CharsetEncoder>> localEncoder) {
+ Reference<CharsetEncoder> ref = localEncoder.get();
+ CharsetEncoder encoder;
+ return ref != null && (encoder = ref.get()) != null && encoder.charset() == charset
+ ? encoder : initEncoder(charset, localEncoder);
+ }
+
+ static CharsetDecoder getDecoder(Charset charset, ThreadLocal<Reference<CharsetDecoder>> localDecoder) {
+ Reference<CharsetDecoder> ref = localDecoder.get();
+ CharsetDecoder decoder;
+ return ref != null && (decoder = ref.get()) != null && decoder.charset() == charset
+ ? decoder : initDecoder(charset, localDecoder);
+ }
+
+ private static CharsetEncoder initEncoder(Charset charset, ThreadLocal<Reference<CharsetEncoder>> localEncoder) {
+ CharsetEncoder encoder = charset.newEncoder();
+ encoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
+ localEncoder.set(new SoftReference<CharsetEncoder>(encoder));
+
+ return encoder;
+ }
+
+ private static CharsetDecoder initDecoder(Charset charset, ThreadLocal<Reference<CharsetDecoder>> localDecoder) {
+ CharsetDecoder decoder = charset.newDecoder();
+ decoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
+ localDecoder.set(new SoftReference<CharsetDecoder>(decoder));
+
+ return decoder;
+ }
+
+ static Charset getCharset(ToNativeContext toNativeContext) {
+ Charset charset = Charset.defaultCharset();
+
+ if (toNativeContext instanceof MethodParameterContext) {
+ // See if the interface class has a global @Encoding declaration
+ Charset cs = getEncodingCharset(Arrays.asList(((MethodParameterContext) toNativeContext).getMethod().getDeclaringClass().getAnnotations()));
+ if (cs != null) {
+ charset = cs;
+ }
+
+ // Allow each method to override the default
+ cs = getEncodingCharset(Arrays.asList(((MethodParameterContext) toNativeContext).getMethod().getAnnotations()));
+ if (cs != null) {
+ charset = cs;
+ }
+ }
+
+ // Override on a per-parameter basis
+ Charset cs = getEncodingCharset(toNativeContext.getAnnotations());
+ if (cs != null) {
+ charset = cs;
+ }
+
+ return charset;
+ }
+
+ private static Charset getEncodingCharset(Collection<Annotation> annotations) {
+ for (Annotation a : annotations) {
+ if (a instanceof Encoding) {
+ return Charset.forName(((Encoding) a).value());
+ }
+ }
+
+ return null;
+ }
+
+ static void throwException(CoderResult result) {
+ try {
+ result.throwException();
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (CharacterCodingException cce) {
+ throw new RuntimeException(cce);
+ }
+ }
+
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final Charset USASCII = Charset.forName("US-ASCII");
+ private static final Charset ISO8859_1 = Charset.forName("ISO-8859-1");
+ private static final Charset UTF16 = Charset.forName("UTF-16");
+ private static final Charset UTF16LE = Charset.forName("UTF-16LE");
+ private static final Charset UTF16BE = Charset.forName("UTF-16BE");
+
+ static int terminatorWidth(Charset charset) {
+ if (charset.equals(UTF8) || charset.equals(USASCII) || charset.equals(ISO8859_1)) {
+ return 1;
+
+ } else if (charset.equals(UTF16) || charset.equals(UTF16LE) || charset.equals(UTF16BE)) {
+ return 2;
+
+ } else {
+ return 4;
+ }
+ }
+
+ static int stringLength(ByteBuffer in, int terminatorWidth) {
+ if (in.hasArray()) {
+ byte[] array = in.array();
+ int end = in.arrayOffset() + in.limit();
+ int tcount = 0;
+ for (int idx = in.arrayOffset() + in.position(); idx < end; ) {
+ if (array[idx++] == 0) {
+ tcount++;
+ } else {
+ tcount = 0;
+ }
+ if (tcount == terminatorWidth) {
+ return idx - terminatorWidth;
+ }
+ }
+ } else {
+ int begin = in.position();
+ int end = in.limit();
+ int tcount = 0;
+ for (int idx = begin; idx < end; ) {
+ if (in.get(idx++) == 0) {
+ tcount++;
+ } else {
+ tcount = 0;
+ }
+ if (tcount == terminatorWidth) {
+ return idx - terminatorWidth;
+ }
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StructArrayParameterConverter.java b/src/main/java/jnr/ffi/provider/converters/StructArrayParameterConverter.java
new file mode 100644
index 0000000..0429593
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StructArrayParameterConverter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Struct;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.DelegatingMemoryIO;
+import jnr.ffi.provider.ParameterFlags;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Converts a Pointer[] array to a long[] array parameter
+ */
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public class StructArrayParameterConverter implements ToNativeConverter<Struct[], Pointer> {
+ protected final jnr.ffi.Runtime runtime;
+ protected final int parameterFlags;
+
+ public static ToNativeConverter<Struct[], Pointer> getInstance(ToNativeContext toNativeContext, Class structClass) {
+ int parameterFlags = ParameterFlags.parse(toNativeContext.getAnnotations());
+ return !ParameterFlags.isOut(parameterFlags)
+ ? new StructArrayParameterConverter(toNativeContext.getRuntime(), parameterFlags)
+ : new StructArrayParameterConverter.Out(toNativeContext.getRuntime(), structClass.asSubclass(Struct.class), parameterFlags);
+ }
+
+ StructArrayParameterConverter(jnr.ffi.Runtime runtime, int parameterFlags) {
+ this.runtime = runtime;
+ this.parameterFlags = parameterFlags;
+ }
+
+ @Override
+ @LongLong
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ @Override
+ public Pointer toNative(Struct[] structs, ToNativeContext context) {
+ if (structs == null) {
+ return null;
+ }
+ Pointer memory = Struct.getMemory(structs[0], parameterFlags);
+ if (!(memory instanceof DelegatingMemoryIO)) {
+ throw new RuntimeException("Struct array must be backed by contiguous array");
+ }
+ return ((DelegatingMemoryIO) memory).getDelegatedMemoryIO();
+ }
+
+ public static final class Out extends StructArrayParameterConverter implements PostInvocation<Struct[], Pointer> {
+ private final Constructor<? extends Struct> constructor;
+ Out(jnr.ffi.Runtime runtime, Class<? extends Struct> structClass, int parameterFlags) {
+ super(runtime, parameterFlags);
+ Constructor<? extends Struct> cons;
+ try {
+ cons = structClass.getConstructor(jnr.ffi.Runtime.class);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(structClass.getName() + " has no constructor that accepts jnr.ffi.Runtime");
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ this.constructor = cons;
+ }
+
+ @Override
+ public void postInvoke(Struct[] structs, Pointer primitive, ToNativeContext context) {
+ if (structs != null && primitive != null) {
+ try {
+ int off = 0;
+ for (int i = 0; i < structs.length; i++) {
+ structs[i] = constructor.newInstance(runtime);
+ int structSize = align(Struct.size(structs[i]), Struct.alignment(structs[i]));
+ structs[i].useMemory(primitive.slice(off = align(off, Struct.alignment(structs[i])), structSize));
+ off += structSize;
+ }
+ } catch (InstantiationException ie) {
+ throw new RuntimeException(ie);
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ } catch (InvocationTargetException ite) {
+ throw new RuntimeException(ite);
+ }
+ }
+ }
+ }
+
+ private static int align(int offset, int align) {
+ return (offset + align - 1) & ~(align - 1);
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StructByReferenceFromNativeConverter.java b/src/main/java/jnr/ffi/provider/converters/StructByReferenceFromNativeConverter.java
new file mode 100644
index 0000000..59b5d48
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StructByReferenceFromNativeConverter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Struct;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * Converts a native pointer result into a {@link jnr.ffi.Struct}
+ */
+public class StructByReferenceFromNativeConverter implements FromNativeConverter<Struct, Pointer> {
+ private final Constructor<? extends Struct> constructor;
+
+ public static FromNativeConverter<Struct, Pointer> getInstance(Class structClass, FromNativeContext toNativeContext) {
+ try {
+ return new StructByReferenceFromNativeConverter(structClass.getConstructor(jnr.ffi.Runtime.class));
+
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(structClass.getName() + " has no constructor that accepts jnr.ffi.Runtime");
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ StructByReferenceFromNativeConverter(Constructor<? extends Struct> constructor) {
+ this.constructor = constructor;
+ }
+
+ @Override
+ public Struct fromNative(Pointer nativeValue, FromNativeContext context) {
+ try {
+ Struct s = constructor.newInstance(context.getRuntime());
+ s.useMemory(nativeValue);
+ return s;
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/converters/StructByReferenceToNativeConverter.java b/src/main/java/jnr/ffi/provider/converters/StructByReferenceToNativeConverter.java
new file mode 100644
index 0000000..daacdc0
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/converters/StructByReferenceToNativeConverter.java
@@ -0,0 +1,29 @@
+package jnr.ffi.provider.converters;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Struct;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.ParameterFlags;
+
+ at ToNativeConverter.NoContext
+ at ToNativeConverter.Cacheable
+public final class StructByReferenceToNativeConverter implements ToNativeConverter<Struct, Pointer> {
+ private final int flags;
+
+ public static ToNativeConverter<Struct, Pointer> getInstance(ToNativeContext toNativeContext) {
+ return new StructByReferenceToNativeConverter(ParameterFlags.parse(toNativeContext.getAnnotations()));
+ }
+
+ StructByReferenceToNativeConverter(int flags) {
+ this.flags = flags;
+ }
+
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ public Pointer toNative(Struct value, ToNativeContext ctx) {
+ return value != null ? Struct.getMemory(value, flags) : null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AbstractAsmLibraryInterface.java b/src/main/java/jnr/ffi/provider/jffi/AbstractAsmLibraryInterface.java
new file mode 100644
index 0000000..ac9ddf2
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AbstractAsmLibraryInterface.java
@@ -0,0 +1,27 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.provider.LoadedLibrary;
+
+/**
+ *
+ */
+public abstract class AbstractAsmLibraryInterface implements LoadedLibrary {
+ public static final com.kenai.jffi.Invoker ffi = com.kenai.jffi.Invoker.getInstance();
+ protected final jnr.ffi.Runtime runtime;
+
+ // Strong ref to keep the library alive
+ protected final NativeLibrary library;
+
+ public AbstractAsmLibraryInterface(jnr.ffi.Runtime runtime, NativeLibrary library) {
+ this.runtime = runtime;
+ this.library = library;
+ }
+
+ public final jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+
+ final NativeLibrary getLibrary() {
+ return library;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AbstractFastNumericMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/AbstractFastNumericMethodGenerator.java
new file mode 100644
index 0000000..a3ba577
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AbstractFastNumericMethodGenerator.java
@@ -0,0 +1,276 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.*;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import org.objectweb.asm.Label;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.*;
+import java.util.*;
+
+import static jnr.ffi.provider.jffi.AsmUtil.*;
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static jnr.ffi.provider.jffi.CodegenUtils.p;
+import static jnr.ffi.provider.jffi.NumberUtil.*;
+
+/**
+ *
+ */
+abstract class AbstractFastNumericMethodGenerator extends BaseMethodGenerator {
+
+ public void generate(final AsmBuilder builder, final SkinnyMethodAdapter mv, LocalVariableAllocator localVariableAllocator, CallContext callContext, final ResultType resultType, final ParameterType[] parameterTypes,
+ boolean ignoreError) {
+ // [ stack contains: Invoker, Function ]
+ final Class nativeIntType = getInvokerType();
+ final LocalVariable objCount = localVariableAllocator.allocate(int.class);
+ final LocalVariable[] parameters = AsmUtil.getParameterVariables(parameterTypes);
+ final LocalVariable[] converted = new LocalVariable[parameterTypes.length];
+ int pointerCount = 0;
+
+ // Load, convert, and un-box parameters
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ converted[i] = loadAndConvertParameter(builder, mv, localVariableAllocator, parameters[i], parameterTypes[i]);
+
+ Class javaParameterType = parameterTypes[i].effectiveJavaType();
+ ToNativeOp op = ToNativeOp.get(parameterTypes[i]);
+ if (op != null && op.isPrimitive()) {
+ op.emitPrimitive(mv, getInvokerType(), parameterTypes[i].getNativeType());
+
+ } else if (hasPointerParameterStrategy(javaParameterType)) {
+ pointerCount = emitDirectCheck(mv, javaParameterType, nativeIntType, converted[i], objCount, pointerCount);
+
+ } else {
+ throw new IllegalArgumentException("unsupported parameter type " + parameterTypes[i].getDeclaredType());
+ }
+ }
+
+ // stack now contains [ IntInvoker, Function, int/long args ]
+ Label hasObjects = new Label();
+ Label convertResult = new Label();
+ if (pointerCount > 0) {
+ mv.iload(objCount);
+ mv.ifne(hasObjects);
+ }
+ mv.invokevirtual(p(com.kenai.jffi.Invoker.class),
+ getInvokerMethodName(resultType, parameterTypes, ignoreError),
+ getInvokerSignature(parameterTypes.length, nativeIntType));
+
+ if (pointerCount > 0) mv.label(convertResult);
+
+ Class javaReturnType = resultType.effectiveJavaType();
+ Class nativeReturnType = nativeIntType;
+
+ // Convert the result from long/int to the correct return type
+ if (Float.class == javaReturnType || float.class == javaReturnType) {
+ narrow(mv, nativeIntType, int.class);
+ mv.invokestatic(Float.class, "intBitsToFloat", float.class, int.class);
+ nativeReturnType = float.class;
+
+ } else if (Double.class == javaReturnType || double.class == javaReturnType) {
+ widen(mv, nativeIntType, long.class);
+ mv.invokestatic(Double.class, "longBitsToDouble", double.class, long.class);
+ nativeReturnType = double.class;
+
+ }
+
+ // box and/or narrow/widen the return value if needed
+ final Class unboxedResultType = unboxedReturnType(javaReturnType);
+ convertPrimitive(mv, nativeReturnType, unboxedResultType, resultType.getNativeType());
+ emitEpilogue(builder, mv, resultType, parameterTypes, parameters, converted, null);
+
+ /* -- method returns above - below is an alternative path -- */
+
+ // Now implement heap object support
+ if (pointerCount > 0) {
+ mv.label(hasObjects);
+
+ if (int.class == nativeIntType) {
+ // For int invoker, need to convert all the int args to long
+ LocalVariable[] tmp = new LocalVariable[parameterTypes.length];
+ for (int i = parameterTypes.length - 1; i > 0; i--) {
+ tmp[i] = localVariableAllocator.allocate(int.class);
+ mv.istore(tmp[i]);
+ }
+ if (parameterTypes.length > 0) mv.i2l();
+ // Now reload them back onto the parameter stack, and widen to long
+ for (int i = 1; i < parameterTypes.length; i++) {
+ mv.iload(tmp[i]);
+ mv.i2l();
+ }
+ }
+
+ mv.iload(objCount);
+ // Need to load all the converters onto the stack
+ LocalVariable[] strategies = new LocalVariable[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Class javaParameterType = parameterTypes[i].effectiveJavaType();
+ if (hasPointerParameterStrategy(javaParameterType)) {
+ mv.aload(converted[i]);
+ emitParameterStrategyLookup(mv, javaParameterType);
+ mv.astore(strategies[i] = localVariableAllocator.allocate(ParameterStrategy.class));
+
+ mv.aload(converted[i]);
+ mv.aload(strategies[i]);
+ mv.aload(0);
+
+ ObjectParameterInfo info = ObjectParameterInfo.create(i,
+ AsmUtil.getNativeArrayFlags(parameterTypes[i].annotations()));
+
+ mv.getfield(builder.getClassNamePath(), builder.getObjectParameterInfoName(info),
+ ci(ObjectParameterInfo.class));
+ }
+ }
+ mv.invokevirtual(p(com.kenai.jffi.Invoker.class),
+ getObjectParameterMethodName(parameterTypes.length),
+ getObjectParameterMethodSignature(parameterTypes.length, pointerCount));
+ narrow(mv, long.class, nativeIntType);
+ mv.go_to(convertResult);
+ }
+ }
+
+ static final Map<Class<? extends ObjectParameterStrategy>, Method> STRATEGY_ADDRESS_METHODS;
+ static final Map<Class, Class<? extends ObjectParameterStrategy>> STRATEGY_PARAMETER_TYPES;
+ static {
+ Map<Class<? extends ObjectParameterStrategy>, Method> strategies = new HashMap<Class<? extends ObjectParameterStrategy>, Method>();
+ addStrategyParameterType(strategies, BufferParameterStrategy.class, Buffer.class);
+ addStrategyParameterType(strategies, PointerParameterStrategy.class, Pointer.class);
+ STRATEGY_ADDRESS_METHODS = Collections.unmodifiableMap(strategies);
+
+ Map<Class, Class<? extends ObjectParameterStrategy>> types = new LinkedHashMap<Class, Class<? extends ObjectParameterStrategy>>();
+ types.put(Pointer.class, PointerParameterStrategy.class);
+ for (Class c : new Class[] { ByteBuffer.class, CharBuffer.class, ShortBuffer.class, IntBuffer.class,
+ LongBuffer.class, FloatBuffer.class, DoubleBuffer.class, Buffer.class }) {
+ types.put(c, BufferParameterStrategy.class);
+ }
+
+ for (Class c : new Class[] { byte[].class, short[].class, char[].class, int[].class, long[].class, float[].class, double[].class, boolean[].class }) {
+ types.put(c, ParameterStrategy.class);
+ }
+
+ STRATEGY_PARAMETER_TYPES = Collections.unmodifiableMap(types);
+ }
+
+ private static void addStrategyParameterType(Map<Class<? extends ObjectParameterStrategy>, Method> map,
+ Class<? extends ObjectParameterStrategy> strategyClass, Class parameterType) {
+ try {
+ Method addressMethod = strategyClass.getDeclaredMethod("address", parameterType);
+ if (Modifier.isPublic(addressMethod.getModifiers()) && Modifier.isPublic(addressMethod.getDeclaringClass().getModifiers())) {
+ map.put(strategyClass, addressMethod);
+ }
+
+ } catch (NoSuchMethodException ignored) {}
+ }
+
+ @SuppressWarnings("unchecked")
+ static boolean hasPointerParameterStrategy(Class javaType) {
+ for (Class c : STRATEGY_PARAMETER_TYPES.keySet()) {
+ if (c.isAssignableFrom(javaType)) {
+ // FIXME: replace special handling for 32/64bit arrays with more generic elsewhere
+ return !(LongBuffer.class.isAssignableFrom(javaType) || long[].class == javaType) || sizeof(NativeType.SLONG) == 8;
+ }
+ }
+
+ return false;
+
+ }
+
+ static Class<? extends ObjectParameterStrategy> emitParameterStrategyLookup(SkinnyMethodAdapter mv, Class javaParameterType) {
+ for (Map.Entry<Class, Class<? extends ObjectParameterStrategy>> e : STRATEGY_PARAMETER_TYPES.entrySet()) {
+ if (e.getKey().isAssignableFrom(javaParameterType)) {
+ mv.invokestatic(AsmRuntime.class, "pointerParameterStrategy", e.getValue(), e.getKey());
+ return e.getValue();
+ }
+ }
+
+ throw new RuntimeException("no conversion strategy for: " + javaParameterType);
+ }
+
+ static void emitParameterStrategyAddress(SkinnyMethodAdapter mv, Class nativeIntType,
+ Class<? extends ObjectParameterStrategy> strategyClass, LocalVariable strategy, LocalVariable parameter) {
+ // Get the native address (will return zero for heap objects)
+ mv.aload(strategy);
+ mv.aload(parameter);
+ Method addressMethod = STRATEGY_ADDRESS_METHODS.get(strategyClass);
+ if (addressMethod != null) {
+ mv.invokevirtual(strategyClass, addressMethod.getName(), addressMethod.getReturnType(), addressMethod.getParameterTypes());
+ } else {
+ mv.invokevirtual(PointerParameterStrategy.class, "address", long.class, Object.class);
+ }
+
+ narrow(mv, long.class, nativeIntType);
+ }
+
+ static int emitDirectCheck(SkinnyMethodAdapter mv, Class javaParameterClass, Class nativeIntType,
+ LocalVariable parameter, LocalVariable objCount, int pointerCount) {
+ if (pointerCount < 1) {
+ mv.iconst_0();
+ mv.istore(objCount);
+ }
+
+ Label next = new Label();
+ Label nullPointer = new Label();
+ mv.ifnull(nullPointer);
+ if (Pointer.class.isAssignableFrom(javaParameterClass)) {
+ mv.aload(parameter);
+ mv.invokevirtual(Pointer.class, "address", long.class);
+ narrow(mv, long.class, nativeIntType);
+ mv.aload(parameter);
+ mv.invokevirtual(Pointer.class, "isDirect", boolean.class);
+ mv.iftrue(next);
+
+ } else if (Buffer.class.isAssignableFrom(javaParameterClass)) {
+ mv.aload(parameter);
+ mv.invokestatic(AsmRuntime.class, "longValue", long.class, Buffer.class);
+ narrow(mv, long.class, nativeIntType);
+ mv.aload(parameter);
+ mv.invokevirtual(Buffer.class, "isDirect", boolean.class);
+ mv.iftrue(next);
+
+ } else if (javaParameterClass.isArray() && javaParameterClass.getComponentType().isPrimitive()) {
+ // address of arrays is always zero - they have to be handled as objects
+ if (long.class == nativeIntType) mv.lconst_0(); else mv.iconst_0();
+
+ } else {
+ throw new UnsupportedOperationException("unsupported parameter type: " + javaParameterClass);
+ }
+
+ mv.iinc(objCount, 1);
+ mv.go_to(next);
+
+ mv.label(nullPointer);
+ if (long.class == nativeIntType) mv.lconst_0(); else mv.iconst_0();
+ mv.label(next);
+
+ return ++pointerCount;
+ }
+
+ static String getObjectParameterMethodName(int parameterCount) {
+ return "invokeN" + parameterCount;
+ }
+
+ static String getObjectParameterMethodSignature(int parameterCount, int pointerCount) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(').append(ci(CallContext.class)).append(ci(long.class));
+ for (int i = 0; i < parameterCount; i++) {
+ sb.append('J');
+ }
+ sb.append('I'); // objCount
+ for (int n = 0; n < pointerCount; n++) {
+ sb.append(ci(Object.class));
+ sb.append(ci(ObjectParameterStrategy.class));
+ sb.append(ci(ObjectParameterInfo.class));
+ }
+ sb.append(")J");
+ return sb.toString();
+ }
+
+ abstract String getInvokerMethodName(ResultType resultType, ParameterType[] parameterTypes,
+ boolean ignoreErrno);
+
+ abstract String getInvokerSignature(int parameterCount, Class nativeIntType);
+ abstract Class getInvokerType();
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AbstractX86StubCompiler.java b/src/main/java/jnr/ffi/provider/jffi/AbstractX86StubCompiler.java
new file mode 100644
index 0000000..96f4006
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AbstractX86StubCompiler.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.MemoryIO;
+import com.kenai.jffi.NativeMethod;
+import com.kenai.jffi.NativeMethods;
+import com.kenai.jffi.PageManager;
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.x86asm.Assembler;
+
+import java.io.PrintStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Base class for most X86_32/X86_64 stub compilers
+ */
+abstract class AbstractX86StubCompiler extends StubCompiler {
+ public final static boolean DEBUG = Boolean.getBoolean("jnr.ffi.compile.dump");
+ private final jnr.ffi.Runtime runtime;
+
+ protected AbstractX86StubCompiler(jnr.ffi.Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ public final Runtime getRuntime() {
+ return runtime;
+ }
+
+ private static final class StaticDataHolder {
+ // Keep a reference from the loaded class to the pages holding the code for that class.
+ static final Map<Class, PageHolder> PAGES
+ = Collections.synchronizedMap(new WeakHashMap<Class, PageHolder>());
+ }
+ final List<Stub> stubs = new LinkedList<Stub>();
+
+
+ static final class Stub {
+ final String name;
+ final String signature;
+ final Assembler assembler;
+
+ public Stub(String name, String signature, Assembler assembler) {
+ this.name = name;
+ this.signature = signature;
+ this.assembler = assembler;
+ }
+ }
+
+ static final class PageHolder {
+ final PageManager pm;
+ final long memory;
+ final long pageCount;
+
+ public PageHolder(PageManager pm, long memory, long pageCount) {
+ this.pm = pm;
+ this.memory = memory;
+ this.pageCount = pageCount;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ pm.freePages(memory, (int) pageCount);
+ } catch (Throwable t) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING,
+ "Exception when freeing native pages: %s", t.getLocalizedMessage());
+ } finally {
+ super.finalize();
+ }
+ }
+
+ }
+
+ @Override
+ void attach(Class clazz) {
+
+ if (stubs.isEmpty()) {
+ return;
+ }
+
+ long codeSize = 0;
+ for (Stub stub : stubs) {
+ // add 8 bytes for alignment
+ codeSize += stub.assembler.codeSize() + 8;
+ }
+
+ PageManager pm = PageManager.getInstance();
+
+ long npages = (codeSize + pm.pageSize() - 1) / pm.pageSize();
+ // Allocate some native memory for it
+ long code = pm.allocatePages((int) npages, PageManager.PROT_READ | PageManager.PROT_WRITE);
+ if (code == 0) {
+ throw new OutOfMemoryError("allocatePages failed for codeSize=" + codeSize);
+ }
+ PageHolder page = new PageHolder(pm, code, npages);
+
+ // Now relocate/copy all the assembler stubs into the real code area
+ List<NativeMethod> methods = new ArrayList<NativeMethod>(stubs.size());
+ long fn = code;
+ PrintStream dbg = System.err;
+ System.out.flush(); System.err.flush();
+
+ for (Stub stub : stubs) {
+ Assembler asm = stub.assembler;
+ // align the start of all functions on a 8 byte boundary
+ fn = align(fn, 8);
+ ByteBuffer buf = ByteBuffer.allocate(asm.codeSize()).order(ByteOrder.LITTLE_ENDIAN);
+ stub.assembler.relocCode(buf, fn);
+ buf.flip();
+ MemoryIO.getInstance().putByteArray(fn, buf.array(), buf.arrayOffset(), buf.limit());
+
+ if (DEBUG && X86Disassembler.isAvailable()) {
+
+ dbg.println(clazz.getName() + "." + stub.name + " " + stub.signature);
+ X86Disassembler disassembler = X86Disassembler.create();
+ disassembler.setMode(Platform.getNativePlatform().getCPU() == Platform.CPU.I386
+ ? X86Disassembler.Mode.I386 : X86Disassembler.Mode.X86_64);
+ disassembler.setSyntax(X86Disassembler.Syntax.INTEL);
+ disassembler.setInputBuffer(MemoryUtil.newPointer(runtime, fn), asm.offset());
+ while (disassembler.disassemble()) {
+ dbg.printf("%8x: %s\n", disassembler.offset(), disassembler.insn());
+ }
+ if (buf.remaining() > asm.offset()) {
+ // libudis86 for some reason cannot understand the code asmjit emits for the trampolines
+ dbg.printf("%8x: <indirect call trampolines>\n", asm.offset());
+ }
+ dbg.println();
+ }
+ methods.add(new NativeMethod(fn, stub.name, stub.signature));
+
+ fn += asm.codeSize();
+ }
+
+ pm.protectPages(code, (int) npages, PageManager.PROT_READ | PageManager.PROT_EXEC);
+
+ NativeMethods.register(clazz, methods);
+ StaticDataHolder.PAGES.put(clazz, page);
+ }
+
+ static int align(int offset, int align) {
+ return (offset + align - 1) & ~(align - 1);
+ }
+
+ static long align(long offset, long align) {
+ return (offset + align - 1) & ~(align - 1);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AllocatedDirectMemoryIO.java b/src/main/java/jnr/ffi/provider/jffi/AllocatedDirectMemoryIO.java
new file mode 100644
index 0000000..b05c719
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AllocatedDirectMemoryIO.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Runtime;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+class AllocatedDirectMemoryIO extends DirectMemoryIO {
+ private final AtomicBoolean allocated = new AtomicBoolean(true);
+ private final int size;
+
+ public AllocatedDirectMemoryIO(Runtime runtime, int size, boolean clear) {
+ super(runtime, IO.allocateMemory(size, clear));
+ this.size = size;
+ if (address() == 0L) {
+ throw new OutOfMemoryError("Failed to allocate " + size + " bytes");
+ }
+ }
+
+ @Override
+ public long size() {
+ return this.size;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AllocatedDirectMemoryIO) {
+ AllocatedDirectMemoryIO mem = (AllocatedDirectMemoryIO) obj;
+ return mem.size == size && mem.address() == address();
+ }
+
+ return super.equals(obj);
+ }
+
+ public final void dispose() {
+ if (allocated.getAndSet(false)) {
+ IO.freeMemory(address());
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (allocated.getAndSet(false)) {
+ IO.freeMemory(address());
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AnnotationTypeMapper.java b/src/main/java/jnr/ffi/provider/jffi/AnnotationTypeMapper.java
new file mode 100644
index 0000000..a62124b
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AnnotationTypeMapper.java
@@ -0,0 +1,104 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.mapper.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class AnnotationTypeMapper extends AbstractSignatureTypeMapper implements SignatureTypeMapper {
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ Method fromNativeMethod = findMethodWithAnnotation(type, FromNativeConverter.FromNative.class);
+ if (fromNativeMethod == null) {
+ return null;
+ }
+
+ if (!Modifier.isStatic(fromNativeMethod.getModifiers())) {
+ throw new IllegalArgumentException(fromNativeMethod.getDeclaringClass().getName() + "."
+ + fromNativeMethod.getName() + " should be declared static");
+ }
+
+ return FromNativeTypes.create(new ReflectionFromNativeConverter(fromNativeMethod,
+ fromNativeMethod.getAnnotation(FromNativeConverter.FromNative.class).nativeType()));
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ Method toNativeMethod = findMethodWithAnnotation(type, ToNativeConverter.ToNative.class);
+ if (toNativeMethod == null) {
+ return null;
+ }
+
+ if (!Modifier.isStatic(toNativeMethod.getModifiers())) {
+ throw new IllegalArgumentException(toNativeMethod.getDeclaringClass().getName() + "."
+ + toNativeMethod.getName() + " should be declared static");
+ }
+
+ return ToNativeTypes.create(new ReflectionToNativeConverter(toNativeMethod,
+ toNativeMethod.getAnnotation(ToNativeConverter.ToNative.class).nativeType()));
+ }
+
+ private static Method findMethodWithAnnotation(SignatureType type, Class<? extends Annotation> annotationClass) {
+ for (Class klass = type.getDeclaredType(); klass != null && klass != Object.class; klass = klass.getSuperclass()) {
+ for (Method m : klass.getDeclaredMethods()) {
+ if (m.isAnnotationPresent(annotationClass)) {
+ return m;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ public abstract class AbstractReflectionConverter {
+ protected final Method method;
+ protected final Class nativeType;
+
+ public AbstractReflectionConverter(Method method, Class nativeType) {
+ this.method = method;
+ this.nativeType = nativeType;
+ }
+
+ protected final Object invoke(Object value, Object context) {
+ try {
+ return method.invoke(method.getDeclaringClass(), value, context);
+
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ } catch (InvocationTargetException ite) {
+ throw new RuntimeException(ite);
+ }
+ }
+
+ public final Class<Object> nativeType() {
+ return nativeType;
+ }
+ }
+
+ @FromNativeConverter.Cacheable
+ public final class ReflectionFromNativeConverter extends AbstractReflectionConverter implements FromNativeConverter<Object, Object> {
+ public ReflectionFromNativeConverter(Method method, Class nativeType) {
+ super(method, nativeType);
+ }
+
+ @Override
+ public Object fromNative(Object nativeValue, FromNativeContext context) {
+ return invoke(nativeValue, context);
+ }
+ }
+
+ @ToNativeConverter.Cacheable
+ public final class ReflectionToNativeConverter extends AbstractReflectionConverter implements ToNativeConverter<Object, Object> {
+ public ReflectionToNativeConverter(Method method, Class nativeType) {
+ super(method, nativeType);
+ }
+
+ @Override
+ public Object toNative(Object nativeValue, ToNativeContext context) {
+ return invoke(nativeValue, context);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ArrayMemoryIO.java b/src/main/java/jnr/ffi/provider/jffi/ArrayMemoryIO.java
new file mode 100644
index 0000000..6936674
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ArrayMemoryIO.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.provider.AbstractArrayMemoryIO;
+
+
+public final class ArrayMemoryIO extends AbstractArrayMemoryIO {
+
+ public ArrayMemoryIO(Runtime runtime, int size) {
+ super(runtime, size);
+ }
+
+ public ArrayMemoryIO(Runtime runtime, byte[] bytes, int off, int len) {
+ super(runtime, bytes, off, len);
+ }
+
+ public Pointer getPointer(long offset) {
+ return MemoryUtil.newPointer(getRuntime(), getAddress(offset));
+ }
+
+ @Override
+ public Pointer getPointer(long offset, long size) {
+ return MemoryUtil.newPointer(getRuntime(), getAddress(offset), size);
+ }
+
+ @Override
+ public void putPointer(long offset, Pointer value) {
+ putAddress(offset, value != null ? value.address() : 0L);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmBuilder.java b/src/main/java/jnr/ffi/provider/jffi/AsmBuilder.java
new file mode 100644
index 0000000..61075c0
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmBuilder.java
@@ -0,0 +1,206 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Function;
+import com.kenai.jffi.ObjectParameterInfo;
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import org.objectweb.asm.ClassVisitor;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+import static jnr.ffi.provider.jffi.AsmUtil.boxedType;
+import static jnr.ffi.provider.jffi.AsmUtil.unboxNumber;
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+
+/**
+ *
+ */
+class AsmBuilder {
+ private final jnr.ffi.Runtime runtime;
+ private final String classNamePath;
+ private final ClassVisitor classVisitor;
+ private final AsmClassLoader classLoader;
+
+ private final ObjectNameGenerator functionId = new ObjectNameGenerator("functionAddress");
+ private final ObjectNameGenerator contextId = new ObjectNameGenerator("callContext");
+ private final ObjectNameGenerator toNativeConverterId = new ObjectNameGenerator("toNativeConverter");
+ private final ObjectNameGenerator toNativeContextId = new ObjectNameGenerator("toNativeContext");
+ private final ObjectNameGenerator fromNativeConverterId = new ObjectNameGenerator("fromNativeConverter");
+ private final ObjectNameGenerator fromNativeContextId = new ObjectNameGenerator("fromNativeContext");
+ private final ObjectNameGenerator objectParameterInfoId = new ObjectNameGenerator("objectParameterInfo");
+ private final ObjectNameGenerator variableAccessorId = new ObjectNameGenerator("variableAccessor");
+ private final ObjectNameGenerator genericObjectId = new ObjectNameGenerator("objectField");
+
+ private final Map<ToNativeConverter, ObjectField> toNativeConverters = new IdentityHashMap<ToNativeConverter, ObjectField>();
+ private final Map<ToNativeContext, ObjectField> toNativeContexts = new IdentityHashMap<ToNativeContext, ObjectField>();
+ private final Map<FromNativeConverter, ObjectField> fromNativeConverters = new IdentityHashMap<FromNativeConverter, ObjectField>();
+ private final Map<FromNativeContext, ObjectField> fromNativeContexts = new IdentityHashMap<FromNativeContext, ObjectField>();
+ private final Map<ObjectParameterInfo, ObjectField> objectParameterInfo = new HashMap<ObjectParameterInfo, ObjectField>();
+ private final Map<Variable, ObjectField> variableAccessors = new HashMap<Variable, ObjectField>();
+ private final Map<CallContext, ObjectField> callContextMap = new HashMap<CallContext, ObjectField>();
+ private final Map<Long, ObjectField> functionAddresses = new HashMap<Long, ObjectField>();
+ private final Map<Object, ObjectField> genericObjects = new IdentityHashMap<Object, ObjectField>();
+ private final List<ObjectField> objectFields = new ArrayList<ObjectField>();
+
+ AsmBuilder(jnr.ffi.Runtime runtime, String classNamePath, ClassVisitor classVisitor, AsmClassLoader classLoader) {
+ this.runtime = runtime;
+ this.classNamePath = classNamePath;
+ this.classVisitor = classVisitor;
+ this.classLoader = classLoader;
+ }
+
+ public String getClassNamePath() {
+ return classNamePath;
+ }
+
+ ClassVisitor getClassVisitor() {
+ return classVisitor;
+ }
+
+ public AsmClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ public jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+
+ private static final class ObjectNameGenerator {
+ private final String baseName;
+ private int value;
+ ObjectNameGenerator(String baseName) {
+ this.baseName = baseName;
+ this.value = 0;
+ }
+
+ String generateName() {
+ return baseName + "_" + ++value;
+ }
+ }
+
+ <T> ObjectField addField(Map<T, ObjectField> map, T value, Class klass, ObjectNameGenerator objectNameGenerator) {
+ ObjectField field = new ObjectField(objectNameGenerator.generateName(), value, klass);
+ objectFields.add(field);
+ map.put(value, field);
+ return field;
+ }
+
+ <T> ObjectField getField(Map<T, ObjectField> map, T value, Class klass, ObjectNameGenerator objectNameGenerator) {
+ ObjectField field = map.get(value);
+ return field != null ? field : addField(map, value, klass, objectNameGenerator);
+ }
+
+ String getCallContextFieldName(Function function) {
+ return getField(callContextMap, function.getCallContext(), CallContext.class, contextId).name;
+ }
+
+ String getCallContextFieldName(CallContext callContext) {
+ return getField(callContextMap, callContext, CallContext.class, contextId).name;
+ }
+
+ String getFunctionAddressFieldName(Function function) {
+ return getField(functionAddresses, function.getFunctionAddress(), long.class, functionId).name;
+ }
+
+ ObjectField getRuntimeField() {
+ return getObjectField(runtime, runtime.getClass());
+ }
+
+ String getFromNativeConverterName(FromNativeConverter converter) {
+ return getFromNativeConverterField(converter).name;
+ }
+
+ String getToNativeConverterName(ToNativeConverter converter) {
+ return getToNativeConverterField(converter).name;
+ }
+
+ private static Class nearestClass(Object obj, Class defaultClass) {
+ return Modifier.isPublic(obj.getClass().getModifiers()) ? obj.getClass() : defaultClass;
+ }
+
+ ObjectField getToNativeConverterField(ToNativeConverter converter) {
+ return getField(toNativeConverters, converter, nearestClass(converter, ToNativeConverter.class), toNativeConverterId);
+ }
+
+ ObjectField getFromNativeConverterField(FromNativeConverter converter) {
+ return getField(fromNativeConverters, converter, nearestClass(converter, FromNativeConverter.class), fromNativeConverterId);
+ }
+
+ ObjectField getToNativeContextField(ToNativeContext context) {
+ return getField(toNativeContexts, context, nearestClass(context, ToNativeContext.class), toNativeContextId);
+ }
+
+ ObjectField getFromNativeContextField(FromNativeContext context) {
+ return getField(fromNativeContexts, context, nearestClass(context, FromNativeContext.class), fromNativeContextId);
+ }
+
+
+ String getObjectParameterInfoName(ObjectParameterInfo info) {
+ return getField(objectParameterInfo, info, ObjectParameterInfo.class, objectParameterInfoId).name;
+ }
+
+ String getObjectFieldName(Object obj, Class klass) {
+ return getField(genericObjects, obj, klass, genericObjectId).name;
+ }
+
+ ObjectField getObjectField(Object obj, Class klass) {
+ return getField(genericObjects, obj, klass, genericObjectId);
+ }
+
+ String getVariableName(Variable variableAccessor) {
+ return getField(variableAccessors, variableAccessor, Variable.class, variableAccessorId).name;
+ }
+
+ public static final class ObjectField {
+ public final String name;
+ public final Object value;
+ public final Class klass;
+
+ public ObjectField(String fieldName, Object fieldValue, Class fieldClass) {
+ this.name = fieldName;
+ this.value = fieldValue;
+ this.klass = fieldClass;
+ }
+ }
+
+ ObjectField[] getObjectFieldArray() {
+ return objectFields.toArray(new ObjectField[objectFields.size()]);
+ }
+
+ Object[] getObjectFieldValues() {
+ Object[] fieldObjects = new Object[objectFields.size()];
+ int i = 0;
+ for (ObjectField f : objectFields) {
+ fieldObjects[i++] = f.value;
+ }
+ return fieldObjects;
+ }
+
+ void emitFieldInitialization(SkinnyMethodAdapter init, int objectsParameterIndex) {
+ int i = 0;
+ for (ObjectField f : objectFields) {
+ getClassVisitor().visitField(ACC_PRIVATE | ACC_FINAL, f.name, ci(f.klass), null, null);
+ init.aload(0);
+ init.aload(objectsParameterIndex);
+ init.pushInt(i++);
+ init.aaload();
+
+ if (f.klass.isPrimitive()) {
+ Class boxedType = boxedType(f.klass);
+ init.checkcast(boxedType);
+ unboxNumber(init, boxedType, f.klass);
+ } else {
+ init.checkcast(f.klass);
+ }
+ init.putfield(getClassNamePath(), f.name, ci(f.klass));
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmClassLoader.java b/src/main/java/jnr/ffi/provider/jffi/AsmClassLoader.java
new file mode 100644
index 0000000..5fcce38
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmClassLoader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static jnr.ffi.provider.jffi.CodegenUtils.p;
+
+final class AsmClassLoader extends ClassLoader {
+ private final ConcurrentMap<String, Class> definedClasses = new ConcurrentHashMap<String, Class>();
+
+ public AsmClassLoader() {
+ }
+
+ public AsmClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+
+ public Class defineClass(String name, byte[] b) {
+ Class klass = defineClass(name, b, 0, b.length);
+ definedClasses.putIfAbsent(name, klass);
+ resolveClass(klass);
+ return klass;
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ Class klass = definedClasses.get(name);
+ if (klass != null) {
+ return klass;
+ }
+ return super.findClass(name);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java
new file mode 100644
index 0000000..b39d42a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.*;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.*;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.InvokerUtil.*;
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+import static org.objectweb.asm.Opcodes.*;
+
+public class AsmLibraryLoader extends LibraryLoader {
+ public final static boolean DEBUG = Boolean.getBoolean("jnr.ffi.compile.dump");
+ private static final AtomicLong nextClassID = new AtomicLong(0);
+ private static final AtomicLong uniqueId = new AtomicLong(0);
+ private static final ThreadLocal<AsmClassLoader> classLoader = new ThreadLocal<AsmClassLoader>();
+
+ private final NativeRuntime runtime = NativeRuntime.getInstance();
+
+ @Override
+ <T> T loadLibrary(NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions) {
+ AsmClassLoader oldClassLoader = classLoader.get();
+
+ // Only create a new class loader if this was not a recursive call (i.e. loading a library as a result of loading another library)
+ if (oldClassLoader == null) {
+ classLoader.set(new AsmClassLoader(interfaceClass.getClassLoader()));
+ }
+ try {
+ return generateInterfaceImpl(library, interfaceClass, libraryOptions, classLoader.get());
+ } finally {
+ if (oldClassLoader == null) classLoader.remove();
+ }
+ }
+
+ private <T> T generateInterfaceImpl(final NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions,
+ AsmClassLoader classLoader) {
+
+ boolean debug = DEBUG && !interfaceClass.isAnnotationPresent(NoTrace.class);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ ClassVisitor cv = debug ? AsmUtil.newCheckClassAdapter(cw) : cw;
+
+ AsmBuilder builder = new AsmBuilder(runtime, p(interfaceClass) + "$jnr$ffi$" + nextClassID.getAndIncrement(), cv, classLoader);
+
+ cv.visit(V1_6, ACC_PUBLIC | ACC_FINAL, builder.getClassNamePath(), null, p(AbstractAsmLibraryInterface.class),
+ new String[] { p(interfaceClass) });
+
+ FunctionMapper functionMapper = libraryOptions.containsKey(LibraryOption.FunctionMapper)
+ ? (FunctionMapper) libraryOptions.get(LibraryOption.FunctionMapper) : IdentityFunctionMapper.getInstance();
+
+ SignatureTypeMapper typeMapper;
+ if (libraryOptions.containsKey(LibraryOption.TypeMapper)) {
+ Object tm = libraryOptions.get(LibraryOption.TypeMapper);
+ if (tm instanceof SignatureTypeMapper) {
+ typeMapper = (SignatureTypeMapper) tm;
+ } else if (tm instanceof TypeMapper) {
+ typeMapper = new SignatureTypeMapperAdapter((TypeMapper) tm);
+ } else {
+ throw new IllegalArgumentException("TypeMapper option is not a valid TypeMapper instance");
+ }
+ } else {
+ typeMapper = new NullTypeMapper();
+ }
+
+ typeMapper = new CompositeTypeMapper(typeMapper,
+ new CachingTypeMapper(new InvokerTypeMapper(new NativeClosureManager(runtime, typeMapper, classLoader), classLoader, NativeLibraryLoader.ASM_ENABLED)),
+ new CachingTypeMapper(new AnnotationTypeMapper()));
+
+ CallingConvention libraryCallingConvention = getCallingConvention(interfaceClass, libraryOptions);
+
+ StubCompiler compiler = StubCompiler.newCompiler(runtime);
+
+ final MethodGenerator[] generators = {
+ !interfaceClass.isAnnotationPresent(NoX86.class)
+ ? new X86MethodGenerator(compiler) : new NotImplMethodGenerator(),
+ new FastIntMethodGenerator(),
+ new FastLongMethodGenerator(),
+ new FastNumericMethodGenerator(),
+ new BufferMethodGenerator()
+ };
+
+ InterfaceScanner scanner = new InterfaceScanner(interfaceClass, typeMapper, libraryCallingConvention);
+
+ for (NativeFunction function : scanner.functions()) {
+ String functionName = functionMapper.mapFunctionName(function.name(), new NativeFunctionMapperContext(library, function.annotations()));
+
+ try {
+ long functionAddress = library.findSymbolAddress(functionName);
+
+ FromNativeContext resultContext = new MethodResultContext(runtime, function.getMethod());
+ SignatureType signatureType = DefaultSignatureType.create(function.getMethod().getReturnType(), resultContext);
+ ResultType resultType = getResultType(runtime, function.getMethod().getReturnType(),
+ resultContext.getAnnotations(), typeMapper.getFromNativeType(signatureType, resultContext),
+ resultContext);
+
+ ParameterType[] parameterTypes = getParameterTypes(runtime, typeMapper, function.getMethod());
+
+ Function jffiFunction = new Function(functionAddress,
+ getCallContext(resultType, parameterTypes,function.convention(), function.isErrnoRequired()));
+
+ for (MethodGenerator g : generators) {
+ if (g.isSupported(resultType, parameterTypes, function.convention())) {
+ g.generate(builder, function.getMethod().getName(), jffiFunction, resultType, parameterTypes, !function.isErrnoRequired());
+ break;
+ }
+ }
+
+ } catch (SymbolNotFoundError ex) {
+ String errorFieldName = "error_" + uniqueId.incrementAndGet();
+ cv.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, errorFieldName, ci(String.class), null, ex.getMessage());
+ generateFunctionNotFound(cv, builder.getClassNamePath(), errorFieldName, functionName,
+ function.getMethod().getReturnType(), function.getMethod().getParameterTypes());
+ }
+ }
+
+ // generate global variable accessors
+ VariableAccessorGenerator variableAccessorGenerator = new VariableAccessorGenerator(runtime);
+ for (NativeVariable v : scanner.variables()) {
+ Method m = v.getMethod();
+ java.lang.reflect.Type variableType = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0];
+ if (!(variableType instanceof Class)) {
+ throw new IllegalArgumentException("unsupported variable class: " + variableType);
+ }
+ String functionName = functionMapper.mapFunctionName(m.getName(), null);
+ try {
+ variableAccessorGenerator.generate(builder, interfaceClass, m.getName(),
+ library.findSymbolAddress(functionName), (Class) variableType, sortedAnnotationCollection(m.getAnnotations()),
+ typeMapper, classLoader);
+
+ } catch (SymbolNotFoundError ex) {
+ String errorFieldName = "error_" + uniqueId.incrementAndGet();
+ cv.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, errorFieldName, ci(String.class), null, ex.getMessage());
+ generateFunctionNotFound(cv, builder.getClassNamePath(), errorFieldName, functionName, m.getReturnType(), m.getParameterTypes());
+ }
+ }
+
+ // Create the constructor to set the instance fields
+ SkinnyMethodAdapter init = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>",
+ sig(void.class, jnr.ffi.Runtime.class, NativeLibrary.class, Object[].class),
+ null, null);
+ init.start();
+ // Invoke the super class constructor as super(Library)
+ init.aload(0);
+ init.aload(1);
+ init.aload(2);
+ init.invokespecial(p(AbstractAsmLibraryInterface.class), "<init>", sig(void.class, jnr.ffi.Runtime.class, NativeLibrary.class));
+
+ builder.emitFieldInitialization(init, 3);
+
+ init.voidreturn();
+ init.visitMaxs(10, 10);
+ init.visitEnd();
+
+ cv.visitEnd();
+
+ try {
+ byte[] bytes = cw.toByteArray();
+ if (debug) {
+ ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
+ new ClassReader(bytes).accept(trace, 0);
+ }
+
+ Class<T> implClass = classLoader.defineClass(builder.getClassNamePath().replace("/", "."), bytes);
+ Constructor<T> cons = implClass.getDeclaredConstructor(jnr.ffi.Runtime.class, NativeLibrary.class, Object[].class);
+ T result = cons.newInstance(runtime, library, builder.getObjectFieldValues());
+
+ // Attach any native method stubs - we have to delay this until the
+ // implementation class is loaded for it to work.
+ System.err.flush();
+ System.out.flush();
+ compiler.attach(implClass);
+
+ return result;
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private void generateFunctionNotFound(ClassVisitor cv, String className, String errorFieldName, String functionName,
+ Class returnType, Class[] parameterTypes) {
+ SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cv, ACC_PUBLIC | ACC_FINAL, functionName,
+ sig(returnType, parameterTypes), null, null);
+ mv.start();
+ mv.getstatic(className, errorFieldName, ci(String.class));
+ mv.invokestatic(AsmRuntime.class, "newUnsatisifiedLinkError", UnsatisfiedLinkError.class, String.class);
+ mv.athrow();
+ mv.visitMaxs(10, 10);
+ mv.visitEnd();
+ }
+
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmRuntime.java b/src/main/java/jnr/ffi/provider/jffi/AsmRuntime.java
new file mode 100644
index 0000000..92c5d45
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmRuntime.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.*;
+import jnr.ffi.Address;
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.*;
+
+import java.nio.*;
+import java.nio.charset.Charset;
+
+/**
+ * Utility methods that are used at runtime by generated code.
+ */
+public final class AsmRuntime {
+ public static final com.kenai.jffi.MemoryIO IO = com.kenai.jffi.MemoryIO.getInstance();
+
+ private AsmRuntime() {}
+
+ public static UnsatisfiedLinkError newUnsatisifiedLinkError(String msg) {
+ return new UnsatisfiedLinkError(msg);
+ }
+
+ public static HeapInvocationBuffer newHeapInvocationBuffer(Function function) {
+ return new HeapInvocationBuffer(function);
+ }
+
+ public static HeapInvocationBuffer newHeapInvocationBuffer(CallContext callContext) {
+ return new HeapInvocationBuffer(callContext);
+ }
+
+ public static HeapInvocationBuffer newHeapInvocationBuffer(CallContext callContext, int objCount) {
+ return new HeapInvocationBuffer(callContext, objCount);
+ }
+
+ public static Pointer pointerValue(long ptr, jnr.ffi.Runtime runtime) {
+ return ptr != 0 ? new DirectMemoryIO(runtime, ptr) : null;
+ }
+
+ public static Pointer pointerValue(int ptr, jnr.ffi.Runtime runtime) {
+ return ptr != 0 ? new DirectMemoryIO(runtime, ptr) : null;
+ }
+
+ public static boolean isDirect(Pointer ptr) {
+ return ptr == null || ptr.isDirect();
+ }
+
+ public static int intValue(Pointer ptr) {
+ return ptr != null ? (int) ptr.address() : 0;
+ }
+
+ public static long longValue(Pointer ptr) {
+ return ptr != null ? ptr.address() : 0L;
+ }
+
+ public static long longValue(Address ptr) {
+ return ptr != null ? ptr.longValue() : 0L;
+ }
+
+ public static int intValue(Address ptr) {
+ return ptr != null ? ptr.intValue() : 0;
+ }
+
+ public static long longValue(Buffer ptr) {
+ return ptr != null && ptr.isDirect() ? MemoryIO.getInstance().getDirectBufferAddress(ptr) : 0L;
+ }
+
+ public static int intValue(Buffer ptr) {
+ return ptr != null && ptr.isDirect() ? (int) MemoryIO.getInstance().getDirectBufferAddress(ptr) : 0;
+ }
+
+ public static ParameterStrategy nullParameterStrategy() {
+ return NullObjectParameterStrategy.NULL;
+ }
+
+ public static PointerParameterStrategy directPointerParameterStrategy() {
+ return PointerParameterStrategy.DIRECT;
+ }
+
+ public static PointerParameterStrategy pointerParameterStrategy(Pointer pointer) {
+ if (pointer == null || pointer.isDirect()) {
+ return PointerParameterStrategy.DIRECT;
+
+ } else {
+ return otherPointerParameterStrategy(pointer);
+ }
+ }
+
+ private static PointerParameterStrategy otherPointerParameterStrategy(Pointer pointer) {
+ if (pointer.hasArray()) {
+ return PointerParameterStrategy.HEAP;
+
+ } else {
+ throw new RuntimeException("cannot convert " + pointer.getClass() + " to native");
+ }
+ }
+
+ public static BufferParameterStrategy bufferParameterStrategy(Buffer buffer, ObjectParameterType.ComponentType componentType) {
+ if (buffer == null || buffer.isDirect()) {
+ return BufferParameterStrategy.direct(componentType);
+
+ } else if (buffer.hasArray()) {
+ return BufferParameterStrategy.heap(componentType);
+
+ } else {
+ throw new IllegalArgumentException("cannot marshal non-direct, non-array Buffer");
+ }
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(Buffer buffer) {
+ if (buffer instanceof ByteBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.BYTE);
+
+ } else if (buffer instanceof ShortBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.SHORT);
+
+ } else if (buffer instanceof CharBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.CHAR);
+
+ } else if (buffer instanceof IntBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.INT);
+
+ } else if (buffer instanceof LongBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.LONG);
+
+ } else if (buffer instanceof FloatBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.FLOAT);
+
+ } else if (buffer instanceof DoubleBuffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.DOUBLE);
+
+ } else if (buffer == null) {
+ return BufferParameterStrategy.direct(ObjectParameterType.BYTE);
+
+ } else {
+ throw new IllegalArgumentException("unsupported java.nio.Buffer subclass: " + buffer.getClass());
+ }
+ }
+ public static BufferParameterStrategy pointerParameterStrategy(ByteBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.BYTE);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(ShortBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.SHORT);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(CharBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.CHAR);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(IntBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.INT);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(LongBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.LONG);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(FloatBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.FLOAT);
+ }
+
+ public static BufferParameterStrategy pointerParameterStrategy(DoubleBuffer buffer) {
+ return bufferParameterStrategy(buffer, ObjectParameterType.DOUBLE);
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(byte[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.BYTE : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(short[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.SHORT : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(char[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.CHAR : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(int[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.INT : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(long[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.LONG : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(float[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.FLOAT : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(double[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.DOUBLE : NullObjectParameterStrategy.NULL;
+ }
+
+ public static ParameterStrategy pointerParameterStrategy(boolean[] array) {
+ return array != null ? PrimitiveArrayParameterStrategy.BOOLEAN : NullObjectParameterStrategy.NULL;
+ }
+
+ public static void postInvoke(ToNativeConverter.PostInvocation postInvocation, Object j, Object n, ToNativeContext context) {
+ try {
+ postInvocation.postInvoke(j, n, context);
+ } catch (Throwable t) {}
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmStructByReferenceFromNativeConverter.java b/src/main/java/jnr/ffi/provider/jffi/AsmStructByReferenceFromNativeConverter.java
new file mode 100644
index 0000000..5213896
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmStructByReferenceFromNativeConverter.java
@@ -0,0 +1,153 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.Struct;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static org.objectweb.asm.Opcodes.*;
+
+ at FromNativeConverter.NoContext
+ at FromNativeConverter.Cacheable
+abstract public class AsmStructByReferenceFromNativeConverter implements FromNativeConverter<Struct, Pointer> {
+ private final jnr.ffi.Runtime runtime;
+ private final int flags;
+
+ protected AsmStructByReferenceFromNativeConverter(jnr.ffi.Runtime runtime, int flags) {
+ this.runtime = runtime;
+ this.flags = flags;
+ }
+
+ public final Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ // getRuntime() is called from generated code
+ protected final jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+
+ static final Map<Class<? extends Struct>, Class<? extends AsmStructByReferenceFromNativeConverter>> converterClasses
+ = new ConcurrentHashMap<Class<? extends Struct>, Class<? extends AsmStructByReferenceFromNativeConverter>>();
+ static AsmStructByReferenceFromNativeConverter newStructByReferenceConverter(jnr.ffi.Runtime runtime, Class<? extends Struct> structClass, int flags, AsmClassLoader classLoader) {
+ try {
+ return newStructByReferenceClass(structClass, classLoader).getConstructor(jnr.ffi.Runtime.class, int.class).newInstance(runtime, flags);
+
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(nsme);
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ } catch (InstantiationException ie) {
+ throw new RuntimeException(ie);
+ } catch (InvocationTargetException ite) {
+ throw new RuntimeException(ite);
+ }
+ }
+
+ private static final AtomicLong nextClassID = new AtomicLong(0);
+
+ static Class<? extends AsmStructByReferenceFromNativeConverter> newStructByReferenceClass(Class<? extends Struct> structClass, AsmClassLoader classLoader) {
+
+ try {
+ Constructor<? extends Struct> cons = structClass.asSubclass(Struct.class).getConstructor(jnr.ffi.Runtime.class);
+ if (!Modifier.isPublic(cons.getModifiers())) {
+ throw new RuntimeException(structClass.getName() + " constructor is not public");
+ }
+ } catch (NoSuchMethodException ex) {
+ throw new RuntimeException("struct subclass " + structClass.getName() + " has no constructor that takes a "
+ + jnr.ffi.Runtime.class.getName(), ex);
+ }
+
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ ClassVisitor cv = AsmLibraryLoader.DEBUG ? AsmUtil.newCheckClassAdapter(cw) : cw;
+
+ final String className = p(structClass) + "$$jnr$$StructByReferenceFromNativeConverter$$" + nextClassID.getAndIncrement();
+
+ cv.visit(V1_5, ACC_PUBLIC | ACC_FINAL, className, null, p(AsmStructByReferenceFromNativeConverter.class),
+ new String[0]);
+
+ cv.visitAnnotation(ci(FromNativeConverter.NoContext.class), true);
+
+ // Create the constructor to set the instance fields
+ SkinnyMethodAdapter init = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>", sig(void.class, jnr.ffi.Runtime.class, int.class), null, null);
+ init.start();
+ // Invoke the super class constructor as super(Library)
+ init.aload(0);
+ init.aload(1);
+ init.iload(2);
+ init.invokespecial(p(AsmStructByReferenceFromNativeConverter.class), "<init>", sig(void.class, jnr.ffi.Runtime.class, int.class));
+ init.voidreturn();
+ init.visitMaxs(10, 10);
+ init.visitEnd();
+
+ SkinnyMethodAdapter fromNative = new SkinnyMethodAdapter(cv, ACC_PUBLIC | ACC_FINAL, "fromNative",
+ sig(structClass, Pointer.class, FromNativeContext.class), null, null);
+
+ fromNative.start();
+ Label nullPointer = new Label();
+ fromNative.aload(1);
+ fromNative.ifnull(nullPointer);
+
+ // Create an instance of the struct subclass
+ fromNative.newobj(p(structClass));
+ fromNative.dup();
+ fromNative.aload(0);
+ fromNative.invokevirtual(p(AsmStructByReferenceFromNativeConverter.class), "getRuntime", sig(Runtime.class));
+ fromNative.invokespecial(structClass, "<init>", void.class, jnr.ffi.Runtime.class);
+
+ // associate the memory with the struct and return the struct
+ fromNative.dup();
+ fromNative.aload(1);
+ fromNative.invokevirtual(structClass, "useMemory", void.class, Pointer.class);
+ fromNative.areturn();
+
+ fromNative.label(nullPointer);
+ fromNative.aconst_null();
+ fromNative.areturn();
+
+ fromNative.visitAnnotation(ci(FromNativeConverter.NoContext.class), true);
+ fromNative.visitMaxs(10, 10);
+ fromNative.visitEnd();
+
+ fromNative = new SkinnyMethodAdapter(cv, ACC_PUBLIC | ACC_FINAL, "fromNative",
+ sig(Object.class, Object.class, FromNativeContext.class), null, null);
+ fromNative.start();
+ fromNative.aload(0);
+ fromNative.aload(1);
+ fromNative.checkcast(Pointer.class);
+ fromNative.aload(2);
+ fromNative.invokevirtual(className, "fromNative", sig(structClass, Pointer.class, FromNativeContext.class));
+ fromNative.areturn();
+ fromNative.visitAnnotation(ci(FromNativeConverter.NoContext.class), true);
+ fromNative.visitMaxs(10, 10);
+ fromNative.visitEnd();
+ cv.visitEnd();
+
+ try {
+ byte[] bytes = cw.toByteArray();
+ if (AsmLibraryLoader.DEBUG) {
+ ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
+ new ClassReader(bytes).accept(trace, 0);
+ }
+
+ return classLoader.defineClass(className.replace("/", "."), bytes);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java b/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java
new file mode 100644
index 0000000..8897390
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Platform;
+import jnr.ffi.Address;
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.provider.*;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.CodegenUtils.p;
+import static jnr.ffi.provider.jffi.NumberUtil.*;
+
+final class AsmUtil {
+ private AsmUtil() {}
+
+ public static MethodVisitor newTraceMethodVisitor(MethodVisitor mv) {
+ try {
+ Class<? extends MethodVisitor> tmvClass = Class.forName("org.objectweb.asm.util.TraceMethodVisitor").asSubclass(MethodVisitor.class);
+ Constructor<? extends MethodVisitor> c = tmvClass.getDeclaredConstructor(MethodVisitor.class);
+ return c.newInstance(mv);
+ } catch (Throwable t) {
+ return mv;
+ }
+ }
+
+ public static ClassVisitor newTraceClassVisitor(ClassVisitor cv, OutputStream out) {
+ return newTraceClassVisitor(cv, new PrintWriter(out, true));
+ }
+
+ public static ClassVisitor newTraceClassVisitor(ClassVisitor cv, PrintWriter out) {
+ try {
+
+ Class<? extends ClassVisitor> tmvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor").asSubclass(ClassVisitor.class);
+ Constructor<? extends ClassVisitor> c = tmvClass.getDeclaredConstructor(ClassVisitor.class, PrintWriter.class);
+ return c.newInstance(cv, out);
+ } catch (Throwable t) {
+ return cv;
+ }
+ }
+
+ public static ClassVisitor newTraceClassVisitor(PrintWriter out) {
+ try {
+
+ Class<? extends ClassVisitor> tmvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor").asSubclass(ClassVisitor.class);
+ Constructor<? extends ClassVisitor> c = tmvClass.getDeclaredConstructor(PrintWriter.class);
+ return c.newInstance(out);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ public static ClassVisitor newCheckClassAdapter(ClassVisitor cv) {
+ try {
+ Class<? extends ClassVisitor> tmvClass = Class.forName("org.objectweb.asm.util.CheckClassAdapter").asSubclass(ClassVisitor.class);
+ Constructor<? extends ClassVisitor> c = tmvClass.getDeclaredConstructor(ClassVisitor.class);
+ return c.newInstance(cv);
+ } catch (Throwable t) {
+ return cv;
+ }
+ }
+
+ public static Class unboxedReturnType(Class type) {
+ return unboxedType(type);
+ }
+
+ public static Class unboxedType(Class boxedType) {
+ if (boxedType == Byte.class) {
+ return byte.class;
+
+ } else if (boxedType == Short.class) {
+ return short.class;
+
+ } else if (boxedType == Integer.class) {
+ return int.class;
+
+ } else if (boxedType == Long.class) {
+ return long.class;
+
+ } else if (boxedType == Float.class) {
+ return float.class;
+
+ } else if (boxedType == Double.class) {
+ return double.class;
+
+ } else if (boxedType == Boolean.class) {
+ return boolean.class;
+
+ } else if (Pointer.class.isAssignableFrom(boxedType)) {
+ return Platform.getPlatform().addressSize() == 32 ? int.class : long.class;
+
+ } else if (Address.class == boxedType) {
+ return Platform.getPlatform().addressSize() == 32 ? int.class : long.class;
+
+ } else {
+ return boxedType;
+ }
+ }
+
+ public static Class boxedType(Class type) {
+ if (type == byte.class) {
+ return Byte.class;
+ } else if (type == short.class) {
+ return Short.class;
+ } else if (type == int.class) {
+ return Integer.class;
+ } else if (type == long.class) {
+ return Long.class;
+ } else if (type == float.class) {
+ return Float.class;
+ } else if (type == double.class) {
+ return Double.class;
+ } else if (type == boolean.class) {
+ return Boolean.class;
+ } else {
+ return type;
+ }
+ }
+
+
+ static void emitReturnOp(SkinnyMethodAdapter mv, Class returnType) {
+ if (!returnType.isPrimitive()) {
+ mv.areturn();
+ } else if (long.class == returnType) {
+ mv.lreturn();
+ } else if (float.class == returnType) {
+ mv.freturn();
+ } else if (double.class == returnType) {
+ mv.dreturn();
+ } else if (void.class == returnType) {
+ mv.voidreturn();
+ } else {
+ mv.ireturn();
+ }
+ }
+
+ /**
+ * Calculates the size of a local variable
+ *
+ * @param type The type of parameter
+ * @return The size in parameter units
+ */
+ static int calculateLocalVariableSpace(Class type) {
+ return long.class == type || double.class == type ? 2 : 1;
+ }
+
+ /**
+ * Calculates the size of a local variable
+ *
+ * @param type The type of parameter
+ * @return The size in parameter units
+ */
+ static int calculateLocalVariableSpace(SigType type) {
+ return calculateLocalVariableSpace(type.getDeclaredType());
+ }
+
+ /**
+ * Calculates the size of a list of types in the local variable area.
+ *
+ * @param types The type of parameter
+ * @return The size in parameter units
+ */
+ static int calculateLocalVariableSpace(Class... types) {
+ int size = 0;
+
+ for (int i = 0; i < types.length; ++i) {
+ size += calculateLocalVariableSpace(types[i]);
+ }
+
+ return size;
+ }
+
+ /**
+ * Calculates the size of a list of types in the local variable area.
+ *
+ * @param types The type of parameter
+ * @return The size in parameter units
+ */
+ static int calculateLocalVariableSpace(SigType... types) {
+ int size = 0;
+
+ for (SigType type : types) {
+ size += calculateLocalVariableSpace(type);
+ }
+
+ return size;
+ }
+
+ private static void unboxPointerOrStruct(final SkinnyMethodAdapter mv, final Class type, final Class nativeType) {
+ mv.invokestatic(p(AsmRuntime.class), long.class == nativeType ? "longValue" : "intValue",
+ sig(nativeType, type));
+ }
+
+ static void unboxPointer(final SkinnyMethodAdapter mv, final Class nativeType) {
+ unboxPointerOrStruct(mv, Pointer.class, nativeType);
+ }
+
+ static void unboxBoolean(final SkinnyMethodAdapter mv, Class boxedType, final Class nativeType) {
+ mv.invokevirtual(p(boxedType), "booleanValue", "()Z");
+ widen(mv, boolean.class, nativeType);
+ }
+
+ static void unboxBoolean(final SkinnyMethodAdapter mv, final Class nativeType) {
+ unboxBoolean(mv, Boolean.class, nativeType);
+ }
+
+ static void unboxNumber(final SkinnyMethodAdapter mv, final Class boxedType, final Class unboxedType,
+ final jnr.ffi.NativeType nativeType) {
+
+ if (Number.class.isAssignableFrom(boxedType)) {
+
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ mv.invokevirtual(p(boxedType), "byteValue", "()B");
+ convertPrimitive(mv, byte.class, unboxedType, nativeType);
+ break;
+
+ case SSHORT:
+ case USHORT:
+ mv.invokevirtual(p(boxedType), "shortValue", "()S");
+ convertPrimitive(mv, short.class, unboxedType, nativeType);
+ break;
+
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ if (sizeof(nativeType) == 4) {
+ mv.invokevirtual(p(boxedType), "intValue", "()I");
+ convertPrimitive(mv, int.class, unboxedType, nativeType);
+ } else {
+ mv.invokevirtual(p(boxedType), "longValue", "()J");
+ convertPrimitive(mv, long.class, unboxedType, nativeType);
+ }
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ mv.invokevirtual(p(boxedType), "longValue", "()J");
+ narrow(mv, long.class, unboxedType);
+ break;
+
+ case FLOAT:
+ mv.invokevirtual(p(boxedType), "floatValue", "()F");
+ break;
+
+ case DOUBLE:
+ mv.invokevirtual(p(boxedType), "doubleValue", "()D");
+ break;
+ }
+
+
+ } else if (Boolean.class.isAssignableFrom(boxedType)) {
+ unboxBoolean(mv, unboxedType);
+
+ } else {
+ throw new IllegalArgumentException("unsupported boxed type: " + boxedType);
+ }
+ }
+
+
+ static void unboxNumber(final SkinnyMethodAdapter mv, final Class boxedType, final Class nativeType) {
+
+ if (Number.class.isAssignableFrom(boxedType)) {
+
+ if (byte.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "byteValue", "()B");
+
+ } else if (short.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "shortValue", "()S");
+
+ } else if (int.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "intValue", "()I");
+
+ } else if (long.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "longValue", "()J");
+
+ } else if (float.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "floatValue", "()F");
+
+ } else if (double.class == nativeType) {
+ mv.invokevirtual(p(boxedType), "doubleValue", "()D");
+
+ } else {
+ throw new IllegalArgumentException("unsupported Number subclass: " + boxedType);
+ }
+
+ } else if (Boolean.class.isAssignableFrom(boxedType)) {
+ unboxBoolean(mv, nativeType);
+
+ } else {
+ throw new IllegalArgumentException("unsupported boxed type: " + boxedType);
+ }
+ }
+
+ static void boxValue(AsmBuilder builder, SkinnyMethodAdapter mv, Class boxedType, Class unboxedType) {
+ if (boxedType == unboxedType || boxedType.isPrimitive()) {
+
+ } else if (Boolean.class.isAssignableFrom(boxedType)) {
+ narrow(mv, unboxedType, boolean.class);
+ mv.invokestatic(Boolean.class, "valueOf", Boolean.class, boolean.class);
+
+ } else if (Pointer.class.isAssignableFrom(boxedType)) {
+ getfield(mv, builder, builder.getRuntimeField());
+ mv.invokestatic(AsmRuntime.class, "pointerValue", Pointer.class, unboxedType, jnr.ffi.Runtime.class);
+
+ } else if (Address.class == boxedType) {
+ mv.invokestatic(boxedType, "valueOf", boxedType, unboxedType);
+
+ } else if (Number.class.isAssignableFrom(boxedType) && boxedType(unboxedType) == boxedType) {
+ mv.invokestatic(boxedType, "valueOf", boxedType, unboxedType);
+
+ } else {
+ throw new IllegalArgumentException("cannot box value of type " + unboxedType + " to " + boxedType);
+ }
+ }
+
+ static int getNativeArrayFlags(int flags) {
+ int nflags = 0;
+ nflags |= ParameterFlags.isIn(flags) ? com.kenai.jffi.ArrayFlags.IN : 0;
+ nflags |= ParameterFlags.isOut(flags) ? com.kenai.jffi.ArrayFlags.OUT : 0;
+ nflags |= (ParameterFlags.isNulTerminate(flags) || ParameterFlags.isIn(flags))
+ ? com.kenai.jffi.ArrayFlags.NULTERMINATE : 0;
+ return nflags;
+ }
+
+ static int getNativeArrayFlags(Collection<Annotation> annotations) {
+ return getNativeArrayFlags(ParameterFlags.parse(annotations));
+ }
+
+ static LocalVariable[] getParameterVariables(ParameterType[] parameterTypes) {
+ LocalVariable[] lvars = new LocalVariable[parameterTypes.length];
+ int lvar = 1;
+ for (int i = 0; i < parameterTypes.length; i++) {
+ lvars[i] = new LocalVariable(parameterTypes[i].getDeclaredType(), lvar);
+ lvar += calculateLocalVariableSpace(parameterTypes[i]);
+ }
+
+ return lvars;
+ }
+
+ static LocalVariable[] getParameterVariables(Class[] parameterTypes) {
+ LocalVariable[] lvars = new LocalVariable[parameterTypes.length];
+ int idx = 1;
+ for (int i = 0; i < parameterTypes.length; i++) {
+ lvars[i] = new LocalVariable(parameterTypes[i], idx);
+ idx += calculateLocalVariableSpace(parameterTypes[i]);
+ }
+
+ return lvars;
+ }
+
+ static void load(SkinnyMethodAdapter mv, Class parameterType, LocalVariable parameter) {
+ if (!parameterType.isPrimitive()) {
+ mv.aload(parameter);
+
+ } else if (long.class == parameterType) {
+ mv.lload(parameter);
+
+ } else if (float.class == parameterType) {
+ mv.fload(parameter);
+
+ } else if (double.class == parameterType) {
+ mv.dload(parameter);
+
+ } else {
+ mv.iload(parameter);
+ }
+
+ }
+
+
+ static void store(SkinnyMethodAdapter mv, Class type, LocalVariable var) {
+ if (!type.isPrimitive()) {
+ mv.astore(var);
+
+ } else if (long.class == type) {
+ mv.lstore(var);
+
+ } else if (double.class == type) {
+ mv.dstore(var);
+
+ } else if (float.class == type) {
+ mv.fstore(var);
+
+ } else {
+ mv.istore(var);
+ }
+ }
+
+ static void emitReturn(AsmBuilder builder, SkinnyMethodAdapter mv, Class returnType, Class nativeIntType) {
+ if (returnType.isPrimitive()) {
+
+ if (long.class == returnType) {
+ mv.lreturn();
+
+ } else if (float.class == returnType) {
+ mv.freturn();
+
+ } else if (double.class == returnType) {
+ mv.dreturn();
+
+ } else if (void.class == returnType) {
+ mv.voidreturn();
+
+ } else {
+ mv.ireturn();
+ }
+
+ } else {
+ boxValue(builder, mv, returnType, nativeIntType);
+ mv.areturn();
+ }
+ }
+
+ static void getfield(SkinnyMethodAdapter mv, AsmBuilder builder, AsmBuilder.ObjectField field) {
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), field.name, ci(field.klass));
+ }
+
+ static void tryfinally(SkinnyMethodAdapter mv, Runnable codeBlock, Runnable finallyBlock) {
+ Label before = new Label(), after = new Label(), ensure = new Label(), done = new Label();
+ mv.trycatch(before, after, ensure, null);
+ mv.label(before);
+ codeBlock.run();
+ mv.label(after);
+ if (finallyBlock != null) finallyBlock.run();
+ mv.go_to(done);
+ if (finallyBlock != null) {
+ mv.label(ensure);
+ finallyBlock.run();
+ mv.athrow();
+ }
+ mv.label(done);
+ }
+
+ static void emitToNativeConversion(AsmBuilder builder, SkinnyMethodAdapter mv, ToNativeType toNativeType) {
+ ToNativeConverter parameterConverter = toNativeType.getToNativeConverter();
+ if (parameterConverter != null) {
+ Method toNativeMethod = getToNativeMethod(toNativeType, builder.getClassLoader());
+
+ if (toNativeType.getDeclaredType().isPrimitive()) {
+ boxValue(builder, mv, getBoxedClass(toNativeType.getDeclaredType()), toNativeType.getDeclaredType());
+ }
+ if (!toNativeMethod.getParameterTypes()[0].isAssignableFrom(getBoxedClass(toNativeType.getDeclaredType()))) {
+ mv.checkcast(toNativeMethod.getParameterTypes()[0]);
+ }
+
+ mv.aload(0);
+ AsmBuilder.ObjectField toNativeConverterField = builder.getToNativeConverterField(parameterConverter);
+ mv.getfield(builder.getClassNamePath(), toNativeConverterField.name, ci(toNativeConverterField.klass));
+ if (!toNativeMethod.getDeclaringClass().equals(toNativeConverterField.klass)) {
+ mv.checkcast(toNativeMethod.getDeclaringClass());
+ }
+
+ // Re-order so the value to be converted is on the top of the stack
+ mv.swap();
+
+ // load context parameter (if there is one)
+ if (toNativeType.getToNativeContext() != null) {
+ getfield(mv, builder, builder.getToNativeContextField(toNativeType.getToNativeContext()));
+ } else {
+ mv.aconst_null();
+ }
+
+ if (toNativeMethod.getDeclaringClass().isInterface()) {
+ mv.invokeinterface(toNativeMethod.getDeclaringClass(), toNativeMethod.getName(),
+ toNativeMethod.getReturnType(), toNativeMethod.getParameterTypes());
+ } else {
+ mv.invokevirtual(toNativeMethod.getDeclaringClass(), toNativeMethod.getName(),
+ toNativeMethod.getReturnType(), toNativeMethod.getParameterTypes());
+ }
+ if (!parameterConverter.nativeType().isAssignableFrom(toNativeMethod.getReturnType())) {
+ mv.checkcast(p(parameterConverter.nativeType()));
+ }
+ }
+ }
+
+ static void emitFromNativeConversion(AsmBuilder builder, SkinnyMethodAdapter mv, FromNativeType fromNativeType, Class nativeClass) {
+ // If there is a result converter, retrieve it and put on the stack
+ FromNativeConverter fromNativeConverter = fromNativeType.getFromNativeConverter();
+ if (fromNativeConverter != null) {
+ convertPrimitive(mv, nativeClass, unboxedType(fromNativeConverter.nativeType()), fromNativeType.getNativeType());
+ boxValue(builder, mv, fromNativeConverter.nativeType(), nativeClass);
+
+ Method fromNativeMethod = getFromNativeMethod(fromNativeType, builder.getClassLoader());
+ getfield(mv, builder, builder.getFromNativeConverterField(fromNativeConverter));
+ mv.swap();
+ if (fromNativeType.getFromNativeContext() != null) {
+ getfield(mv, builder, builder.getFromNativeContextField(fromNativeType.getFromNativeContext()));
+ } else {
+ mv.aconst_null();
+ }
+
+ if (fromNativeMethod.getDeclaringClass().isInterface()) {
+ mv.invokeinterface(fromNativeMethod.getDeclaringClass(), fromNativeMethod.getName(),
+ fromNativeMethod.getReturnType(), fromNativeMethod.getParameterTypes());
+ } else {
+ mv.invokevirtual(fromNativeMethod.getDeclaringClass(), fromNativeMethod.getName(),
+ fromNativeMethod.getReturnType(), fromNativeMethod.getParameterTypes());
+ }
+
+ if (fromNativeType.getDeclaredType().isPrimitive()) {
+ // The actual return type is a primitive, but there was a converter for it - extract the primitive value
+ Class boxedType = getBoxedClass(fromNativeType.getDeclaredType());
+ if (!boxedType.isAssignableFrom(fromNativeMethod.getReturnType())) mv.checkcast(p(boxedType));
+ unboxNumber(mv, boxedType, fromNativeType.getDeclaredType(), fromNativeType.getNativeType());
+
+ } else if (!fromNativeType.getDeclaredType().isAssignableFrom(fromNativeMethod.getReturnType())) {
+ mv.checkcast(p(fromNativeType.getDeclaredType()));
+ }
+
+ } else if (!fromNativeType.getDeclaredType().isPrimitive()) {
+ Class unboxedType = unboxedType(fromNativeType.getDeclaredType());
+ convertPrimitive(mv, nativeClass, unboxedType, fromNativeType.getNativeType());
+ boxValue(builder, mv, fromNativeType.getDeclaredType(), unboxedType);
+
+ }
+ }
+
+ static Method getToNativeMethod(ToNativeType toNativeType, AsmClassLoader classLoader) {
+ ToNativeConverter toNativeConverter = toNativeType.getToNativeConverter();
+ if (toNativeConverter == null) {
+ return null;
+ }
+
+ try {
+ Class<? extends ToNativeConverter> toNativeConverterClass = toNativeConverter.getClass();
+ if (Modifier.isPublic(toNativeConverterClass.getModifiers())) {
+ for (Method method : toNativeConverterClass.getMethods()) {
+ if (!method.getName().equals("toNative")) continue;
+ Class[] methodParameterTypes = method.getParameterTypes();
+ if (toNativeConverter.nativeType().isAssignableFrom(method.getReturnType())
+ && methodParameterTypes.length == 2
+ && methodParameterTypes[0].isAssignableFrom(toNativeType.getDeclaredType())
+ && methodParameterTypes[1] == ToNativeContext.class
+ && methodIsAccessible(method)
+ && classIsVisible(classLoader, method.getDeclaringClass())) {
+ return method;
+ }
+ }
+ }
+ Method method = toNativeConverterClass.getMethod("toNative", Object.class, ToNativeContext.class);
+ return methodIsAccessible(method) && classIsVisible(classLoader, method.getDeclaringClass())
+ ? method : ToNativeConverter.class.getDeclaredMethod("toNative", Object.class, ToNativeContext.class);
+
+ } catch (NoSuchMethodException nsme) {
+ try {
+ return ToNativeConverter.class.getDeclaredMethod("toNative", Object.class, ToNativeContext.class);
+ } catch (NoSuchMethodException nsme2) {
+ throw new RuntimeException("internal error. " + ToNativeConverter.class + " has no toNative() method");
+ }
+ }
+ }
+
+
+ static Method getFromNativeMethod(FromNativeType fromNativeType, AsmClassLoader classLoader) {
+ FromNativeConverter fromNativeConverter = fromNativeType.getFromNativeConverter();
+ if (fromNativeConverter == null) {
+ return null;
+ }
+
+ try {
+ Class<? extends FromNativeConverter> fromNativeConverterClass = fromNativeConverter.getClass();
+ if (Modifier.isPublic(fromNativeConverterClass.getModifiers())) {
+ for (Method method : fromNativeConverterClass.getMethods()) {
+ if (!method.getName().equals("fromNative")) continue;
+ Class[] methodParameterTypes = method.getParameterTypes();
+ Class javaType = fromNativeType.getDeclaredType().isPrimitive()
+ ? boxedType(fromNativeType.getDeclaredType())
+ : fromNativeType.getDeclaredType();
+ if (javaType.isAssignableFrom(method.getReturnType())
+ && methodParameterTypes.length == 2
+ && methodParameterTypes[0].isAssignableFrom(fromNativeConverter.nativeType())
+ && methodParameterTypes[1] == FromNativeContext.class
+ && methodIsAccessible(method)
+ && classIsVisible(classLoader, method.getDeclaringClass())) {
+ return method;
+ }
+ }
+ }
+ Method method = fromNativeConverterClass.getMethod("fromNative", Object.class, FromNativeContext.class);
+ return methodIsAccessible(method) && classIsVisible(classLoader, method.getDeclaringClass())
+ ? method : FromNativeConverter.class.getDeclaredMethod("fromNative", Object.class, FromNativeContext.class);
+
+ } catch (NoSuchMethodException nsme) {
+ try {
+ return FromNativeConverter.class.getDeclaredMethod("fromNative", Object.class, FromNativeContext.class);
+ } catch (NoSuchMethodException nsme2) {
+ throw new RuntimeException("internal error. " + FromNativeConverter.class + " has no fromNative() method");
+ }
+ }
+ }
+
+ static boolean methodIsAccessible(Method method) {
+ return Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers());
+ }
+
+ private static boolean classIsVisible(ClassLoader classLoader, Class klass) {
+ try {
+ return classLoader.loadClass(klass.getName()) == klass;
+
+ } catch (ClassNotFoundException cnfe) {
+ return false;
+ }
+ }
+
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/BaseMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/BaseMethodGenerator.java
new file mode 100644
index 0000000..1cfebc1
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/BaseMethodGenerator.java
@@ -0,0 +1,127 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Function;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.*;
+
+import static jnr.ffi.provider.jffi.AsmUtil.*;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
+/**
+ *
+ */
+abstract class BaseMethodGenerator implements MethodGenerator {
+
+ public void generate(AsmBuilder builder, String functionName, Function function,
+ ResultType resultType, ParameterType[] parameterTypes, boolean ignoreError) {
+ Class[] javaParameterTypes = new Class[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ javaParameterTypes[i] = parameterTypes[i].getDeclaredType();
+ }
+
+ SkinnyMethodAdapter mv = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL,
+ functionName,
+ sig(resultType.getDeclaredType(), javaParameterTypes), null, null);
+ mv.start();
+
+ // Retrieve the static 'ffi' Invoker instance
+ mv.getstatic(p(AbstractAsmLibraryInterface.class), "ffi", ci(com.kenai.jffi.Invoker.class));
+
+ // retrieve the call context and function address
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getCallContextFieldName(function.getCallContext()), ci(CallContext.class));
+
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getFunctionAddressFieldName(function), ci(long.class));
+
+ LocalVariableAllocator localVariableAllocator = new LocalVariableAllocator(parameterTypes);
+
+ generate(builder, mv, localVariableAllocator, function.getCallContext(), resultType, parameterTypes, ignoreError);
+
+ mv.visitMaxs(100, localVariableAllocator.getSpaceUsed());
+ mv.visitEnd();
+ }
+
+ abstract void generate(AsmBuilder builder, SkinnyMethodAdapter mv, LocalVariableAllocator localVariableAllocator, CallContext callContext, ResultType resultType, ParameterType[] parameterTypes,
+ boolean ignoreError);
+
+ static LocalVariable loadAndConvertParameter(AsmBuilder builder, SkinnyMethodAdapter mv,
+ LocalVariableAllocator localVariableAllocator,
+ LocalVariable parameter, jnr.ffi.provider.ToNativeType parameterType) {
+ AsmUtil.load(mv, parameterType.getDeclaredType(), parameter);
+ emitToNativeConversion(builder, mv, parameterType);
+
+ if (parameterType.getToNativeConverter() != null) {
+ LocalVariable converted = localVariableAllocator.allocate(parameterType.getToNativeConverter().nativeType());
+ mv.astore(converted);
+ mv.aload(converted);
+ return converted;
+ }
+
+ return parameter;
+ }
+
+ static boolean isPostInvokeRequired(ParameterType[] parameterTypes) {
+ for (ParameterType parameterType : parameterTypes) {
+ if (parameterType.getToNativeConverter() instanceof ToNativeConverter.PostInvocation) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static void emitEpilogue(final AsmBuilder builder, final SkinnyMethodAdapter mv, final ResultType resultType,
+ final ParameterType[] parameterTypes,
+ final LocalVariable[] parameters, final LocalVariable[] converted, final Runnable sessionCleanup) {
+ final Class unboxedResultType = unboxedReturnType(resultType.effectiveJavaType());
+ if (isPostInvokeRequired(parameterTypes) || sessionCleanup != null) {
+ tryfinally(mv, new Runnable() {
+ public void run() {
+ emitFromNativeConversion(builder, mv, resultType, unboxedResultType);
+ // ensure there is always at least one instruction inside the try {} block
+ mv.nop();
+ }
+ },
+ new Runnable() {
+ public void run() {
+ emitPostInvoke(builder, mv, parameterTypes, parameters, converted);
+ if (sessionCleanup != null) {
+ sessionCleanup.run();
+ }
+ }
+ }
+ );
+ } else {
+ emitFromNativeConversion(builder, mv, resultType, unboxedResultType);
+ }
+ emitReturnOp(mv, resultType.getDeclaredType());
+ }
+
+ static void emitPostInvoke(AsmBuilder builder, final SkinnyMethodAdapter mv, ParameterType[] parameterTypes,
+ LocalVariable[] parameters, LocalVariable[] converted) {
+ for (int i = 0; i < converted.length; ++i) {
+ if (converted[i] != null && parameterTypes[i].getToNativeConverter() instanceof ToNativeConverter.PostInvocation) {
+ mv.aload(0);
+ AsmBuilder.ObjectField toNativeConverterField = builder.getToNativeConverterField(parameterTypes[i].getToNativeConverter());
+ mv.getfield(builder.getClassNamePath(), toNativeConverterField.name, ci(toNativeConverterField.klass));
+ if (!ToNativeConverter.PostInvocation.class.isAssignableFrom(toNativeConverterField.klass)) {
+ mv.checkcast(ToNativeConverter.PostInvocation.class);
+ }
+ mv.aload(parameters[i]);
+ mv.aload(converted[i]);
+ if (parameterTypes[i].getToNativeContext() != null) {
+ getfield(mv, builder, builder.getToNativeContextField(parameterTypes[i].getToNativeContext()));
+ } else {
+ mv.aconst_null();
+ }
+
+ mv.invokestatic(AsmRuntime.class, "postInvoke", void.class,
+ ToNativeConverter.PostInvocation.class, Object.class, Object.class, ToNativeContext.class);
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/BufferMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/BufferMethodGenerator.java
new file mode 100644
index 0000000..6b079e3
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/BufferMethodGenerator.java
@@ -0,0 +1,197 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.HeapInvocationBuffer;
+import com.kenai.jffi.Invoker;
+import com.kenai.jffi.ObjectParameterStrategy;
+import jnr.ffi.NativeType;
+import jnr.ffi.provider.InvocationSession;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+
+import static jnr.ffi.provider.jffi.AbstractFastNumericMethodGenerator.emitParameterStrategyLookup;
+import static jnr.ffi.provider.jffi.AbstractFastNumericMethodGenerator.hasPointerParameterStrategy;
+import static jnr.ffi.provider.jffi.AsmUtil.unboxedReturnType;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.NumberUtil.convertPrimitive;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+
+/**
+ *
+ */
+final class BufferMethodGenerator extends BaseMethodGenerator {
+ private static abstract class Operation {
+ final String methodName;
+ final Class primitiveClass;
+
+ private Operation(String methodName, Class primitiveClass) {
+ this.methodName = methodName;
+ this.primitiveClass = primitiveClass;
+ }
+ }
+
+ private static final class MarshalOp extends Operation {
+ private MarshalOp(String methodName, Class primitiveClass) {
+ super("put" + methodName, primitiveClass);
+ }
+ }
+
+ private static final class InvokeOp extends Operation {
+ private InvokeOp(String methodName, Class primitiveClass) {
+ super("invoke" + methodName, primitiveClass);
+ }
+ }
+
+ static final Map<NativeType, MarshalOp> marshalOps;
+ static final Map<NativeType, InvokeOp> invokeOps;
+ static {
+ Map<NativeType, MarshalOp> mops = new EnumMap<NativeType, MarshalOp>(NativeType.class);
+ Map<NativeType, InvokeOp> iops = new EnumMap<NativeType, InvokeOp>(NativeType.class);
+ mops.put(NativeType.SCHAR, new MarshalOp("Byte", int.class));
+ mops.put(NativeType.UCHAR, new MarshalOp("Byte", int.class));
+ mops.put(NativeType.SSHORT, new MarshalOp("Short", int.class));
+ mops.put(NativeType.USHORT, new MarshalOp("Short", int.class));
+ mops.put(NativeType.SINT, new MarshalOp("Int", int.class));
+ mops.put(NativeType.UINT, new MarshalOp("Int", int.class));
+ mops.put(NativeType.SLONGLONG, new MarshalOp("Long", long.class));
+ mops.put(NativeType.ULONGLONG, new MarshalOp("Long", long.class));
+ mops.put(NativeType.FLOAT, new MarshalOp("Float", float.class));
+ mops.put(NativeType.DOUBLE, new MarshalOp("Double", double.class));
+ mops.put(NativeType.ADDRESS, new MarshalOp("Address", long.class));
+ if (sizeof(NativeType.SLONG) == 4) {
+ mops.put(NativeType.SLONG, new MarshalOp("Int", int.class));
+ mops.put(NativeType.ULONG, new MarshalOp("Int", int.class));
+ } else {
+ mops.put(NativeType.SLONG, new MarshalOp("Long", long.class));
+ mops.put(NativeType.ULONG, new MarshalOp("Long", long.class));
+ }
+
+ iops.put(NativeType.SCHAR, new InvokeOp("Int", int.class));
+ iops.put(NativeType.UCHAR, new InvokeOp("Int", int.class));
+ iops.put(NativeType.SSHORT, new InvokeOp("Int", int.class));
+ iops.put(NativeType.USHORT, new InvokeOp("Int", int.class));
+ iops.put(NativeType.SINT, new InvokeOp("Int", int.class));
+ iops.put(NativeType.UINT, new InvokeOp("Int", int.class));
+ iops.put(NativeType.VOID, new InvokeOp("Int", int.class));
+ iops.put(NativeType.SLONGLONG, new InvokeOp("Long", long.class));
+ iops.put(NativeType.ULONGLONG, new InvokeOp("Long", long.class));
+ iops.put(NativeType.FLOAT, new InvokeOp("Float", float.class));
+ iops.put(NativeType.DOUBLE, new InvokeOp("Double", double.class));
+ iops.put(NativeType.ADDRESS, new InvokeOp("Address", long.class));
+ if (sizeof(NativeType.SLONG) == 4) {
+ iops.put(NativeType.SLONG, new InvokeOp("Int", int.class));
+ iops.put(NativeType.ULONG, new InvokeOp("Int", int.class));
+ } else {
+ iops.put(NativeType.SLONG, new InvokeOp("Long", long.class));
+ iops.put(NativeType.ULONG, new InvokeOp("Long", long.class));
+ }
+ marshalOps = Collections.unmodifiableMap(mops);
+ invokeOps = Collections.unmodifiableMap(iops);
+ }
+
+ @Override
+ void generate(AsmBuilder builder, SkinnyMethodAdapter mv, LocalVariableAllocator localVariableAllocator, CallContext callContext, ResultType resultType, ParameterType[] parameterTypes, boolean ignoreError) {
+ generateBufferInvocation(builder, mv, localVariableAllocator, callContext, resultType, parameterTypes);
+ }
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ // Buffer invocation supports everything
+ return true;
+ }
+
+ private static void emitPrimitiveOp(final SkinnyMethodAdapter mv, ParameterType parameterType, ToNativeOp op) {
+ MarshalOp marshalOp = marshalOps.get(parameterType.getNativeType());
+ if (marshalOp == null) {
+ throw new IllegalArgumentException("unsupported parameter type " + parameterType);
+ }
+
+ op.emitPrimitive(mv, marshalOp.primitiveClass, parameterType.getNativeType());
+ mv.invokevirtual(HeapInvocationBuffer.class, marshalOp.methodName, void.class, marshalOp.primitiveClass);
+ }
+
+ static boolean isSessionRequired(ParameterType parameterType) {
+ return false;
+ }
+
+
+ static boolean isSessionRequired(ParameterType[] parameterTypes) {
+ for (ParameterType parameterType : parameterTypes) {
+ if (isSessionRequired(parameterType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void generateBufferInvocation(final AsmBuilder builder, final SkinnyMethodAdapter mv, LocalVariableAllocator localVariableAllocator, CallContext callContext, final ResultType resultType, final ParameterType[] parameterTypes) {
+ // [ stack contains: Invoker, Function ]
+ final boolean sessionRequired = isSessionRequired(parameterTypes);
+ final LocalVariable session = localVariableAllocator.allocate(InvocationSession.class);
+
+ if (sessionRequired) {
+ mv.newobj(p(InvocationSession.class));
+ mv.dup();
+ mv.invokespecial(InvocationSession.class, "<init>", void.class);
+ mv.astore(session);
+ }
+
+ // Create a new InvocationBuffer
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getCallContextFieldName(callContext), ci(CallContext.class));
+ mv.invokestatic(AsmRuntime.class, "newHeapInvocationBuffer", HeapInvocationBuffer.class, CallContext.class);
+ // [ stack contains: Invoker, Function, HeapInvocationBuffer ]
+
+ final LocalVariable[] parameters = AsmUtil.getParameterVariables(parameterTypes);
+ final LocalVariable[] converted = new LocalVariable[parameterTypes.length];
+ LocalVariable[] strategies = new LocalVariable[parameterTypes.length];
+
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ mv.dup(); // dup ref to HeapInvocationBuffer
+
+ if (isSessionRequired(parameterTypes[i])) {
+ mv.aload(session);
+ }
+ converted[i] = loadAndConvertParameter(builder, mv, localVariableAllocator, parameters[i], parameterTypes[i]);
+
+ final Class javaParameterType = parameterTypes[i].effectiveJavaType();
+ ToNativeOp op = ToNativeOp.get(parameterTypes[i]);
+ if (op != null && op.isPrimitive()) {
+ emitPrimitiveOp(mv, parameterTypes[i], op);
+
+ } else if (hasPointerParameterStrategy(javaParameterType)) {
+ emitParameterStrategyLookup(mv, javaParameterType);
+ mv.astore(strategies[i] = localVariableAllocator.allocate(PointerParameterStrategy.class));
+
+ mv.aload(converted[i]);
+ mv.aload(strategies[i]);
+ mv.pushInt(AsmUtil.getNativeArrayFlags(parameterTypes[i].annotations()));
+ mv.invokevirtual(HeapInvocationBuffer.class, "putObject", void.class, Object.class, ObjectParameterStrategy.class, int.class);
+
+ } else {
+ throw new IllegalArgumentException("unsupported parameter type " + parameterTypes[i]);
+ }
+ }
+
+ InvokeOp iop = invokeOps.get(resultType.getNativeType());
+ if (iop == null) {
+ throw new IllegalArgumentException("unsupported return type " + resultType.getDeclaredType());
+ }
+
+ mv.invokevirtual(Invoker.class, iop.methodName, iop.primitiveClass, CallContext.class, long.class, HeapInvocationBuffer.class);
+
+ // box and/or narrow/widen the return value if needed
+ convertPrimitive(mv, iop.primitiveClass, unboxedReturnType(resultType.effectiveJavaType()), resultType.getNativeType());
+ emitEpilogue(builder, mv, resultType, parameterTypes, parameters, converted, sessionRequired ? new Runnable() {
+ public void run() {
+ mv.aload(session);
+ mv.invokevirtual(p(InvocationSession.class), "finish", "()V");
+ }
+ } : null);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/BufferParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/BufferParameterStrategy.java
new file mode 100644
index 0000000..15f24dd
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/BufferParameterStrategy.java
@@ -0,0 +1,87 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.MemoryIO;
+import com.kenai.jffi.ObjectParameterType;
+
+import java.nio.Buffer;
+import java.util.EnumSet;
+
+/**
+ *
+ */
+public final class BufferParameterStrategy extends ParameterStrategy {
+ private final int shift;
+
+ private BufferParameterStrategy(StrategyType type, ObjectParameterType.ComponentType componentType) {
+ super(type, ObjectParameterType.create(ObjectParameterType.ObjectType.ARRAY, componentType));
+ this.shift = calculateShift(componentType);
+ }
+
+ public long address(Buffer buffer) {
+ return buffer != null && buffer.isDirect() ? MemoryIO.getInstance().getDirectBufferAddress(buffer) + (buffer.position() << shift) : 0L;
+ }
+
+ @Override
+ public long address(Object o) {
+ return address((Buffer) o);
+ }
+
+ @Override
+ public Object object(Object o) {
+ return ((Buffer) o).array();
+ }
+
+ @Override
+ public int offset(Object o) {
+ Buffer buffer = (Buffer) o;
+ return buffer.arrayOffset() + buffer.position();
+ }
+
+ @Override
+ public int length(Object o) {
+ return ((Buffer) o).remaining();
+ }
+
+ static int calculateShift(ObjectParameterType.ComponentType componentType) {
+ switch (componentType) {
+ case BYTE:
+ return 0;
+
+ case SHORT:
+ case CHAR:
+ return 1;
+
+ case INT:
+ case BOOLEAN:
+ case FLOAT:
+ return 2;
+
+ case LONG:
+ case DOUBLE:
+ return 3;
+ default:
+ throw new IllegalArgumentException("unsupported component type: " + componentType);
+ }
+ }
+
+
+ private static final BufferParameterStrategy[] DIRECT_BUFFER_PARAMETER_STRATEGIES;
+ private static final BufferParameterStrategy[] HEAP_BUFFER_PARAMETER_STRATEGIES;
+ static {
+ EnumSet<ObjectParameterType.ComponentType> componentTypes = EnumSet.allOf(ObjectParameterType.ComponentType.class);
+ DIRECT_BUFFER_PARAMETER_STRATEGIES = new BufferParameterStrategy[componentTypes.size()];
+ HEAP_BUFFER_PARAMETER_STRATEGIES = new BufferParameterStrategy[componentTypes.size()];
+ for (ObjectParameterType.ComponentType componentType : componentTypes) {
+ DIRECT_BUFFER_PARAMETER_STRATEGIES[componentType.ordinal()] = new BufferParameterStrategy(DIRECT, componentType);
+ HEAP_BUFFER_PARAMETER_STRATEGIES[componentType.ordinal()] = new BufferParameterStrategy(HEAP, componentType);
+ }
+ }
+
+ static BufferParameterStrategy direct(ObjectParameterType.ComponentType componentType) {
+ return DIRECT_BUFFER_PARAMETER_STRATEGIES[componentType.ordinal()];
+ }
+
+ static BufferParameterStrategy heap(ObjectParameterType.ComponentType componentType) {
+ return HEAP_BUFFER_PARAMETER_STRATEGIES[componentType.ordinal()];
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ByteBufferMemoryIO.java b/src/main/java/jnr/ffi/provider/jffi/ByteBufferMemoryIO.java
new file mode 100644
index 0000000..8feb790
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ByteBufferMemoryIO.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.MemoryIO;
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.AbstractBufferMemoryIO;
+
+import java.nio.ByteBuffer;
+
+public class ByteBufferMemoryIO extends AbstractBufferMemoryIO {
+
+ public ByteBufferMemoryIO(jnr.ffi.Runtime runtime, ByteBuffer buffer) {
+ super(runtime, buffer, address(buffer));
+ }
+
+ public Pointer getPointer(long offset) {
+ return MemoryUtil.newPointer(getRuntime(), getAddress(offset));
+ }
+
+ public Pointer getPointer(long offset, long size) {
+ return MemoryUtil.newPointer(getRuntime(), getAddress(offset), size);
+ }
+
+ public void putPointer(long offset, Pointer value) {
+ putAddress(offset, value != null ? value.address() : 0L);
+ }
+
+ private static long address(ByteBuffer buffer) {
+ if (buffer.isDirect()) {
+ long address = MemoryIO.getInstance().getDirectBufferAddress(buffer);
+ return address != 0L ? address + buffer.position() : 0L;
+ }
+
+ return 0L;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ClosureFromNativeConverter.java b/src/main/java/jnr/ffi/provider/jffi/ClosureFromNativeConverter.java
new file mode 100644
index 0000000..8eb2e99
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ClosureFromNativeConverter.java
@@ -0,0 +1,184 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.StdCall;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.InAccessibleMemoryIO;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.ClosureUtil.getDelegateMethod;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.InvokerUtil.*;
+import static org.objectweb.asm.Opcodes.*;
+
+/**
+ *
+ */
+abstract public class ClosureFromNativeConverter implements FromNativeConverter<Object, Pointer> {
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ public static FromNativeConverter<?, Pointer> getInstance(jnr.ffi.Runtime runtime, SignatureType type, AsmClassLoader classLoader, SignatureTypeMapper typeMapper) {
+ return newClosureConverter(runtime, classLoader, type.getDeclaredType(), typeMapper);
+ }
+
+ public static final class ProxyConverter extends ClosureFromNativeConverter {
+ private final jnr.ffi.Runtime runtime;
+ private final Constructor closureConstructor;
+ private final Object[] initFields;
+
+ public ProxyConverter(jnr.ffi.Runtime runtime, Constructor closureConstructor, Object[] initFields) {
+ this.runtime = runtime;
+ this.closureConstructor = closureConstructor;
+ this.initFields = initFields.clone();
+ }
+
+ @Override
+ public Object fromNative(Pointer nativeValue, FromNativeContext context) {
+ try {
+ return closureConstructor.newInstance(runtime, nativeValue.address(), initFields);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ public static abstract class AbstractClosurePointer extends InAccessibleMemoryIO {
+ public static final com.kenai.jffi.Invoker ffi = com.kenai.jffi.Invoker.getInstance();
+ protected final long functionAddress;
+
+ protected AbstractClosurePointer(jnr.ffi.Runtime runtime, long functionAddress) {
+ super(runtime, functionAddress, true);
+ this.functionAddress = functionAddress;
+ }
+
+ @Override
+ public final long size() {
+ return 0;
+ }
+ }
+
+
+ private static final AtomicLong nextClassID = new AtomicLong(0);
+ private static FromNativeConverter newClosureConverter(jnr.ffi.Runtime runtime, AsmClassLoader classLoader, Class closureClass,
+ SignatureTypeMapper typeMapper) {
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ ClassVisitor cv = AsmLibraryLoader.DEBUG ? AsmUtil.newCheckClassAdapter(cw) : cw;
+
+ final String className = p(closureClass) + "$jnr$fromNativeConverter$" + nextClassID.getAndIncrement();
+ AsmBuilder builder = new AsmBuilder(runtime, className, cv, classLoader);
+
+ cv.visit(V1_6, ACC_PUBLIC | ACC_FINAL, className, null, p(AbstractClosurePointer.class),
+ new String[] { p(closureClass) } );
+
+ cv.visitAnnotation(ci(FromNativeConverter.NoContext.class), true);
+
+ generateInvocation(runtime, builder, closureClass, typeMapper);
+
+ // Create the constructor to set the instance fields
+ SkinnyMethodAdapter init = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>",
+ sig(void.class, jnr.ffi.Runtime.class, long.class, Object[].class), null, null);
+ init.start();
+ // Invoke the super class constructor as super(functionAddress)
+ init.aload(0);
+ init.aload(1);
+ init.lload(2);
+ init.invokespecial(p(AbstractClosurePointer.class), "<init>", sig(void.class, jnr.ffi.Runtime.class, long.class));
+ builder.emitFieldInitialization(init, 4);
+ init.voidreturn();
+ init.visitMaxs(10, 10);
+ init.visitEnd();
+
+ Class implClass = loadClass(classLoader, className, cw);
+ try {
+ return new ProxyConverter(runtime, implClass.getConstructor(jnr.ffi.Runtime.class, long.class, Object[].class), builder.getObjectFieldValues());
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static Class loadClass(AsmClassLoader classLoader, String className, ClassWriter cw) {
+ try {
+ byte[] bytes = cw.toByteArray();
+ if (AsmLibraryLoader.DEBUG) {
+ ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
+ new ClassReader(bytes).accept(trace, 0);
+ }
+
+ return classLoader.defineClass(className.replace("/", "."), bytes);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static void generateInvocation(jnr.ffi.Runtime runtime, AsmBuilder builder, Class closureClass, SignatureTypeMapper typeMapper) {
+ Method closureMethod = getDelegateMethod(closureClass);
+
+ FromNativeContext resultContext = new MethodResultContext(runtime, closureMethod);
+ SignatureType signatureType = DefaultSignatureType.create(closureMethod.getReturnType(), resultContext);
+ jnr.ffi.mapper.FromNativeType fromNativeType = typeMapper.getFromNativeType(signatureType, resultContext);
+ FromNativeConverter fromNativeConverter = fromNativeType != null ? fromNativeType.getFromNativeConverter() : null;
+ ResultType resultType = getResultType(runtime, closureMethod.getReturnType(),
+ resultContext.getAnnotations(), fromNativeConverter, resultContext);
+
+ ParameterType[] parameterTypes = getParameterTypes(runtime, typeMapper, closureMethod);
+
+ // Allow individual methods to set the calling convention to stdcall
+ CallingConvention callingConvention = closureClass.isAnnotationPresent(StdCall.class)
+ ? CallingConvention.STDCALL : CallingConvention.DEFAULT;
+ CallContext callContext = getCallContext(resultType, parameterTypes, callingConvention, true);
+ LocalVariableAllocator localVariableAllocator = new LocalVariableAllocator(parameterTypes);
+
+
+ Class[] javaParameterTypes = new Class[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ javaParameterTypes[i] = parameterTypes[i].getDeclaredType();
+ }
+
+ SkinnyMethodAdapter mv = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL,
+ closureMethod.getName(),
+ sig(resultType.getDeclaredType(), javaParameterTypes), null, null);
+ mv.start();
+ // Retrieve the static 'ffi' Invoker instance
+ mv.getstatic(p(AbstractClosurePointer.class), "ffi", ci(com.kenai.jffi.Invoker.class));
+
+ // retrieve the call context and function address
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getCallContextFieldName(callContext), ci(CallContext.class));
+
+ mv.aload(0);
+ mv.getfield(p(AbstractClosurePointer.class), "functionAddress", ci(long.class));
+
+ final BaseMethodGenerator[] generators = {
+ new FastIntMethodGenerator(),
+ new FastLongMethodGenerator(),
+ new FastNumericMethodGenerator(),
+ new BufferMethodGenerator()
+ };
+
+ for (BaseMethodGenerator generator : generators) {
+ if (generator.isSupported(resultType, parameterTypes, callingConvention)) {
+ generator.generate(builder, mv, localVariableAllocator, callContext, resultType, parameterTypes, false);
+ }
+ }
+
+ mv.visitMaxs(100, 10 + localVariableAllocator.getSpaceUsed());
+ mv.visitEnd();
+ }
+
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ClosureTypeMapper.java b/src/main/java/jnr/ffi/provider/jffi/ClosureTypeMapper.java
new file mode 100644
index 0000000..03f91e6
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ClosureTypeMapper.java
@@ -0,0 +1,47 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Struct;
+import jnr.ffi.mapper.*;
+import jnr.ffi.mapper.FromNativeType;
+import jnr.ffi.mapper.ToNativeType;
+import jnr.ffi.provider.converters.EnumConverter;
+import jnr.ffi.provider.ParameterFlags;
+import jnr.ffi.provider.converters.StringResultConverter;
+import jnr.ffi.provider.converters.StructByReferenceToNativeConverter;
+
+final class ClosureTypeMapper implements SignatureTypeMapper {
+ private FromNativeConverter getFromNativeConverter(SignatureType type, FromNativeContext context) {
+ if (Enum.class.isAssignableFrom(type.getDeclaredType())) {
+ return EnumConverter.getInstance(type.getDeclaredType().asSubclass(Enum.class));
+
+ } else if (CharSequence.class.isAssignableFrom(type.getDeclaredType())) {
+ return StringResultConverter.getInstance(context);
+
+ } else {
+ return null;
+ }
+ }
+
+ private ToNativeConverter getToNativeConverter(SignatureType type, ToNativeContext context) {
+ if (Enum.class.isAssignableFrom(type.getDeclaredType())) {
+ return EnumConverter.getInstance(type.getDeclaredType().asSubclass(Enum.class));
+
+ } else if (Struct.class.isAssignableFrom(type.getDeclaredType())) {
+ return StructByReferenceToNativeConverter.getInstance(context);
+
+
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return FromNativeTypes.create(getFromNativeConverter(type, context));
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return ToNativeTypes.create(getToNativeConverter(type, context));
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ClosureUtil.java b/src/main/java/jnr/ffi/provider/jffi/ClosureUtil.java
new file mode 100644
index 0000000..8cb5c96
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ClosureUtil.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.mapper.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+ *
+ */
+final class ClosureUtil {
+ private ClosureUtil() {
+ }
+
+ static jnr.ffi.provider.ToNativeType getResultType(jnr.ffi.Runtime runtime, Method m, SignatureTypeMapper typeMapper) {
+ Collection<Annotation> annotations = sortedAnnotationCollection(m.getAnnotations());
+ ToNativeContext context = new SimpleNativeContext(runtime, annotations);
+ SignatureType signatureType = DefaultSignatureType.create(m.getReturnType(), context);
+ jnr.ffi.mapper.ToNativeType toNativeType = typeMapper.getToNativeType(signatureType, context);
+ ToNativeConverter converter = toNativeType != null ? toNativeType.getToNativeConverter() : null;
+ Class javaClass = converter != null ? converter.nativeType() : m.getReturnType();
+ NativeType nativeType = Types.getType(runtime, javaClass, annotations).getNativeType();
+ return new jnr.ffi.provider.ToNativeType(m.getReturnType(), nativeType, annotations, converter, null);
+ }
+
+ static jnr.ffi.provider.FromNativeType getParameterType(jnr.ffi.Runtime runtime, Method m, int idx, SignatureTypeMapper typeMapper) {
+ Collection<Annotation> annotations = sortedAnnotationCollection(m.getParameterAnnotations()[idx]);
+ Class declaredJavaClass = m.getParameterTypes()[idx];
+ FromNativeContext context = new SimpleNativeContext(runtime, annotations);
+ SignatureType signatureType = DefaultSignatureType.create(declaredJavaClass, context);
+ jnr.ffi.mapper.FromNativeType fromNativeType = typeMapper.getFromNativeType(signatureType, context);
+ FromNativeConverter converter = fromNativeType != null ? fromNativeType.getFromNativeConverter() : null;
+ Class javaClass = converter != null ? converter.nativeType() : declaredJavaClass;
+ NativeType nativeType = Types.getType(runtime, javaClass, annotations).getNativeType();
+ return new jnr.ffi.provider.FromNativeType(declaredJavaClass, nativeType, annotations, converter, null);
+ }
+
+
+ static Method getDelegateMethod(Class closureClass) {
+ Method callMethod = null;
+ for (Method m : closureClass.getMethods()) {
+ if (m.isAnnotationPresent(Delegate.class) && Modifier.isPublic(m.getModifiers())
+ && !Modifier.isStatic(m.getModifiers())) {
+ callMethod = m;
+ break;
+ }
+ }
+ if (callMethod == null) {
+ throw new NoSuchMethodError("no public non-static delegate method defined in " + closureClass.getName());
+ }
+
+ return callMethod;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/CodegenUtils.java b/src/main/java/jnr/ffi/provider/jffi/CodegenUtils.java
new file mode 100644
index 0000000..73bdb57
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/CodegenUtils.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class CodegenUtils {
+ /**
+ * Creates a dotted class name from a path/package name
+ */
+ public static String c(String p) {
+ return p.replace('/', '.');
+ }
+
+ /**
+ * Creates a class path name, from a Class.
+ */
+ public static String p(Class n) {
+ return n.getName().replace('.','/');
+ }
+
+ /**
+ * Creates a class path name, from a class name.
+ */
+ public static String p(String n) {
+ return n.replace('.', '/');
+ }
+
+ /**
+ * Creates a class identifier of form Labc/abc;, from a Class.
+ */
+ public static String ci(Class n) {
+ if (n.isArray()) {
+ n = n.getComponentType();
+ if (n.isPrimitive()) {
+ if (n == Byte.TYPE) {
+ return "[B";
+ } else if (n == Boolean.TYPE) {
+ return "[Z";
+ } else if (n == Short.TYPE) {
+ return "[S";
+ } else if (n == Character.TYPE) {
+ return "[C";
+ } else if (n == Integer.TYPE) {
+ return "[I";
+ } else if (n == Float.TYPE) {
+ return "[F";
+ } else if (n == Double.TYPE) {
+ return "[D";
+ } else if (n == Long.TYPE) {
+ return "[J";
+ } else {
+ throw new RuntimeException("Unrecognized type in compiler: " + n.getName());
+ }
+ } else {
+ return "[" + ci(n);
+ }
+ } else {
+ if (n.isPrimitive()) {
+ if (n == Byte.TYPE) {
+ return "B";
+ } else if (n == Boolean.TYPE) {
+ return "Z";
+ } else if (n == Short.TYPE) {
+ return "S";
+ } else if (n == Character.TYPE) {
+ return "C";
+ } else if (n == Integer.TYPE) {
+ return "I";
+ } else if (n == Float.TYPE) {
+ return "F";
+ } else if (n == Double.TYPE) {
+ return "D";
+ } else if (n == Long.TYPE) {
+ return "J";
+ } else if (n == Void.TYPE) {
+ return "V";
+ } else {
+ throw new RuntimeException("Unrecognized type in compiler: " + n.getName());
+ }
+ } else {
+ return "L" + p(n) + ";";
+ }
+ }
+ }
+
+ /**
+ * Creates a human-readable representation, from a Class.
+ */
+ public static String human(Class n) {
+ return n.getCanonicalName();
+ }
+
+ /**
+ * Create a method signature from the given param types and return values
+ */
+ public static String sig(Class retval, Class... params) {
+ return sigParams(params) + ci(retval);
+ }
+
+ public static String sig(Class retval, String descriptor, Class... params) {
+ return sigParams(descriptor, params) + ci(retval);
+ }
+
+ public static String sigParams(Class... params) {
+ StringBuilder signature = new StringBuilder("(");
+
+ for (int i = 0; i < params.length; i++) {
+ signature.append(ci(params[i]));
+ }
+
+ signature.append(")");
+
+ return signature.toString();
+ }
+
+ public static String sigParams(String descriptor, Class... params) {
+ StringBuilder signature = new StringBuilder("(");
+
+ signature.append(descriptor);
+
+ for (int i = 0; i < params.length; i++) {
+ signature.append(ci(params[i]));
+ }
+
+ signature.append(")");
+
+ return signature.toString();
+ }
+
+ public static String pretty(Class retval, Class... params) {
+ return prettyParams(params) + human(retval);
+ }
+
+ public static String prettyParams(Class... params) {
+ StringBuilder signature = new StringBuilder("(");
+
+ for (int i = 0; i < params.length; i++) {
+ signature.append(human(params[i]));
+ if (i < params.length - 1) signature.append(',');
+ }
+
+ signature.append(")");
+
+ return signature.toString();
+ }
+
+ public static Class[] params(Class... classes) {
+ return classes;
+ }
+
+ public static Class[] params(Class cls, int times) {
+ Class[] classes = new Class[times];
+ Arrays.fill(classes, cls);
+ return classes;
+ }
+
+ public static Class[] params(Class cls1, Class clsFill, int times) {
+ Class[] classes = new Class[times + 1];
+ Arrays.fill(classes, clsFill);
+ classes[0] = cls1;
+ return classes;
+ }
+
+ public static String getAnnotatedBindingClassName(String javaMethodName, String typeName, boolean isStatic, int required, int optional, boolean multi, boolean framed) {
+ String commonClassSuffix;
+ String marker = framed ? "$RUBYFRAMEDINVOKER$" : "$RUBYINVOKER$";
+ if (multi) {
+ commonClassSuffix = (isStatic ? "$s" : "$i" ) + "_method_multi" + marker + javaMethodName;
+ } else {
+ commonClassSuffix = (isStatic ? "$s" : "$i" ) + "_method_" + required + "_" + optional + marker + javaMethodName;
+ }
+ return typeName + commonClassSuffix;
+ }
+
+ public static void visitAnnotationFields(AnnotationVisitor visitor, Map<String, Object> fields) {
+ for (Map.Entry<String, Object> fieldEntry : fields.entrySet()) {
+ Object value = fieldEntry.getValue();
+ if (value.getClass().isArray()) {
+ Object[] values = (Object[]) value;
+ AnnotationVisitor arrayV = visitor.visitArray(fieldEntry.getKey());
+ for (int i = 0; i < values.length; i++) {
+ arrayV.visit(null, values[i]);
+ }
+ arrayV.visitEnd();
+ } else if (value.getClass().isEnum()) {
+ visitor.visitEnum(fieldEntry.getKey(), ci(value.getClass()), value.toString());
+ } else if (value instanceof Class) {
+ visitor.visit(fieldEntry.getKey(), Type.getType((Class)value));
+ } else {
+ visitor.visit(fieldEntry.getKey(), value);
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ConverterMetaData.java b/src/main/java/jnr/ffi/provider/jffi/ConverterMetaData.java
new file mode 100644
index 0000000..08d5bc9
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ConverterMetaData.java
@@ -0,0 +1,107 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.util.Annotations;
+
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ *
+ */
+class ConverterMetaData {
+ private static volatile Reference<Map<Class, ConverterMetaData>> cacheReference;
+ final Collection<Annotation> classAnnotations;
+ final Collection<Annotation> toNativeMethodAnnotations, fromNativeMethodAnnotations, nativeTypeMethodAnnotations;
+ final Collection<Annotation> toNativeAnnotations, fromNativeAnnotations;
+
+ ConverterMetaData(Class converterClass, Class nativeType) {
+ classAnnotations = Annotations.sortedAnnotationCollection(converterClass.getAnnotations());
+ nativeTypeMethodAnnotations = getConverterMethodAnnotations(converterClass, "nativeType");
+ fromNativeMethodAnnotations = getConverterMethodAnnotations(converterClass, "fromNative", nativeType, FromNativeContext.class);
+ toNativeMethodAnnotations = getConverterMethodAnnotations(converterClass, "toNative", nativeType, ToNativeContext.class);;
+ toNativeAnnotations = Annotations.mergeAnnotations(classAnnotations, toNativeMethodAnnotations, nativeTypeMethodAnnotations);
+ fromNativeAnnotations = Annotations.mergeAnnotations(classAnnotations, fromNativeMethodAnnotations, nativeTypeMethodAnnotations);
+ }
+
+
+ private static Collection<Annotation> getToNativeMethodAnnotations(Class converterClass, Class resultClass) {
+ try {
+ final Method baseMethod = converterClass.getMethod("toNative", Object.class, ToNativeContext.class);
+ for (Method m : converterClass.getMethods()) {
+ if (!m.getName().equals("toNative")) {
+ continue;
+ }
+ if (!resultClass.isAssignableFrom(m.getReturnType())) {
+ continue;
+ }
+
+ Class[] methodParameterTypes = m.getParameterTypes();
+ if (methodParameterTypes.length != 2 || !methodParameterTypes[1].isAssignableFrom(ToNativeContext.class)) {
+ continue;
+ }
+
+ return Annotations.mergeAnnotations(Annotations.sortedAnnotationCollection(m.getAnnotations()), Annotations.sortedAnnotationCollection(baseMethod.getAnnotations()));
+ }
+
+ return Annotations.EMPTY_ANNOTATIONS;
+ } catch (SecurityException se) {
+ return Annotations.EMPTY_ANNOTATIONS;
+ } catch (NoSuchMethodException ignored) {
+ return Annotations.EMPTY_ANNOTATIONS;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Collection<Annotation> getConverterMethodAnnotations(Class converterClass, String methodName, Class... parameterClasses) {
+ try {
+ return Annotations.sortedAnnotationCollection(converterClass.getMethod(methodName).getAnnotations());
+ } catch (NoSuchMethodException ignored) {
+ return Annotations.EMPTY_ANNOTATIONS;
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static ConverterMetaData getMetaData(Class converterClass, Class nativeType) {
+ Map<Class, ConverterMetaData> cache = cacheReference != null ? cacheReference.get() : null;
+ ConverterMetaData metaData;
+ if (cache != null && (metaData = cache.get(converterClass)) != null) {
+ return metaData;
+ }
+
+ return addMetaData(converterClass, nativeType);
+ }
+
+ private static synchronized ConverterMetaData addMetaData(Class converterClass, Class nativeType) {
+ Map<Class, ConverterMetaData> cache = cacheReference != null ? cacheReference.get() : null;
+ ConverterMetaData metaData;
+ if (cache != null && (metaData = cache.get(converterClass)) != null) {
+ return metaData;
+ }
+
+ Map<Class, ConverterMetaData> m = new HashMap<Class, ConverterMetaData>(cache != null ? cache : Collections.EMPTY_MAP);
+ m.put(converterClass, metaData = new ConverterMetaData(converterClass, nativeType));
+ cacheReference = new SoftReference<Map<Class, ConverterMetaData>>(cache = new IdentityHashMap<Class, ConverterMetaData>(m));
+
+ return metaData;
+ }
+
+ static Collection<Annotation> getAnnotations(ToNativeConverter toNativeConverter) {
+ return toNativeConverter != null
+ ? getMetaData(toNativeConverter.getClass(), toNativeConverter.nativeType()).toNativeAnnotations
+ : Annotations.EMPTY_ANNOTATIONS;
+ }
+
+ static Collection<Annotation> getAnnotations(FromNativeConverter fromNativeConverter) {
+ return fromNativeConverter != null
+ ? getMetaData(fromNativeConverter.getClass(), fromNativeConverter.nativeType()).fromNativeAnnotations
+ : Annotations.EMPTY_ANNOTATIONS;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java b/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java
new file mode 100644
index 0000000..a75dd87
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2008-2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import com.kenai.jffi.HeapInvocationBuffer;
+import com.kenai.jffi.ObjectParameterStrategy;
+import com.kenai.jffi.ObjectParameterType;
+import jnr.ffi.Address;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.*;
+
+import java.lang.annotation.Annotation;
+import java.nio.*;
+import java.util.Collection;
+
+import static jnr.ffi.provider.jffi.NumberUtil.getBoxedClass;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+
+final class DefaultInvokerFactory {
+
+ final jnr.ffi.provider.Invoker createInvoker(jnr.ffi.Runtime runtime, NativeLibrary nativeLibrary, Function function, ResultType resultType, ParameterType[] parameterTypes) {
+
+ Marshaller[] marshallers = new Marshaller[parameterTypes.length];
+ for (int i = 0; i < marshallers.length; ++i) {
+ marshallers[i] = getMarshaller(parameterTypes[i]);
+ }
+
+ FunctionInvoker invoker = getFunctionInvoker(resultType);
+ if (resultType.getFromNativeConverter() != null) {
+ invoker = new ConvertingInvoker(resultType.getFromNativeConverter(), resultType.getFromNativeContext(), invoker);
+ }
+
+ return new DefaultInvoker(runtime, nativeLibrary, function, invoker, marshallers);
+ }
+
+ private static FunctionInvoker getFunctionInvoker(ResultType resultType) {
+ Class returnType = resultType.effectiveJavaType();
+ if (Void.class.isAssignableFrom(returnType) || void.class == returnType) {
+ return VoidInvoker.INSTANCE;
+
+ } else if (Boolean.class.isAssignableFrom(returnType) || boolean.class == returnType) {
+ return BooleanInvoker.INSTANCE;
+
+ } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) {
+ return new ConvertingInvoker(getNumberResultConverter(resultType), null,
+ new ConvertingInvoker(getNumberDataConverter(resultType.getNativeType()), null, getNumberFunctionInvoker(resultType.getNativeType())));
+
+ } else if (Pointer.class.isAssignableFrom(returnType)) {
+ return PointerInvoker.INSTANCE;
+
+ } else {
+ throw new IllegalArgumentException("Unknown return type: " + returnType);
+ }
+ }
+
+ private static FunctionInvoker getNumberFunctionInvoker(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case ADDRESS:
+ return sizeof(nativeType) <= 4 ? IntInvoker.INSTANCE : LongInvoker.INSTANCE;
+
+ case FLOAT:
+ return Float32Invoker.INSTANCE;
+
+ case DOUBLE:
+ return Float64Invoker.INSTANCE;
+ }
+
+ throw new UnsupportedOperationException("unsupported numeric type: " + nativeType);
+ }
+
+ static Marshaller getMarshaller(ParameterType parameterType) {
+ Marshaller marshaller = getMarshaller(parameterType.effectiveJavaType(), parameterType.getNativeType(), parameterType.getAnnotations());
+ return parameterType.getToNativeConverter() != null
+ ? new ToNativeConverterMarshaller(parameterType.getToNativeConverter(), parameterType.getToNativeContext(), marshaller)
+ : marshaller;
+ }
+
+ static Marshaller getMarshaller(Class type, NativeType nativeType, Collection<Annotation> annotations) {
+ if (Number.class.isAssignableFrom(type) || (type.isPrimitive() && Number.class.isAssignableFrom(getBoxedClass(type)))) {
+ switch (nativeType) {
+ case SCHAR:
+ return new Int8Marshaller(Signed8Converter.INSTANCE);
+ case UCHAR:
+ return new Int8Marshaller(Unsigned8Converter.INSTANCE);
+
+ case SSHORT:
+ return new Int16Marshaller(Signed16Converter.INSTANCE);
+ case USHORT:
+ return new Int16Marshaller(Unsigned16Converter.INSTANCE);
+
+ case SINT:
+ return new Int32Marshaller(Signed32Converter.INSTANCE);
+ case UINT:
+ return new Int32Marshaller(Unsigned32Converter.INSTANCE);
+
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ return sizeof(nativeType) == 4 ? new Int32Marshaller(getNumberDataConverter(nativeType)): Int64Marshaller.INSTANCE;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return Int64Marshaller.INSTANCE;
+
+ case FLOAT:
+ return Float32Marshaller.INSTANCE;
+
+ case DOUBLE:
+ return Float64Marshaller.INSTANCE;
+ default:
+ throw new IllegalArgumentException("Unsupported parameter type: " + type);
+ }
+
+ } else if (Boolean.class.isAssignableFrom(type) || boolean.class == type) {
+ return BooleanMarshaller.INSTANCE;
+
+ } else if (Pointer.class.isAssignableFrom(type)) {
+ return new PointerMarshaller(annotations);
+
+ } else if (ByteBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.BYTE, annotations);
+
+ } else if (ShortBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.SHORT, annotations);
+
+ } else if (IntBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.INT, annotations);
+
+ } else if (LongBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.LONG, annotations);
+
+ } else if (FloatBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.FLOAT, annotations);
+
+ } else if (DoubleBuffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(ObjectParameterType.ComponentType.DOUBLE, annotations);
+
+ } else if (Buffer.class.isAssignableFrom(type)) {
+ return new BufferMarshaller(null, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == byte.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.BYTE, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == short.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.SHORT, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == int.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.INT, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == long.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.LONG, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == float.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.FLOAT, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == double.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.DOUBLE, annotations);
+
+ } else if (type.isArray() && type.getComponentType() == boolean.class) {
+ return new PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy.BOOLEAN, annotations);
+
+ } else {
+ throw new IllegalArgumentException("Unsupported parameter type: " + type);
+ }
+ }
+
+ static class DefaultInvoker implements jnr.ffi.provider.Invoker {
+ protected final jnr.ffi.Runtime runtime;
+ final Function function;
+ final FunctionInvoker functionInvoker;
+ final Marshaller[] marshallers;
+ final NativeLibrary nativeLibrary;
+
+ DefaultInvoker(jnr.ffi.Runtime runtime, NativeLibrary nativeLibrary, Function function, FunctionInvoker invoker, Marshaller[] marshallers) {
+ this.runtime = runtime;
+ this.nativeLibrary = nativeLibrary;
+ this.function = function;
+ this.functionInvoker = invoker;
+ this.marshallers = marshallers;
+ }
+
+ public final Object invoke(Object self, Object[] parameters) {
+ InvocationSession session = new InvocationSession();
+ HeapInvocationBuffer buffer = new HeapInvocationBuffer(function.getCallContext());
+ try {
+ if (parameters != null) for (int i = 0; i < parameters.length; ++i) {
+ marshallers[i].marshal(session, buffer, parameters[i]);
+ }
+
+ return functionInvoker.invoke(runtime, function, buffer);
+ } finally {
+ session.finish();
+ }
+ }
+ }
+
+ static interface Marshaller {
+ public abstract void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter);
+ }
+
+ static interface FunctionInvoker {
+ Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer);
+ }
+
+ static abstract class BaseInvoker implements FunctionInvoker {
+ static com.kenai.jffi.Invoker invoker = com.kenai.jffi.Invoker.getInstance();
+ }
+
+ static class ConvertingInvoker extends BaseInvoker {
+ private final FromNativeConverter fromNativeConverter;
+ private final FromNativeContext fromNativeContext;
+ private final FunctionInvoker nativeInvoker;
+
+ public ConvertingInvoker(FromNativeConverter converter, FromNativeContext context, FunctionInvoker nativeInvoker) {
+ this.fromNativeConverter = converter;
+ this.fromNativeContext = context;
+ this.nativeInvoker = nativeInvoker;
+ }
+
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return fromNativeConverter.fromNative(nativeInvoker.invoke(runtime, function, buffer), fromNativeContext);
+ }
+ }
+
+ static class VoidInvoker extends BaseInvoker {
+ static FunctionInvoker INSTANCE = new VoidInvoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ invoker.invokeInt(function, buffer);
+ return null;
+ }
+ }
+
+ static class BooleanInvoker extends BaseInvoker {
+ static FunctionInvoker INSTANCE = new BooleanInvoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return invoker.invokeInt(function, buffer) != 0;
+ }
+ }
+
+ static class IntInvoker extends BaseInvoker {
+ static final FunctionInvoker INSTANCE = new IntInvoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return invoker.invokeInt(function, buffer);
+ }
+ }
+
+ static class LongInvoker extends BaseInvoker {
+ static final FunctionInvoker INSTANCE = new LongInvoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return invoker.invokeLong(function, buffer);
+ }
+ }
+
+ static class Float32Invoker extends BaseInvoker {
+ static final FunctionInvoker INSTANCE = new Float32Invoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return invoker.invokeFloat(function, buffer);
+ }
+ }
+ static class Float64Invoker extends BaseInvoker {
+ static final FunctionInvoker INSTANCE = new Float64Invoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return invoker.invokeDouble(function, buffer);
+ }
+ }
+
+ static class PointerInvoker extends BaseInvoker {
+ static final FunctionInvoker INSTANCE = new PointerInvoker();
+ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
+ return MemoryUtil.newPointer(runtime, invoker.invokeAddress(function, buffer));
+ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+ static class BooleanMarshaller implements Marshaller {
+ static final Marshaller INSTANCE = new BooleanMarshaller();
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putInt(((Boolean) parameter).booleanValue() ? 1 : 0);
+ }
+ }
+
+ static class Int8Marshaller implements Marshaller {
+ private final ToNativeConverter<Number, Number> toNativeConverter;
+
+ Int8Marshaller(ToNativeConverter<Number, Number> toNativeConverter) {
+ this.toNativeConverter = toNativeConverter;
+ }
+
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putByte(toNativeConverter.toNative((Number) parameter, null).intValue());
+ }
+ }
+
+ static class Int16Marshaller implements Marshaller {
+ private final ToNativeConverter<Number, Number> toNativeConverter;
+
+ Int16Marshaller(ToNativeConverter<Number, Number> toNativeConverter) {
+ this.toNativeConverter = toNativeConverter;
+ }
+
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putShort(toNativeConverter.toNative((Number) parameter, null).intValue());
+ }
+ }
+
+ static class Int32Marshaller implements Marshaller {
+ private final ToNativeConverter<Number, Number> toNativeConverter;
+
+ Int32Marshaller(ToNativeConverter<Number, Number> toNativeConverter) {
+ this.toNativeConverter = toNativeConverter;
+ }
+
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putInt(toNativeConverter.toNative((Number) parameter, null).intValue());
+ }
+ }
+
+ static class Int64Marshaller implements Marshaller {
+ static final Marshaller INSTANCE = new Int64Marshaller();
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putLong(((Number) parameter).longValue());
+ }
+ }
+
+ static class Float32Marshaller implements Marshaller {
+ static final Marshaller INSTANCE = new Float32Marshaller();
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putFloat(((Number) parameter).floatValue());
+ }
+ }
+ static class Float64Marshaller implements Marshaller {
+ static final Marshaller INSTANCE = new Float64Marshaller();
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putDouble(((Number) parameter).doubleValue());
+ }
+ }
+ static class PointerMarshaller implements Marshaller {
+ private final int flags;
+
+ PointerMarshaller(Collection<Annotation> annotations) {
+ this.flags = AsmUtil.getNativeArrayFlags(annotations);
+ }
+
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putObject(parameter, AsmRuntime.pointerParameterStrategy((Pointer) parameter), flags);
+ }
+ }
+
+ static class PrimitiveArrayMarshaller implements Marshaller {
+ private final PrimitiveArrayParameterStrategy strategy;
+ private final int flags;
+
+ protected PrimitiveArrayMarshaller(PrimitiveArrayParameterStrategy strategy, Collection<Annotation> annotations) {
+ this.strategy = strategy;
+ this.flags = AsmUtil.getNativeArrayFlags(annotations);
+ }
+
+ public final void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ buffer.putObject(parameter, parameter != null ? strategy : NullObjectParameterStrategy.NULL, flags);
+ }
+ }
+
+ static class BufferMarshaller implements Marshaller {
+ private final ObjectParameterType.ComponentType componentType;
+ private final int flags;
+
+ BufferMarshaller(ObjectParameterType.ComponentType componentType, Collection<Annotation> annotations) {
+ this.componentType = componentType;
+ this.flags = AsmUtil.getNativeArrayFlags(annotations);
+ }
+
+ public final void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
+ ObjectParameterStrategy strategy = componentType != null
+ ? AsmRuntime.bufferParameterStrategy((Buffer) parameter, componentType)
+ : AsmRuntime.pointerParameterStrategy((Buffer) parameter);
+ buffer.putObject(parameter, strategy, flags);
+ }
+ }
+
+ static class ToNativeConverterMarshaller implements Marshaller {
+ private final ToNativeConverter converter;
+ private final ToNativeContext context;
+ private final Marshaller marshaller;
+ private final boolean isPostInvokeRequired;
+
+ public ToNativeConverterMarshaller(ToNativeConverter toNativeConverter, ToNativeContext toNativeContext, Marshaller marshaller) {
+ this.converter = toNativeConverter;
+ this.context = toNativeContext;
+ this.marshaller = marshaller;
+ this.isPostInvokeRequired = converter instanceof ToNativeConverter.PostInvocation;
+ }
+
+ @Override
+ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, final Object parameter) {
+ final Object nativeValue = converter.toNative(parameter, context);
+ marshaller.marshal(session, buffer, nativeValue);
+
+ if (isPostInvokeRequired) {
+ session.addPostInvoke(new InvocationSession.PostInvoke() {
+ @Override
+ public void postInvoke() {
+ ((ToNativeConverter.PostInvocation) converter).postInvoke(parameter, nativeValue, context);
+ }
+ });
+ } else {
+ // hold on to the native value until the entire call session is complete
+ session.keepAlive(nativeValue);
+ }
+ }
+
+ }
+
+ private static boolean isUnsigned(NativeType nativeType) {
+ switch (nativeType) {
+ case UCHAR:
+ case USHORT:
+ case UINT:
+ case ULONG:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ static DataConverter<Number, Number> getNumberDataConverter(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ return DefaultInvokerFactory.Signed8Converter.INSTANCE;
+
+ case UCHAR:
+ return DefaultInvokerFactory.Unsigned8Converter.INSTANCE;
+
+ case SSHORT:
+ return DefaultInvokerFactory.Signed16Converter.INSTANCE;
+
+ case USHORT:
+ return DefaultInvokerFactory.Unsigned16Converter.INSTANCE;
+
+ case SINT:
+ return DefaultInvokerFactory.Signed32Converter.INSTANCE;
+
+ case UINT:
+ return DefaultInvokerFactory.Unsigned32Converter.INSTANCE;
+
+ case SLONG:
+ return sizeof(nativeType) == 4 ? DefaultInvokerFactory.Signed32Converter.INSTANCE : DefaultInvokerFactory.LongLongConverter.INSTANCE;
+
+ case ULONG:
+ case ADDRESS:
+ return sizeof(nativeType) == 4 ? DefaultInvokerFactory.Unsigned32Converter.INSTANCE : DefaultInvokerFactory.LongLongConverter.INSTANCE;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return DefaultInvokerFactory.LongLongConverter.INSTANCE;
+
+ case FLOAT:
+ return DefaultInvokerFactory.FloatConverter.INSTANCE;
+
+ case DOUBLE:
+ return DefaultInvokerFactory.DoubleConverter.INSTANCE;
+
+ }
+ throw new UnsupportedOperationException("cannot convert " + nativeType);
+ }
+
+ static abstract class NumberDataConverter implements DataConverter<Number, Number> {
+ @Override
+ public final Class<Number> nativeType() {
+ return Number.class;
+ }
+ }
+
+ static final class Signed8Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Signed8Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.byteValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.byteValue();
+ }
+ }
+
+ static final class Unsigned8Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Unsigned8Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ int value = nativeValue.byteValue();
+ return value < 0 ? ((value & 0x7f) + 0x80) : value;
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.intValue() & 0xffff;
+ }
+ }
+
+ static final class Signed16Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Signed16Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.shortValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.shortValue();
+ }
+ }
+
+ static final class Unsigned16Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Unsigned16Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ int value = nativeValue.shortValue();
+ return value < 0 ? ((value & 0x7fff) + 0x8000) : value;
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.intValue() & 0xffff;
+ }
+ }
+
+ static final class Signed32Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Signed32Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.intValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.intValue();
+ }
+ }
+
+ static final class Unsigned32Converter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new Unsigned32Converter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ long value = nativeValue.intValue();
+ return value < 0 ? ((value & 0x7fffffffL) + 0x80000000L) : value;
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.longValue() & 0xffffffffL;
+ }
+ }
+
+ static final class LongLongConverter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new LongLongConverter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.longValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.longValue();
+ }
+ }
+
+ static final class FloatConverter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new FloatConverter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.floatValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.floatValue();
+ }
+ }
+
+ static final class DoubleConverter extends NumberDataConverter {
+ static final NumberDataConverter INSTANCE = new DoubleConverter();
+ @Override
+ public Number fromNative(Number nativeValue, FromNativeContext context) {
+ return nativeValue.doubleValue();
+ }
+
+ @Override
+ public Number toNative(Number value, ToNativeContext context) {
+ return value.doubleValue();
+ }
+ }
+
+ static final class BooleanConverter implements DataConverter<Boolean, Number> {
+ static final DataConverter<Boolean, Number> INSTANCE = new BooleanConverter();
+ @Override
+ public Boolean fromNative(Number nativeValue, FromNativeContext context) {
+ return (nativeValue.intValue() & 0x1) != 0;
+ }
+
+ @Override
+ public Number toNative(Boolean value, ToNativeContext context) {
+ return value ? 1 : 0;
+ }
+
+ @Override
+ public Class<Number> nativeType() {
+ return Number.class;
+ }
+ }
+
+ static interface ResultConverter<J, N> extends FromNativeConverter<J, N> {
+ J fromNative(N value, FromNativeContext fromNativeContext);
+ }
+
+ static ResultConverter<? extends Number, Number> getNumberResultConverter(jnr.ffi.provider.FromNativeType fromNativeType) {
+ if (Byte.class == fromNativeType.effectiveJavaType() || byte.class == fromNativeType.effectiveJavaType()) {
+ return ByteResultConverter.INSTANCE;
+
+ } else if (Short.class == fromNativeType.effectiveJavaType() || short.class == fromNativeType.effectiveJavaType()) {
+ return ShortResultConverter.INSTANCE;
+
+ } else if (Integer.class == fromNativeType.effectiveJavaType() || int.class == fromNativeType.effectiveJavaType()) {
+ return IntegerResultConverter.INSTANCE;
+
+ } else if (Long.class == fromNativeType.effectiveJavaType() || long.class == fromNativeType.effectiveJavaType()) {
+ return LongResultConverter.INSTANCE;
+
+ } else if (Float.class == fromNativeType.effectiveJavaType() || float.class == fromNativeType.effectiveJavaType()) {
+ return FloatResultConverter.INSTANCE;
+
+ } else if (Double.class == fromNativeType.effectiveJavaType() || double.class == fromNativeType.effectiveJavaType()) {
+ return DoubleResultConverter.INSTANCE;
+
+ } else if (Address.class == fromNativeType.effectiveJavaType()) {
+ return AddressResultConverter.INSTANCE;
+
+ } else {
+ throw new UnsupportedOperationException("cannot convert to " + fromNativeType.effectiveJavaType());
+ }
+ }
+
+
+ static abstract class AbstractNumberResultConverter<T> implements ResultConverter<T, Number> {
+ @Override
+ public final Class<Number> nativeType() {
+ return Number.class;
+ }
+ }
+
+ static final class ByteResultConverter extends AbstractNumberResultConverter<Byte> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new ByteResultConverter();
+ @Override
+ public Byte fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.byteValue();
+ }
+ }
+
+ static final class ShortResultConverter extends AbstractNumberResultConverter<Short> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new ShortResultConverter();
+ @Override
+ public Short fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.shortValue();
+ }
+ }
+
+ static final class IntegerResultConverter extends AbstractNumberResultConverter<Integer> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new IntegerResultConverter();
+ @Override
+ public Integer fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.intValue();
+ }
+ }
+
+ static final class LongResultConverter extends AbstractNumberResultConverter<Long> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new LongResultConverter();
+ @Override
+ public Long fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.longValue();
+ }
+ }
+
+ static final class FloatResultConverter extends AbstractNumberResultConverter<Float> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new FloatResultConverter();
+ @Override
+ public Float fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.floatValue();
+ }
+ }
+
+ static final class DoubleResultConverter extends AbstractNumberResultConverter<Double> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new DoubleResultConverter();
+ @Override
+ public Double fromNative(Number value, FromNativeContext fromNativeContext) {
+ return value.doubleValue();
+ }
+ }
+
+ static final class AddressResultConverter extends AbstractNumberResultConverter<Address> {
+ static final ResultConverter<? extends Number, Number> INSTANCE = new AddressResultConverter();
+ @Override
+ public Address fromNative(Number value, FromNativeContext fromNativeContext) {
+ return Address.valueOf(value.longValue());
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/DirectMemoryIO.java b/src/main/java/jnr/ffi/provider/jffi/DirectMemoryIO.java
new file mode 100644
index 0000000..477892d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/DirectMemoryIO.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.provider.AbstractMemoryIO;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+class DirectMemoryIO extends AbstractMemoryIO {
+ static final com.kenai.jffi.MemoryIO IO = com.kenai.jffi.MemoryIO.getInstance();
+ DirectMemoryIO(Runtime runtime, long address) {
+ super(runtime, address, true);
+ }
+
+ DirectMemoryIO(Runtime runtime, int address) {
+ super(runtime, (long) address & 0xffffffffL, true);
+ }
+
+ public long size() {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public boolean hasArray() {
+ return false;
+ }
+
+ @Override
+ public Object array() {
+ throw new UnsupportedOperationException("no array");
+ }
+
+ @Override
+ public int arrayOffset() {
+ throw new UnsupportedOperationException("no array");
+ }
+
+ @Override
+ public int arrayLength() {
+ throw new UnsupportedOperationException("no array");
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) ((address() << 32L) ^ address());
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Pointer && ((Pointer) obj).address() == address() && ((Pointer) obj).getRuntime().isCompatible(getRuntime());
+ }
+
+
+ public final byte getByte(long offset) {
+ return IO.getByte(address() + offset);
+ }
+
+ public final short getShort(long offset) {
+ return IO.getShort(address() + offset);
+ }
+
+ public final int getInt(long offset) {
+ return IO.getInt(address() + offset);
+ }
+
+ public final long getLongLong(long offset) {
+ return IO.getLong(address() + offset);
+ }
+
+ public final float getFloat(long offset) {
+ return IO.getFloat(address() + offset);
+ }
+
+ public final double getDouble(long offset) {
+ return IO.getDouble(address() + offset);
+ }
+
+ public final void putByte(long offset, byte value) {
+ IO.putByte(address() + offset, value);
+ }
+
+ public final void putShort(long offset, short value) {
+ IO.putShort(address() + offset, value);
+ }
+
+ public final void putInt(long offset, int value) {
+ IO.putInt(address() + offset, value);
+ }
+
+ public final void putLongLong(long offset, long value) {
+ IO.putLong(address() + offset, value);
+ }
+
+ public final void putFloat(long offset, float value) {
+ IO.putFloat(address() + offset, value);
+ }
+
+ public final void putDouble(long offset, double value) {
+ IO.putDouble(address() + offset, value);
+ }
+
+ public final void get(long offset, byte[] dst, int off, int len) {
+ IO.getByteArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, byte[] src, int off, int len) {
+ IO.putByteArray(address() + offset, src, off, len);
+ }
+
+ public final void get(long offset, short[] dst, int off, int len) {
+ IO.getShortArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, short[] src, int off, int len) {
+ IO.putShortArray(address() + offset, src, off, len);
+ }
+
+ public final void get(long offset, int[] dst, int off, int len) {
+ IO.getIntArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, int[] src, int off, int len) {
+ IO.putIntArray(address() + offset, src, off, len);
+ }
+
+ public final void get(long offset, long[] dst, int off, int len) {
+ IO.getLongArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, long[] src, int off, int len) {
+ IO.putLongArray(address() + offset, src, off, len);
+ }
+
+ public final void get(long offset, float[] dst, int off, int len) {
+ IO.getFloatArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, float[] src, int off, int len) {
+ IO.putFloatArray(address() + offset, src, off, len);
+ }
+
+ public final void get(long offset, double[] dst, int off, int len) {
+ IO.getDoubleArray(address() + offset, dst, off, len);
+ }
+
+ public final void put(long offset, double[] src, int off, int len) {
+ IO.putDoubleArray(address() + offset, src, off, len);
+ }
+
+ public Pointer getPointer(long offset) {
+ return MemoryUtil.newPointer(getRuntime(), IO.getAddress(address() + offset));
+ }
+
+ public Pointer getPointer(long offset, long size) {
+ return MemoryUtil.newPointer(getRuntime(), IO.getAddress(this.address() + offset), size);
+ }
+
+ public void putPointer(long offset, Pointer value) {
+ IO.putAddress(address() + offset, value != null ? value.address() : 0L);
+ }
+
+ public String getString(long offset) {
+ return Charset.defaultCharset().decode(ByteBuffer.wrap(IO.getZeroTerminatedByteArray(address() + offset))).toString();
+ }
+
+
+ public String getString(long offset, int maxLength, Charset cs) {
+ final byte[] bytes = IO.getZeroTerminatedByteArray(address() + offset, maxLength);
+ return cs.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
+ public void putString(long offset, String string, int maxLength, Charset cs) {
+ ByteBuffer buf = cs.encode(string);
+ int len = Math.min(maxLength, buf.remaining());
+ IO.putZeroTerminatedByteArray(address() + offset, buf.array(), buf.arrayOffset() + buf.position(), len);
+ }
+
+ public void putZeroTerminatedByteArray(long offset, byte[] src, int off, int len) {
+ IO.putZeroTerminatedByteArray(address() + offset, src, off, len);
+ }
+
+
+ public int indexOf(long offset, byte value, int maxlen) {
+ return (int) IO.indexOf(address() + offset, value, maxlen);
+ }
+
+ public final void setMemory(long offset, long size, byte value) {
+ IO.setMemory(this.address() + offset, size, value);
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/FastIntMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/FastIntMethodGenerator.java
new file mode 100644
index 0000000..ca86b40
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/FastIntMethodGenerator.java
@@ -0,0 +1,141 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Platform;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.ffi.provider.SigType;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+import static jnr.ffi.provider.jffi.Util.getBooleanProperty;
+
+/**
+ *
+ */
+final class FastIntMethodGenerator extends AbstractFastNumericMethodGenerator {
+ private static final boolean ENABLED = getBooleanProperty("jnr.ffi.fast-int.enabled", true);
+ private static final int MAX_FASTINT_PARAMETERS = getMaximumFastIntParameters();
+
+ private static final String[] signatures;
+
+ private static final String[] methodNames = {
+ "invokeI0", "invokeI1", "invokeI2", "invokeI3", "invokeI4", "invokeI5", "invokeI6"
+ };
+
+ static {
+ signatures = new String[MAX_FASTINT_PARAMETERS + 1];
+ for (int i = 0; i <= MAX_FASTINT_PARAMETERS; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(').append(ci(CallContext.class)).append(ci(long.class));
+ for (int n = 0; n < i; n++) {
+ sb.append('I');
+ }
+ signatures[i] = sb.append(")I").toString();
+ }
+ }
+
+ @Override
+ String getInvokerMethodName(ResultType resultType, ParameterType[] parameterTypes, boolean ignoreErrno) {
+ final int parameterCount = parameterTypes.length;
+
+ if (parameterCount <= MAX_FASTINT_PARAMETERS && parameterCount <= methodNames.length) {
+ return methodNames[parameterCount];
+
+ } else {
+ throw new IllegalArgumentException("invalid fast-int parameter count: " + parameterCount);
+ }
+ }
+
+ @Override
+ String getInvokerSignature(int parameterCount, Class nativeIntType) {
+ if (parameterCount <= MAX_FASTINT_PARAMETERS && parameterCount <= signatures.length) {
+ return signatures[parameterCount];
+ }
+ throw new IllegalArgumentException("invalid fast-int parameter count: " + parameterCount);
+ }
+
+ final Class getInvokerType() {
+ return int.class;
+ }
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ final int parameterCount = parameterTypes.length;
+
+ if (!ENABLED) {
+ return false;
+ }
+
+ if (!callingConvention.equals(CallingConvention.DEFAULT) || parameterCount > MAX_FASTINT_PARAMETERS) {
+ return false;
+ }
+
+ final Platform platform = Platform.getPlatform();
+
+ if (platform.getOS().equals(Platform.OS.WINDOWS)) {
+ return false;
+ }
+
+ if (!platform.getCPU().equals(Platform.CPU.I386) && !platform.getCPU().equals(Platform.CPU.X86_64)) {
+ return false;
+ }
+
+ for (ParameterType parameterType : parameterTypes) {
+ if (!isFastIntParameter(platform, parameterType)) {
+ return false;
+ }
+ }
+
+ return isFastIntResult(platform, resultType);
+ }
+
+
+ static int getMaximumFastIntParameters() {
+ try {
+ com.kenai.jffi.Invoker.class.getDeclaredMethod("invokeI6", CallContext.class, long.class,
+ int.class, int.class, int.class, int.class, int.class, int.class);
+ return 6;
+
+ } catch (Throwable t) {
+ return 0;
+ }
+ }
+
+ static boolean isFastIntType(Platform platform, SigType type) {
+ switch (type.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ return sizeof(type.getNativeType()) <= 4;
+
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isSupportedPointerParameterType(Class javaParameterType) {
+ return Pointer.class.isAssignableFrom(javaParameterType);
+ }
+
+ static boolean isFastIntResult(Platform platform, ResultType resultType) {
+ return isFastIntType(platform, resultType)
+ || resultType.getNativeType() == NativeType.VOID
+ || (resultType.getNativeType() == NativeType.ADDRESS && sizeof(resultType)== 4)
+ ;
+ }
+
+
+ static boolean isFastIntParameter(Platform platform, ParameterType parameterType) {
+ return isFastIntType(platform, parameterType)
+ || (parameterType.getNativeType() == NativeType.ADDRESS && sizeof(parameterType)== 4)
+ && isSupportedPointerParameterType(parameterType.effectiveJavaType());
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/FastLongMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/FastLongMethodGenerator.java
new file mode 100644
index 0000000..27a8df9
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/FastLongMethodGenerator.java
@@ -0,0 +1,125 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Platform;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.NativeType;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.ffi.provider.SigType;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static jnr.ffi.provider.jffi.FastIntMethodGenerator.isFastIntType;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+import static jnr.ffi.provider.jffi.Util.getBooleanProperty;
+
+/**
+ *
+ */
+public class FastLongMethodGenerator extends AbstractFastNumericMethodGenerator {
+ private static final boolean ENABLED = getBooleanProperty("jnr.ffi.fast-long.enabled", true);
+ private static final int MAX_PARAMETERS = getMaximumFastLongParameters();
+ private static final String[] signatures;
+
+ private static final String[] methodNames = {
+ "invokeL0", "invokeL1", "invokeL2", "invokeL3", "invokeL4", "invokeL5", "invokeL6"
+ };
+
+ static {
+ signatures = new String[MAX_PARAMETERS + 1];
+ for (int i = 0; i <= MAX_PARAMETERS; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(').append(ci(CallContext.class)).append(ci(long.class));
+ for (int n = 0; n < i; n++) {
+ sb.append('J');
+ }
+ signatures[i] = sb.append(")J").toString();
+ }
+ }
+
+ @Override
+ String getInvokerMethodName(ResultType resultType, ParameterType[] parameterTypes, boolean ignoreErrno) {
+ final int parameterCount = parameterTypes.length;
+
+ if (parameterCount <= MAX_PARAMETERS && parameterCount <= methodNames.length) {
+ return methodNames[parameterCount];
+
+ } else {
+ throw new IllegalArgumentException("invalid fast-int parameter count: " + parameterCount);
+ }
+ }
+
+ @Override
+ String getInvokerSignature(int parameterCount, Class nativeIntType) {
+
+ if (parameterCount <= MAX_PARAMETERS && parameterCount <= signatures.length) {
+ return signatures[parameterCount];
+
+ } else {
+ throw new IllegalArgumentException("invalid fast-int parameter count: " + parameterCount);
+ }
+ }
+
+ @Override
+ Class getInvokerType() {
+ return long.class;
+ }
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ final int parameterCount = parameterTypes.length;
+
+ if (!ENABLED) {
+ return false;
+ }
+
+ if (callingConvention != CallingConvention.DEFAULT || parameterCount > MAX_PARAMETERS) {
+ return false;
+ }
+ final Platform platform = Platform.getPlatform();
+ // Only supported on amd64 arches
+ if (platform.getCPU() != Platform.CPU.X86_64) {
+ return false;
+ }
+
+ if (platform.getOS().equals(Platform.OS.WINDOWS)) {
+ return false;
+ }
+
+
+ for (ParameterType parameterType : parameterTypes) {
+ if (!isFastLongParameter(platform, parameterType)) {
+ return false;
+ }
+ }
+
+ return isFastLongResult(platform, resultType);
+ }
+
+ static int getMaximumFastLongParameters() {
+ try {
+ com.kenai.jffi.Invoker.class.getDeclaredMethod("invokeL6", CallContext.class, long.class,
+ long.class, long.class, long.class, long.class, long.class, long.class);
+ return 6;
+ } catch (Throwable t) {
+ return 0;
+ }
+ }
+
+ private static boolean isFastLongType(Platform platform, SigType type) {
+ return isFastIntType(platform, type)
+ || (type.getNativeType() == NativeType.ADDRESS && sizeof(NativeType.ADDRESS) == 8)
+ || type.getNativeType() == NativeType.SLONG || type.getNativeType() == NativeType.ULONG
+ || type.getNativeType() == NativeType.SLONGLONG || type.getNativeType() == NativeType.ULONGLONG;
+ }
+
+ static boolean isFastLongResult(Platform platform, ResultType resultType) {
+ return isFastLongType(platform, resultType)
+ || resultType.getNativeType() == NativeType.VOID
+ || (resultType.getNativeType() == NativeType.ADDRESS && sizeof(NativeType.ADDRESS) == 8)
+ ;
+ }
+
+ static boolean isFastLongParameter(Platform platform, ParameterType type) {
+ return isFastLongType(platform, type);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/FastNumericMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/FastNumericMethodGenerator.java
new file mode 100644
index 0000000..66ef2ed
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/FastNumericMethodGenerator.java
@@ -0,0 +1,150 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Platform;
+import com.kenai.jffi.Type;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.ffi.provider.SigType;
+
+import java.nio.*;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.ci;
+import static jnr.ffi.provider.jffi.FastIntMethodGenerator.isFastIntType;
+import static jnr.ffi.provider.jffi.Util.getBooleanProperty;
+
+/**
+ *
+ */
+class FastNumericMethodGenerator extends AbstractFastNumericMethodGenerator {
+ private static final boolean ENABLED = getBooleanProperty("jnr.ffi.fast-numeric.enabled", true);
+ private static final int MAX_PARAMETERS = getMaximumParameters();
+ private static final String[] signatures;
+
+ private static final String[] methodNames = {
+ "invokeN0", "invokeN1", "invokeN2", "invokeN3", "invokeN4", "invokeN5", "invokeN6"
+ };
+
+ static {
+ signatures = new String[MAX_PARAMETERS + 1];
+ for (int i = 0; i <= MAX_PARAMETERS; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(').append(ci(CallContext.class)).append(ci(long.class));
+ for (int n = 0; n < i; n++) {
+ sb.append('J');
+ }
+ signatures[i] = sb.append(")J").toString();
+ }
+ }
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ final int parameterCount = parameterTypes.length;
+
+ if (!ENABLED) {
+ return false;
+ }
+
+ if (callingConvention != CallingConvention.DEFAULT || parameterCount > MAX_PARAMETERS) {
+ return false;
+ }
+ final Platform platform = Platform.getPlatform();
+
+ // Only supported on i386 and amd64 arches
+ if (platform.getCPU() != Platform.CPU.I386 && platform.getCPU() != Platform.CPU.X86_64) {
+ return false;
+ }
+
+ if (platform.getOS().equals(Platform.OS.WINDOWS)) {
+ return false;
+ }
+
+ for (ParameterType parameterType : parameterTypes) {
+ if (!isFastNumericParameter(platform, parameterType)) {
+ return false;
+ }
+ }
+
+ return isFastNumericResult(platform, resultType);
+ }
+
+
+ @Override
+ String getInvokerMethodName(ResultType resultType, ParameterType[] parameterTypes, boolean ignoreErrno) {
+ final int parameterCount = parameterTypes.length;
+
+ if (parameterCount <= MAX_PARAMETERS && parameterCount <= methodNames.length) {
+ return methodNames[parameterCount];
+
+ } else {
+ throw new IllegalArgumentException("invalid fast-numeric parameter count: " + parameterCount);
+ }
+ }
+
+ @Override
+ String getInvokerSignature(int parameterCount, Class nativeIntType) {
+
+ if (parameterCount <= MAX_PARAMETERS && parameterCount <= signatures.length) {
+ return signatures[parameterCount];
+
+ } else {
+ throw new IllegalArgumentException("invalid fast-numeric parameter count: " + parameterCount);
+ }
+ }
+
+ @Override
+ Class getInvokerType() {
+ return long.class;
+ }
+
+ private static boolean isNumericType(Platform platform, SigType type) {
+ return isFastIntType(platform, type)
+ || type.getNativeType() == NativeType.SLONG || type.getNativeType() == NativeType.ULONG
+ || type.getNativeType() == NativeType.SLONGLONG || type.getNativeType() == NativeType.ULONGLONG
+ || type.getNativeType() == NativeType.FLOAT || type.getNativeType() == NativeType.DOUBLE
+ ;
+ }
+
+ static boolean isFastNumericResult(Platform platform, ResultType type) {
+ return isNumericType(platform, type)
+ || NativeType.VOID == type.getNativeType()
+ || NativeType.ADDRESS == type.getNativeType()
+ ;
+ }
+
+ static boolean isFastNumericParameter(Platform platform, ParameterType parameterType) {
+ return isNumericType(platform, parameterType)
+ || (parameterType.getNativeType() == NativeType.ADDRESS && isSupportedPointerParameterType(parameterType.effectiveJavaType()));
+ }
+
+ private static boolean isSupportedPointerParameterType(Class javaParameterType) {
+ return Pointer.class.isAssignableFrom(javaParameterType)
+ || ByteBuffer.class.isAssignableFrom(javaParameterType)
+ || ShortBuffer.class.isAssignableFrom(javaParameterType)
+ || IntBuffer.class.isAssignableFrom(javaParameterType)
+ || (LongBuffer.class.isAssignableFrom(javaParameterType) && Type.SLONG.size() == 8)
+ || FloatBuffer.class.isAssignableFrom(javaParameterType)
+ || DoubleBuffer.class.isAssignableFrom(javaParameterType)
+ || byte[].class == javaParameterType
+ || short[].class == javaParameterType
+ || int[].class == javaParameterType
+ || (long[].class == javaParameterType && Type.SLONG.size() == 8)
+ || float[].class == javaParameterType
+ || double[].class == javaParameterType
+ || boolean[].class == javaParameterType
+ ;
+ }
+
+
+ static int getMaximumParameters() {
+ try {
+ com.kenai.jffi.Invoker.class.getDeclaredMethod("invokeN6", CallContext.class, long.class,
+ long.class, long.class, long.class, long.class, long.class, long.class);
+ return 6;
+ } catch (Throwable t) {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/HeapBufferParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/HeapBufferParameterStrategy.java
new file mode 100644
index 0000000..0bd0541
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/HeapBufferParameterStrategy.java
@@ -0,0 +1,52 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.ObjectParameterType;
+
+import java.nio.Buffer;
+import java.util.EnumSet;
+
+/**
+ *
+ */
+final class HeapBufferParameterStrategy extends ParameterStrategy {
+
+ public HeapBufferParameterStrategy(ObjectParameterType.ComponentType componentType) {
+ super(HEAP, ObjectParameterType.create(ObjectParameterType.ARRAY, componentType));
+ }
+
+ @Override
+ public long address(Object o) {
+ return 0;
+ }
+
+ @Override
+ public Object object(Object o) {
+ return ((Buffer) o).array();
+ }
+
+ @Override
+ public int offset(Object o) {
+ Buffer buffer = (Buffer) o;
+ return buffer.arrayOffset() + buffer.position();
+ }
+
+ @Override
+ public int length(Object o) {
+ return ((Buffer) o).remaining();
+ }
+
+
+ private static final HeapBufferParameterStrategy[] heapBufferStrategies;
+ static {
+ EnumSet<ObjectParameterType.ComponentType> componentTypes = EnumSet.allOf(ObjectParameterType.ComponentType.class);
+ heapBufferStrategies = new HeapBufferParameterStrategy[componentTypes.size()];
+ for (ObjectParameterType.ComponentType componentType : componentTypes) {
+ heapBufferStrategies[componentType.ordinal()] = new HeapBufferParameterStrategy(componentType);
+ }
+ }
+
+ static HeapBufferParameterStrategy get(ObjectParameterType.ComponentType componentType) {
+ return heapBufferStrategies[componentType.ordinal()];
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java
new file mode 100644
index 0000000..1dfcca5
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java
@@ -0,0 +1,157 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeLong;
+import jnr.ffi.Pointer;
+import jnr.ffi.Struct;
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.byref.ByReference;
+import jnr.ffi.mapper.*;
+import jnr.ffi.mapper.FromNativeType;
+import jnr.ffi.mapper.ToNativeType;
+import jnr.ffi.provider.ParameterFlags;
+import jnr.ffi.provider.converters.*;
+
+import java.lang.reflect.Method;
+import java.util.EnumSet;
+import java.util.Set;
+
+final class InvokerTypeMapper extends AbstractSignatureTypeMapper implements SignatureTypeMapper {
+ private final NativeClosureManager closureManager;
+ private final AsmClassLoader classLoader;
+ private final StructByReferenceResultConverterFactory structResultConverterFactory;
+
+
+ public InvokerTypeMapper(NativeClosureManager closureManager, AsmClassLoader classLoader, boolean asmEnabled) {
+ this.closureManager = closureManager;
+ this.classLoader = classLoader;
+ this.structResultConverterFactory = new StructByReferenceResultConverterFactory(classLoader, asmEnabled);
+ }
+
+ public FromNativeConverter getFromNativeConverter(SignatureType signatureType, FromNativeContext fromNativeContext) {
+ FromNativeConverter converter;
+
+ if (Enum.class.isAssignableFrom(signatureType.getDeclaredType())) {
+ return EnumConverter.getInstance(signatureType.getDeclaredType().asSubclass(Enum.class));
+
+ } else if (Struct.class.isAssignableFrom(signatureType.getDeclaredType())) {
+ return structResultConverterFactory.get(signatureType.getDeclaredType().asSubclass(Struct.class), fromNativeContext);
+
+ } else if (closureManager != null && isDelegate(signatureType.getDeclaredType())) {
+ return ClosureFromNativeConverter.getInstance(fromNativeContext.getRuntime(), signatureType, classLoader, this);
+
+ } else if (NativeLong.class == signatureType.getDeclaredType()) {
+ return NativeLongConverter.getInstance();
+
+ } else if (String.class == signatureType.getDeclaredType() || CharSequence.class == signatureType.getDeclaredType()) {
+ return StringResultConverter.getInstance(fromNativeContext);
+
+ } else if ((Set.class == signatureType.getDeclaredType() || EnumSet.class == signatureType.getDeclaredType()) && (converter = EnumSetConverter.getFromNativeConverter(signatureType, fromNativeContext)) != null) {
+ return converter;
+
+ } else {
+ return null;
+ }
+
+ }
+
+ public ToNativeConverter getToNativeConverter(SignatureType signatureType, ToNativeContext context) {
+ Class javaType = signatureType.getDeclaredType();
+ ToNativeConverter converter;
+
+ if (Enum.class.isAssignableFrom(javaType)) {
+ return EnumConverter.getInstance(javaType.asSubclass(Enum.class));
+
+ } else if (Set.class.isAssignableFrom(javaType) && (converter = EnumSetConverter.getToNativeConverter(signatureType, context)) != null) {
+ return converter;
+
+ } else if (isDelegate(javaType)) {
+ return closureManager.newClosureSite(javaType);
+
+ } else if (ByReference.class.isAssignableFrom(javaType)) {
+ return ByReferenceParameterConverter.getInstance(context);
+
+ } else if (Struct.class.isAssignableFrom(javaType)) {
+ return StructByReferenceToNativeConverter.getInstance(context);
+
+ } else if (NativeLong.class.isAssignableFrom(javaType)) {
+ return NativeLongConverter.getInstance();
+
+ } else if (StringBuilder.class.isAssignableFrom(javaType)) {
+ return StringBuilderParameterConverter.getInstance(ParameterFlags.parse(context.getAnnotations()), context);
+
+ } else if (StringBuffer.class.isAssignableFrom(javaType)) {
+ return StringBufferParameterConverter.getInstance(ParameterFlags.parse(context.getAnnotations()), context);
+
+ } else if (CharSequence.class.isAssignableFrom(javaType)) {
+ return CharSequenceParameterConverter.getInstance(context);
+
+ } else if (Byte[].class.isAssignableFrom(javaType)) {
+ return BoxedByteArrayParameterConverter.getInstance(context);
+
+ } else if (Short[].class.isAssignableFrom(javaType)) {
+ return BoxedShortArrayParameterConverter.getInstance(context);
+
+ } else if (Integer[].class.isAssignableFrom(javaType)) {
+ return BoxedIntegerArrayParameterConverter.getInstance(context);
+
+ } else if (Long[].class.isAssignableFrom(javaType)) {
+ return Types.getType(context.getRuntime(), javaType.getComponentType(), context.getAnnotations()).size() == 4
+ ? BoxedLong32ArrayParameterConverter.getInstance(context)
+ : BoxedLong64ArrayParameterConverter.getInstance(context);
+
+ } else if (NativeLong[].class.isAssignableFrom(javaType)) {
+ return Types.getType(context.getRuntime(), javaType.getComponentType(), context.getAnnotations()).size() == 4
+ ? NativeLong32ArrayParameterConverter.getInstance(context)
+ : NativeLong64ArrayParameterConverter.getInstance(context);
+
+ } else if (Float[].class.isAssignableFrom(javaType)) {
+ return BoxedFloatArrayParameterConverter.getInstance(context);
+
+ } else if (Double[].class.isAssignableFrom(javaType)) {
+ return BoxedDoubleArrayParameterConverter.getInstance(context);
+
+ } else if (Boolean[].class.isAssignableFrom(javaType)) {
+ return BoxedBooleanArrayParameterConverter.getInstance(context);
+
+ } else if (javaType.isArray() && Pointer.class.isAssignableFrom(javaType.getComponentType())) {
+ return context.getRuntime().addressSize() == 4
+ ? Pointer32ArrayParameterConverter.getInstance(context)
+ : Pointer64ArrayParameterConverter.getInstance(context);
+
+ } else if (long[].class.isAssignableFrom(javaType) && Types.getType(context.getRuntime(), javaType.getComponentType(), context.getAnnotations()).size() == 4) {
+ return Long32ArrayParameterConverter.getInstance(context);
+
+ } else if (javaType.isArray() && Struct.class.isAssignableFrom(javaType.getComponentType())) {
+ return StructArrayParameterConverter.getInstance(context, javaType.getComponentType());
+
+ } else if (javaType.isArray() && CharSequence.class.isAssignableFrom(javaType.getComponentType())) {
+ return CharSequenceArrayParameterConverter.getInstance(context);
+
+ } else {
+ return null;
+ }
+ }
+
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return FromNativeTypes.create(getFromNativeConverter(type, context));
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return ToNativeTypes.create(getToNativeConverter(type, context));
+ }
+
+
+ private static boolean isDelegate(Class klass) {
+ for (Method m : klass.getMethods()) {
+ if (m.isAnnotationPresent(Delegate.class)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/InvokerUtil.java b/src/main/java/jnr/ffi/provider/jffi/InvokerUtil.java
new file mode 100644
index 0000000..1af8f12
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/InvokerUtil.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.CallContextCache;
+import com.kenai.jffi.Type;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.LibraryOption;
+import jnr.ffi.NativeType;
+import jnr.ffi.annotations.IgnoreError;
+import jnr.ffi.annotations.SaveError;
+import jnr.ffi.annotations.StdCall;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.ffi.provider.SigType;
+import jnr.ffi.util.Annotations;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+
+final class InvokerUtil {
+
+ public static boolean requiresErrno(Method method) {
+ boolean saveError = true;
+ for (Annotation a : method.getAnnotations()) {
+ if (a instanceof IgnoreError) {
+ saveError = false;
+ } else if (a instanceof SaveError) {
+ saveError = true;
+ }
+ }
+ return saveError;
+ }
+
+ public static jnr.ffi.CallingConvention getCallingConvention(Map<LibraryOption, ?> libraryOptions) {
+ Object convention = libraryOptions.get(LibraryOption.CallingConvention);
+
+ // If someone passed in the jffi calling convention, just use it.
+ if (convention instanceof com.kenai.jffi.CallingConvention) {
+ return com.kenai.jffi.CallingConvention.DEFAULT.equals(convention) ? jnr.ffi.CallingConvention.DEFAULT : jnr.ffi.CallingConvention.STDCALL;
+ }
+
+ if (convention instanceof jnr.ffi.CallingConvention) switch ((jnr.ffi.CallingConvention) convention) {
+ case DEFAULT:
+ return jnr.ffi.CallingConvention.DEFAULT;
+ case STDCALL:
+ return jnr.ffi.CallingConvention.STDCALL;
+
+ } else if (convention != null) {
+ throw new IllegalArgumentException("unknown calling convention: " + convention);
+ }
+
+ return jnr.ffi.CallingConvention.DEFAULT;
+ }
+
+ public static jnr.ffi.CallingConvention getCallingConvention(Class interfaceClass, Map<LibraryOption, ?> options) {
+ if (interfaceClass.isAnnotationPresent(StdCall.class)) {
+ return jnr.ffi.CallingConvention.STDCALL;
+ }
+ return InvokerUtil.getCallingConvention(options);
+ }
+
+ public static boolean hasAnnotation(Collection<Annotation> annotations, Class<? extends Annotation> annotationClass) {
+ for (Annotation a : annotations) {
+ if (annotationClass.isInstance(a)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static final Map<jnr.ffi.NativeType, Type> jffiTypes;
+ static {
+ Map<jnr.ffi.NativeType, Type> m = new EnumMap<NativeType, Type>(jnr.ffi.NativeType.class);
+
+ m.put(NativeType.VOID, Type.VOID);
+ m.put(NativeType.SCHAR, Type.SCHAR);
+ m.put(NativeType.UCHAR, Type.UCHAR);
+ m.put(NativeType.SSHORT, Type.SSHORT);
+ m.put(NativeType.USHORT, Type.USHORT);
+ m.put(NativeType.SINT, Type.SINT);
+ m.put(NativeType.UINT, Type.UINT);
+ m.put(NativeType.SLONG, Type.SLONG);
+ m.put(NativeType.ULONG, Type.ULONG);
+ m.put(NativeType.SLONGLONG, Type.SLONG_LONG);
+ m.put(NativeType.ULONGLONG, Type.ULONG_LONG);
+ m.put(NativeType.FLOAT, Type.FLOAT);
+ m.put(NativeType.DOUBLE, Type.DOUBLE);
+ m.put(NativeType.ADDRESS, Type.POINTER);
+
+ jffiTypes = Collections.unmodifiableMap(m);
+ }
+
+ static Type jffiType(jnr.ffi.NativeType jnrType) {
+ Type jffiType = jffiTypes.get(jnrType);
+ if (jffiType != null) {
+ return jffiType;
+ }
+
+ throw new IllegalArgumentException("unsupported parameter type: " + jnrType);
+ }
+
+ static NativeType nativeType(jnr.ffi.Type jnrType) {
+ return jnrType.getNativeType();
+ }
+
+
+ static Collection<Annotation> getAnnotations(jnr.ffi.mapper.FromNativeType fromNativeType) {
+ return fromNativeType != null ? ConverterMetaData.getAnnotations(fromNativeType.getFromNativeConverter()) : Annotations.EMPTY_ANNOTATIONS;
+ }
+
+ static Collection<Annotation> getAnnotations(jnr.ffi.mapper.ToNativeType toNativeType) {
+ return toNativeType != null ? ConverterMetaData.getAnnotations(toNativeType.getToNativeConverter()) : Annotations.EMPTY_ANNOTATIONS;
+ }
+
+ static ResultType getResultType(jnr.ffi.Runtime runtime, Class type, Collection<Annotation> annotations,
+ FromNativeConverter fromNativeConverter, FromNativeContext fromNativeContext) {
+ Collection<Annotation> converterAnnotations = ConverterMetaData.getAnnotations(fromNativeConverter);
+ Collection<Annotation> allAnnotations = Annotations.mergeAnnotations(annotations, converterAnnotations);
+ NativeType nativeType = getMethodResultNativeType(runtime,
+ fromNativeConverter != null ? fromNativeConverter.nativeType() : type, allAnnotations);
+ boolean useContext = fromNativeConverter != null && !hasAnnotation(converterAnnotations, FromNativeConverter.NoContext.class);
+ return new ResultType(type, nativeType, allAnnotations, fromNativeConverter, useContext ? fromNativeContext : null);
+ }
+
+ static ResultType getResultType(jnr.ffi.Runtime runtime, Class type, Collection<Annotation> annotations,
+ jnr.ffi.mapper.FromNativeType fromNativeType, FromNativeContext fromNativeContext) {
+ Collection<Annotation> converterAnnotations = getAnnotations(fromNativeType);
+ Collection<Annotation> allAnnotations = Annotations.mergeAnnotations(annotations, converterAnnotations);
+ FromNativeConverter fromNativeConverter = fromNativeType != null ? fromNativeType.getFromNativeConverter() : null;
+ NativeType nativeType = getMethodResultNativeType(runtime,
+ fromNativeConverter != null ? fromNativeConverter.nativeType() : type, allAnnotations);
+ boolean useContext = fromNativeConverter != null && !hasAnnotation(converterAnnotations, FromNativeConverter.NoContext.class);
+ return new ResultType(type, nativeType, allAnnotations, fromNativeConverter, useContext ? fromNativeContext : null);
+ }
+
+ private static ParameterType getParameterType(jnr.ffi.Runtime runtime, Class type, Collection<Annotation> annotations,
+ ToNativeConverter toNativeConverter, ToNativeContext toNativeContext) {
+ NativeType nativeType = getMethodParameterNativeType(runtime,
+ toNativeConverter != null ? toNativeConverter.nativeType() : type, annotations);
+ return new ParameterType(type, nativeType, annotations, toNativeConverter, toNativeContext);
+ }
+
+ private static ParameterType getParameterType(jnr.ffi.Runtime runtime, Class type, Collection<Annotation> annotations,
+ jnr.ffi.mapper.ToNativeType toNativeType, ToNativeContext toNativeContext) {
+ ToNativeConverter toNativeConverter = toNativeType != null ? toNativeType.getToNativeConverter() : null;
+ NativeType nativeType = getMethodParameterNativeType(runtime,
+ toNativeConverter != null ? toNativeConverter.nativeType() : type, annotations);
+ return new ParameterType(type, nativeType, annotations, toNativeConverter, toNativeContext);
+ }
+
+ static ParameterType[] getParameterTypes(jnr.ffi.Runtime runtime, SignatureTypeMapper typeMapper,
+ Method m) {
+ final Class[] javaParameterTypes = m.getParameterTypes();
+ final Annotation[][] parameterAnnotations = m.getParameterAnnotations();
+ ParameterType[] parameterTypes = new ParameterType[javaParameterTypes.length];
+
+ for (int pidx = 0; pidx < javaParameterTypes.length; ++pidx) {
+ Collection<Annotation> annotations = Annotations.sortedAnnotationCollection(parameterAnnotations[pidx]);
+ ToNativeContext toNativeContext = new MethodParameterContext(runtime, m, pidx, annotations);
+ SignatureType signatureType = DefaultSignatureType.create(javaParameterTypes[pidx], toNativeContext);
+ jnr.ffi.mapper.ToNativeType toNativeType = typeMapper.getToNativeType(signatureType, toNativeContext);
+ ToNativeConverter toNativeConverter = toNativeType != null ? toNativeType.getToNativeConverter() : null;
+ Collection<Annotation> converterAnnotations = ConverterMetaData.getAnnotations(toNativeConverter);
+ Collection<Annotation> allAnnotations = Annotations.mergeAnnotations(annotations, converterAnnotations);
+
+ boolean contextRequired = toNativeConverter != null && !hasAnnotation(converterAnnotations, ToNativeConverter.NoContext.class);
+ parameterTypes[pidx] = getParameterType(runtime, javaParameterTypes[pidx],
+ allAnnotations, toNativeConverter, contextRequired ? toNativeContext : null);
+ }
+
+ return parameterTypes;
+ }
+
+ static CallContext getCallContext(SigType resultType, SigType[] parameterTypes, jnr.ffi.CallingConvention convention, boolean requiresErrno) {
+ com.kenai.jffi.Type[] nativeParamTypes = new com.kenai.jffi.Type[parameterTypes.length];
+
+ for (int i = 0; i < nativeParamTypes.length; ++i) {
+ nativeParamTypes[i] = jffiType(parameterTypes[i].getNativeType());
+ }
+
+ return CallContextCache.getInstance().getCallContext(jffiType(resultType.getNativeType()),
+ nativeParamTypes, jffiConvention(convention), requiresErrno);
+ }
+
+
+ public static jnr.ffi.CallingConvention getNativeCallingConvention(Method m) {
+ if (m.isAnnotationPresent(StdCall.class) || m.getDeclaringClass().isAnnotationPresent(StdCall.class)) {
+ return CallingConvention.STDCALL;
+ }
+
+ return CallingConvention.DEFAULT;
+ }
+
+
+ static NativeType getMethodParameterNativeType(jnr.ffi.Runtime runtime, Class parameterClass, Collection<Annotation> annotations) {
+ return Types.getType(runtime, parameterClass, annotations).getNativeType();
+ }
+
+ static NativeType getMethodResultNativeType(jnr.ffi.Runtime runtime, Class resultClass, Collection<Annotation> annotations) {
+ return Types.getType(runtime, resultClass, annotations).getNativeType();
+ }
+
+ public static final com.kenai.jffi.CallingConvention jffiConvention(jnr.ffi.CallingConvention callingConvention) {
+ return callingConvention == jnr.ffi.CallingConvention.DEFAULT ? com.kenai.jffi.CallingConvention.DEFAULT : com.kenai.jffi.CallingConvention.STDCALL;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/JNIInvokeInterface.java b/src/main/java/jnr/ffi/provider/jffi/JNIInvokeInterface.java
new file mode 100644
index 0000000..af0f29d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/JNIInvokeInterface.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+/**
+ * Indexes of JavaVM methods
+ */
+public class JNIInvokeInterface {
+ public static final int DestroyJavaVM = 3;
+ public static final int AttachCurrentThread = 4;
+ public static final int DetachCurrentThread = 5;
+ public static final int GetEnv = 6;
+ public static final int AttachCurrentThreadAsDaemon = 7;
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/JNINativeInterface.java b/src/main/java/jnr/ffi/provider/jffi/JNINativeInterface.java
new file mode 100644
index 0000000..551ae4f
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/JNINativeInterface.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+/**
+ *
+ * WARNING: Highly experimental!!!
+ *
+ * This class contains constants that are the indexes withing the JNINativeInterface_
+ * struct of each of the JNIEnv functions. To invoke one of the functions, the
+ * index _must_ be multiplied by sizeof(void *), then used as an index into the table.
+ *
+ * These indexes are valid on both 32bit and 64bit linux and macos. Win32 also
+ * seems to be the same. Other platforms may be completely different.
+ */
+public final class JNINativeInterface {
+
+ private JNINativeInterface() {
+ }
+
+ public static final int GetVersion = 4;
+ public static final int DefineClass = 5;
+ public static final int FindClass = 6;
+ public static final int FromReflectedMethod = 7;
+ public static final int FromReflectedField = 8;
+ public static final int ToReflectedMethod = 9;
+ public static final int GetSuperclass = 10;
+ public static final int IsAssignableFrom = 11;
+ public static final int ToReflectedField = 12;
+ public static final int Throw = 13;
+ public static final int ThrowNew = 14;
+ public static final int ExceptionOccurred = 15;
+ public static final int ExceptionDescribe = 16;
+ public static final int ExceptionClear = 17;
+ public static final int FatalError = 18;
+ public static final int PushLocalFrame = 19;
+ public static final int PopLocalFrame = 20;
+ public static final int NewGlobalRef = 21;
+ public static final int DeleteGlobalRef = 22;
+ public static final int DeleteLocalRef = 23;
+ public static final int IsSameObject = 24;
+ public static final int NewLocalRef = 25;
+ public static final int EnsureLocalCapacity = 26;
+ public static final int AllocObject = 27;
+ public static final int NewObject = 28;
+ public static final int NewObjectV = 29;
+ public static final int NewObjectA = 30;
+ public static final int GetObjectClass = 31;
+ public static final int IsInstanceOf = 32;
+ public static final int GetMethodID = 33;
+ public static final int CallObjectMethod = 34;
+ public static final int CallObjectMethodV = 35;
+ public static final int CallObjectMethodA = 36;
+ public static final int CallBooleanMethod = 37;
+ public static final int CallBooleanMethodV = 38;
+ public static final int CallBooleanMethodA = 39;
+ public static final int CallByteMethod = 40;
+ public static final int CallByteMethodV = 41;
+ public static final int CallByteMethodA = 42;
+ public static final int CallCharMethod = 43;
+ public static final int CallCharMethodV = 44;
+ public static final int CallCharMethodA = 45;
+ public static final int CallShortMethod = 46;
+ public static final int CallShortMethodV = 47;
+ public static final int CallShortMethodA = 48;
+ public static final int CallIntMethod = 49;
+ public static final int CallIntMethodV = 50;
+ public static final int CallIntMethodA = 51;
+ public static final int CallLongMethod = 52;
+ public static final int CallLongMethodV = 53;
+ public static final int CallLongMethodA = 54;
+ public static final int CallFloatMethod = 55;
+ public static final int CallFloatMethodV = 56;
+ public static final int CallFloatMethodA = 57;
+ public static final int CallDoubleMethod = 58;
+ public static final int CallDoubleMethodV = 59;
+ public static final int CallDoubleMethodA = 60;
+ public static final int CallVoidMethod = 61;
+ public static final int CallVoidMethodV = 62;
+ public static final int CallVoidMethodA = 63;
+ public static final int CallNonvirtualObjectMethod = 64;
+ public static final int CallNonvirtualObjectMethodV = 65;
+ public static final int CallNonvirtualObjectMethodA = 66;
+ public static final int CallNonvirtualBooleanMethod = 67;
+ public static final int CallNonvirtualBooleanMethodV = 68;
+ public static final int CallNonvirtualBooleanMethodA = 69;
+ public static final int CallNonvirtualByteMethod = 70;
+ public static final int CallNonvirtualByteMethodV = 71;
+ public static final int CallNonvirtualByteMethodA = 72;
+ public static final int CallNonvirtualCharMethod = 73;
+ public static final int CallNonvirtualCharMethodV = 74;
+ public static final int CallNonvirtualCharMethodA = 75;
+ public static final int CallNonvirtualShortMethod = 76;
+ public static final int CallNonvirtualShortMethodV = 77;
+ public static final int CallNonvirtualShortMethodA = 78;
+ public static final int CallNonvirtualIntMethod = 79;
+ public static final int CallNonvirtualIntMethodV = 80;
+ public static final int CallNonvirtualIntMethodA = 81;
+ public static final int CallNonvirtualLongMethod = 82;
+ public static final int CallNonvirtualLongMethodV = 83;
+ public static final int CallNonvirtualLongMethodA = 84;
+ public static final int CallNonvirtualFloatMethod = 85;
+ public static final int CallNonvirtualFloatMethodV = 86;
+ public static final int CallNonvirtualFloatMethodA = 87;
+ public static final int CallNonvirtualDoubleMethod = 88;
+ public static final int CallNonvirtualDoubleMethodV = 89;
+ public static final int CallNonvirtualDoubleMethodA = 90;
+ public static final int CallNonvirtualVoidMethod = 91;
+ public static final int CallNonvirtualVoidMethodV = 92;
+ public static final int CallNonvirtualVoidMethodA = 93;
+ public static final int GetFieldID = 94;
+ public static final int GetObjectField = 95;
+ public static final int GetBooleanField = 96;
+ public static final int GetByteField = 97;
+ public static final int GetCharField = 98;
+ public static final int GetShortField = 99;
+ public static final int GetIntField = 100;
+ public static final int GetLongField = 101;
+ public static final int GetFloatField = 102;
+ public static final int GetDoubleField = 103;
+ public static final int SetObjectField = 104;
+ public static final int SetBooleanField = 105;
+ public static final int SetByteField = 106;
+ public static final int SetCharField = 107;
+ public static final int SetShortField = 108;
+ public static final int SetIntField = 109;
+ public static final int SetLongField = 110;
+ public static final int SetFloatField = 111;
+ public static final int SetDoubleField = 112;
+ public static final int GetStaticMethodID = 113;
+ public static final int CallStaticObjectMethod = 114;
+ public static final int CallStaticObjectMethodV = 115;
+ public static final int CallStaticObjectMethodA = 116;
+ public static final int CallStaticBooleanMethod = 117;
+ public static final int CallStaticBooleanMethodV = 118;
+ public static final int CallStaticBooleanMethodA = 119;
+ public static final int CallStaticByteMethod = 120;
+ public static final int CallStaticByteMethodV = 121;
+ public static final int CallStaticByteMethodA = 122;
+ public static final int CallStaticCharMethod = 123;
+ public static final int CallStaticCharMethodV = 124;
+ public static final int CallStaticCharMethodA = 125;
+ public static final int CallStaticShortMethod = 126;
+ public static final int CallStaticShortMethodV = 127;
+ public static final int CallStaticShortMethodA = 128;
+ public static final int CallStaticIntMethod = 129;
+ public static final int CallStaticIntMethodV = 130;
+ public static final int CallStaticIntMethodA = 131;
+ public static final int CallStaticLongMethod = 132;
+ public static final int CallStaticLongMethodV = 133;
+ public static final int CallStaticLongMethodA = 134;
+ public static final int CallStaticFloatMethod = 135;
+ public static final int CallStaticFloatMethodV = 136;
+ public static final int CallStaticFloatMethodA = 137;
+ public static final int CallStaticDoubleMethod = 138;
+ public static final int CallStaticDoubleMethodV = 139;
+ public static final int CallStaticDoubleMethodA = 140;
+ public static final int CallStaticVoidMethod = 141;
+ public static final int CallStaticVoidMethodV = 142;
+ public static final int CallStaticVoidMethodA = 143;
+ public static final int GetStaticFieldID = 144;
+ public static final int GetStaticObjectField = 145;
+ public static final int GetStaticBooleanField = 146;
+ public static final int GetStaticByteField = 147;
+ public static final int GetStaticCharField = 148;
+ public static final int GetStaticShortField = 149;
+ public static final int GetStaticIntField = 150;
+ public static final int GetStaticLongField = 151;
+ public static final int GetStaticFloatField = 152;
+ public static final int GetStaticDoubleField = 153;
+ public static final int SetStaticObjectField = 154;
+ public static final int SetStaticBooleanField = 155;
+ public static final int SetStaticByteField = 156;
+ public static final int SetStaticCharField = 157;
+ public static final int SetStaticShortField = 158;
+ public static final int SetStaticIntField = 159;
+ public static final int SetStaticLongField = 160;
+ public static final int SetStaticFloatField = 161;
+ public static final int SetStaticDoubleField = 162;
+ public static final int NewString = 163;
+ public static final int GetStringLength = 164;
+ public static final int GetStringChars = 165;
+ public static final int ReleaseStringChars = 166;
+ public static final int NewStringUTF = 167;
+ public static final int GetStringUTFLength = 168;
+ public static final int GetStringUTFChars = 169;
+ public static final int ReleaseStringUTFChars = 170;
+ public static final int GetArrayLength = 171;
+ public static final int NewObjectArray = 172;
+ public static final int GetObjectArrayElement = 173;
+ public static final int SetObjectArrayElement = 174;
+ public static final int NewBooleanArray = 175;
+ public static final int NewByteArray = 176;
+ public static final int NewCharArray = 177;
+ public static final int NewShortArray = 178;
+ public static final int NewIntArray = 179;
+ public static final int NewLongArray = 180;
+ public static final int NewFloatArray = 181;
+ public static final int NewDoubleArray = 182;
+ public static final int GetBooleanArrayElements = 183;
+ public static final int GetByteArrayElements = 184;
+ public static final int GetCharArrayElements = 185;
+ public static final int GetShortArrayElements = 186;
+ public static final int GetIntArrayElements = 187;
+ public static final int GetLongArrayElements = 188;
+ public static final int GetFloatArrayElements = 189;
+ public static final int GetDoubleArrayElements = 190;
+ public static final int ReleaseBooleanArrayElements = 191;
+ public static final int ReleaseByteArrayElements = 192;
+ public static final int ReleaseCharArrayElements = 193;
+ public static final int ReleaseShortArrayElements = 194;
+ public static final int ReleaseIntArrayElements = 195;
+ public static final int ReleaseLongArrayElements = 196;
+ public static final int ReleaseFloatArrayElements = 197;
+ public static final int ReleaseDoubleArrayElements = 198;
+ public static final int GetBooleanArrayRegion = 199;
+ public static final int GetByteArrayRegion = 200;
+ public static final int GetCharArrayRegion = 201;
+ public static final int GetShortArrayRegion = 202;
+ public static final int GetIntArrayRegion = 203;
+ public static final int GetLongArrayRegion = 204;
+ public static final int GetFloatArrayRegion = 205;
+ public static final int GetDoubleArrayRegion = 206;
+ public static final int SetBooleanArrayRegion = 207;
+ public static final int SetByteArrayRegion = 208;
+ public static final int SetCharArrayRegion = 209;
+ public static final int SetShortArrayRegion = 210;
+ public static final int SetIntArrayRegion = 211;
+ public static final int SetLongArrayRegion = 212;
+ public static final int SetFloatArrayRegion = 213;
+ public static final int SetDoubleArrayRegion = 214;
+ public static final int RegisterNatives = 215;
+ public static final int UnregisterNatives = 216;
+ public static final int MonitorEnter = 217;
+ public static final int MonitorExit = 218;
+ public static final int GetJavaVM = 219;
+ public static final int GetStringRegion = 220;
+ public static final int GetStringUTFRegion = 221;
+ public static final int GetPrimitiveArrayCritical = 222;
+ public static final int ReleasePrimitiveArrayCritical = 223;
+ public static final int GetStringCritical = 224;
+ public static final int ReleaseStringCritical = 225;
+ public static final int NewWeakGlobalRef = 226;
+ public static final int DeleteWeakGlobalRef = 227;
+ public static final int ExceptionCheck = 228;
+ public static final int NewDirectByteBuffer = 229;
+ public static final int GetDirectBufferAddress = 230;
+ public static final int GetDirectBufferCapacity = 231;
+ public static final int GetObjectRefType = 232;
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/LibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/LibraryLoader.java
new file mode 100644
index 0000000..af2a039
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/LibraryLoader.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.LibraryOption;
+
+import java.util.Map;
+
+public abstract class LibraryLoader {
+ abstract <T> T loadLibrary(NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions);
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/LocalVariable.java b/src/main/java/jnr/ffi/provider/jffi/LocalVariable.java
new file mode 100644
index 0000000..993fa77
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/LocalVariable.java
@@ -0,0 +1,14 @@
+package jnr.ffi.provider.jffi;
+
+/**
+ *
+ */
+class LocalVariable {
+ final Class type;
+ final int idx;
+
+ public LocalVariable(Class type, int idx) {
+ this.type = type;
+ this.idx = idx;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/LocalVariableAllocator.java b/src/main/java/jnr/ffi/provider/jffi/LocalVariableAllocator.java
new file mode 100644
index 0000000..1a8bc4e
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/LocalVariableAllocator.java
@@ -0,0 +1,32 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.provider.SigType;
+
+/**
+ *
+ */
+class LocalVariableAllocator {
+ private int nextIndex;
+
+ LocalVariableAllocator(SigType[] parameterTypes) {
+ this.nextIndex = AsmUtil.calculateLocalVariableSpace(parameterTypes) + 1;
+ }
+
+ LocalVariableAllocator(Class... parameterTypes) {
+ this.nextIndex = AsmUtil.calculateLocalVariableSpace(parameterTypes) + 1;
+ }
+
+ LocalVariableAllocator(int nextIndex) {
+ this.nextIndex = nextIndex;
+ }
+
+ LocalVariable allocate(Class type) {
+ LocalVariable var = new LocalVariable(type, nextIndex);
+ this.nextIndex += AsmUtil.calculateLocalVariableSpace(type);
+ return var;
+ }
+
+ int getSpaceUsed() {
+ return nextIndex;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/MemoryUtil.java b/src/main/java/jnr/ffi/provider/jffi/MemoryUtil.java
new file mode 100644
index 0000000..0d11259
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/MemoryUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.provider.BoundedMemoryIO;
+import jnr.ffi.provider.NullMemoryIO;
+
+public final class MemoryUtil {
+ private MemoryUtil() {}
+
+ static jnr.ffi.Pointer newPointer(jnr.ffi.Runtime runtime, long ptr) {
+ return ptr != 0 ? new DirectMemoryIO(runtime, ptr) : null;
+ }
+
+ static jnr.ffi.Pointer newPointer(jnr.ffi.Runtime runtime, int ptr) {
+ return ptr != 0 ? new DirectMemoryIO(runtime, ptr) : null;
+ }
+
+ static jnr.ffi.Pointer newPointer(jnr.ffi.Runtime runtime, long ptr, long size) {
+ return ptr != 0 ? new BoundedMemoryIO(new DirectMemoryIO(runtime, ptr), 0, size) : null;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/MethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/MethodGenerator.java
new file mode 100644
index 0000000..b76c733
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/MethodGenerator.java
@@ -0,0 +1,16 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+
+/**
+ *
+ */
+public interface MethodGenerator {
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention);
+ public void generate(AsmBuilder builder, String functionName, Function function,
+ ResultType resultType, ParameterType[] parameterTypes, boolean ignoreError);
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeClosureFactory.java b/src/main/java/jnr/ffi/provider/jffi/NativeClosureFactory.java
new file mode 100644
index 0000000..9c4bc70
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeClosureFactory.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.CallContext;
+import com.kenai.jffi.Closure;
+import com.kenai.jffi.ClosureMagazine;
+import com.kenai.jffi.ClosureManager;
+import jnr.ffi.Pointer;
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.mapper.SignatureTypeMapper;
+import jnr.ffi.provider.FromNativeType;
+import jnr.ffi.provider.ToNativeType;
+import jnr.ffi.util.ref.FinalizableWeakReference;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
+import static jnr.ffi.provider.jffi.ClosureUtil.getParameterType;
+import static jnr.ffi.provider.jffi.ClosureUtil.getResultType;
+import static jnr.ffi.provider.jffi.InvokerUtil.getCallContext;
+import static jnr.ffi.provider.jffi.InvokerUtil.getNativeCallingConvention;
+
+/**
+ *
+ */
+public final class NativeClosureFactory<T> {
+ private final jnr.ffi.Runtime runtime;
+ private final ConcurrentMap<Integer, ClosureReference> closures = new ConcurrentHashMap<Integer, ClosureReference>();
+ private final CallContext callContext;
+ private final NativeClosureProxy.Factory closureProxyFactory;
+ private final ConcurrentLinkedQueue<NativeClosurePointer> freeQueue = new ConcurrentLinkedQueue<NativeClosurePointer>();
+ private ClosureMagazine currentMagazine;
+
+
+ protected NativeClosureFactory(jnr.ffi.Runtime runtime, CallContext callContext,
+ NativeClosureProxy.Factory closureProxyFactory) {
+ this.runtime = runtime;
+ this.closureProxyFactory = closureProxyFactory;
+ this.callContext = callContext;
+ }
+
+ static <T> NativeClosureFactory newClosureFactory(jnr.ffi.Runtime runtime, Class<T> closureClass,
+ SignatureTypeMapper typeMapper, AsmClassLoader classLoader) {
+
+ Method callMethod = null;
+ for (Method m : closureClass.getMethods()) {
+ if (m.isAnnotationPresent(Delegate.class) && Modifier.isPublic(m.getModifiers())
+ && !Modifier.isStatic(m.getModifiers())) {
+ callMethod = m;
+ break;
+ }
+ }
+ if (callMethod == null) {
+ throw new NoSuchMethodError("no public non-static delegate method defined in " + closureClass.getName());
+ }
+
+ Class[] parameterTypes = callMethod.getParameterTypes();
+ FromNativeType[] parameterSigTypes = new FromNativeType[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ parameterSigTypes[i] = getParameterType(runtime, callMethod, i, typeMapper);
+ }
+ ToNativeType resultType = getResultType(runtime, callMethod, typeMapper);
+
+ return new NativeClosureFactory(runtime, getCallContext(resultType, parameterSigTypes, getNativeCallingConvention(callMethod), false),
+ NativeClosureProxy.newProxyFactory(runtime, callMethod, resultType, parameterSigTypes, classLoader));
+ }
+
+ private void expunge(ClosureReference ref, Integer key) {
+ // Fast case - no chained elements; can just remove from the hash map
+ if (ref.next == null && closures.remove(key, ref)) {
+ return;
+ }
+
+ // Remove from chained list
+ synchronized (closures) {
+ for (ClosureReference clref = closures.get(key), prev = clref; clref != null; prev = clref, clref = clref.next) {
+ if (clref == ref) {
+ if (prev != clref) {
+ // if not first element in list, just remove this one
+ prev.next = clref.next;
+
+ } else {
+ // first element in list, replace with the next if non-null, else remove from map
+ if (clref.next != null) {
+ closures.replace(key, clref, clref.next);
+ } else {
+ closures.remove(key, clref);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private void recycle(NativeClosurePointer ptr) {
+ freeQueue.add(ptr);
+ }
+
+ final class ClosureReference extends FinalizableWeakReference<Object> {
+ volatile ClosureReference next;
+ private final NativeClosureFactory factory;
+ private final NativeClosurePointer pointer;
+ private final Integer key;
+
+
+ private ClosureReference(Object referent, Integer key, NativeClosureFactory factory,
+ NativeClosurePointer pointer) {
+ super(referent, NativeFinalizer.getInstance().getFinalizerQueue());
+ this.factory = factory;
+ this.key = key;
+ this.pointer = pointer;
+ }
+
+ public void finalizeReferent() {
+ clear();
+ factory.expunge(this, key);
+ factory.recycle(pointer);
+ }
+
+ Object getCallable() {
+ return get();
+ }
+
+ Pointer getPointer() {
+ return pointer;
+ }
+ }
+
+ NativeClosurePointer allocateClosurePointer() {
+ NativeClosurePointer closurePointer = freeQueue.poll();
+ if (closurePointer != null) {
+ return closurePointer;
+ }
+
+ NativeClosureProxy proxy = closureProxyFactory.newClosureProxy();
+ Closure.Handle closureHandle = null;
+
+ synchronized (this) {
+ do {
+ if (currentMagazine == null || ((closureHandle = currentMagazine.allocate(proxy)) == null)) {
+ currentMagazine = ClosureManager.getInstance().newClosureMagazine(callContext,
+ closureProxyFactory.getInvokeMethod());
+ }
+ } while (closureHandle == null);
+ }
+
+ return new NativeClosurePointer(runtime, closureHandle, proxy);
+ }
+
+ NativeClosurePointer newClosure(Object callable, Integer key) {
+ return newClosureReference(callable, key).pointer;
+ }
+
+ ClosureReference newClosureReference(Object callable, Integer key) {
+
+ NativeClosurePointer ptr = allocateClosurePointer();
+ ClosureReference ref = new ClosureReference(callable, key, this, ptr);
+ ptr.proxy.closureReference = ref;
+ if (closures.putIfAbsent(key, ref) == null) {
+ return ref;
+ }
+
+ synchronized (closures) {
+ do {
+ // prepend and make new pointer the list head
+ ref.next = closures.get(key);
+
+ // If old value already removed (e.g. by expunge), just put the new value in
+ if (ref.next == null && closures.putIfAbsent(key, ref) == null) {
+ break;
+ }
+ } while (!closures.replace(key, ref.next, ref));
+ }
+
+ return ref;
+ }
+
+ ClosureReference getClosureReference(Object callable) {
+ Integer key = System.identityHashCode(callable);
+ ClosureReference ref = closures.get(key);
+ if (ref != null) {
+ // Simple case - no identity hash code clash - just return the ptr
+ if (ref.getCallable() == callable) {
+ return ref;
+ }
+
+ // There has been a key clash, search the list
+ synchronized (closures) {
+ while ((ref = ref.next) != null) {
+ if (ref.getCallable() == callable) {
+ return ref;
+ }
+ }
+ }
+ }
+
+ return newClosureReference(callable, key);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeClosureManager.java b/src/main/java/jnr/ffi/provider/jffi/NativeClosureManager.java
new file mode 100644
index 0000000..93b418e
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeClosureManager.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.ClosureManager;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+final class NativeClosureManager implements ClosureManager {
+ private volatile Map<Class<?>, NativeClosureFactory> factories = new IdentityHashMap<Class<?>, NativeClosureFactory>();
+ private final jnr.ffi.Runtime runtime;
+ private final SignatureTypeMapper typeMapper;
+ private final AsmClassLoader classLoader;
+
+ NativeClosureManager(jnr.ffi.Runtime runtime, SignatureTypeMapper typeMapper, AsmClassLoader classLoader) {
+ this.runtime = runtime;
+ this.typeMapper = new CompositeTypeMapper(typeMapper, new CachingTypeMapper(new ClosureTypeMapper()));
+ this.classLoader = classLoader;
+ }
+
+ <T> NativeClosureFactory<T> getClosureFactory(Class<T> closureClass) {
+ NativeClosureFactory<T> factory = factories.get(closureClass);
+ if (factory != null) {
+ return factory;
+ }
+
+ return initClosureFactory(closureClass);
+ }
+
+ public <T> T newClosure(Class<? extends T> closureClass, T instance) {
+ NativeClosureFactory<T> factory = factories.get(closureClass);
+ if (factory != null) {
+ //return factory.newClosure(instance);
+ }
+ return null;
+ }
+
+ public final <T> jnr.ffi.Pointer getClosurePointer(Class<? extends T> closureClass, T instance) {
+ return getClosureFactory(closureClass).getClosureReference(instance).getPointer();
+ }
+
+ synchronized <T> NativeClosureFactory<T> initClosureFactory(Class<T> closureClass) {
+ NativeClosureFactory<T> factory = factories.get(closureClass);
+ if (factory != null) {
+ return factory;
+ }
+
+
+ factory = NativeClosureFactory.newClosureFactory(runtime, closureClass, typeMapper, classLoader);
+ Map<Class<?>, NativeClosureFactory> factories = new IdentityHashMap<Class<?>, NativeClosureFactory>();
+ factories.putAll(this.factories);
+ factories.put(closureClass, factory);
+ this.factories = factories;
+
+ return factory;
+ }
+
+ <T> ToNativeConverter<T, Pointer> newClosureSite(Class<T> closureClass) {
+ return new ClosureSite<T>(getClosureFactory(closureClass));
+ }
+
+ @ToNativeConverter.NoContext
+ public static final class ClosureSite<T> implements ToNativeConverter<T, Pointer> {
+ private final NativeClosureFactory<T> factory;
+ private NativeClosureFactory.ClosureReference closureReference = null;
+
+ private ClosureSite(NativeClosureFactory<T> factory) {
+ this.factory = factory;
+ }
+
+ public Pointer toNative(T value, ToNativeContext context) {
+ // If passing down a function pointer, don't re-wrap it
+ if (value instanceof ClosureFromNativeConverter.AbstractClosurePointer) {
+ return (ClosureFromNativeConverter.AbstractClosurePointer) value;
+ }
+
+ NativeClosureFactory.ClosureReference ref = closureReference;
+ // Fast path - same delegate as last call to this site - just re-use the native closure
+ if (ref != null && ref.getCallable() == value) {
+ return ref.getPointer();
+ }
+
+ ref = factory.getClosureReference(value);
+ // Cache the new native closure, if this site has no valid native closure
+ if (closureReference == null || closureReference.get() == null) {
+ closureReference = ref;
+ }
+
+ return ref.getPointer();
+ }
+
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeClosurePointer.java b/src/main/java/jnr/ffi/provider/jffi/NativeClosurePointer.java
new file mode 100644
index 0000000..624d353
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeClosurePointer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.provider.InAccessibleMemoryIO;
+
+/**
+ *
+ */
+class NativeClosurePointer extends InAccessibleMemoryIO {
+ private final com.kenai.jffi.Closure.Handle handle;
+ final NativeClosureProxy proxy;
+
+
+ public NativeClosurePointer(jnr.ffi.Runtime runtime, com.kenai.jffi.Closure.Handle handle, NativeClosureProxy proxy) {
+ super(runtime, handle.getAddress(), true);
+ this.handle = handle;
+ this.proxy = proxy;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeClosureProxy.java b/src/main/java/jnr/ffi/provider/jffi/NativeClosureProxy.java
new file mode 100644
index 0000000..96308eb
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeClosureProxy.java
@@ -0,0 +1,276 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.FromNativeType;
+import jnr.ffi.provider.ToNativeType;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.PrintWriter;
+import java.lang.ref.Reference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.AsmUtil.*;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.NumberUtil.convertPrimitive;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+import static org.objectweb.asm.Opcodes.*;
+
+/**
+ *
+ */
+public abstract class NativeClosureProxy {
+ protected final jnr.ffi.Runtime runtime;
+ volatile Reference<?> closureReference;
+
+ protected NativeClosureProxy(NativeRuntime runtime) {
+ this.runtime = runtime;
+ }
+
+ protected Object getCallable() {
+ Object callable = closureReference != null ? closureReference.get() : null;
+ if (callable != null) {
+ return callable;
+ }
+ throw new NullPointerException("callable is null");
+ }
+
+ static class Factory {
+ private final jnr.ffi.Runtime runtime;
+ private final Constructor<? extends NativeClosureProxy> constructor;
+ private final Object[] objectFields;
+ private final Method invokeMethod;
+
+ Factory(jnr.ffi.Runtime runtime, Constructor<? extends NativeClosureProxy> constructor,
+ Method invokeMethod, Object[] objectFields) {
+ this.runtime = runtime;
+ this.constructor = constructor;
+ this.invokeMethod = invokeMethod;
+ this.objectFields = objectFields;
+ }
+
+ NativeClosureProxy newClosureProxy() {
+ try {
+ return constructor.newInstance(runtime, objectFields);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ Method getInvokeMethod() {
+ return invokeMethod;
+ }
+ }
+
+ public final static boolean DEBUG = Boolean.getBoolean("jnr.ffi.compile.dump");
+ private static final AtomicLong nextClassID = new AtomicLong(0);
+
+ static Factory newProxyFactory(jnr.ffi.Runtime runtime, Method callMethod,
+ ToNativeType resultType, FromNativeType[] parameterTypes, AsmClassLoader classLoader) {
+ final String closureProxyClassName = p(NativeClosureProxy.class) + "$$impl$$" + nextClassID.getAndIncrement();
+ final ClassWriter closureClassWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ final ClassVisitor closureClassVisitor = DEBUG ? AsmUtil.newCheckClassAdapter(closureClassWriter) : closureClassWriter;
+ AsmBuilder builder = new AsmBuilder(runtime, closureProxyClassName, closureClassVisitor, classLoader);
+
+ closureClassVisitor.visit(V1_6, ACC_PUBLIC | ACC_FINAL, closureProxyClassName, null, p(NativeClosureProxy.class),
+ new String[]{ });
+
+ Class[] nativeParameterClasses = new Class[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ nativeParameterClasses[i] = getNativeClass(parameterTypes[i].getNativeType());
+ }
+
+ Class nativeResultClass = getNativeClass(resultType.getNativeType());
+
+ SkinnyMethodAdapter mv = new SkinnyMethodAdapter(closureClassVisitor, ACC_PUBLIC | ACC_FINAL, "invoke",
+ sig(nativeResultClass, nativeParameterClasses),
+ null, null);
+ mv.start();
+
+ // Cast the callable instance to the correct class
+ mv.aload(0);
+ mv.invokevirtual(NativeClosureProxy.class, "getCallable", Object.class);
+ mv.checkcast(p(callMethod.getDeclaringClass()));
+
+ LocalVariable[] parameterVariables = AsmUtil.getParameterVariables(nativeParameterClasses);
+
+ // Construct callback method
+ LocalVariableAllocator localVariableAllocator = new LocalVariableAllocator(nativeParameterClasses);
+
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ FromNativeType parameterType = parameterTypes[i];
+ Class parameterClass = parameterType.effectiveJavaType();
+
+ if (!isParameterTypeSupported(parameterClass)) {
+ throw new IllegalArgumentException("unsupported closure parameter type " + parameterTypes[i].getDeclaredType());
+ }
+
+ AsmUtil.load(mv, nativeParameterClasses[i], parameterVariables[i]);
+ if (!parameterClass.isPrimitive()) {
+ emitFromNativeConversion(builder, mv, parameterTypes[i], nativeParameterClasses[i]);
+ } else {
+ convertPrimitive(mv, nativeParameterClasses[i], parameterClass, parameterType.getNativeType());
+ }
+ }
+
+ // dispatch to java method
+ if (callMethod.getDeclaringClass().isInterface()) {
+ mv.invokeinterface(p(callMethod.getDeclaringClass()), callMethod.getName(), sig(callMethod.getReturnType(), callMethod.getParameterTypes()));
+ } else {
+ mv.invokevirtual(p(callMethod.getDeclaringClass()), callMethod.getName(), sig(callMethod.getReturnType(), callMethod.getParameterTypes()));
+ }
+
+ if (!isReturnTypeSupported(resultType.effectiveJavaType())) {
+ throw new IllegalArgumentException("unsupported closure return type " + resultType.getDeclaredType());
+ }
+
+ emitToNativeConversion(builder, mv, resultType);
+ if (!resultType.effectiveJavaType().isPrimitive()) {
+ if (Number.class.isAssignableFrom(resultType.effectiveJavaType())) {
+ AsmUtil.unboxNumber(mv, resultType.effectiveJavaType(), nativeResultClass, resultType.getNativeType());
+
+ } else if (Boolean.class.isAssignableFrom(resultType.effectiveJavaType())) {
+ AsmUtil.unboxBoolean(mv, nativeResultClass);
+
+ } else if (Pointer.class.isAssignableFrom(resultType.effectiveJavaType())) {
+ AsmUtil.unboxPointer(mv, nativeResultClass);
+
+ }
+ }
+
+ emitReturnOp(mv, nativeResultClass);
+ mv.visitMaxs(10, 10 + localVariableAllocator.getSpaceUsed());
+ mv.visitEnd();
+
+ SkinnyMethodAdapter closureInit = new SkinnyMethodAdapter(closureClassVisitor, ACC_PUBLIC, "<init>",
+ sig(void.class, NativeRuntime.class, Object[].class),
+ null, null);
+ closureInit.start();
+ closureInit.aload(0);
+ closureInit.aload(1);
+ closureInit.invokespecial(p(NativeClosureProxy.class), "<init>", sig(void.class, NativeRuntime.class));
+
+ AsmBuilder.ObjectField[] fields = builder.getObjectFieldArray();
+ Object[] fieldObjects = new Object[fields.length];
+ for (int i = 0; i < fieldObjects.length; i++) {
+ fieldObjects[i] = fields[i].value;
+ String fieldName = fields[i].name;
+ builder.getClassVisitor().visitField(ACC_PRIVATE | ACC_FINAL, fieldName, ci(fields[i].klass), null, null);
+ closureInit.aload(0);
+ closureInit.aload(2);
+ closureInit.pushInt(i);
+ closureInit.aaload();
+ if (fields[i].klass.isPrimitive()) {
+ Class unboxedType = unboxedType(fields[i].klass);
+ closureInit.checkcast(unboxedType);
+ unboxNumber(closureInit, unboxedType, fields[i].klass);
+ } else {
+ closureInit.checkcast(fields[i].klass);
+ }
+ closureInit.putfield(builder.getClassNamePath(), fieldName, ci(fields[i].klass));
+ }
+
+ closureInit.voidreturn();
+ closureInit.visitMaxs(10, 10);
+ closureInit.visitEnd();
+
+ closureClassVisitor.visitEnd();
+
+ try {
+ byte[] closureImpBytes = closureClassWriter.toByteArray();
+ if (DEBUG) {
+ ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
+ new ClassReader(closureImpBytes).accept(trace, 0);
+ }
+ ClassLoader cl = NativeClosureFactory.class.getClassLoader();
+ if (cl == null) {
+ cl = Thread.currentThread().getContextClassLoader();
+ }
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ Class<? extends NativeClosureProxy> klass = builder.getClassLoader().defineClass(c(closureProxyClassName), closureImpBytes);
+ Constructor<? extends NativeClosureProxy> constructor
+ = klass.getConstructor(NativeRuntime.class, Object[].class);
+
+ return new Factory(runtime, constructor, klass.getMethod("invoke", nativeParameterClasses), fieldObjects);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+
+
+ private static boolean isReturnTypeSupported(Class type) {
+ return type.isPrimitive()
+ || boolean.class == type || Boolean.class == type
+ || Byte.class == type
+ || Short.class == type || Integer.class == type
+ || Long.class == type || Float.class == type
+ || Double.class == type
+ || Pointer.class == type
+ ;
+ }
+
+ private static boolean isParameterTypeSupported(Class type) {
+ return type.isPrimitive()
+ || boolean.class == type || Boolean.class == type
+ || Byte.class == type
+ || Short.class == type || Integer.class == type
+ || Long.class == type || Float.class == type
+ || Double.class == type
+ || Pointer.class == type
+ /*
+ || CharSequence.class == type
+ || Buffer.class.isAssignableFrom(type)
+ || (type.isArray() && type.getComponentType().isPrimitive())
+ || (type.isArray() && Struct.class.isAssignableFrom(type.getComponentType()))
+ || (type.isArray() && Pointer.class.isAssignableFrom(type.getComponentType()))
+ || (type.isArray() && CharSequence.class.isAssignableFrom(type.getComponentType()))
+ || ByReference.class.isAssignableFrom(type)
+ */
+ ;
+ }
+
+ static Class getNativeClass(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ return byte.class;
+
+ case SSHORT:
+ case USHORT:
+ return short.class;
+
+ case SINT:
+ case UINT:
+ return int.class;
+
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ return sizeof(nativeType) <= 4 ? int.class : long.class;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return long.class;
+
+
+ case FLOAT:
+ return float.class;
+
+ case DOUBLE:
+ return double.class;
+
+ case VOID:
+ return void.class;
+
+ default:
+ throw new IllegalArgumentException("unsupported native type: " + nativeType);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeFinalizer.java b/src/main/java/jnr/ffi/provider/jffi/NativeFinalizer.java
new file mode 100644
index 0000000..a79e6aa
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeFinalizer.java
@@ -0,0 +1,22 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.util.ref.FinalizableReferenceQueue;
+
+/**
+ *
+ */
+class NativeFinalizer {
+ private final FinalizableReferenceQueue finalizerQueue = new FinalizableReferenceQueue();
+
+ private static final class SingletonHolder {
+ private static final NativeFinalizer INSTANCE = new NativeFinalizer();
+ }
+
+ public static NativeFinalizer getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ public FinalizableReferenceQueue getFinalizerQueue() {
+ return finalizerQueue;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeFunctionMapperContext.java b/src/main/java/jnr/ffi/provider/jffi/NativeFunctionMapperContext.java
new file mode 100644
index 0000000..0cf54cd
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeFunctionMapperContext.java
@@ -0,0 +1,30 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Library;
+import jnr.ffi.mapper.FunctionMapper;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+public final class NativeFunctionMapperContext implements FunctionMapper.Context {
+ private final NativeLibrary library;
+
+ private final Collection<Annotation> annotations;
+
+ public NativeFunctionMapperContext(NativeLibrary library, Collection<Annotation> annotations) {
+ this.library = library;
+ this.annotations = annotations;
+ }
+
+ public Library getLibrary() {
+ return null;
+ }
+
+ public boolean isSymbolPresent(String name) {
+ return library.getSymbolAddress(name) != 0L;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java b/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java
new file mode 100644
index 0000000..97011e0
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeLibrary.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Platform;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NativeLibrary {
+ private final List<String> libraryNames;
+ private final List<String> searchPaths;
+
+ private volatile List<com.kenai.jffi.Library> nativeLibraries = Collections.emptyList();
+
+ NativeLibrary(Collection<String> libraryNames, Collection<String> searchPaths) {
+ this.libraryNames = Collections.unmodifiableList(new ArrayList<String>(libraryNames));
+ this.searchPaths = Collections.unmodifiableList(new ArrayList<String>(searchPaths));
+ }
+
+ private String locateLibrary(String libraryName) {
+ if (new File(libraryName).isAbsolute()) {
+ return libraryName;
+ }
+
+ return Platform.getNativePlatform().locateLibrary(libraryName, searchPaths);
+ }
+
+ long getSymbolAddress(String name) {
+ for (com.kenai.jffi.Library l : getNativeLibraries()) {
+ long address = l.getSymbolAddress(name);
+ if (address != 0) {
+ return address;
+ }
+ }
+ return 0;
+ }
+
+ long findSymbolAddress(String name) {
+ long address = getSymbolAddress(name);
+ if (address == 0) {
+ throw new SymbolNotFoundError(com.kenai.jffi.Library.getLastError());
+ }
+ return address;
+ }
+
+ private synchronized List<com.kenai.jffi.Library> getNativeLibraries() {
+ if (!this.nativeLibraries.isEmpty()) {
+ return nativeLibraries;
+ }
+ return nativeLibraries = loadNativeLibraries();
+ }
+
+ private synchronized List<com.kenai.jffi.Library> loadNativeLibraries() {
+ List<com.kenai.jffi.Library> libs = new ArrayList<com.kenai.jffi.Library>();
+
+ for (String libraryName : libraryNames) {
+ com.kenai.jffi.Library lib;
+
+ lib = openLibrary(libraryName);
+ if (lib == null) {
+ String path;
+ if (libraryName != null && (path = locateLibrary(libraryName)) != null && !libraryName.equals(path)) {
+ lib = openLibrary(path);
+ }
+ }
+ if (lib == null) {
+ throw new UnsatisfiedLinkError(com.kenai.jffi.Library.getLastError());
+ }
+ libs.add(lib);
+ }
+
+ return Collections.unmodifiableList(libs);
+ }
+
+ private static final Pattern BAD_ELF = Pattern.compile("(.*): invalid ELF header");
+ private static final Pattern ELF_GROUP = Pattern.compile("GROUP\\s*\\(\\s*(\\S*).*\\)");
+
+ private static com.kenai.jffi.Library openLibrary(String path) {
+ com.kenai.jffi.Library lib;
+
+ lib = com.kenai.jffi.Library.getCachedInstance(path, com.kenai.jffi.Library.LAZY | com.kenai.jffi.Library.GLOBAL);
+ if (lib != null) {
+ return lib;
+ }
+
+ // If dlopen() fails with 'invalid ELF header', then it is likely to be a ld script - parse it for the real library path
+ Matcher badElf = BAD_ELF.matcher(com.kenai.jffi.Library.getLastError());
+ if (badElf.lookingAt()) {
+ File f = new File(badElf.group(1));
+ if (f.isFile() && f.length() < (4 * 1024)) {
+ Matcher sharedObject = ELF_GROUP.matcher(readAll(f));
+ if (sharedObject.lookingAt()) {
+ return com.kenai.jffi.Library.getCachedInstance(sharedObject.group(1), com.kenai.jffi.Library.LAZY | com.kenai.jffi.Library.GLOBAL);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static String readAll(File f) {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ return sb.toString();
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+
+ } finally {
+ if (br != null) try { br.close(); } catch (IOException e) { throw new RuntimeException(e); }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeLibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/NativeLibraryLoader.java
new file mode 100644
index 0000000..82698a7
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeLibraryLoader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.*;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static jnr.ffi.provider.jffi.Util.getBooleanProperty;
+
+/**
+ *
+ */
+class NativeLibraryLoader<T> extends jnr.ffi.LibraryLoader<T> {
+ static final boolean ASM_ENABLED = getBooleanProperty("jnr.ffi.asm.enabled", true);
+
+ NativeLibraryLoader(Class<T> interfaceClass) {
+ super(interfaceClass);
+ }
+
+ public T loadLibrary(Class<T> interfaceClass, Collection<String> libraryNames, Collection<String> searchPaths,
+ Map<LibraryOption, Object> options) {
+ NativeLibrary nativeLibrary = new NativeLibrary(libraryNames, searchPaths);
+
+ try {
+ return ASM_ENABLED
+ ? new AsmLibraryLoader().loadLibrary(nativeLibrary, interfaceClass, options)
+ : new ReflectionLibraryLoader().loadLibrary(nativeLibrary, interfaceClass, options);
+
+ } catch (RuntimeException ex) {
+ throw ex;
+
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeMemoryManager.java b/src/main/java/jnr/ffi/provider/jffi/NativeMemoryManager.java
new file mode 100644
index 0000000..55468a5
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeMemoryManager.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.BoundedMemoryIO;
+import jnr.ffi.provider.IntPointer;
+
+import java.nio.ByteBuffer;
+
+public class NativeMemoryManager implements jnr.ffi.provider.MemoryManager {
+ private final jnr.ffi.Runtime runtime;
+ private final long addressMask;
+
+ public NativeMemoryManager(NativeRuntime runtime) {
+ this.runtime = runtime;
+ this.addressMask = runtime.addressMask();
+ }
+
+ public Pointer allocate(int size) {
+ return new ArrayMemoryIO(runtime, size);
+ }
+
+ public Pointer allocateDirect(int size) {
+ return new BoundedMemoryIO(TransientNativeMemory.allocate(runtime, size, 8, true), 0, size);
+ }
+
+ public Pointer allocateDirect(int size, boolean clear) {
+ return new BoundedMemoryIO(TransientNativeMemory.allocate(runtime, size, 8, clear), 0, size);
+ }
+
+ public Pointer allocateTemporary(int size) {
+ return new BoundedMemoryIO(TransientNativeMemory.allocate(runtime, size, 8, true), 0, size);
+ }
+
+ public Pointer allocateTemporary(int size, boolean clear) {
+ return new BoundedMemoryIO(TransientNativeMemory.allocate(runtime, size, 8, clear), 0, size);
+ }
+
+ public Pointer newPointer(ByteBuffer buffer) {
+ return new ByteBufferMemoryIO(runtime, buffer);
+ }
+
+ public Pointer newPointer(long address) {
+ return new DirectMemoryIO(runtime, address & addressMask);
+ }
+
+ public Pointer newPointer(long address, long size) {
+ return new BoundedMemoryIO(new DirectMemoryIO(runtime, address & addressMask), 0, size);
+ }
+
+ public Pointer newOpaquePointer(long address) {
+ return new IntPointer(runtime, address);
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NativeRuntime.java b/src/main/java/jnr/ffi/provider/jffi/NativeRuntime.java
new file mode 100644
index 0000000..a564e6a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NativeRuntime.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.mapper.DefaultTypeMapper;
+import jnr.ffi.mapper.SignatureTypeMapperAdapter;
+import jnr.ffi.provider.AbstractRuntime;
+import jnr.ffi.provider.BadType;
+import jnr.ffi.provider.DefaultObjectReferenceManager;
+
+import java.lang.reflect.Field;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+public final class NativeRuntime extends AbstractRuntime {
+ private final NativeMemoryManager mm = new NativeMemoryManager(this);
+ private final NativeClosureManager closureManager = new NativeClosureManager(this,
+ new SignatureTypeMapperAdapter(new DefaultTypeMapper()),
+ new AsmClassLoader(ClassLoader.getSystemClassLoader()));
+ private final Type[] aliases;
+
+ public static NativeRuntime getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ private static final class SingletonHolder {
+ public static final NativeRuntime INSTANCE = new NativeRuntime();
+ }
+
+ private NativeRuntime() {
+ super(ByteOrder.nativeOrder(), buildTypeMap());
+ NativeType[] nativeAliases = buildNativeTypeAliases();
+ EnumSet<TypeAlias> typeAliasSet = EnumSet.allOf(TypeAlias.class);
+ aliases = new Type[typeAliasSet.size()];
+
+ for (TypeAlias alias : typeAliasSet) {
+ if (nativeAliases.length > alias.ordinal() && nativeAliases[alias.ordinal()] != NativeType.VOID) {
+ aliases[alias.ordinal()] = findType(nativeAliases[alias.ordinal()]);
+ } else {
+ aliases[alias.ordinal()] = new BadType(alias.name());
+ }
+ }
+ }
+
+ private static EnumMap<NativeType, Type> buildTypeMap() {
+ EnumMap<NativeType, Type> typeMap = new EnumMap<NativeType, Type>(NativeType.class);
+ EnumSet<NativeType> nativeTypes = EnumSet.allOf(NativeType.class);
+
+ for (NativeType t : nativeTypes) {
+ typeMap.put(t, jafflType(t));
+ }
+
+ return typeMap;
+ }
+
+ private static NativeType[] buildNativeTypeAliases() {
+ Platform platform = Platform.getNativePlatform();
+ Package pkg = NativeRuntime.class.getPackage();
+ String cpu = platform.getCPU().toString();
+ String os = platform.getOS().toString();
+ EnumSet<TypeAlias> typeAliases = EnumSet.allOf(TypeAlias.class);
+ NativeType[] aliases = {};
+ Class cls;
+ try {
+ cls = Class.forName(pkg.getName() + ".platform." + cpu + "." + os + ".TypeAliases");
+ Field aliasesField = cls.getField("ALIASES");
+ Map aliasMap = Map.class.cast(aliasesField.get(cls));
+ aliases = new NativeType[typeAliases.size()];
+ for (TypeAlias t : typeAliases) {
+ aliases[t.ordinal()] = (NativeType) aliasMap.get(t);
+ if (aliases[t.ordinal()] == null) {
+ aliases[t.ordinal()] = NativeType.VOID;
+ }
+ }
+ } catch (ClassNotFoundException cne) {
+ Logger.getLogger(NativeRuntime.class.getName()).log(Level.SEVERE, "failed to load type aliases: " + cne);
+ } catch (NoSuchFieldException nsfe) {
+ Logger.getLogger(NativeRuntime.class.getName()).log(Level.SEVERE, "failed to load type aliases: " + nsfe);
+ } catch (IllegalAccessException iae) {
+ Logger.getLogger(NativeRuntime.class.getName()).log(Level.SEVERE, "failed to load type aliases: " + iae);
+ }
+
+ return aliases;
+ }
+
+ @Override
+ public Type findType(TypeAlias type) {
+ return aliases[type.ordinal()];
+ }
+
+ public final NativeMemoryManager getMemoryManager() {
+ return mm;
+ }
+
+ public NativeClosureManager getClosureManager() {
+ return closureManager;
+ }
+
+ @Override
+ public ObjectReferenceManager newObjectReferenceManager() {
+ return new DefaultObjectReferenceManager(this);
+ }
+
+ @Override
+ public int getLastError() {
+ return com.kenai.jffi.LastError.getInstance().get();
+ }
+
+
+ @Override
+ public void setLastError(int error) {
+ com.kenai.jffi.LastError.getInstance().set(error);
+ }
+
+ @Override
+ public boolean isCompatible(Runtime other) {
+ return other instanceof NativeRuntime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ NativeRuntime that = (NativeRuntime) o;
+
+ return Arrays.equals(aliases, that.aliases) && closureManager.equals(that.closureManager) && mm.equals(that.mm);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mm.hashCode();
+ result = 31 * result + closureManager.hashCode();
+ result = 31 * result + Arrays.hashCode(aliases);
+ return result;
+ }
+
+ private static final class TypeDelegate extends jnr.ffi.Type {
+ private final com.kenai.jffi.Type type;
+ private final NativeType nativeType;
+
+ public TypeDelegate(com.kenai.jffi.Type type, NativeType nativeType) {
+ this.type = type;
+ this.nativeType = nativeType;
+ }
+
+ public int alignment() {
+ return type.alignment();
+ }
+
+ public int size() {
+ return type.size();
+ }
+
+ public NativeType getNativeType() {
+ return nativeType;
+ }
+
+ public String toString() {
+ return type.toString();
+ }
+ }
+
+ private static jnr.ffi.Type jafflType(NativeType type) {
+ switch (type) {
+ case VOID:
+ return new TypeDelegate(com.kenai.jffi.Type.VOID, NativeType.VOID);
+ case SCHAR:
+ return new TypeDelegate(com.kenai.jffi.Type.SCHAR, NativeType.SCHAR);
+ case UCHAR:
+ return new TypeDelegate(com.kenai.jffi.Type.UCHAR, NativeType.UCHAR);
+ case SSHORT:
+ return new TypeDelegate(com.kenai.jffi.Type.SSHORT, NativeType.SSHORT);
+ case USHORT:
+ return new TypeDelegate(com.kenai.jffi.Type.USHORT, NativeType.USHORT);
+ case SINT:
+ return new TypeDelegate(com.kenai.jffi.Type.SINT, NativeType.SINT);
+ case UINT:
+ return new TypeDelegate(com.kenai.jffi.Type.UINT, NativeType.UINT);
+ case SLONG:
+ return new TypeDelegate(com.kenai.jffi.Type.SLONG, NativeType.SLONG);
+ case ULONG:
+ return new TypeDelegate(com.kenai.jffi.Type.ULONG, NativeType.ULONG);
+ case SLONGLONG:
+ return new TypeDelegate(com.kenai.jffi.Type.SINT64, NativeType.SLONGLONG);
+ case ULONGLONG:
+ return new TypeDelegate(com.kenai.jffi.Type.UINT64, NativeType.ULONGLONG);
+ case FLOAT:
+ return new TypeDelegate(com.kenai.jffi.Type.FLOAT, NativeType.FLOAT);
+ case DOUBLE:
+ return new TypeDelegate(com.kenai.jffi.Type.DOUBLE, NativeType.DOUBLE);
+ case ADDRESS:
+ return new TypeDelegate(com.kenai.jffi.Type.POINTER, NativeType.ADDRESS);
+ default:
+ return new BadType(type.toString());
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NoTrace.java b/src/main/java/jnr/ffi/provider/jffi/NoTrace.java
new file mode 100644
index 0000000..689be03
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NoTrace.java
@@ -0,0 +1,14 @@
+package jnr.ffi.provider.jffi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Turns off debug tracing
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.TYPE, ElementType.METHOD})
+public @interface NoTrace {
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NoX86.java b/src/main/java/jnr/ffi/provider/jffi/NoX86.java
new file mode 100644
index 0000000..c8f9481
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NoX86.java
@@ -0,0 +1,14 @@
+package jnr.ffi.provider.jffi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.TYPE, ElementType.METHOD})
+public @interface NoX86 {
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NotImplMethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/NotImplMethodGenerator.java
new file mode 100644
index 0000000..aaa1eee
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NotImplMethodGenerator.java
@@ -0,0 +1,19 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+
+/**
+ *
+ */
+class NotImplMethodGenerator implements MethodGenerator {
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ return false;
+ }
+
+ public void generate(AsmBuilder builder, String functionName, Function function, ResultType resultType, ParameterType[] parameterTypes, boolean ignoreError) {
+ throw new UnsupportedOperationException("not supported");
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NullObjectParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/NullObjectParameterStrategy.java
new file mode 100644
index 0000000..542c1bf
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NullObjectParameterStrategy.java
@@ -0,0 +1,32 @@
+package jnr.ffi.provider.jffi;
+
+/**
+ *
+ */
+public final class NullObjectParameterStrategy extends ParameterStrategy {
+ public static final ParameterStrategy NULL = new NullObjectParameterStrategy();
+
+ public NullObjectParameterStrategy() {
+ super(DIRECT);
+ }
+
+ @Override
+ public long address(Object parameter) {
+ return 0;
+ }
+
+ @Override
+ public Object object(Object parameter) {
+ throw new NullPointerException("null reference");
+ }
+
+ @Override
+ public int offset(Object parameter) {
+ throw new NullPointerException("null reference");
+ }
+
+ @Override
+ public int length(Object parameter) {
+ throw new NullPointerException("null reference");
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java b/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java
new file mode 100644
index 0000000..3ca02c5
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.provider.SigType;
+
+public final class NumberUtil {
+ private NumberUtil() {}
+
+ static Class getBoxedClass(Class c) {
+ if (!c.isPrimitive()) {
+ return c;
+ }
+
+ if (void.class == c) {
+ return Void.class;
+
+ } else if (byte.class == c) {
+ return Byte.class;
+
+ } else if (char.class == c) {
+ return Character.class;
+
+ } else if (short.class == c) {
+ return Short.class;
+
+ } else if (int.class == c) {
+ return Integer.class;
+
+ } else if (long.class == c) {
+ return Long.class;
+
+ } else if (float.class == c) {
+ return Float.class;
+
+ } else if (double.class == c) {
+ return Double.class;
+
+ } else if (boolean.class == c) {
+ return Boolean.class;
+
+ } else {
+ throw new IllegalArgumentException("unknown primitive class");
+ }
+ }
+
+ static Class getPrimitiveClass(Class c) {
+ if (Void.class == c) {
+ return void.class;
+
+ } else if (Boolean.class == c) {
+ return boolean.class;
+
+ } else if (Byte.class == c) {
+ return byte.class;
+
+ } else if (Character.class == c) {
+ return char.class;
+
+ } else if (Short.class == c) {
+ return short.class;
+
+ } else if (Integer.class == c) {
+ return int.class;
+
+ } else if (Long.class == c) {
+ return long.class;
+
+ } else if (Float.class == c) {
+ return float.class;
+
+ } else if (Double.class == c) {
+ return double.class;
+
+ } else if (c.isPrimitive()) {
+ return c;
+ } else {
+ throw new IllegalArgumentException("unsupported number class");
+ }
+ }
+
+ public static boolean isPrimitiveInt(Class c) {
+ return byte.class == c || char.class == c || short.class == c || int.class == c || boolean.class == c;
+ }
+
+
+ public static void widen(SkinnyMethodAdapter mv, Class from, Class to) {
+ if (long.class == to && long.class != from && isPrimitiveInt(from)) {
+ mv.i2l();
+
+ } else if (boolean.class == to && boolean.class != from && isPrimitiveInt(from)) {
+ // Ensure only 0x0 and 0x1 values are used for boolean
+ mv.iconst_1();
+ mv.iand();
+ }
+ }
+
+ public static void widen(SkinnyMethodAdapter mv, Class from, Class to, NativeType nativeType) {
+ if (isPrimitiveInt(from)) {
+ if (nativeType == NativeType.UCHAR) {
+ mv.pushInt(0xff);
+ mv.iand();
+
+ } else if (nativeType == NativeType.USHORT) {
+ mv.pushInt(0xffff);
+ mv.iand();
+ }
+
+ if (long.class == to) {
+ mv.i2l();
+ switch (nativeType) {
+ case UINT:
+ case ULONG:
+ case ADDRESS:
+ if (sizeof(nativeType) < 8) {
+ // strip off bits 32:63
+ mv.ldc(0xffffffffL);
+ mv.land();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+
+ public static void narrow(SkinnyMethodAdapter mv, Class from, Class to) {
+ if (!from.equals(to)) {
+ if (byte.class == to || short.class == to || char.class == to || int.class == to || boolean.class == to) {
+ if (long.class == from) {
+ mv.l2i();
+ }
+
+ if (byte.class == to) {
+ mv.i2b();
+
+ } else if (short.class == to) {
+ mv.i2s();
+
+ } else if (char.class == to) {
+ mv.i2c();
+
+ } else if (boolean.class == to) {
+ // Ensure only 0x0 and 0x1 values are used for boolean
+ mv.iconst_1();
+ mv.iand();
+ }
+ }
+ }
+ }
+
+
+ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, final Class to) {
+ narrow(mv, from, to);
+ widen(mv, from, to);
+ }
+
+
+ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, final Class to, final NativeType nativeType) {
+ if (boolean.class == to) {
+ narrow(mv, from, to);
+ return;
+ }
+
+ switch (nativeType) {
+ case SCHAR:
+ narrow(mv, from, byte.class);
+ widen(mv, byte.class, to);
+ break;
+
+ case SSHORT:
+ narrow(mv, from, short.class);
+ widen(mv, short.class, to);
+ break;
+
+ case SINT:
+ narrow(mv, from, int.class);
+ widen(mv, int.class, to);
+ break;
+
+ case UCHAR:
+ narrow(mv, from, int.class);
+ mv.pushInt(0xff);
+ mv.iand();
+ widen(mv, int.class, to);
+ break;
+
+ case USHORT:
+ narrow(mv, from, int.class);
+ mv.pushInt(0xffff);
+ mv.iand();
+ widen(mv, int.class, to);
+ break;
+
+ case UINT:
+ case ULONG:
+ case ADDRESS:
+ if (sizeof(nativeType) <= 4) {
+ narrow(mv, from, int.class);
+ if (long.class == to) {
+ mv.i2l();
+ // strip off bits 32:63
+ mv.ldc(0xffffffffL);
+ mv.land();
+ }
+ } else {
+ widen(mv, from, to);
+ }
+ break;
+
+
+ case FLOAT:
+ case DOUBLE:
+ break;
+
+ default:
+ narrow(mv, from, to);
+ widen(mv, from, to);
+ break;
+ }
+ }
+
+ static int sizeof(SigType type) {
+ return sizeof(type.getNativeType());
+ }
+
+ static int sizeof(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ return com.kenai.jffi.Type.SCHAR.size();
+
+ case UCHAR:
+ return com.kenai.jffi.Type.UCHAR.size();
+
+ case SSHORT:
+ return com.kenai.jffi.Type.SSHORT.size();
+
+ case USHORT:
+ return com.kenai.jffi.Type.USHORT.size();
+
+ case SINT:
+ return com.kenai.jffi.Type.SINT.size();
+
+ case UINT:
+ return com.kenai.jffi.Type.UINT.size();
+
+ case SLONG:
+ return com.kenai.jffi.Type.SLONG.size();
+
+ case ULONG:
+ return com.kenai.jffi.Type.ULONG.size();
+
+ case SLONGLONG:
+ return com.kenai.jffi.Type.SLONG_LONG.size();
+
+ case ULONGLONG:
+ return com.kenai.jffi.Type.ULONG_LONG.size();
+
+ case FLOAT:
+ return com.kenai.jffi.Type.FLOAT.size();
+
+ case DOUBLE:
+ return com.kenai.jffi.Type.DOUBLE.size();
+
+ case ADDRESS:
+ return com.kenai.jffi.Type.POINTER.size();
+
+ case VOID:
+ return 0;
+
+ default:
+ throw new UnsupportedOperationException("cannot determine size of " + nativeType);
+ }
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/ParameterStrategy.java
new file mode 100644
index 0000000..684f84c
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ParameterStrategy.java
@@ -0,0 +1,22 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.ObjectParameterStrategy;
+import com.kenai.jffi.ObjectParameterType;
+
+/**
+ *
+ */
+abstract public class ParameterStrategy extends ObjectParameterStrategy {
+ /* objectCount is accessed directly from asm code - do not change */
+ public final int objectCount;
+
+ protected ParameterStrategy(StrategyType type) {
+ super(type);
+ objectCount = type == HEAP ? 1 : 0;
+ }
+
+ protected ParameterStrategy(StrategyType type, ObjectParameterType parameterType) {
+ super(type, parameterType);
+ objectCount = type == HEAP ? 1 : 0;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/PointerParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/PointerParameterStrategy.java
new file mode 100644
index 0000000..b5db598
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/PointerParameterStrategy.java
@@ -0,0 +1,40 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.ObjectParameterType;
+import jnr.ffi.Pointer;
+
+/**
+ *
+ */
+public final class PointerParameterStrategy extends ParameterStrategy {
+ public static final PointerParameterStrategy DIRECT = new PointerParameterStrategy(StrategyType.DIRECT);
+ public static final PointerParameterStrategy HEAP = new PointerParameterStrategy(StrategyType.HEAP);
+
+ PointerParameterStrategy(StrategyType type) {
+ super(type, ObjectParameterType.create(ObjectParameterType.ARRAY, ObjectParameterType.BYTE));
+ }
+
+ @Override
+ public long address(Object o) {
+ return address((Pointer) o);
+ }
+
+ public long address(Pointer pointer) {
+ return pointer != null ? pointer.address() : 0L;
+ }
+
+ @Override
+ public Object object(Object o) {
+ return ((Pointer) o).array();
+ }
+
+ @Override
+ public int offset(Object o) {
+ return ((Pointer) o).arrayOffset();
+ }
+
+ @Override
+ public int length(Object o) {
+ return ((Pointer) o).arrayLength();
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/PrimitiveArrayParameterStrategy.java b/src/main/java/jnr/ffi/provider/jffi/PrimitiveArrayParameterStrategy.java
new file mode 100644
index 0000000..4927416
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/PrimitiveArrayParameterStrategy.java
@@ -0,0 +1,75 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.ObjectParameterType;
+
+/**
+ *
+ */
+abstract public class PrimitiveArrayParameterStrategy extends ParameterStrategy {
+ static final PrimitiveArrayParameterStrategy BYTE = new PrimitiveArrayParameterStrategy(ObjectParameterType.BYTE) {
+ public int length(Object o) {
+ return ((byte[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy SHORT = new PrimitiveArrayParameterStrategy(ObjectParameterType.SHORT) {
+ public int length(Object o) {
+ return ((short[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy CHAR = new PrimitiveArrayParameterStrategy(ObjectParameterType.CHAR) {
+ public int length(Object o) {
+ return ((char[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy INT = new PrimitiveArrayParameterStrategy(ObjectParameterType.INT) {
+ public int length(Object o) {
+ return ((int[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy LONG = new PrimitiveArrayParameterStrategy(ObjectParameterType.LONG) {
+ public int length(Object o) {
+ return ((long[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy FLOAT = new PrimitiveArrayParameterStrategy(ObjectParameterType.FLOAT) {
+ public int length(Object o) {
+ return ((float[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy DOUBLE = new PrimitiveArrayParameterStrategy(ObjectParameterType.DOUBLE) {
+ public int length(Object o) {
+ return ((double[]) o).length;
+ }
+ };
+
+ static final PrimitiveArrayParameterStrategy BOOLEAN = new PrimitiveArrayParameterStrategy(ObjectParameterType.BOOLEAN) {
+ public int length(Object o) {
+ return ((boolean[]) o).length;
+ }
+ };
+
+ PrimitiveArrayParameterStrategy(ObjectParameterType.ComponentType componentType) {
+ super(HEAP, ObjectParameterType.create(ObjectParameterType.ObjectType.ARRAY, componentType));
+ }
+
+ @Override
+ public final long address(Object o) {
+ return 0;
+ }
+
+ @Override
+ public final Object object(Object o) {
+ return o;
+ }
+
+ @Override
+ public final int offset(Object o) {
+ return 0;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/Provider.java b/src/main/java/jnr/ffi/provider/jffi/Provider.java
new file mode 100644
index 0000000..77cbee6
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/Provider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Runtime;
+import jnr.ffi.provider.FFIProvider;
+
+
+public final class Provider extends FFIProvider {
+ private final NativeRuntime runtime;
+
+ public Provider() {
+ this.runtime = NativeRuntime.getInstance();
+ }
+
+ public final Runtime getRuntime() {
+ return runtime;
+ }
+
+ public <T> jnr.ffi.LibraryLoader<T> createLibraryLoader(Class<T> interfaceClass) {
+ return new NativeLibraryLoader<T>(interfaceClass);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ReflectionLibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/ReflectionLibraryLoader.java
new file mode 100644
index 0000000..dbf5f94
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ReflectionLibraryLoader.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.LibraryOption;
+import jnr.ffi.Runtime;
+import jnr.ffi.Variable;
+import jnr.ffi.annotations.StdCall;
+import jnr.ffi.annotations.Synchronized;
+import jnr.ffi.mapper.*;
+import jnr.ffi.provider.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import static jnr.ffi.provider.jffi.InvokerUtil.*;
+import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
+
+/**
+ *
+ */
+class ReflectionLibraryLoader extends LibraryLoader {
+
+ @Override
+ <T> T loadLibrary(NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions) {
+ return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(),
+ new Class[]{ interfaceClass, LoadedLibrary.class }, new NativeInvocationHandler(new LazyLoader<T>(library, interfaceClass, libraryOptions))));
+ }
+
+ private static final class SynchronizedInvoker implements Invoker {
+ private final Invoker invoker;
+ public SynchronizedInvoker(Invoker invoker) {
+ this.invoker = invoker;
+ }
+
+ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
+ public Object invoke(Object self, Object[] parameters) {
+ synchronized (self) {
+ return invoker.invoke(self, parameters);
+ }
+ }
+ }
+
+ private static final class FunctionNotFoundInvoker implements Invoker {
+ private final Method method;
+ private final String functionName;
+
+ private FunctionNotFoundInvoker(Method method, String functionName) {
+ this.method = method;
+ this.functionName = functionName;
+ }
+
+ @Override
+ public Object invoke(Object self, Object[] parameters) {
+ throw new UnsatisfiedLinkError(String.format("native method '%s' not found for method %s", functionName, method));
+ }
+ }
+
+ private static final class GetRuntimeInvoker implements Invoker {
+ private final jnr.ffi.Runtime runtime;
+
+ private GetRuntimeInvoker(Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ @Override
+ public Object invoke(Object self, Object[] parameters) {
+ return runtime;
+ }
+ }
+
+ private static final class LazyLoader<T> extends AbstractMap<Method, Invoker> {
+ private final DefaultInvokerFactory invokerFactory = new DefaultInvokerFactory();
+ private final jnr.ffi.Runtime runtime = NativeRuntime.getInstance();
+ private final AsmClassLoader classLoader = new AsmClassLoader();
+ private final SignatureTypeMapper typeMapper;
+ private final FunctionMapper functionMapper;
+ private final jnr.ffi.CallingConvention libraryCallingConvention;
+
+ private final boolean libraryIsSynchronized;
+
+ @SuppressWarnings("unused")
+ private final NativeLibrary library;
+ @SuppressWarnings("unused")
+ private final Class<T> interfaceClass;
+ @SuppressWarnings("unused")
+ private final Map<LibraryOption, ?> libraryOptions;
+
+ private LazyLoader(NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions) {
+ this.library = library;
+ this.interfaceClass = interfaceClass;
+ this.libraryOptions = libraryOptions;
+
+ this.functionMapper = libraryOptions.containsKey(LibraryOption.FunctionMapper)
+ ? (FunctionMapper) libraryOptions.get(LibraryOption.FunctionMapper) : IdentityFunctionMapper.getInstance();
+
+ SignatureTypeMapper typeMapper;
+ if (libraryOptions.containsKey(LibraryOption.TypeMapper)) {
+ Object tm = libraryOptions.get(LibraryOption.TypeMapper);
+ if (tm instanceof SignatureTypeMapper) {
+ typeMapper = (SignatureTypeMapper) tm;
+ } else if (tm instanceof TypeMapper) {
+ typeMapper = new SignatureTypeMapperAdapter((TypeMapper) tm);
+ } else {
+ throw new IllegalArgumentException("TypeMapper option is not a valid TypeMapper instance");
+ }
+ } else {
+ typeMapper = new NullTypeMapper();
+ }
+
+ this.typeMapper = new CompositeTypeMapper(typeMapper,
+ new CachingTypeMapper(new InvokerTypeMapper(new NativeClosureManager(runtime, typeMapper, classLoader), classLoader, NativeLibraryLoader.ASM_ENABLED)));
+ libraryCallingConvention = getCallingConvention(interfaceClass, libraryOptions);
+ libraryIsSynchronized = interfaceClass.isAnnotationPresent(Synchronized.class);
+ }
+
+ @Override
+ public Set<Entry<Method, Invoker>> entrySet() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public synchronized Invoker get(Object key) {
+
+ if (!(key instanceof Method)) {
+ throw new IllegalArgumentException("key not instance of Method");
+ }
+
+ Method method = (Method) key;
+ if (Variable.class.isAssignableFrom(method.getReturnType())) {
+ return getVariableAccessor(method);
+
+ } else if (method.getName().equals("getRuntime") && method.getReturnType().isAssignableFrom(NativeRuntime.class)) {
+ return new GetRuntimeInvoker(runtime);
+ } else {
+ return getFunctionInvoker(method);
+ }
+ }
+
+ private Invoker getFunctionInvoker(Method method) {
+ Collection<Annotation> annotations = sortedAnnotationCollection(method.getAnnotations());
+ String functionName = functionMapper.mapFunctionName(method.getName(), new NativeFunctionMapperContext(library, annotations));
+ long functionAddress = library.getSymbolAddress(functionName);
+ if (functionAddress == 0L) {
+ return new FunctionNotFoundInvoker(method, functionName);
+ }
+
+ FromNativeContext resultContext = new MethodResultContext(NativeRuntime.getInstance(), method);
+ SignatureType signatureType = DefaultSignatureType.create(method.getReturnType(), resultContext);
+ ResultType resultType = getResultType(runtime, method.getReturnType(),
+ resultContext.getAnnotations(), typeMapper.getFromNativeType(signatureType, resultContext),
+ resultContext);
+
+ ParameterType[] parameterTypes = getParameterTypes(runtime, typeMapper, method);
+
+ // Allow individual methods to set the calling convention to stdcall
+ CallingConvention callingConvention = method.isAnnotationPresent(StdCall.class)
+ ? CallingConvention.STDCALL : libraryCallingConvention;
+
+ Function function = new Function(functionAddress,
+ getCallContext(resultType, parameterTypes, callingConvention, InvokerUtil.requiresErrno(method)));
+
+ Invoker invoker = invokerFactory.createInvoker(runtime, library, function, resultType, parameterTypes);
+
+ //
+ // If either the method or the library is specified as requiring
+ // synchronization, then wrap the raw invoker in a synchronized proxy
+ //
+ return libraryIsSynchronized || method.isAnnotationPresent(Synchronized.class)
+ ? new SynchronizedInvoker(invoker) : invoker;
+ }
+
+ private Invoker getVariableAccessor(Method method) {
+ Collection<Annotation> annotations = sortedAnnotationCollection(method.getAnnotations());
+
+ String functionName = functionMapper.mapFunctionName(method.getName(), new NativeFunctionMapperContext(library, annotations));
+ long symbolAddress = library.getSymbolAddress(functionName);
+ if (symbolAddress == 0L) {
+ return new FunctionNotFoundInvoker(method, functionName);
+ }
+ Variable variable = ReflectionVariableAccessorGenerator.createVariableAccessor(runtime, method, symbolAddress,
+ typeMapper, annotations);
+ return new VariableAcccessorInvoker(variable);
+ }
+
+
+ private static final class VariableAcccessorInvoker implements Invoker {
+ private final Variable variable;
+
+ private VariableAcccessorInvoker(Variable variable) {
+ this.variable = variable;
+ }
+
+ @Override
+ public Object invoke(Object self, Object[] parameters) {
+ return variable;
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ReflectionVariableAccessorGenerator.java b/src/main/java/jnr/ffi/provider/jffi/ReflectionVariableAccessorGenerator.java
new file mode 100644
index 0000000..2ba3a39
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ReflectionVariableAccessorGenerator.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.Variable;
+import jnr.ffi.mapper.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.util.Collection;
+
+import static jnr.ffi.provider.jffi.DefaultInvokerFactory.getNumberDataConverter;
+import static jnr.ffi.provider.jffi.DefaultInvokerFactory.getNumberResultConverter;
+import static jnr.ffi.provider.jffi.NumberUtil.sizeof;
+
+/**
+ *
+ */
+class ReflectionVariableAccessorGenerator {
+ static Variable createVariableAccessor(jnr.ffi.Runtime runtime, Method method, long symbolAddress, SignatureTypeMapper typeMapper, Collection<Annotation> annotations) {
+
+ java.lang.reflect.Type variableType = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
+ if (!(variableType instanceof Class)) {
+ throw new IllegalArgumentException("unsupported variable class: " + variableType);
+ }
+
+ Class javaType = (Class) variableType;
+
+ SimpleNativeContext context = new SimpleNativeContext(runtime, annotations);
+ SignatureType signatureType = DefaultSignatureType.create(javaType, (FromNativeContext) context);
+ jnr.ffi.mapper.FromNativeType mappedFromNativeType = typeMapper.getFromNativeType(signatureType, context);
+ FromNativeConverter fromNativeConverter = mappedFromNativeType != null ? mappedFromNativeType.getFromNativeConverter() : null;
+ jnr.ffi.mapper.ToNativeType mappedToNativeType = typeMapper.getToNativeType(signatureType, context);
+ ToNativeConverter toNativeConverter = mappedToNativeType != null ? mappedToNativeType.getToNativeConverter() : null;
+
+
+ Class boxedType = toNativeConverter != null ? toNativeConverter.nativeType() : javaType;
+ NativeType nativeType = Types.getType(runtime, boxedType, annotations).getNativeType();
+ jnr.ffi.provider.ToNativeType toNativeType = new jnr.ffi.provider.ToNativeType(javaType, nativeType, annotations, toNativeConverter, null);
+ jnr.ffi.provider.FromNativeType fromNativeType = new jnr.ffi.provider.FromNativeType(javaType, nativeType, annotations, fromNativeConverter, null);
+ Variable variable;
+ Pointer memory = MemoryUtil.newPointer(runtime, symbolAddress);
+ variable = getNativeVariableAccessor(memory, toNativeType, fromNativeType);
+ return toNativeType.getToNativeConverter() != null
+ ? getConvertingVariable(variable, toNativeType.getToNativeConverter(), fromNativeType.getFromNativeConverter())
+ : variable;
+ }
+
+ static Variable getConvertingVariable(Variable nativeVariable, ToNativeConverter toNativeConverter, FromNativeConverter fromNativeConverter) {
+ if ((toNativeConverter != null && fromNativeConverter == null) || toNativeConverter == null && fromNativeConverter != null) {
+ throw new UnsupportedOperationException("convertible types must have both a ToNativeConverter and a FromNativeConverter");
+ }
+ return new ConvertingVariable(nativeVariable, toNativeConverter, fromNativeConverter);
+ }
+
+ static Variable getNativeVariableAccessor(Pointer memory, jnr.ffi.provider.ToNativeType toNativeType, jnr.ffi.provider.FromNativeType fromNativeType) {
+ if (Pointer.class == toNativeType.effectiveJavaType()) {
+ return new PointerVariable(memory);
+
+ } else if (Number.class.isAssignableFrom(toNativeType.effectiveJavaType())) {
+ return new NumberVariable(memory, getPointerOp(toNativeType.getNativeType()),
+ getNumberDataConverter(toNativeType.getNativeType()), getNumberResultConverter(fromNativeType));
+
+ } else {
+ throw new UnsupportedOperationException("unsupported variable type: " + toNativeType.effectiveJavaType());
+ }
+ }
+
+ private static PointerOp<Number> getPointerOp(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ return Int8PointerOp.INSTANCE;
+
+ case SSHORT:
+ case USHORT:
+ return Int16PointerOp.INSTANCE;
+
+ case SINT:
+ case UINT:
+ return Int32PointerOp.INSTANCE;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return Int64PointerOp.INSTANCE;
+
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ return sizeof(nativeType) == 4 ? Int32PointerOp.INSTANCE : Int64PointerOp.INSTANCE;
+
+ case FLOAT:
+ return FloatPointerOp.INSTANCE;
+
+ case DOUBLE:
+ return DoublePointerOp.INSTANCE;
+
+ }
+ throw new UnsupportedOperationException("cannot convert " + nativeType);
+ }
+
+ private static abstract class AbstractVariable<T> implements Variable<T> {
+ protected final Pointer memory;
+
+ protected AbstractVariable(Pointer memory) {
+ this.memory = memory;
+ }
+ }
+
+ private static final class ConvertingVariable implements Variable {
+ private final Variable variable;
+ private final ToNativeConverter toNativeConverter;
+ private final FromNativeConverter fromNativeConverter;
+
+ private ConvertingVariable(Variable variable, ToNativeConverter toNativeConverter, FromNativeConverter fromNativeConverter) {
+ this.variable = variable;
+ this.toNativeConverter = toNativeConverter;
+ this.fromNativeConverter = fromNativeConverter;
+ }
+
+ @Override
+ public Object get() {
+ return fromNativeConverter.fromNative(variable.get(), null);
+ }
+
+ @Override
+ public void set(Object value) {
+ variable.set(toNativeConverter.toNative(value, null));
+ }
+ }
+
+
+ private static final class NumberVariable extends AbstractVariable<Number> {
+ private final DataConverter<Number, Number> dataConverter;
+ private final DefaultInvokerFactory.ResultConverter<? extends Number, Number> resultConverter;
+ private final PointerOp<Number> pointerOp;
+ private NumberVariable(Pointer memory, PointerOp<Number> pointerOp,
+ DataConverter<Number, Number> dataConverter,
+ DefaultInvokerFactory.ResultConverter<? extends Number, Number> resultConverter) {
+ super(memory);
+ this.pointerOp = pointerOp;
+ this.dataConverter = dataConverter;
+ this.resultConverter = resultConverter;
+ }
+
+ @Override
+ public Number get() {
+ return resultConverter.fromNative(dataConverter.fromNative(pointerOp.get(memory), null), null);
+ }
+
+ @Override
+ public void set(Number value) {
+ pointerOp.put(memory, dataConverter.toNative(value, null));
+ }
+ }
+
+
+ private static final class PointerVariable extends AbstractVariable<Pointer> {
+ private PointerVariable(Pointer memory) {
+ super(memory);
+ }
+
+ public Pointer get() {
+ return memory.getPointer(0);
+ }
+
+ public void set(Pointer value) {
+ if (value != null) memory.putPointer(0, value); else memory.putAddress(0, 0L);
+ }
+ }
+
+
+ private static interface PointerOp<T> {
+ public T get(Pointer memory);
+ public void put(Pointer memory, T value);
+ }
+
+ private static final class Int8PointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new Int8PointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getByte(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putByte(0, value.byteValue());
+ }
+ }
+
+ private static final class Int16PointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new Int16PointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getShort(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putShort(0, value.shortValue());
+ }
+ }
+
+ private static final class Int32PointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new Int32PointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getInt(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putInt(0, value.intValue());
+ }
+ }
+
+ private static final class Int64PointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new Int64PointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getLongLong(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putLongLong(0, value.longValue());
+ }
+ }
+
+ private static final class FloatPointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new FloatPointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getFloat(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putFloat(0, value.floatValue());
+ }
+ }
+
+
+ private static final class DoublePointerOp implements PointerOp<Number> {
+ static final PointerOp<Number> INSTANCE = new DoublePointerOp();
+ @Override
+ public Number get(Pointer memory) {
+ return memory.getFloat(0);
+ }
+
+ @Override
+ public void put(Pointer memory, Number value) {
+ memory.putFloat(0, value.floatValue());
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/SimpleNativeContext.java b/src/main/java/jnr/ffi/provider/jffi/SimpleNativeContext.java
new file mode 100644
index 0000000..3a29bca
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/SimpleNativeContext.java
@@ -0,0 +1,25 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.ToNativeContext;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+public class SimpleNativeContext implements ToNativeContext, FromNativeContext {
+ private final jnr.ffi.Runtime runtime;
+ private final Collection<Annotation> annotations;
+
+ SimpleNativeContext(jnr.ffi.Runtime runtime, Collection<Annotation> annotations) {
+ this.runtime = runtime;
+ this.annotations = annotations;
+ }
+
+ public Collection<Annotation> getAnnotations() {
+ return annotations;
+ }
+
+ public final jnr.ffi.Runtime getRuntime() {
+ return runtime;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/SkinnyMethodAdapter.java b/src/main/java/jnr/ffi/provider/jffi/SkinnyMethodAdapter.java
new file mode 100644
index 0000000..acd9c69
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/SkinnyMethodAdapter.java
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import org.objectweb.asm.*;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+
+/**
+ *
+ * @author headius
+ */
+public class SkinnyMethodAdapter extends MethodVisitor implements Opcodes {
+ private final static boolean DEBUG = Boolean.getBoolean("jnr.ffi.compile.dump");
+ private MethodVisitor method;
+
+ /** Creates a new instance of SkinnyMethodAdapter */
+ public SkinnyMethodAdapter(ClassVisitor cv, int flags, String name, String signature, String something, String[] exceptions) {
+ super(Opcodes.ASM4);
+ setMethodVisitor(cv.visitMethod(flags, name, signature, something, exceptions));
+ }
+
+ public MethodVisitor getMethodVisitor() {
+ return method;
+ }
+
+ public void setMethodVisitor(MethodVisitor mv) {
+ this.method = DEBUG ? AsmUtil.newTraceMethodVisitor(mv) : mv;
+ }
+
+ public void aload(int arg0) {
+ getMethodVisitor().visitVarInsn(ALOAD, arg0);
+ }
+
+ public void aload(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(ALOAD, arg0.idx);
+ }
+
+ public void aload(int... args) {
+ for (int arg : args) {
+ getMethodVisitor().visitVarInsn(ALOAD, arg);
+ }
+ }
+
+ public void aload(LocalVariable... args) {
+ for (LocalVariable arg : args) {
+ getMethodVisitor().visitVarInsn(ALOAD, arg.idx);
+ }
+ }
+
+ public void iload(int arg0) {
+ getMethodVisitor().visitVarInsn(ILOAD, arg0);
+ }
+
+ public void iload(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(ILOAD, arg0.idx);
+ }
+
+ public void iload(int... args) {
+ for (int arg : args) {
+ getMethodVisitor().visitVarInsn(ILOAD, arg);
+ }
+ }
+
+ public void iload(LocalVariable... args) {
+ for (LocalVariable arg : args) {
+ getMethodVisitor().visitVarInsn(ILOAD, arg.idx);
+ }
+ }
+
+ public void lload(int arg0) {
+ getMethodVisitor().visitVarInsn(LLOAD, arg0);
+ }
+
+ public void lload(int... args) {
+ for (int arg : args) {
+ getMethodVisitor().visitVarInsn(LLOAD, arg);
+ }
+ }
+
+ public void lload(LocalVariable... args) {
+ for (LocalVariable arg : args) {
+ getMethodVisitor().visitVarInsn(LLOAD, arg.idx);
+ }
+ }
+
+ public void fload(int arg0) {
+ getMethodVisitor().visitVarInsn(FLOAD, arg0);
+ }
+
+ public void fload(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(FLOAD, arg0.idx);
+ }
+
+ public void fload(int... args) {
+ for (int arg : args) {
+ getMethodVisitor().visitVarInsn(FLOAD, arg);
+ }
+ }
+
+ public void dload(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(DLOAD, arg0.idx);
+ }
+
+ public void dload(int arg0) {
+ getMethodVisitor().visitVarInsn(DLOAD, arg0);
+ }
+
+ public void dload(int... args) {
+ for (int arg : args) {
+ getMethodVisitor().visitVarInsn(DLOAD, arg);
+ }
+ }
+
+ public void astore(int arg0) {
+ getMethodVisitor().visitVarInsn(ASTORE, arg0);
+ }
+
+ public void astore(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(ASTORE, arg0.idx);
+ }
+
+ public void istore(int arg0) {
+ getMethodVisitor().visitVarInsn(ISTORE, arg0);
+ }
+
+ public void istore(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(ISTORE, arg0.idx);
+ }
+
+ public void lstore(int arg0) {
+ getMethodVisitor().visitVarInsn(LSTORE, arg0);
+ }
+
+ public void lstore(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(LSTORE, arg0.idx);
+ }
+
+ public void fstore(int arg0) {
+ getMethodVisitor().visitVarInsn(FSTORE, arg0);
+ }
+
+ public void fstore(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(FSTORE, arg0.idx);
+ }
+
+ public void dstore(int arg0) {
+ getMethodVisitor().visitVarInsn(DSTORE, arg0);
+ }
+
+ public void dstore(LocalVariable arg0) {
+ getMethodVisitor().visitVarInsn(DSTORE, arg0.idx);
+ }
+
+ public void ldc(Object arg0) {
+ getMethodVisitor().visitLdcInsn(arg0);
+ }
+
+ public void bipush(int arg) {
+ getMethodVisitor().visitIntInsn(BIPUSH, arg);
+ }
+
+ public void sipush(int arg) {
+ getMethodVisitor().visitIntInsn(SIPUSH, arg);
+ }
+
+ public void pushInt(int value) {
+ if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
+ switch (value) {
+ case -1:
+ iconst_m1();
+ break;
+ case 0:
+ iconst_0();
+ break;
+ case 1:
+ iconst_1();
+ break;
+ case 2:
+ iconst_2();
+ break;
+ case 3:
+ iconst_3();
+ break;
+ case 4:
+ iconst_4();
+ break;
+ case 5:
+ iconst_5();
+ break;
+ default:
+ bipush(value);
+ break;
+ }
+ } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
+ sipush(value);
+ } else {
+ ldc(value);
+ }
+ }
+
+ public void pushBoolean(boolean bool) {
+ if (bool) iconst_1(); else iconst_0();
+ }
+
+ public void invokestatic(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(INVOKESTATIC, arg1, arg2, arg3);
+ }
+
+ public void invokestatic(Class recv, String methodName, Class returnType, Class... parameterTypes) {
+ getMethodVisitor().visitMethodInsn(INVOKESTATIC, p(recv), methodName, sig(returnType, parameterTypes));
+ }
+
+ public void invokespecial(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(INVOKESPECIAL, arg1, arg2, arg3);
+ }
+
+ public void invokespecial(Class recv, String methodName, Class returnType, Class... parameterTypes) {
+ getMethodVisitor().visitMethodInsn(INVOKESPECIAL, p(recv), methodName, sig(returnType, parameterTypes));
+ }
+
+ public void invokevirtual(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(INVOKEVIRTUAL, arg1, arg2, arg3);
+ }
+
+ public void invokevirtual(Class recv, String methodName, Class returnType, Class... parameterTypes) {
+ getMethodVisitor().visitMethodInsn(INVOKEVIRTUAL, p(recv), methodName, sig(returnType, parameterTypes));
+ }
+
+ public void invokeinterface(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, arg1, arg2, arg3);
+ }
+
+ public void invokeinterface(Class recv, String methodName, Class returnType, Class... parameterTypes) {
+ getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, p(recv), methodName, sig(returnType, parameterTypes));
+ }
+
+ public void invokedynamic(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(INVOKEDYNAMIC, arg1, arg2, arg3);
+ }
+
+ public void aprintln() {
+ dup();
+ getstatic(p(System.class), "out", ci(PrintStream.class));
+ swap();
+ invokevirtual(p(PrintStream.class), "println", sig(void.class, params(Object.class)));
+ }
+
+ public void areturn() {
+ getMethodVisitor().visitInsn(ARETURN);
+ }
+
+ public void ireturn() {
+ getMethodVisitor().visitInsn(IRETURN);
+ }
+
+ public void freturn() {
+ getMethodVisitor().visitInsn(FRETURN);
+ }
+
+ public void lreturn() {
+ getMethodVisitor().visitInsn(LRETURN);
+ }
+
+ public void dreturn() {
+ getMethodVisitor().visitInsn(DRETURN);
+ }
+
+ public void newobj(String arg0) {
+ getMethodVisitor().visitTypeInsn(NEW, arg0);
+ }
+
+ public void dup() {
+ getMethodVisitor().visitInsn(DUP);
+ }
+
+ public void swap() {
+ getMethodVisitor().visitInsn(SWAP);
+ }
+
+ public void swap2() {
+ dup2_x2();
+ pop2();
+ }
+
+ public void getstatic(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitFieldInsn(GETSTATIC, arg1, arg2, arg3);
+ }
+
+ public void putstatic(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitFieldInsn(PUTSTATIC, arg1, arg2, arg3);
+ }
+
+ public void getfield(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitFieldInsn(GETFIELD, arg1, arg2, arg3);
+ }
+
+ public void putfield(String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitFieldInsn(PUTFIELD, arg1, arg2, arg3);
+ }
+
+ public void voidreturn() {
+ getMethodVisitor().visitInsn(RETURN);
+ }
+
+ public void anewarray(String arg0) {
+ getMethodVisitor().visitTypeInsn(ANEWARRAY, arg0);
+ }
+
+ public void multianewarray(String arg0, int dims) {
+ getMethodVisitor().visitMultiANewArrayInsn(arg0, dims);
+ }
+
+ public void newarray(int arg0) {
+ getMethodVisitor().visitIntInsn(NEWARRAY, arg0);
+ }
+
+ public void iconst_m1() {
+ getMethodVisitor().visitInsn(ICONST_M1);
+ }
+
+ public void iconst_0() {
+ getMethodVisitor().visitInsn(ICONST_0);
+ }
+
+ public void iconst_1() {
+ getMethodVisitor().visitInsn(ICONST_1);
+ }
+
+ public void iconst_2() {
+ getMethodVisitor().visitInsn(ICONST_2);
+ }
+
+ public void iconst_3() {
+ getMethodVisitor().visitInsn(ICONST_3);
+ }
+
+ public void iconst_4() {
+ getMethodVisitor().visitInsn(ICONST_4);
+ }
+
+ public void iconst_5() {
+ getMethodVisitor().visitInsn(ICONST_5);
+ }
+
+ public void lconst_0() {
+ getMethodVisitor().visitInsn(LCONST_0);
+ }
+
+ public void aconst_null() {
+ getMethodVisitor().visitInsn(ACONST_NULL);
+ }
+
+ public void label(Label label) {
+ getMethodVisitor().visitLabel(label);
+ }
+
+ public void nop() {
+ getMethodVisitor().visitInsn(NOP);
+ }
+
+ public void pop() {
+ getMethodVisitor().visitInsn(POP);
+ }
+
+ public void pop2() {
+ getMethodVisitor().visitInsn(POP2);
+ }
+
+ public void arrayload() {
+ getMethodVisitor().visitInsn(AALOAD);
+ }
+
+ public void arraystore() {
+ getMethodVisitor().visitInsn(AASTORE);
+ }
+
+ public void iarrayload() {
+ getMethodVisitor().visitInsn(IALOAD);
+ }
+
+ public void barrayload() {
+ getMethodVisitor().visitInsn(BALOAD);
+ }
+
+ public void barraystore() {
+ getMethodVisitor().visitInsn(BASTORE);
+ }
+
+ public void aaload() {
+ getMethodVisitor().visitInsn(AALOAD);
+ }
+
+ public void aastore() {
+ getMethodVisitor().visitInsn(AASTORE);
+ }
+
+ public void iaload() {
+ getMethodVisitor().visitInsn(IALOAD);
+ }
+
+ public void iastore() {
+ getMethodVisitor().visitInsn(IASTORE);
+ }
+
+ public void laload() {
+ getMethodVisitor().visitInsn(LALOAD);
+ }
+
+ public void lastore() {
+ getMethodVisitor().visitInsn(LASTORE);
+ }
+
+ public void baload() {
+ getMethodVisitor().visitInsn(BALOAD);
+ }
+
+ public void bastore() {
+ getMethodVisitor().visitInsn(BASTORE);
+ }
+
+ public void saload() {
+ getMethodVisitor().visitInsn(SALOAD);
+ }
+
+ public void sastore() {
+ getMethodVisitor().visitInsn(SASTORE);
+ }
+
+ public void caload() {
+ getMethodVisitor().visitInsn(CALOAD);
+ }
+
+ public void castore() {
+ getMethodVisitor().visitInsn(CASTORE);
+ }
+
+ public void faload() {
+ getMethodVisitor().visitInsn(FALOAD);
+ }
+
+ public void fastore() {
+ getMethodVisitor().visitInsn(FASTORE);
+ }
+
+ public void daload() {
+ getMethodVisitor().visitInsn(DALOAD);
+ }
+
+ public void dastore() {
+ getMethodVisitor().visitInsn(DASTORE);
+ }
+
+ public void fcmpl() {
+ getMethodVisitor().visitInsn(FCMPL);
+ }
+
+ public void fcmpg() {
+ getMethodVisitor().visitInsn(FCMPG);
+ }
+
+ public void dcmpl() {
+ getMethodVisitor().visitInsn(DCMPL);
+ }
+
+ public void dcmpg() {
+ getMethodVisitor().visitInsn(DCMPG);
+ }
+
+ public void dup_x2() {
+ getMethodVisitor().visitInsn(DUP_X2);
+ }
+
+ public void dup_x1() {
+ getMethodVisitor().visitInsn(DUP_X1);
+ }
+
+ public void dup2_x2() {
+ getMethodVisitor().visitInsn(DUP2_X2);
+ }
+
+ public void dup2_x1() {
+ getMethodVisitor().visitInsn(DUP2_X1);
+ }
+
+ public void dup2() {
+ getMethodVisitor().visitInsn(DUP2);
+ }
+
+ public void trycatch(Label arg0, Label arg1, Label arg2,
+ String arg3) {
+ getMethodVisitor().visitTryCatchBlock(arg0, arg1, arg2, arg3);
+ }
+
+ public void trycatch(String type, Runnable body, Runnable catchBody) {
+ Label before = new Label();
+ Label after = new Label();
+ Label catchStart = new Label();
+ Label done = new Label();
+
+ trycatch(before, after, catchStart, type);
+ label(before);
+ body.run();
+ label(after);
+ go_to(done);
+ if (catchBody != null) {
+ label(catchStart);
+ catchBody.run();
+ }
+ label(done);
+ }
+
+ public void go_to(Label arg0) {
+ getMethodVisitor().visitJumpInsn(GOTO, arg0);
+ }
+
+ public void lookupswitch(Label arg0, int[] arg1, Label[] arg2) {
+ getMethodVisitor().visitLookupSwitchInsn(arg0, arg1, arg2);
+ }
+
+ public void athrow() {
+ getMethodVisitor().visitInsn(ATHROW);
+ }
+
+ public void instance_of(String arg0) {
+ getMethodVisitor().visitTypeInsn(INSTANCEOF, arg0);
+ }
+
+ public void ifeq(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFEQ, arg0);
+ }
+
+ public void iffalse(Label arg0) {
+ ifeq(arg0);
+ }
+
+ public void ifne(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFNE, arg0);
+ }
+
+ public void iftrue(Label arg0) {
+ ifne(arg0);
+ }
+
+ public void if_acmpne(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ACMPNE, arg0);
+ }
+
+ public void if_acmpeq(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ACMPEQ, arg0);
+ }
+
+ public void if_icmple(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPLE, arg0);
+ }
+
+ public void if_icmpgt(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPGT, arg0);
+ }
+
+ public void if_icmpge(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPGE, arg0);
+ }
+
+ public void if_icmplt(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPLT, arg0);
+ }
+
+ public void if_icmpne(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPNE, arg0);
+ }
+
+ public void if_icmpeq(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IF_ICMPEQ, arg0);
+ }
+
+ public void checkcast(String arg0) {
+ getMethodVisitor().visitTypeInsn(CHECKCAST, arg0);
+ }
+
+ public void checkcast(Class clazz) {
+ getMethodVisitor().visitTypeInsn(CHECKCAST, p(clazz));
+ }
+
+ public void start() {
+ getMethodVisitor().visitCode();
+ }
+ private void dump() {
+ PrintWriter pw = new PrintWriter(System.out);
+
+ Class tmvClass = getMethodVisitor().getClass();
+ try {
+ Method print = tmvClass.getDeclaredMethod("print", PrintWriter.class);
+
+ pw.write("*** Dumping ***\n");
+
+ print.invoke(getMethodVisitor(), pw);
+ } catch (Throwable ex) {
+ } finally {
+ pw.flush();
+ }
+ }
+ public void end() {
+ if (DEBUG) {
+ dump();
+ }
+ getMethodVisitor().visitMaxs(1, 1);
+ getMethodVisitor().visitEnd();
+ }
+
+ public void line(int line) {
+ Label label = new Label();
+ label(label);
+ visitLineNumber(line, label);
+ }
+
+ public void line(int line, Label label) {
+ visitLineNumber(line, label);
+ }
+
+ public void ifnonnull(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFNONNULL, arg0);
+ }
+
+ public void ifnull(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFNULL, arg0);
+ }
+
+ public void iflt(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFLT, arg0);
+ }
+
+ public void ifle(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFLE, arg0);
+ }
+
+ public void ifgt(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFGT, arg0);
+ }
+
+ public void ifge(Label arg0) {
+ getMethodVisitor().visitJumpInsn(IFGE, arg0);
+ }
+
+ public void arraylength() {
+ getMethodVisitor().visitInsn(ARRAYLENGTH);
+ }
+
+ public void ishr() {
+ getMethodVisitor().visitInsn(ISHR);
+ }
+
+ public void ishl() {
+ getMethodVisitor().visitInsn(ISHL);
+ }
+
+ public void iushr() {
+ getMethodVisitor().visitInsn(IUSHR);
+ }
+
+ public void lshr() {
+ getMethodVisitor().visitInsn(LSHR);
+ }
+
+ public void lshl() {
+ getMethodVisitor().visitInsn(LSHL);
+ }
+
+ public void lushr() {
+ getMethodVisitor().visitInsn(LUSHR);
+ }
+
+ public void lcmp() {
+ getMethodVisitor().visitInsn(LCMP);
+ }
+
+ public void iand() {
+ getMethodVisitor().visitInsn(IAND);
+ }
+
+ public void ior() {
+ getMethodVisitor().visitInsn(IOR);
+ }
+
+ public void ixor() {
+ getMethodVisitor().visitInsn(IXOR);
+ }
+
+ public void land() {
+ getMethodVisitor().visitInsn(LAND);
+ }
+
+ public void lor() {
+ getMethodVisitor().visitInsn(LOR);
+ }
+
+ public void lxor() {
+ getMethodVisitor().visitInsn(LXOR);
+ }
+
+ public void iadd() {
+ getMethodVisitor().visitInsn(IADD);
+ }
+
+ public void ladd() {
+ getMethodVisitor().visitInsn(LADD);
+ }
+
+ public void fadd() {
+ getMethodVisitor().visitInsn(FADD);
+ }
+
+ public void dadd() {
+ getMethodVisitor().visitInsn(DADD);
+ }
+
+ public void isub() {
+ getMethodVisitor().visitInsn(ISUB);
+ }
+
+ public void lsub() {
+ getMethodVisitor().visitInsn(LSUB);
+ }
+
+ public void fsub() {
+ getMethodVisitor().visitInsn(FSUB);
+ }
+
+ public void dsub() {
+ getMethodVisitor().visitInsn(DSUB);
+ }
+
+ public void idiv() {
+ getMethodVisitor().visitInsn(IDIV);
+ }
+
+ public void irem() {
+ getMethodVisitor().visitInsn(IREM);
+ }
+
+ public void ineg() {
+ getMethodVisitor().visitInsn(INEG);
+ }
+
+ public void i2d() {
+ getMethodVisitor().visitInsn(I2D);
+ }
+
+ public void i2l() {
+ getMethodVisitor().visitInsn(I2L);
+ }
+
+ public void i2f() {
+ getMethodVisitor().visitInsn(I2F);
+ }
+
+ public void i2s() {
+ getMethodVisitor().visitInsn(I2S);
+ }
+
+ public void i2c() {
+ getMethodVisitor().visitInsn(I2C);
+ }
+
+ public void i2b() {
+ getMethodVisitor().visitInsn(I2B);
+ }
+
+ public void ldiv() {
+ getMethodVisitor().visitInsn(LDIV);
+ }
+
+ public void lrem() {
+ getMethodVisitor().visitInsn(LREM);
+ }
+
+ public void lneg() {
+ getMethodVisitor().visitInsn(LNEG);
+ }
+
+ public void l2d() {
+ getMethodVisitor().visitInsn(L2D);
+ }
+
+ public void l2i() {
+ getMethodVisitor().visitInsn(L2I);
+ }
+
+ public void l2f() {
+ getMethodVisitor().visitInsn(L2F);
+ }
+
+ public void fdiv() {
+ getMethodVisitor().visitInsn(FDIV);
+ }
+
+ public void frem() {
+ getMethodVisitor().visitInsn(FREM);
+ }
+
+ public void fneg() {
+ getMethodVisitor().visitInsn(FNEG);
+ }
+
+ public void f2d() {
+ getMethodVisitor().visitInsn(F2D);
+ }
+
+ public void f2i() {
+ getMethodVisitor().visitInsn(F2D);
+ }
+
+ public void f2l() {
+ getMethodVisitor().visitInsn(F2L);
+ }
+
+ public void ddiv() {
+ getMethodVisitor().visitInsn(DDIV);
+ }
+
+ public void drem() {
+ getMethodVisitor().visitInsn(DREM);
+ }
+
+ public void dneg() {
+ getMethodVisitor().visitInsn(DNEG);
+ }
+
+ public void d2f() {
+ getMethodVisitor().visitInsn(D2F);
+ }
+
+ public void d2i() {
+ getMethodVisitor().visitInsn(D2I);
+ }
+
+ public void d2l() {
+ getMethodVisitor().visitInsn(D2L);
+ }
+
+ public void imul() {
+ getMethodVisitor().visitInsn(IMUL);
+ }
+
+ public void lmul() {
+ getMethodVisitor().visitInsn(LMUL);
+ }
+
+ public void fmul() {
+ getMethodVisitor().visitInsn(FMUL);
+ }
+
+ public void dmul() {
+ getMethodVisitor().visitInsn(DMUL);
+ }
+
+ public void iinc(int arg0, int arg1) {
+ getMethodVisitor().visitIincInsn(arg0, arg1);
+ }
+
+ public void iinc(LocalVariable arg0, int arg1) {
+ getMethodVisitor().visitIincInsn(arg0.idx, arg1);
+ }
+
+ public void monitorenter() {
+ getMethodVisitor().visitInsn(MONITORENTER);
+ }
+
+ public void monitorexit() {
+ getMethodVisitor().visitInsn(MONITOREXIT);
+ }
+
+ public void jsr(Label branch) {
+ getMethodVisitor().visitJumpInsn(JSR, branch);
+ }
+
+ public void ret(int arg0) {
+ getMethodVisitor().visitVarInsn(RET, arg0);
+ }
+
+ public AnnotationVisitor visitAnnotationDefault() {
+ return getMethodVisitor().visitAnnotationDefault();
+ }
+
+ public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
+ return getMethodVisitor().visitAnnotation(arg0, arg1);
+ }
+
+ public AnnotationVisitor visitParameterAnnotation(int arg0, String arg1,
+ boolean arg2) {
+ return getMethodVisitor().visitParameterAnnotation(arg0, arg1, arg2);
+ }
+
+ public void visitAnnotationWithFields(String name, boolean visible, Map<String,Object> fields) {
+ AnnotationVisitor visitor = visitAnnotation(name, visible);
+ visitAnnotationFields(visitor, fields);
+ visitor.visitEnd();
+ }
+
+ public void visitParameterAnnotationWithFields(int param, String name, boolean visible, Map<String,Object> fields) {
+ AnnotationVisitor visitor = visitParameterAnnotation(param, name, visible);
+ visitAnnotationFields(visitor, fields);
+ visitor.visitEnd();
+ }
+
+ public void visitAttribute(Attribute arg0) {
+ getMethodVisitor().visitAttribute(arg0);
+ }
+
+ public void visitCode() {
+ getMethodVisitor().visitCode();
+ }
+
+ public void visitInsn(int arg0) {
+ getMethodVisitor().visitInsn(arg0);
+ }
+
+ public void visitIntInsn(int arg0, int arg1) {
+ getMethodVisitor().visitIntInsn(arg0, arg1);
+ }
+
+ public void visitVarInsn(int arg0, int arg1) {
+ getMethodVisitor().visitVarInsn(arg0, arg1);
+ }
+
+ public void visitTypeInsn(int arg0, String arg1) {
+ getMethodVisitor().visitTypeInsn(arg0, arg1);
+ }
+
+ public void visitFieldInsn(int arg0, String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitFieldInsn(arg0, arg1, arg2, arg3);
+ }
+
+ public void visitMethodInsn(int arg0, String arg1, String arg2, String arg3) {
+ getMethodVisitor().visitMethodInsn(arg0, arg1, arg2, arg3);
+ }
+
+ public void visitJumpInsn(int arg0, Label arg1) {
+ getMethodVisitor().visitJumpInsn(arg0, arg1);
+ }
+
+ public void visitLabel(Label arg0) {
+ getMethodVisitor().visitLabel(arg0);
+ }
+
+ public void visitLdcInsn(Object arg0) {
+ getMethodVisitor().visitLdcInsn(arg0);
+ }
+
+ public void visitIincInsn(int arg0, int arg1) {
+ getMethodVisitor().visitIincInsn(arg0, arg1);
+ }
+
+ public void visitTableSwitchInsn(int arg0, int arg1, Label arg2,
+ Label[] arg3) {
+ getMethodVisitor().visitTableSwitchInsn(arg0, arg1, arg2, arg3);
+ }
+
+ public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) {
+ getMethodVisitor().visitLookupSwitchInsn(arg0, arg1, arg2);
+ }
+
+ public void visitMultiANewArrayInsn(String arg0, int arg1) {
+ getMethodVisitor().visitMultiANewArrayInsn(arg0, arg1);
+ }
+
+ public void visitTryCatchBlock(Label arg0, Label arg1, Label arg2,
+ String arg3) {
+ getMethodVisitor().visitTryCatchBlock(arg0, arg1, arg2, arg3);
+ }
+
+ public void visitLocalVariable(String arg0, String arg1, String arg2,
+ Label arg3, Label arg4, int arg5) {
+ getMethodVisitor().visitLocalVariable(arg0, arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public void visitLineNumber(int arg0, Label arg1) {
+ getMethodVisitor().visitLineNumber(arg0, arg1);
+ }
+
+ public void visitMaxs(int arg0, int arg1) {
+ if (DEBUG) {
+ dump();
+ }
+ getMethodVisitor().visitMaxs(arg0, arg1);
+ }
+
+ public void visitEnd() {
+ getMethodVisitor().visitEnd();
+ }
+
+ public void tableswitch(int min, int max, Label defaultLabel, Label[] cases) {
+ getMethodVisitor().visitTableSwitchInsn(min, max, defaultLabel, cases);
+ }
+
+ public void visitFrame(int arg0, int arg1, Object[] arg2, int arg3, Object[] arg4) {
+ getMethodVisitor().visitFrame(arg0, arg1, arg2, arg3, arg4);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/StructByReferenceResultConverterFactory.java b/src/main/java/jnr/ffi/provider/jffi/StructByReferenceResultConverterFactory.java
new file mode 100644
index 0000000..b621481
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/StructByReferenceResultConverterFactory.java
@@ -0,0 +1,44 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.*;
+import jnr.ffi.mapper.FromNativeContext;
+import jnr.ffi.mapper.FromNativeConverter;
+import jnr.ffi.provider.converters.StructByReferenceFromNativeConverter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+final class StructByReferenceResultConverterFactory {
+ private final Map<Class<? extends Struct>, FromNativeConverter<? extends Struct, Pointer>> converters
+ = new ConcurrentHashMap<Class<? extends Struct>, FromNativeConverter<? extends Struct, Pointer>>();
+
+ private final AsmClassLoader classLoader;
+ private final boolean asmEnabled;
+
+ public StructByReferenceResultConverterFactory(AsmClassLoader classLoader, boolean asmEnabled) {
+ this.classLoader = classLoader;
+ this.asmEnabled = asmEnabled;
+ }
+
+ public final FromNativeConverter<? extends Struct, Pointer> get(Class<? extends Struct> structClass,
+ FromNativeContext fromNativeContext) {
+ FromNativeConverter<? extends Struct, Pointer> converter = converters.get(structClass);
+ if (converter == null) {
+ synchronized (converters) {
+ if ((converter = converters.get(structClass)) == null) {
+ converters.put(structClass, converter = createConverter(fromNativeContext.getRuntime(), structClass, fromNativeContext));
+ }
+ }
+ }
+
+ return converter;
+ }
+
+ private FromNativeConverter<? extends Struct, Pointer> createConverter(jnr.ffi.Runtime runtime,
+ Class<? extends Struct> structClass,
+ FromNativeContext fromNativeContext) {
+ return asmEnabled
+ ? AsmStructByReferenceFromNativeConverter.newStructByReferenceConverter(runtime, structClass, 0, classLoader)
+ : StructByReferenceFromNativeConverter.getInstance(structClass, fromNativeContext);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/StubCompiler.java b/src/main/java/jnr/ffi/provider/jffi/StubCompiler.java
new file mode 100644
index 0000000..8f4b917
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/StubCompiler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.*;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.x86asm.Assembler;
+import jnr.x86asm.CPU;
+
+/**
+ * Compiles asm trampoline stubs for java class methods
+ */
+abstract class StubCompiler {
+ // If the version of jffi exports the jffi_save_errno function address,
+ // then it is recent enough to support PageManager and NativeMethods as well.
+ static final long errnoFunctionAddress = getErrnoSaveFunction();
+ static final boolean hasPageManager = hasPageManager();
+ static final boolean hasAssembler = hasAssembler();
+
+ public static StubCompiler newCompiler(jnr.ffi.Runtime runtime) {
+ if (errnoFunctionAddress != 0 && hasPageManager && hasAssembler) {
+ switch (Platform.getPlatform().getCPU()) {
+ case I386:
+ if (Platform.getPlatform().getOS() != Platform.OS.WINDOWS) {
+ return new X86_32StubCompiler(runtime);
+ }
+ break;
+ case X86_64:
+ if (Platform.getPlatform().getOS() != Platform.OS.WINDOWS) {
+ return new X86_64StubCompiler(runtime);
+ }
+ break;
+ }
+ }
+
+ return new DummyStubCompiler();
+ }
+
+ abstract boolean canCompile(ResultType returnType, ParameterType[] parameterTypes, CallingConvention convention);
+
+ abstract void compile(Function function, String name, ResultType returnType, ParameterType[] parameterTypes,
+ Class resultClass, Class[] parameterClasses, CallingConvention convention, boolean saveErrno);
+
+ abstract void attach(Class clazz);
+
+ static final class DummyStubCompiler extends StubCompiler {
+
+ boolean canCompile(ResultType returnType, ParameterType[] parameterTypes, CallingConvention convention) {
+ return false;
+ }
+
+ @Override
+ void compile(Function function, String name, ResultType returnType, ParameterType[] parameterTypes,
+ Class resultClass, Class[] parameterClasses, CallingConvention convention, boolean saveErrno) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ void attach(Class clazz) {
+ // do nothing
+ }
+
+ }
+
+ private static long getErrnoSaveFunction() {
+ try {
+ return Internals.getErrnoSaveFunction();
+
+ } catch (Throwable t) {
+ return 0;
+ }
+ }
+
+ private static boolean hasPageManager() {
+ try {
+ // Just try and allocate/free a page to check the PageManager is working
+ long page = PageManager.getInstance().allocatePages(1, PageManager.PROT_READ | PageManager.PROT_WRITE);
+ PageManager.getInstance().freePages(page, 1);
+ return true;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ private static boolean hasAssembler() {
+ try {
+ switch (Platform.getPlatform().getCPU()) {
+ case I386:
+ new Assembler(CPU.X86_32);
+ return true;
+ case X86_64:
+ new Assembler(CPU.X86_64);
+ return true;
+ default:
+ return false;
+ }
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/SymbolNotFoundError.java b/src/main/java/jnr/ffi/provider/jffi/SymbolNotFoundError.java
new file mode 100644
index 0000000..68e2672
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/SymbolNotFoundError.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+public class SymbolNotFoundError extends java.lang.UnsatisfiedLinkError {
+
+ public SymbolNotFoundError(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/ToNativeOp.java b/src/main/java/jnr/ffi/provider/jffi/ToNativeOp.java
new file mode 100644
index 0000000..9c3c37a
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/ToNativeOp.java
@@ -0,0 +1,144 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Address;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.provider.ToNativeType;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import static jnr.ffi.provider.jffi.AsmUtil.*;
+import static jnr.ffi.provider.jffi.NumberUtil.narrow;
+import static jnr.ffi.provider.jffi.NumberUtil.widen;
+
+/**
+ * Emits appropriate asm code to convert the parameter to a native value
+ */
+abstract class ToNativeOp {
+ private final boolean isPrimitive;
+
+ protected ToNativeOp(boolean primitive) {
+ isPrimitive = primitive;
+ }
+
+ final boolean isPrimitive() {
+ return isPrimitive;
+ }
+
+ abstract void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType);
+
+ private static final Map<Class, ToNativeOp> operations;
+ static {
+ Map<Class, ToNativeOp> m = new IdentityHashMap<Class, ToNativeOp>();
+ for (Class c : new Class[] { byte.class, char.class, short.class, int.class, long.class, boolean.class }) {
+ m.put(c, new Integral(c));
+ m.put(boxedType(c), new Integral(boxedType(c)));
+ }
+ m.put(float.class, new Float32(float.class));
+ m.put(Float.class, new Float32(Float.class));
+ m.put(double.class, new Float64(float.class));
+ m.put(Double.class, new Float64(Float.class));
+ m.put(Address.class, new AddressOp());
+
+ operations = Collections.unmodifiableMap(m);
+ }
+
+ static ToNativeOp get(ToNativeType type) {
+ ToNativeOp op = operations.get(type.effectiveJavaType());
+ if (op != null) {
+ return op;
+
+ } else {
+ return null;
+ }
+ }
+
+ static abstract class Primitive extends ToNativeOp {
+ protected final Class javaType;
+ protected Primitive(Class javaType) {
+ super(true);
+ this.javaType = javaType;
+ }
+ }
+
+
+ static class Integral extends Primitive {
+ Integral(Class javaType) {
+ super(javaType);
+ }
+
+ @Override
+ public void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType) {
+ if (javaType.isPrimitive()) {
+ NumberUtil.convertPrimitive(mv, javaType, primitiveClass, nativeType);
+ } else {
+ unboxNumber(mv, javaType, primitiveClass, nativeType);
+ }
+ }
+ }
+
+ static class Float32 extends Primitive {
+ Float32(Class javaType) {
+ super(javaType);
+ }
+
+ @Override
+ void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType) {
+ if (!javaType.isPrimitive()) {
+ unboxNumber(mv, javaType, float.class);
+ }
+ if (primitiveClass != float.class) {
+ mv.invokestatic(Float.class, "floatToRawIntBits", int.class, float.class);
+ widen(mv, int.class, primitiveClass);
+ }
+ }
+ }
+
+ static class Float64 extends Primitive {
+ Float64(Class javaType) {
+ super(javaType);
+ }
+
+ @Override
+ void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType) {
+ if (!javaType.isPrimitive()) {
+ unboxNumber(mv, javaType, double.class);
+ }
+ if (primitiveClass != double.class) {
+ mv.invokestatic(Double.class, "doubleToRawLongBits", long.class, double.class);
+ narrow(mv, long.class, primitiveClass);
+ }
+ }
+ }
+
+ static class Delegate extends Primitive {
+ static final ToNativeOp INSTANCE = new Delegate();
+ Delegate() {
+ super(Pointer.class);
+ }
+
+ @Override
+ void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType) {
+ // delegates are always direct, so handle without the strategy processing
+ unboxPointer(mv, primitiveClass);
+ }
+ }
+
+ static class AddressOp extends Primitive {
+ AddressOp() {
+ super(Address.class);
+ }
+
+ @Override
+ void emitPrimitive(SkinnyMethodAdapter mv, Class primitiveClass, NativeType nativeType) {
+ if (long.class == primitiveClass) {
+ mv.invokestatic(AsmRuntime.class, "longValue", long.class, Address.class);
+ } else {
+ mv.invokestatic(AsmRuntime.class, "intValue", int.class, Address.class);
+ narrow(mv, int.class, primitiveClass);
+ }
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/TransientNativeMemory.java b/src/main/java/jnr/ffi/provider/jffi/TransientNativeMemory.java
new file mode 100644
index 0000000..e251b57
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/TransientNativeMemory.java
@@ -0,0 +1,137 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.PageManager;
+import jnr.ffi.util.ref.FinalizablePhantomReference;
+import jnr.ffi.util.ref.FinalizableReferenceQueue;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ */
+public class TransientNativeMemory extends DirectMemoryIO {
+ /** Keeps strong references to the magazine until cleanup */
+ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+ private static final Map<Magazine, Boolean> referenceSet = new ConcurrentHashMap<Magazine, Boolean>();
+
+ private static final ThreadLocal<Magazine> currentMagazine = new ThreadLocal<Magazine>();
+ private static final int PAGES_PER_MAGAZINE = 2;
+
+ private final Sentinel sentinel;
+ private final int size;
+
+ public static DirectMemoryIO allocate(jnr.ffi.Runtime runtime, int size, int align, boolean clear) {
+ if (size < 0) {
+ throw new IllegalArgumentException("negative size: " + size);
+ }
+
+ if (size > 256) { /* Only use the transient allocator for small, short lived allocations */
+ return new AllocatedDirectMemoryIO(runtime, size, clear);
+ }
+
+ Magazine magazine = currentMagazine.get();
+ Sentinel sentinel = magazine != null ? magazine.sentinel() : null;
+ long address;
+
+ if (sentinel == null || (address = magazine.allocate(size, align)) == 0) {
+ PageManager pm = PageManager.getInstance();
+ long memory;
+ do {
+ memory = pm.allocatePages(PAGES_PER_MAGAZINE, PageManager.PROT_READ | PageManager.PROT_WRITE);
+ if (memory != 0L && memory != -1L) {
+ break;
+ }
+
+ // No available pages; trigger a full GC to reclaim some memory
+ System.gc();
+ FinalizableReferenceQueue.cleanUpAll();
+ } while (true);
+
+ referenceSet.put(magazine = new Magazine(sentinel = new Sentinel(), pm, memory, PAGES_PER_MAGAZINE), Boolean.TRUE);
+ currentMagazine.set(magazine);
+ address = magazine.allocate(size, align);
+ }
+
+ return new TransientNativeMemory(runtime, sentinel, address, size);
+ }
+
+
+ TransientNativeMemory(jnr.ffi.Runtime runtime, Sentinel sentinel, long address, int size) {
+ super(runtime, address);
+ this.sentinel = sentinel;
+ this.size = size;
+ }
+
+ private static long align(long offset, long align) {
+ return (offset + align - 1L) & ~(align - 1L);
+ }
+
+
+ @Override
+ public long size() {
+ return this.size;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TransientNativeMemory) {
+ TransientNativeMemory mem = (TransientNativeMemory) obj;
+ return mem.size == size && mem.address() == address();
+ }
+
+ return super.equals(obj);
+ }
+
+ public final void dispose() { /* not-implemented */ }
+
+
+ private static final class Sentinel {}
+
+ /**
+ * Holder for a group of memory allocations.
+ */
+ private static final class Magazine extends FinalizablePhantomReference<Sentinel> {
+ private final Reference<Sentinel> sentinelReference;
+ private final PageManager pm;
+ private final long page;
+ private final long end;
+ private final int pageCount;
+ private long memory;
+
+ Magazine(Sentinel sentinel, PageManager pm, long page, int pageCount) {
+ super(sentinel, NativeFinalizer.getInstance().getFinalizerQueue());
+ this.sentinelReference = new WeakReference<Sentinel>(sentinel);
+ this.pm = pm;
+ this.memory = this.page = page;
+ this.pageCount = pageCount;
+ this.end = memory + (pageCount * pm.pageSize());
+ }
+
+ Sentinel sentinel() {
+ return sentinelReference.get();
+ }
+
+ long allocate(int size, int align) {
+ long address = align(this.memory, align);
+ if (address + size <= end) {
+ memory = address + size;
+ return address;
+ }
+
+ return 0L;
+ }
+
+ public final void finalizeReferent() {
+ pm.freePages(page, pageCount);
+ referenceSet.remove(this);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/Types.java b/src/main/java/jnr/ffi/provider/jffi/Types.java
new file mode 100644
index 0000000..9cd593d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/Types.java
@@ -0,0 +1,109 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.Address;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.Type;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.nio.Buffer;
+import java.util.*;
+
+
+/**
+ *
+ */
+class Types {
+ private static Reference<Map<Class, Map<Collection<Annotation>, Type>>> typeCacheReference;
+
+ static Type getType(jnr.ffi.Runtime runtime, Class javaType, Collection<Annotation> annotations) {
+ Map<Class, Map<Collection<Annotation>, Type>> cache = typeCacheReference != null ? typeCacheReference.get() : null;
+ Map<Collection<Annotation>, Type> aliasCache = cache != null ? cache.get(javaType) : null;
+ Type type = aliasCache != null ? aliasCache.get(annotations) : null;
+
+ return type != null ? type : lookupAndCacheType(runtime, javaType, annotations);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static synchronized Type lookupAndCacheType(jnr.ffi.Runtime runtime, Class javaType, Collection<Annotation> annotations) {
+ Map<Class, Map<Collection<Annotation>, Type>> cache = typeCacheReference != null ? typeCacheReference.get() : null;
+ Map<Collection<Annotation>, Type> aliasCache = cache != null ? cache.get(javaType) : null;
+ Type type = aliasCache != null ? aliasCache.get(annotations) : null;
+ if (type != null) {
+ return type;
+ }
+ cache = new HashMap<Class, Map<Collection<Annotation>, Type>>(cache != null ? cache : Collections.EMPTY_MAP);
+
+ aliasCache = new HashMap<Collection<Annotation>, Type>(aliasCache != null ? aliasCache : Collections.EMPTY_MAP);
+ aliasCache.put(annotations, type = lookupType(runtime, javaType, annotations));
+ cache.put(javaType, Collections.unmodifiableMap(aliasCache));
+
+ typeCacheReference = new SoftReference<Map<Class, Map<Collection<Annotation>, Type>>>(Collections.unmodifiableMap(new IdentityHashMap<Class, Map<Collection<Annotation>, Type>>(cache)));
+
+ return type;
+ }
+
+ private static Type lookupAliasedType(jnr.ffi.Runtime runtime, Collection<Annotation> annotations) {
+ for (Annotation a : annotations) {
+ TypeDefinition typedef = a.annotationType().getAnnotation(TypeDefinition.class);
+ if (typedef != null) {
+ return runtime.findType(typedef.alias());
+ }
+ }
+
+ return null;
+ }
+
+ static Type lookupType(jnr.ffi.Runtime runtime, Class type, Collection<Annotation> annotations) {
+ Type aliasedType = lookupAliasedType(runtime, annotations);
+ if (aliasedType != null) {
+ return aliasedType;
+
+ } else if (Void.class.isAssignableFrom(type) || void.class == type) {
+ return runtime.findType(NativeType.VOID);
+
+ } else if (Boolean.class.isAssignableFrom(type) || boolean.class == type) {
+ return runtime.findType(NativeType.SINT);
+
+ } else if (Byte.class.isAssignableFrom(type) || byte.class == type) {
+ return runtime.findType(NativeType.SCHAR);
+
+ } else if (Short.class.isAssignableFrom(type) || short.class == type) {
+ return runtime.findType(NativeType.SSHORT);
+
+ } else if (Integer.class.isAssignableFrom(type) || int.class == type) {
+ return runtime.findType(NativeType.SINT);
+
+ } else if (Long.class.isAssignableFrom(type) || long.class == type) {
+ return runtime.findType(NativeType.SLONG);
+
+ } else if (Float.class.isAssignableFrom(type) || float.class == type) {
+ return runtime.findType(NativeType.FLOAT);
+
+ } else if (Double.class.isAssignableFrom(type) || double.class == type) {
+ return runtime.findType(NativeType.DOUBLE);
+
+ } else if (Pointer.class.isAssignableFrom(type)) {
+ return runtime.findType(NativeType.ADDRESS);
+
+ } else if (Address.class.isAssignableFrom(type)) {
+ return runtime.findType(NativeType.ADDRESS);
+
+ } else if (Buffer.class.isAssignableFrom(type)) {
+ return runtime.findType(NativeType.ADDRESS);
+
+ } else if (CharSequence.class.isAssignableFrom(type)) {
+ return runtime.findType(NativeType.ADDRESS);
+
+ } else if (type.isArray()) {
+ return runtime.findType(NativeType.ADDRESS);
+
+ } else {
+ throw new IllegalArgumentException("unsupported type: " + type);
+ }
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/Util.java b/src/main/java/jnr/ffi/provider/jffi/Util.java
new file mode 100644
index 0000000..84d9a11
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/Util.java
@@ -0,0 +1,13 @@
+package jnr.ffi.provider.jffi;
+
+/**
+ */
+final class Util {
+ static boolean getBooleanProperty(String propertyName, boolean defaultValue) {
+ try {
+ return Boolean.valueOf(System.getProperty(propertyName, Boolean.valueOf(defaultValue).toString()));
+ } catch (SecurityException se) {
+ return defaultValue;
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/VariableAccessorGenerator.java b/src/main/java/jnr/ffi/provider/jffi/VariableAccessorGenerator.java
new file mode 100644
index 0000000..ec165c3
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/VariableAccessorGenerator.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2012 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.Variable;
+import jnr.ffi.mapper.*;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.AsmUtil.*;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.InvokerUtil.hasAnnotation;
+import static org.objectweb.asm.Opcodes.*;
+
+/**
+ * Generate global variable accessors
+ */
+public class VariableAccessorGenerator {
+ private final AtomicLong nextClassID = new AtomicLong(0);
+ private final jnr.ffi.Runtime runtime;
+ static final Map<NativeType, PointerOp> pointerOperations;
+
+ public VariableAccessorGenerator(jnr.ffi.Runtime runtime) {
+ this.runtime = runtime;
+ }
+
+ public void generate(AsmBuilder builder, Class interfaceClass, String variableName, long address,
+ Class javaType, Collection<Annotation> annotations,
+ SignatureTypeMapper typeMapper, AsmClassLoader classLoader) {
+
+ if (!NativeLibraryLoader.ASM_ENABLED) {
+ throw new UnsupportedOperationException("asm bytecode generation not supported");
+ }
+
+ SimpleNativeContext context = new SimpleNativeContext(builder.getRuntime(), annotations);
+ SignatureType signatureType = DefaultSignatureType.create(javaType, (FromNativeContext) context);
+ jnr.ffi.mapper.FromNativeType fromNativeType = typeMapper.getFromNativeType(signatureType, context);
+ FromNativeConverter fromNativeConverter = fromNativeType != null ? fromNativeType.getFromNativeConverter() : null;
+ jnr.ffi.mapper.ToNativeType toNativeType = typeMapper.getToNativeType(signatureType, context);
+ ToNativeConverter toNativeConverter = toNativeType != null ? toNativeType.getToNativeConverter() : null;
+
+ Variable variableAccessor = buildVariableAccessor(builder.getRuntime(), address, interfaceClass, javaType, annotations,
+ toNativeConverter, fromNativeConverter, classLoader);
+ SkinnyMethodAdapter mv = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL,
+ variableName, sig(Variable.class), null, null);
+ mv.start();
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getVariableName(variableAccessor), ci(Variable.class));
+ mv.areturn();
+ mv.visitMaxs(10, 10);
+ mv.visitEnd();
+ }
+
+ Variable buildVariableAccessor(jnr.ffi.Runtime runtime, long address, Class interfaceClass, Class javaType, Collection<Annotation> annotations,
+ ToNativeConverter toNativeConverter, FromNativeConverter fromNativeConverter,
+ AsmClassLoader classLoader) {
+ boolean debug = AsmLibraryLoader.DEBUG && !hasAnnotation(annotations, NoTrace.class);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ ClassVisitor cv = debug ? AsmUtil.newCheckClassAdapter(cw) : cw;
+
+ AsmBuilder builder = new AsmBuilder(runtime, p(interfaceClass) + "$VariableAccessor$$" + nextClassID.getAndIncrement(), cv, classLoader);
+ cv.visit(V1_6, ACC_PUBLIC | ACC_FINAL, builder.getClassNamePath(), null, p(Object.class),
+ new String[] { p(Variable.class) });
+
+ SkinnyMethodAdapter set = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL, "set",
+ sig(void.class, Object.class),
+ null, null);
+
+ Class boxedType = toNativeConverter != null ? toNativeConverter.nativeType() : javaType;
+ NativeType nativeType = Types.getType(runtime, boxedType, annotations).getNativeType();
+ jnr.ffi.provider.ToNativeType toNativeType = new jnr.ffi.provider.ToNativeType(javaType, nativeType, annotations, toNativeConverter, null);
+ jnr.ffi.provider.FromNativeType fromNativeType = new jnr.ffi.provider.FromNativeType(javaType, nativeType, annotations, fromNativeConverter, null);
+ PointerOp pointerOp = pointerOperations.get(nativeType);
+ if (pointerOp == null) {
+ throw new IllegalArgumentException("global variable type not supported: " + javaType);
+ }
+
+ set.start();
+ set.aload(0);
+ Pointer pointer = DirectMemoryIO.wrap(runtime, address);
+ set.getfield(builder.getClassNamePath(), builder.getObjectFieldName(pointer, Pointer.class), ci(Pointer.class));
+ set.lconst_0();
+
+ set.aload(1);
+ set.checkcast(javaType);
+ emitToNativeConversion(builder, set, toNativeType);
+
+ ToNativeOp toNativeOp = ToNativeOp.get(toNativeType);
+ if (toNativeOp != null && toNativeOp.isPrimitive()) {
+ toNativeOp.emitPrimitive(set, pointerOp.nativeIntClass, toNativeType.getNativeType());
+
+ } else if (Pointer.class.isAssignableFrom(toNativeType.effectiveJavaType())) {
+ pointerOp = POINTER_OP_POINTER;
+
+ } else {
+ throw new IllegalArgumentException("global variable type not supported: " + javaType);
+ }
+
+ pointerOp.put(set);
+
+ set.voidreturn();
+ set.visitMaxs(10, 10);
+ set.visitEnd();
+
+ SkinnyMethodAdapter get = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL, "get",
+ sig(Object.class),
+ null, null);
+
+ get.start();
+ get.aload(0);
+ get.getfield(builder.getClassNamePath(), builder.getObjectFieldName(pointer, Pointer.class), ci(Pointer.class));
+ get.lconst_0();
+ pointerOp.get(get);
+ emitFromNativeConversion(builder, get, fromNativeType, pointerOp.nativeIntClass);
+ get.areturn();
+ get.visitMaxs(10, 10);
+ get.visitEnd();
+
+ SkinnyMethodAdapter init = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>",
+ sig(void.class, Object[].class),
+ null, null);
+ init.start();
+ init.aload(0);
+ init.invokespecial(p(Object.class), "<init>", sig(void.class));
+
+ builder.emitFieldInitialization(init, 1);
+
+ init.voidreturn();
+ init.visitMaxs(10, 10);
+ init.visitEnd();
+
+ cv.visitEnd();
+
+ try {
+ byte[] bytes = cw.toByteArray();
+ if (debug) {
+ ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
+ new ClassReader(bytes).accept(trace, 0);
+ }
+
+ Class<Variable> implClass = classLoader.defineClass(builder.getClassNamePath().replace("/", "."), bytes);
+ Constructor<Variable> cons = implClass.getDeclaredConstructor(Object[].class);
+ return cons.newInstance(new Object[] { builder.getObjectFieldValues() });
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ static {
+ Map<NativeType, PointerOp> ops = new EnumMap<NativeType, PointerOp>(NativeType.class);
+ op(ops, NativeType.SCHAR, "Byte", byte.class);
+ op(ops, NativeType.UCHAR, "Byte", byte.class);
+ op(ops, NativeType.SSHORT, "Short", short.class);
+ op(ops, NativeType.USHORT, "Short", short.class);
+ op(ops, NativeType.SINT, "Int", int.class);
+ op(ops, NativeType.UINT, "Int", int.class);
+ op(ops, NativeType.SLONG, "Long", long.class);
+ op(ops, NativeType.ULONG, "Long", long.class);
+ op(ops, NativeType.SLONGLONG, "LongLong", long.class);
+ op(ops, NativeType.ULONGLONG, "LongLong", long.class);
+ op(ops, NativeType.FLOAT, "Float", float.class);
+ op(ops, NativeType.DOUBLE, "Double", double.class);
+ op(ops, NativeType.ADDRESS, "Address", long.class);
+
+ pointerOperations = Collections.unmodifiableMap(ops);
+ }
+
+ private static void op(Map<NativeType, PointerOp> ops, NativeType type, String name, Class nativeIntType) {
+ ops.put(type, new PointerOp(name, nativeIntType));
+ }
+
+ private static final PointerOp POINTER_OP_POINTER = new PointerOp("Pointer", Pointer.class);
+
+ private static final class PointerOp {
+ private final String getMethodName;
+ private final String putMethodName;
+ final Class nativeIntClass;
+
+ private PointerOp(String name, Class nativeIntClass) {
+ this.getMethodName = "get" + name;
+ this.putMethodName = "put" + name;
+ this.nativeIntClass = nativeIntClass;
+ }
+
+ void put(SkinnyMethodAdapter mv) {
+ mv.invokevirtual(Pointer.class, putMethodName, void.class, long.class, nativeIntClass);
+ }
+
+ void get(SkinnyMethodAdapter mv) {
+ mv.invokevirtual(Pointer.class, getMethodName, nativeIntClass, long.class);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/X86Disassembler.java b/src/main/java/jnr/ffi/provider/jffi/X86Disassembler.java
new file mode 100644
index 0000000..0f9b13c
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/X86Disassembler.java
@@ -0,0 +1,126 @@
+package jnr.ffi.provider.jffi;
+
+import jnr.ffi.*;
+import jnr.ffi.LibraryLoader;
+import jnr.ffi.Runtime;
+import jnr.ffi.mapper.DefaultTypeMapper;
+import jnr.ffi.mapper.ToNativeContext;
+import jnr.ffi.mapper.ToNativeConverter;
+import jnr.ffi.types.intptr_t;
+import jnr.ffi.types.size_t;
+import jnr.ffi.types.u_int64_t;
+import jnr.ffi.types.u_int8_t;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+class X86Disassembler {
+
+ public enum Syntax { INTEL, ATT }
+
+ public enum Mode { I386, X86_64 }
+
+ private final UDis86 udis86;
+ final Pointer ud;
+
+
+ static final class SingletonHolder {
+ static final UDis86 INSTANCE = loadUDis86();
+ static final long intel = ((AbstractAsmLibraryInterface) INSTANCE).getLibrary().findSymbolAddress("ud_translate_intel");
+ static final long att = ((AbstractAsmLibraryInterface) INSTANCE).getLibrary().findSymbolAddress("ud_translate_att");
+ }
+
+ static UDis86 loadUDis86() {
+ DefaultTypeMapper typeMapper = new DefaultTypeMapper();
+ typeMapper.put(X86Disassembler.class, new X86DisassemblerConverter());
+ return LibraryLoader.create(UDis86.class)
+ .library("udis86")
+ .search("/usr/local/lib")
+ .search("/opt/local/lib")
+ .search("/usr/lib")
+ .mapper(typeMapper)
+ .load();
+ }
+
+ @ToNativeConverter.NoContext
+ public static final class X86DisassemblerConverter implements ToNativeConverter<X86Disassembler, Pointer> {
+ public Pointer toNative(X86Disassembler value, ToNativeContext context) {
+ return value.ud;
+ }
+
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+ }
+
+ static boolean isAvailable() {
+ try {
+ return SingletonHolder.INSTANCE != null;
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ static X86Disassembler create() {
+ return new X86Disassembler(SingletonHolder.INSTANCE);
+ }
+
+ private X86Disassembler(UDis86 udis86) {
+ this.udis86 = udis86;
+ this.ud = Memory.allocateDirect(Runtime.getRuntime(udis86), 1024, true);
+ this.udis86.ud_init(this.ud);
+ }
+
+ public void setSyntax(Syntax syntax) {
+ udis86.ud_set_syntax(this, syntax == Syntax.INTEL ? SingletonHolder.intel : SingletonHolder.att);
+ }
+
+ public void setMode(Mode mode) {
+ udis86.ud_set_mode(this, mode == Mode.I386 ? 32 : 64);
+ }
+
+ public void setInputBuffer(Pointer buffer, int size) {
+ udis86.ud_set_input_buffer(this, buffer, size);
+ }
+
+ public boolean disassemble() {
+ return udis86.ud_disassemble(this) != 0;
+ }
+
+ public String insn() {
+ return udis86.ud_insn_asm(this);
+ }
+
+ public long offset() {
+ return udis86.ud_insn_off(this);
+ }
+
+ public String hex() {
+ return udis86.ud_insn_hex(this);
+ }
+
+ @NoX86
+ @NoTrace
+ public static interface UDis86 {
+ void ud_init(Pointer ud);
+ void ud_set_mode(X86Disassembler ud, @u_int8_t int mode);
+ void ud_set_pc(X86Disassembler ud, @u_int64_t int pc);
+ void ud_set_input_buffer(X86Disassembler ud, Pointer data, @size_t long len);
+ void ud_set_vendor(X86Disassembler ud, int vendor);
+ void ud_set_syntax(X86Disassembler ud, @intptr_t long translator);
+ void ud_input_skip(X86Disassembler ud, @size_t long size);
+ int ud_input_end(X86Disassembler ud);
+ int ud_decode(X86Disassembler ud);
+ int ud_disassemble(X86Disassembler ud);
+ String ud_insn_asm(X86Disassembler ud);
+ @intptr_t long ud_insn_ptr(X86Disassembler ud);
+ @u_int64_t long ud_insn_off(X86Disassembler ud);
+ String ud_insn_hex(X86Disassembler ud);
+ int ud_insn_len(X86Disassembler ud);
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/X86MethodGenerator.java b/src/main/java/jnr/ffi/provider/jffi/X86MethodGenerator.java
new file mode 100644
index 0000000..a2e8f7d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/X86MethodGenerator.java
@@ -0,0 +1,329 @@
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.*;
+import jnr.ffi.NativeType;
+import jnr.ffi.Pointer;
+import jnr.ffi.CallingConvention;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.ffi.provider.SigType;
+import org.objectweb.asm.Label;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import static jnr.ffi.provider.jffi.AbstractFastNumericMethodGenerator.*;
+import static jnr.ffi.provider.jffi.AsmUtil.unboxedReturnType;
+import static jnr.ffi.provider.jffi.BaseMethodGenerator.emitEpilogue;
+import static jnr.ffi.provider.jffi.BaseMethodGenerator.loadAndConvertParameter;
+import static jnr.ffi.provider.jffi.CodegenUtils.*;
+import static jnr.ffi.provider.jffi.NumberUtil.*;
+import static jnr.ffi.provider.jffi.Util.getBooleanProperty;
+import static org.objectweb.asm.Opcodes.*;
+
+/**
+ *
+ */
+class X86MethodGenerator implements MethodGenerator {
+ private static final boolean ENABLED = getBooleanProperty("jnr.ffi.x86asm.enabled", true);
+ private final AtomicLong nextMethodID = new AtomicLong(0);
+ private final StubCompiler compiler;
+
+ X86MethodGenerator(StubCompiler compiler) {
+ this.compiler = compiler;
+ }
+
+ public boolean isSupported(ResultType resultType, ParameterType[] parameterTypes, CallingConvention callingConvention) {
+ if (!ENABLED) {
+ return false;
+ }
+
+ final Platform platform = Platform.getPlatform();
+
+ if (platform.getOS().equals(Platform.OS.WINDOWS)) {
+ return false;
+ }
+
+ if (!platform.getCPU().equals(Platform.CPU.I386) && !platform.getCPU().equals(Platform.CPU.X86_64)) {
+ return false;
+ }
+
+ if (!callingConvention.equals(CallingConvention.DEFAULT)) {
+ return false;
+ }
+
+ int objectCount = 0;
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ if (!isSupportedParameter(parameterTypes[i])) {
+ return false;
+ }
+
+ if (isSupportedObjectParameterType(parameterTypes[i])) {
+ objectCount++;
+ }
+ }
+
+ if (objectCount > 0) {
+ if (parameterTypes.length > 4 || objectCount > 3) {
+ return false;
+ }
+ }
+
+ return isSupportedResult(resultType)
+ && compiler.canCompile(resultType, parameterTypes, callingConvention);
+ }
+
+ public void generate(AsmBuilder builder, String functionName, Function function,
+ ResultType resultType, ParameterType[] parameterTypes, boolean ignoreError) {
+
+ Class[] nativeParameterTypes = new Class[parameterTypes.length];
+ boolean wrapperNeeded = false;
+
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ wrapperNeeded |= parameterTypes[i].getToNativeConverter() != null || !parameterTypes[i].effectiveJavaType().isPrimitive();
+ if (!parameterTypes[i].effectiveJavaType().isPrimitive()) {
+ nativeParameterTypes[i] = getNativeClass(parameterTypes[i].getNativeType());
+ } else {
+ nativeParameterTypes[i] = parameterTypes[i].effectiveJavaType();
+ }
+ }
+
+ Class nativeReturnType;
+ wrapperNeeded |= resultType.getFromNativeConverter() != null || !resultType.effectiveJavaType().isPrimitive();
+ if (resultType.effectiveJavaType().isPrimitive()) {
+ nativeReturnType = resultType.effectiveJavaType();
+ } else {
+ nativeReturnType = getNativeClass(resultType.getNativeType());
+ }
+
+ String stubName = functionName + (wrapperNeeded ? "$jni$" + nextMethodID.incrementAndGet() : "");
+
+ builder.getClassVisitor().visitMethod(ACC_PUBLIC | ACC_FINAL | ACC_NATIVE | (wrapperNeeded ? ACC_STATIC : 0),
+ stubName, sig(nativeReturnType, nativeParameterTypes), null, null);
+
+ compiler.compile(function, stubName, resultType, parameterTypes, nativeReturnType, nativeParameterTypes,
+ CallingConvention.DEFAULT, !ignoreError);
+
+ // If unboxing of parameters is required, generate a wrapper
+ if (wrapperNeeded) {
+ generateWrapper(builder, functionName, function, resultType, parameterTypes,
+ stubName, nativeReturnType, nativeParameterTypes);
+ }
+ }
+
+ private static void generateWrapper(final AsmBuilder builder, String functionName, Function function,
+ final ResultType resultType, final ParameterType[] parameterTypes,
+ String nativeMethodName, Class nativeReturnType, Class[] nativeParameterTypes) {
+ Class[] javaParameterTypes = new Class[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ javaParameterTypes[i] = parameterTypes[i].getDeclaredType();
+ }
+
+ final SkinnyMethodAdapter mv = new SkinnyMethodAdapter(builder.getClassVisitor(),
+ ACC_PUBLIC | ACC_FINAL,
+ functionName, sig(resultType.getDeclaredType(), javaParameterTypes), null, null);
+ mv.setMethodVisitor(AsmUtil.newTraceMethodVisitor(mv.getMethodVisitor()));
+ mv.start();
+
+
+ LocalVariableAllocator localVariableAllocator = new LocalVariableAllocator(parameterTypes);
+ final LocalVariable objCount = localVariableAllocator.allocate(int.class);
+ final LocalVariable[] parameters = AsmUtil.getParameterVariables(parameterTypes);
+ final LocalVariable[] converted = new LocalVariable[parameterTypes.length];
+ int pointerCount = 0;
+
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ Class javaParameterClass = parameterTypes[i].effectiveJavaType();
+ Class nativeParameterClass = nativeParameterTypes[i];
+
+ converted[i] = loadAndConvertParameter(builder, mv, localVariableAllocator, parameters[i], parameterTypes[i]);
+
+ ToNativeOp toNativeOp = ToNativeOp.get(parameterTypes[i]);
+ if (toNativeOp != null && toNativeOp.isPrimitive()) {
+ toNativeOp.emitPrimitive(mv, nativeParameterClass, parameterTypes[i].getNativeType());
+
+ } else if (hasPointerParameterStrategy(javaParameterClass)) {
+ pointerCount = emitDirectCheck(mv, javaParameterClass, nativeParameterClass, converted[i], objCount, pointerCount);
+
+ } else if (!javaParameterClass.isPrimitive()) {
+ throw new IllegalArgumentException("unsupported type " + javaParameterClass);
+ }
+ }
+ Label hasObjects = new Label();
+ Label convertResult = new Label();
+
+ // If there are any objects, jump to the fast-object path
+ if (pointerCount > 0) {
+ mv.iload(objCount);
+ mv.ifne(hasObjects);
+ }
+
+ // invoke the compiled stub
+ mv.invokestatic(builder.getClassNamePath(), nativeMethodName, sig(nativeReturnType, nativeParameterTypes));
+
+ // If boxing is neccessary, perform conversions
+ final Class unboxedResultType = unboxedReturnType(resultType.effectiveJavaType());
+ convertPrimitive(mv, nativeReturnType, unboxedResultType);
+
+ if (pointerCount > 0) {
+ mv.label(convertResult);
+ }
+ emitEpilogue(builder, mv, resultType, parameterTypes, parameters, converted, null);
+
+ /* -- method returns above - below is the object path, which will jump back above to return -- */
+
+ // Now implement heap object support
+ if (pointerCount > 0) {
+ mv.label(hasObjects);
+
+ // Store all the native args
+ LocalVariable[] tmp = new LocalVariable[parameterTypes.length];
+ for (int i = parameterTypes.length - 1; i >= 0; i--) {
+ tmp[i] = localVariableAllocator.allocate(long.class);
+ if (float.class == nativeParameterTypes[i]) {
+ mv.invokestatic(Float.class, "floatToRawIntBits", int.class, float.class);
+ mv.i2l();
+
+ } else if (double.class == nativeParameterTypes[i]) {
+ mv.invokestatic(Double.class, "doubleToRawLongBits", long.class, double.class);
+
+ } else {
+ convertPrimitive(mv, nativeParameterTypes[i], long.class, parameterTypes[i].getNativeType());
+ }
+ mv.lstore(tmp[i]);
+ }
+
+ // Retrieve the static 'ffi' Invoker instance
+ mv.getstatic(p(AbstractAsmLibraryInterface.class), "ffi", ci(com.kenai.jffi.Invoker.class));
+
+ // retrieve the call context and function address
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getCallContextFieldName(function), ci(CallContext.class));
+
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getFunctionAddressFieldName(function), ci(long.class));
+
+ // Now reload the args back onto the parameter stack
+ mv.lload(tmp);
+
+ mv.iload(objCount);
+ // Need to load all the converters onto the stack
+ for (int i = 0; i < parameterTypes.length; i++) {
+ LocalVariable[] strategies = new LocalVariable[parameterTypes.length];
+ Class javaParameterType = parameterTypes[i].effectiveJavaType();
+ if (hasPointerParameterStrategy(javaParameterType)) {
+ mv.aload(converted[i]);
+ emitParameterStrategyLookup(mv, javaParameterType);
+ mv.astore(strategies[i] = localVariableAllocator.allocate(ParameterStrategy.class));
+
+ mv.aload(converted[i]);
+ mv.aload(strategies[i]);
+
+ ObjectParameterInfo info = ObjectParameterInfo.create(i,
+ AsmUtil.getNativeArrayFlags(parameterTypes[i].annotations()));
+
+ mv.aload(0);
+ mv.getfield(builder.getClassNamePath(), builder.getObjectParameterInfoName(info),
+ ci(ObjectParameterInfo.class));
+ }
+ }
+
+ mv.invokevirtual(p(com.kenai.jffi.Invoker.class),
+ AbstractFastNumericMethodGenerator.getObjectParameterMethodName(parameterTypes.length),
+ AbstractFastNumericMethodGenerator.getObjectParameterMethodSignature(parameterTypes.length, pointerCount));
+
+ // Convert the result from long/int to the correct return type
+ if (float.class == nativeReturnType) {
+ narrow(mv, long.class, int.class);
+ mv.invokestatic(Float.class, "intBitsToFloat", float.class, int.class);
+
+ } else if (double.class == nativeReturnType) {
+ mv.invokestatic(Double.class, "longBitsToDouble", double.class, long.class);
+
+ } else if (void.class == nativeReturnType) {
+ mv.pop2();
+
+ }
+ convertPrimitive(mv, long.class, unboxedResultType, resultType.getNativeType());
+
+ // Jump to the main conversion/boxing code above
+ mv.go_to(convertResult);
+ }
+ mv.visitMaxs(100, localVariableAllocator.getSpaceUsed());
+ mv.visitEnd();
+ }
+
+ void attach(Class clazz) {
+ compiler.attach(clazz);
+ }
+
+
+ private static boolean isSupportedObjectParameterType(ParameterType type) {
+ return Pointer.class.isAssignableFrom(type.effectiveJavaType());
+ }
+
+
+ private static boolean isSupportedType(SigType type) {
+ switch (type.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+
+ static boolean isSupportedResult(ResultType resultType) {
+ return isSupportedType(resultType) || void.class == resultType.effectiveJavaType()
+ || resultType.getNativeType() == NativeType.ADDRESS
+ ;
+ }
+
+ static boolean isSupportedParameter(ParameterType parameterType) {
+ return isSupportedType(parameterType)
+ || isSupportedObjectParameterType(parameterType)
+ ;
+ }
+
+ static Class getNativeClass(NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ case SLONGLONG:
+ case ULONGLONG:
+ // Since the asm code does any sign/zero extension, we can pass everything down as int/long
+ // which potentially saves some java conversion ops
+ return sizeof(nativeType) <= 4 ? int.class : long.class;
+
+ case FLOAT:
+ return float.class;
+
+ case DOUBLE:
+ return double.class;
+
+ case VOID:
+ return void.class;
+
+ default:
+ throw new IllegalArgumentException("unsupported native type: " + nativeType);
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/X86_32StubCompiler.java b/src/main/java/jnr/ffi/provider/jffi/X86_32StubCompiler.java
new file mode 100644
index 0000000..fdcc42f
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/X86_32StubCompiler.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.*;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.x86asm.Assembler;
+import jnr.x86asm.Mem;
+import jnr.x86asm.Register;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.sig;
+import static jnr.x86asm.Asm.*;
+
+/**
+ * Stub compiler for i386 unix
+ */
+final class X86_32StubCompiler extends AbstractX86StubCompiler {
+
+ X86_32StubCompiler(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ boolean canCompile(ResultType returnType, ParameterType[] parameterTypes, CallingConvention convention) {
+
+ switch (returnType.getNativeType()) {
+ case VOID:
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case FLOAT:
+ case DOUBLE:
+ case ADDRESS:
+ break;
+
+ default:
+ return false;
+ }
+
+ // There is only one calling convention; SYSV, so abort if someone tries to use stdcall
+ if (convention != CallingConvention.DEFAULT) {
+ return false;
+ }
+
+ int fCount = 0;
+ int iCount = 0;
+
+ for (ParameterType t : parameterTypes) {
+ switch (t.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case ADDRESS:
+ ++iCount;
+ break;
+
+ case FLOAT:
+ case DOUBLE:
+ ++fCount;
+ break;
+
+ default:
+ // Fail on anything else
+ return false;
+ }
+ }
+
+ // We can only safely compile methods with up to 6 integer and 8 floating point parameters
+ return true;
+ }
+
+
+ @Override
+ void compile(Function function, String name, ResultType resultType, ParameterType[] parameterTypes, Class resultClass, Class[] parameterClasses, CallingConvention convention, boolean saveErrno) {
+
+ int psize = 0;
+ for (ParameterType t : parameterTypes) {
+ psize += parameterSize(t);
+ }
+
+ int rsize = resultSize(resultType);
+
+
+ //
+ // JNI functions all look like:
+ // foo(JNIEnv* env, jobject self, arg...)
+
+ // We need to align the stack to 16 bytes, then copy all the old args
+ // into the new parameter space.
+ // It already has 4 bytes pushed (the return address) so we need to account for that.
+ //
+ final int stackadj = align(Math.max(psize, rsize) + 4, 16) - 4;
+
+ Assembler a = new Assembler(X86_32);
+
+ a.sub(esp, imm(stackadj));
+
+ // copy and convert the parameters from the orig stack to the new location
+ for (int i = 0, srcoff = 0, dstoff = 0; i < parameterTypes.length; i++) {
+ int srcParameterSize = parameterSize(parameterClasses[i]);
+ int dstParameterSize = parameterSize(parameterTypes[i]);
+ int disp = stackadj + 4 + 8 + srcoff;
+
+ switch (parameterTypes[i].getNativeType()) {
+ case SCHAR:
+ case SSHORT:
+ a.movsx(eax, ptr(esp, disp, parameterTypes[i].getNativeType()));
+ break;
+
+ case UCHAR:
+ case USHORT:
+ a.movzx(eax, ptr(esp, disp, parameterTypes[i].getNativeType()));
+ break;
+
+ default:
+ a.mov(eax, dword_ptr(esp, disp));
+ break;
+ }
+ a.mov(dword_ptr(esp, dstoff), eax);
+
+ if (dstParameterSize > 4) {
+ if (parameterTypes[i].getNativeType() == NativeType.SLONGLONG && long.class != parameterClasses[i]) {
+ // sign extend from int.class -> long long
+ a.sar(eax, imm(31));
+
+ } else if (parameterTypes[i].getNativeType() == NativeType.ULONGLONG && long.class != parameterClasses[i]) {
+ // zero extend from int.class -> unsigned long long
+ a.mov(dword_ptr(esp, dstoff + 4), imm(0));
+
+ } else {
+ a.mov(eax, dword_ptr(esp, disp + 4));
+ }
+ a.mov(dword_ptr(esp, dstoff + 4), eax);
+ }
+
+ dstoff += dstParameterSize;
+ srcoff += srcParameterSize;
+ }
+
+
+ // Call to the actual native function
+ a.call(imm(function.getFunctionAddress() & 0xffffffffL));
+
+ if (saveErrno) {
+ int save = 0;
+ switch (resultType.getNativeType()) {
+ case FLOAT:
+ a.fstp(dword_ptr(esp, save));
+ break;
+
+ case DOUBLE:
+ a.fstp(qword_ptr(esp, save));
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ a.mov(dword_ptr(esp, save), eax);
+ a.mov(dword_ptr(esp, save + 4), edx);
+ break;
+
+ case VOID:
+ // No need to save for void values
+ break;
+
+ default:
+ a.mov(dword_ptr(esp, save), eax);
+ }
+
+ // Save the errno in a thread-local variable
+ a.call(imm(errnoFunctionAddress & 0xffffffffL));
+
+ // Retrieve return value and put it back in the appropriate return register
+ switch (resultType.getNativeType()) {
+ case FLOAT:
+ a.fld(dword_ptr(esp, save));
+ break;
+
+ case DOUBLE:
+ a.fld(qword_ptr(esp, save));
+ break;
+
+ case SCHAR:
+ a.movsx(eax, byte_ptr(esp, save));
+ break;
+
+ case UCHAR:
+ a.movzx(eax, byte_ptr(esp, save));
+ break;
+
+ case SSHORT:
+ a.movsx(eax, word_ptr(esp, save));
+ break;
+
+ case USHORT:
+ a.movzx(eax, word_ptr(esp, save));
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ a.mov(eax, dword_ptr(esp, save));
+ a.mov(edx, dword_ptr(esp, save + 4));
+ break;
+
+ case VOID:
+ // No need to save for void values
+ break;
+
+ default:
+ a.mov(eax, dword_ptr(esp, save));
+ }
+
+ } else {
+
+ switch (resultType.getNativeType()) {
+ case SCHAR:
+ a.movsx(eax, al);
+ break;
+
+ case UCHAR:
+ a.movzx(eax, al);
+ break;
+
+ case SSHORT:
+ a.movsx(eax, ax);
+ break;
+
+ case USHORT:
+ a.movzx(eax, ax);
+ break;
+ }
+ }
+
+ if (long.class == resultClass) {
+ // sign or zero extend the result to 64 bits
+ switch (resultType.getNativeType()) {
+ case SCHAR:
+ case SSHORT:
+ case SINT:
+ case SLONG:
+ // sign extend eax to edx:eax
+ a.mov(edx, eax);
+ a.sar(edx, imm(31));
+ break;
+
+ case UCHAR:
+ case USHORT:
+ case UINT:
+ case ULONG:
+ case ADDRESS:
+ a.mov(edx, imm(0));
+ break;
+ }
+
+ }
+ // Restore esp to the original position and return
+ a.add(esp, imm(stackadj));
+ a.ret();
+
+ stubs.add(new Stub(name, sig(resultClass, parameterClasses), a));
+ }
+
+ static int parameterSize(ParameterType parameterType) {
+ switch (parameterType.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ case FLOAT:
+ return 4;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ case DOUBLE:
+ return 8;
+
+ default:
+ throw new IllegalArgumentException("invalid parameter type" + parameterType);
+ }
+ }
+
+ static int parameterSize(Class t) {
+ if (byte.class == t || short.class == t || char.class == t | int.class == t || float.class == t) {
+ return 4;
+
+ } else if (long.class == t || double.class == t) {
+ return 8;
+ }
+ throw new IllegalArgumentException("invalid parameter type" + t);
+ }
+
+
+ static int resultSize(ResultType resultType) {
+ switch (resultType.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case ADDRESS:
+ return 4;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ return 8;
+
+ case FLOAT:
+ case DOUBLE:
+ return 16;
+
+ case VOID:
+ return 0;
+
+ default:
+ throw new IllegalArgumentException("invalid return type " + resultType);
+ }
+ }
+
+ static Mem ptr(Register base, long disp, NativeType nativeType) {
+ switch (nativeType) {
+ case SCHAR:
+ case UCHAR:
+ return byte_ptr(base, disp);
+
+ case SSHORT:
+ case USHORT:
+ return word_ptr(base, disp);
+
+ default:
+ return dword_ptr(base, disp);
+ }
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java b/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java
new file mode 100644
index 0000000..28bd137
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.provider.jffi;
+
+import com.kenai.jffi.Function;
+import jnr.ffi.*;
+import jnr.ffi.provider.ParameterType;
+import jnr.ffi.provider.ResultType;
+import jnr.x86asm.Assembler;
+import jnr.x86asm.REG;
+import jnr.x86asm.Register;
+
+import static jnr.ffi.provider.jffi.CodegenUtils.sig;
+import static jnr.x86asm.Asm.*;
+
+/**
+ * Compilers method trampoline stubs for x86_64
+ */
+final class X86_64StubCompiler extends AbstractX86StubCompiler {
+
+ X86_64StubCompiler(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ boolean canCompile(ResultType returnType, ParameterType[] parameterTypes, CallingConvention convention) {
+
+ // There is only one calling convention; SYSV, so abort if someone tries to use stdcall
+ if (convention != CallingConvention.DEFAULT) {
+ return false;
+ }
+
+ switch (returnType.getNativeType()) {
+ case VOID:
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case FLOAT:
+ case DOUBLE:
+ case ADDRESS:
+ break;
+
+ default:
+ return false;
+ }
+
+
+ int fCount = 0;
+ int iCount = 0;
+
+ for (ParameterType t : parameterTypes) {
+ switch (t.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case ADDRESS:
+ ++iCount;
+ break;
+
+ case FLOAT:
+ case DOUBLE:
+ ++fCount;
+ break;
+
+ default:
+ // Fail on anything else
+ return false;
+ }
+ }
+
+ // We can only safely compile methods with up to 6 integer and 8 floating point parameters
+ return iCount <= 6 && fCount <= 8;
+ }
+
+
+ static final Register[] srcRegisters8 = { dl, cl, r8b, r9b };
+ static final Register[] srcRegisters16 = { dx, cx, r8w, r9w };
+ static final Register[] srcRegisters32 = { edx, ecx, Register.gpr(REG.REG_R8D), Register.gpr(REG.REG_R9D) };
+ static final Register[] srcRegisters64 = { rdx, rcx, r8, r9 };
+ static final Register[] dstRegisters32 = { edi, esi, edx, ecx, Register.gpr(REG.REG_R8D), Register.gpr(REG.REG_R9D) };
+ static final Register[] dstRegisters64 = { rdi, rsi, rdx, rcx, r8, r9 };
+
+ @Override
+ final void compile(Function function, String name, ResultType resultType, ParameterType[] parameterTypes,
+ Class resultClass, Class[] parameterClasses, CallingConvention convention, boolean saveErrno) {
+
+ Assembler a = new Assembler(X86_64);
+ int iCount = iCount(parameterTypes);
+ int fCount = fCount(parameterTypes);
+
+ boolean canJumpToTarget = !saveErrno & iCount <= 6 & fCount <= 8;
+ switch (resultType.getNativeType()) {
+ case SINT:
+ case UINT:
+ canJumpToTarget &= int.class == resultClass;
+ break;
+
+ case SLONGLONG:
+ case ULONGLONG:
+ canJumpToTarget &= long.class == resultClass;
+ break;
+
+ case FLOAT:
+ canJumpToTarget &= float.class == resultClass;
+ break;
+
+ case DOUBLE:
+ canJumpToTarget &= double.class == resultClass;
+ break;
+
+ case VOID:
+ break;
+
+ default:
+ canJumpToTarget = false;
+ break;
+ }
+
+ // JNI functions all look like:
+ // foo(JNIEnv* env, jobject self, arg...)
+ // on AMD64, those sit in %rdi, %rsi, %rdx, %rcx, %r8 and %r9
+ // So we need to shuffle all the integer args up to over-write the
+ // env and self arguments
+ //
+ for (int i = 0; i < Math.min(iCount, 4); i++) {
+ switch (parameterTypes[i].getNativeType()) {
+ case SCHAR:
+ a.movsx(dstRegisters64[i], srcRegisters8[i]);
+ break;
+
+ case UCHAR:
+ a.movzx(dstRegisters64[i], srcRegisters8[i]);
+ break;
+
+ case SSHORT:
+ a.movsx(dstRegisters64[i], srcRegisters16[i]);
+ break;
+
+ case USHORT:
+ a.movzx(dstRegisters64[i], srcRegisters16[i]);
+ break;
+
+ case SINT:
+ a.movsxd(dstRegisters64[i], srcRegisters32[i]);
+ break;
+
+ case UINT:
+ // mov with a 32bit dst reg zero extends to 64bit
+ a.mov(dstRegisters32[i], srcRegisters32[i]);
+ break;
+
+ default:
+ a.mov(dstRegisters64[i], srcRegisters64[i]);
+ break;
+ }
+ }
+
+ if (iCount > 6) {
+ throw new IllegalArgumentException("integer argument count > 6");
+ }
+
+ // For args 5 & 6 of the function, they would have been pushed on the stack
+ for (int i = 4; i < iCount; i++) {
+ int disp = 8 + ((4 - i) * 8);
+ switch (parameterTypes[i].getNativeType()) {
+ case SCHAR:
+ a.movsx(dstRegisters64[i], byte_ptr(rsp, disp));
+ break;
+
+ case UCHAR:
+ a.movzx(dstRegisters64[i], byte_ptr(rsp, disp));
+ break;
+
+ case SSHORT:
+ a.movsx(dstRegisters64[i], word_ptr(rsp, disp));
+ break;
+
+ case USHORT:
+ a.movzx(dstRegisters64[i], word_ptr(rsp, disp));
+ break;
+
+ case SINT:
+ a.movsxd(dstRegisters64[i], dword_ptr(rsp, disp));
+ break;
+
+ case UINT:
+ // mov with a 32bit dst reg zero extends to 64bit
+ a.mov(dstRegisters32[i], dword_ptr(rsp, disp));
+ break;
+
+ default:
+ a.mov(dstRegisters64[i], qword_ptr(rsp, disp));
+ break;
+ }
+ }
+
+ // All the integer registers are loaded; there nothing to do for the floating
+ // registers, as the first 8 args are already in xmm0..xmm7, so just sanity check
+ if (fCount > 8) {
+ throw new IllegalArgumentException("float argument count > 8");
+ }
+
+ if (canJumpToTarget) {
+ a.jmp(imm(function.getFunctionAddress()));
+ stubs.add(new Stub(name, sig(resultClass, parameterClasses), a));
+ return;
+ }
+
+ // Need to align the stack to 16 bytes for function call.
+ // It already has 8 bytes pushed (the return address), so making space
+ // to save the return value from the function neatly aligns it to 16 bytes
+ int space = resultClass == float.class || resultClass == double.class
+ ? 24 : 8;
+ a.sub(rsp, imm(space));
+
+ // Clear %rax, since it is used by varargs functions to determine the number of float registers to be saved
+ a.mov(rax, imm(0));
+
+ // Call to the actual native function
+ a.call(imm(function.getFunctionAddress()));
+
+ if (saveErrno) {
+ // Save the return on the stack
+ switch (resultType.getNativeType()) {
+ case VOID:
+ // No need to save/reload return value registers
+ break;
+
+ case FLOAT:
+ a.movss(dword_ptr(rsp, 0), xmm0);
+ break;
+
+ case DOUBLE:
+ a.movsd(qword_ptr(rsp, 0), xmm0);
+ break;
+
+ default:
+ a.mov(qword_ptr(rsp, 0), rax);
+ }
+
+ // Save the errno in a thread-local variable
+ a.call(imm(errnoFunctionAddress));
+
+ // Retrieve return value and put it back in the appropriate return register
+ switch (resultType.getNativeType()) {
+ case VOID:
+ // No need to save/reload return value registers
+ break;
+
+ case SCHAR:
+ a.movsx(rax, byte_ptr(rsp, 0));
+ break;
+
+ case UCHAR:
+ a.movzx(rax, byte_ptr(rsp, 0));
+ break;
+
+ case SSHORT:
+ a.movsx(rax, word_ptr(rsp, 0));
+ break;
+
+ case USHORT:
+ a.movzx(rax, word_ptr(rsp, 0));
+ break;
+
+ case SINT:
+ a.movsxd(rax, dword_ptr(rsp, 0));
+ break;
+
+ case UINT:
+ // storing a value in eax zeroes out the upper 32 bits of rax
+ a.mov(eax, dword_ptr(rsp, 0));
+ break;
+
+ case FLOAT:
+ a.movss(xmm0, dword_ptr(rsp, 0));
+ break;
+
+ case DOUBLE:
+ a.movsd(xmm0, qword_ptr(rsp, 0));
+ break;
+
+ default:
+ a.mov(rax, qword_ptr(rsp, 0));
+ break;
+ }
+
+ } else {
+ // sign/zero extend the result
+ switch (resultType.getNativeType()) {
+ case SCHAR:
+ a.movsx(rax, al);
+ break;
+
+ case UCHAR:
+ a.movzx(rax, al);
+ break;
+
+ case SSHORT:
+ a.movsx(rax, ax);
+ break;
+
+ case USHORT:
+ a.movzx(rax, ax);
+ break;
+
+ case SINT:
+ if (long.class == resultClass) a.movsxd(rax, eax);
+ break;
+
+ case UINT:
+ if (long.class == resultClass) a.mov(eax, eax);
+ break;
+ }
+ }
+
+ // Restore rsp to original position
+ a.add(rsp, imm(space));
+ a.ret();
+
+ stubs.add(new Stub(name, sig(resultClass, parameterClasses), a));
+ }
+
+ static int fCount(ParameterType[] parameterTypes) {
+ int fCount = 0;
+
+ for (ParameterType t : parameterTypes) {
+ switch (t.getNativeType()) {
+ case FLOAT:
+ case DOUBLE:
+ ++fCount;
+ break;
+ }
+ }
+
+ return fCount;
+ }
+
+ static int iCount(ParameterType[] parameterTypes) {
+ int iCount = 0;
+
+ for (ParameterType t : parameterTypes) {
+ switch (t.getNativeType()) {
+ case SCHAR:
+ case UCHAR:
+ case SSHORT:
+ case USHORT:
+ case SINT:
+ case UINT:
+ case SLONG:
+ case ULONG:
+ case SLONGLONG:
+ case ULONGLONG:
+ case ADDRESS:
+ ++iCount;
+ break;
+ }
+ }
+
+ return iCount;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/arm/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/arm/linux/TypeAliases.java
new file mode 100644
index 0000000..6e64d25
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/arm/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.arm.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/darwin/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/darwin/TypeAliases.java
new file mode 100644
index 0000000..3c13ccc
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/darwin/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.i386.darwin;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.USHORT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.ULONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.UINT);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.UINT);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/freebsd/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/freebsd/TypeAliases.java
new file mode 100644
index 0000000..d80be0b
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/freebsd/TypeAliases.java
@@ -0,0 +1,49 @@
+package jnr.ffi.provider.jffi.platform.i386.freebsd;
+import jnr.ffi.TypeAlias;
+import jnr.ffi.NativeType;
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.UINT);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.UINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.UINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.UINT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SLONG);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.USHORT);
+ m.put(TypeAlias.id_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.ULONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SINT);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.SLONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/linux/TypeAliases.java
new file mode 100644
index 0000000..29d0f49
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.i386.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/openbsd/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/openbsd/TypeAliases.java
new file mode 100644
index 0000000..03a7b9d
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/openbsd/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.i386.openbsd;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.UINT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SLONG);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SINT);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SINT);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/solaris/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/solaris/TypeAliases.java
new file mode 100644
index 0000000..142412f
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/solaris/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.i386.solaris;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.UINT);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.ULONG);
+ m.put(TypeAlias.nlink_t, NativeType.ULONG);
+ m.put(TypeAlias.id_t, NativeType.SLONG);
+ m.put(TypeAlias.pid_t, NativeType.SLONG);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/i386/windows/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/i386/windows/TypeAliases.java
new file mode 100644
index 0000000..477b73b
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/i386/windows/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.i386.windows;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SLONG);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.ULONG);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.USHORT);
+ m.put(TypeAlias.id_t, NativeType.ULONG);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.ULONG);
+ m.put(TypeAlias.clock_t, NativeType.ULONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.SINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/mips/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/mips/linux/TypeAliases.java
new file mode 100644
index 0000000..4f9f192
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/mips/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.mips.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/mipsel/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/mipsel/linux/TypeAliases.java
new file mode 100644
index 0000000..ec46ef4
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/mipsel/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.mipsel.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/ppc/aix/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/aix/TypeAliases.java
new file mode 100644
index 0000000..8aa28e0
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/aix/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.ppc.aix;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.UINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SINT);
+ m.put(TypeAlias.blksize_t, NativeType.SINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.UINT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.SSHORT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SINT);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SINT);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.ULONG);
+ m.put(TypeAlias.rlim_t, NativeType.ULONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/ppc/darwin/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/darwin/TypeAliases.java
new file mode 100644
index 0000000..72d0665
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/darwin/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.ppc.darwin;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.USHORT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.ULONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.UINT);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.UINT);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/ppc/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/linux/TypeAliases.java
new file mode 100644
index 0000000..b287648
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/ppc/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.ppc.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/s390/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/s390/linux/TypeAliases.java
new file mode 100644
index 0000000..eb03441
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/s390/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.s390.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/s390x/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/s390x/linux/TypeAliases.java
new file mode 100644
index 0000000..c900a41
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/s390x/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.s390x.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.ULONG);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/sparc/solaris/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/sparc/solaris/TypeAliases.java
new file mode 100644
index 0000000..c790807
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/sparc/solaris/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.sparc.solaris;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.UINT);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.SLONG);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.ULONG);
+ m.put(TypeAlias.nlink_t, NativeType.ULONG);
+ m.put(TypeAlias.id_t, NativeType.SLONG);
+ m.put(TypeAlias.pid_t, NativeType.SLONG);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.SLONG);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/sparcv9/solaris/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/sparcv9/solaris/TypeAliases.java
new file mode 100644
index 0000000..d18938f
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/sparcv9/solaris/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.sparcv9.solaris;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SINT);
+ m.put(TypeAlias.uintptr_t, NativeType.UINT);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.SLONG);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.ULONG);
+ m.put(TypeAlias.nlink_t, NativeType.ULONG);
+ m.put(TypeAlias.id_t, NativeType.SLONG);
+ m.put(TypeAlias.pid_t, NativeType.SLONG);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.SLONG);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.UINT);
+ m.put(TypeAlias.ssize_t, NativeType.SINT);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/darwin/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/darwin/TypeAliases.java
new file mode 100644
index 0000000..402b4ae
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/darwin/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.x86_64.darwin;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.USHORT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.ULONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.UINT);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.UINT);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/freebsd/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/freebsd/TypeAliases.java
new file mode 100644
index 0000000..3363967
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/freebsd/TypeAliases.java
@@ -0,0 +1,49 @@
+package jnr.ffi.provider.jffi.platform.x86_64.freebsd;
+import jnr.ffi.TypeAlias;
+import jnr.ffi.NativeType;
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.UINT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SLONG);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SINT);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SINT);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/linux/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/linux/TypeAliases.java
new file mode 100644
index 0000000..a298665
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/linux/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.x86_64.linux;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.ULONG);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/openbsd/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/openbsd/TypeAliases.java
new file mode 100644
index 0000000..5c89094
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/openbsd/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.x86_64.openbsd;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.SINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.UINT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SLONG);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.UINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SINT);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SINT);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SINT);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.UCHAR);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONGLONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/solaris/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/solaris/TypeAliases.java
new file mode 100644
index 0000000..d910af3
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/solaris/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.x86_64.solaris;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.ULONG);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SINT);
+ m.put(TypeAlias.gid_t, NativeType.UINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.ULONG);
+ m.put(TypeAlias.ino64_t, NativeType.ULONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.UINT);
+ m.put(TypeAlias.nlink_t, NativeType.UINT);
+ m.put(TypeAlias.id_t, NativeType.SINT);
+ m.put(TypeAlias.pid_t, NativeType.SINT);
+ m.put(TypeAlias.off_t, NativeType.SLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.UINT);
+ m.put(TypeAlias.clock_t, NativeType.SLONG);
+ m.put(TypeAlias.size_t, NativeType.ULONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.UINT);
+ m.put(TypeAlias.rlim_t, NativeType.ULONG);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/windows/TypeAliases.java b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/windows/TypeAliases.java
new file mode 100644
index 0000000..e97cfed
--- /dev/null
+++ b/src/main/java/jnr/ffi/provider/jffi/platform/x86_64/windows/TypeAliases.java
@@ -0,0 +1,51 @@
+package jnr.ffi.provider.jffi.platform.x86_64.windows;
+
+import jnr.ffi.NativeType;
+import jnr.ffi.TypeAlias;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public final class TypeAliases {
+ public static final Map<TypeAlias, jnr.ffi.NativeType> ALIASES = buildTypeMap();
+ private static Map<TypeAlias, jnr.ffi.NativeType> buildTypeMap() {
+ Map<TypeAlias, jnr.ffi.NativeType> m = new EnumMap<TypeAlias, jnr.ffi.NativeType>(TypeAlias.class);
+ m.put(TypeAlias.int8_t, NativeType.SCHAR);
+ m.put(TypeAlias.u_int8_t, NativeType.UCHAR);
+ m.put(TypeAlias.int16_t, NativeType.SSHORT);
+ m.put(TypeAlias.u_int16_t, NativeType.USHORT);
+ m.put(TypeAlias.int32_t, NativeType.SINT);
+ m.put(TypeAlias.u_int32_t, NativeType.UINT);
+ m.put(TypeAlias.int64_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.u_int64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.intptr_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.uintptr_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.caddr_t, NativeType.ADDRESS);
+ m.put(TypeAlias.dev_t, NativeType.UINT);
+ m.put(TypeAlias.blkcnt_t, NativeType.SLONG);
+ m.put(TypeAlias.blksize_t, NativeType.SLONG);
+ m.put(TypeAlias.gid_t, NativeType.SINT);
+ m.put(TypeAlias.in_addr_t, NativeType.UINT);
+ m.put(TypeAlias.in_port_t, NativeType.USHORT);
+ m.put(TypeAlias.ino_t, NativeType.USHORT);
+ m.put(TypeAlias.ino64_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.key_t, NativeType.SINT);
+ m.put(TypeAlias.mode_t, NativeType.USHORT);
+ m.put(TypeAlias.nlink_t, NativeType.SINT);
+ m.put(TypeAlias.id_t, NativeType.SINT);
+ m.put(TypeAlias.pid_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.off_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.swblk_t, NativeType.SLONG);
+ m.put(TypeAlias.uid_t, NativeType.SINT);
+ m.put(TypeAlias.clock_t, NativeType.SINT);
+ m.put(TypeAlias.size_t, NativeType.ULONGLONG);
+ m.put(TypeAlias.ssize_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.time_t, NativeType.SLONGLONG);
+ m.put(TypeAlias.fsblkcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.fsfilcnt_t, NativeType.ULONG);
+ m.put(TypeAlias.sa_family_t, NativeType.USHORT);
+ m.put(TypeAlias.socklen_t, NativeType.SINT);
+ m.put(TypeAlias.rlim_t, NativeType.SINT);
+ return m;
+ }
+}
diff --git a/src/main/java/jnr/ffi/types/blkcnt_t.java b/src/main/java/jnr/ffi/types/blkcnt_t.java
new file mode 100644
index 0000000..7e1d512
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/blkcnt_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.blkcnt_t)
+public @interface blkcnt_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/blksize_t.java b/src/main/java/jnr/ffi/types/blksize_t.java
new file mode 100644
index 0000000..42bd506
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/blksize_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.blksize_t)
+public @interface blksize_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/caddr_t.java b/src/main/java/jnr/ffi/types/caddr_t.java
new file mode 100644
index 0000000..eaaea5b
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/caddr_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.caddr_t)
+public @interface caddr_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/clock_t.java b/src/main/java/jnr/ffi/types/clock_t.java
new file mode 100644
index 0000000..be09616
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/clock_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.clock_t)
+public @interface clock_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/dev_t.java b/src/main/java/jnr/ffi/types/dev_t.java
new file mode 100644
index 0000000..57b435d
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/dev_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.dev_t)
+public @interface dev_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/fsblkcnt_t.java b/src/main/java/jnr/ffi/types/fsblkcnt_t.java
new file mode 100644
index 0000000..ee98f89
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/fsblkcnt_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.fsblkcnt_t)
+public @interface fsblkcnt_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/fsfilcnt_t.java b/src/main/java/jnr/ffi/types/fsfilcnt_t.java
new file mode 100644
index 0000000..fb1655c
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/fsfilcnt_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.fsfilcnt_t)
+public @interface fsfilcnt_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/gid_t.java b/src/main/java/jnr/ffi/types/gid_t.java
new file mode 100644
index 0000000..6429457
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/gid_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.gid_t)
+public @interface gid_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/id_t.java b/src/main/java/jnr/ffi/types/id_t.java
new file mode 100644
index 0000000..301c0e4
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/id_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.id_t)
+public @interface id_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/in_addr_t.java b/src/main/java/jnr/ffi/types/in_addr_t.java
new file mode 100644
index 0000000..43d619f
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/in_addr_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.in_addr_t)
+public @interface in_addr_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/in_port_t.java b/src/main/java/jnr/ffi/types/in_port_t.java
new file mode 100644
index 0000000..def7360
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/in_port_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.in_port_t)
+public @interface in_port_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/ino64_t.java b/src/main/java/jnr/ffi/types/ino64_t.java
new file mode 100644
index 0000000..835ebe7
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/ino64_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.ino64_t)
+public @interface ino64_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/ino_t.java b/src/main/java/jnr/ffi/types/ino_t.java
new file mode 100644
index 0000000..347ea1f
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/ino_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.ino_t)
+public @interface ino_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/int16_t.java b/src/main/java/jnr/ffi/types/int16_t.java
new file mode 100644
index 0000000..7d5da90
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/int16_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.int16_t)
+public @interface int16_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/int32_t.java b/src/main/java/jnr/ffi/types/int32_t.java
new file mode 100644
index 0000000..2d5eb12
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/int32_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.int32_t)
+public @interface int32_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/int64_t.java b/src/main/java/jnr/ffi/types/int64_t.java
new file mode 100644
index 0000000..6c64d3d
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/int64_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.int64_t)
+public @interface int64_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/int8_t.java b/src/main/java/jnr/ffi/types/int8_t.java
new file mode 100644
index 0000000..1f783ac
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/int8_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.int8_t)
+public @interface int8_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/intptr_t.java b/src/main/java/jnr/ffi/types/intptr_t.java
new file mode 100644
index 0000000..30d6795
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/intptr_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.intptr_t)
+public @interface intptr_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/key_t.java b/src/main/java/jnr/ffi/types/key_t.java
new file mode 100644
index 0000000..34a3c9b
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/key_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.key_t)
+public @interface key_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/mode_t.java b/src/main/java/jnr/ffi/types/mode_t.java
new file mode 100644
index 0000000..5e7a251
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/mode_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.mode_t)
+public @interface mode_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/nlink_t.java b/src/main/java/jnr/ffi/types/nlink_t.java
new file mode 100644
index 0000000..f176c42
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/nlink_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.nlink_t)
+public @interface nlink_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/off_t.java b/src/main/java/jnr/ffi/types/off_t.java
new file mode 100644
index 0000000..7a73241
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/off_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.off_t)
+public @interface off_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/pid_t.java b/src/main/java/jnr/ffi/types/pid_t.java
new file mode 100644
index 0000000..bb86abb
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/pid_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.pid_t)
+public @interface pid_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/rlim_t.java b/src/main/java/jnr/ffi/types/rlim_t.java
new file mode 100644
index 0000000..21eae9a
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/rlim_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.rlim_t)
+public @interface rlim_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/sa_family_t.java b/src/main/java/jnr/ffi/types/sa_family_t.java
new file mode 100644
index 0000000..35efb51
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/sa_family_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.sa_family_t)
+public @interface sa_family_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/size_t.java b/src/main/java/jnr/ffi/types/size_t.java
new file mode 100644
index 0000000..f4e70d1
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/size_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.size_t)
+public @interface size_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/socklen_t.java b/src/main/java/jnr/ffi/types/socklen_t.java
new file mode 100644
index 0000000..fc20ca5
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/socklen_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.socklen_t)
+public @interface socklen_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/ssize_t.java b/src/main/java/jnr/ffi/types/ssize_t.java
new file mode 100644
index 0000000..f353219
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/ssize_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.ssize_t)
+public @interface ssize_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/swblk_t.java b/src/main/java/jnr/ffi/types/swblk_t.java
new file mode 100644
index 0000000..2a3ed6d
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/swblk_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.swblk_t)
+public @interface swblk_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/time_t.java b/src/main/java/jnr/ffi/types/time_t.java
new file mode 100644
index 0000000..92cbcbf
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/time_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.time_t)
+public @interface time_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/u_int16_t.java b/src/main/java/jnr/ffi/types/u_int16_t.java
new file mode 100644
index 0000000..dcaa86c
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/u_int16_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.u_int16_t)
+public @interface u_int16_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/u_int32_t.java b/src/main/java/jnr/ffi/types/u_int32_t.java
new file mode 100644
index 0000000..d66a35f
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/u_int32_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.u_int32_t)
+public @interface u_int32_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/u_int64_t.java b/src/main/java/jnr/ffi/types/u_int64_t.java
new file mode 100644
index 0000000..1fc368a
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/u_int64_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.u_int64_t)
+public @interface u_int64_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/u_int8_t.java b/src/main/java/jnr/ffi/types/u_int8_t.java
new file mode 100644
index 0000000..1a280cf
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/u_int8_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.u_int8_t)
+public @interface u_int8_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/uid_t.java b/src/main/java/jnr/ffi/types/uid_t.java
new file mode 100644
index 0000000..5f8b4a4
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/uid_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.uid_t)
+public @interface uid_t {
+
+}
diff --git a/src/main/java/jnr/ffi/types/uintptr_t.java b/src/main/java/jnr/ffi/types/uintptr_t.java
new file mode 100644
index 0000000..ff51bb9
--- /dev/null
+++ b/src/main/java/jnr/ffi/types/uintptr_t.java
@@ -0,0 +1,17 @@
+
+package jnr.ffi.types;
+
+import jnr.ffi.TypeAlias;
+import jnr.ffi.annotations.TypeDefinition;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(value = { ElementType.PARAMETER, ElementType.METHOD })
+ at TypeDefinition(alias = TypeAlias.uintptr_t)
+public @interface uintptr_t {
+
+}
diff --git a/src/main/java/jnr/ffi/util/AnnotationNameComparator.java b/src/main/java/jnr/ffi/util/AnnotationNameComparator.java
new file mode 100644
index 0000000..578ddcf
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/AnnotationNameComparator.java
@@ -0,0 +1,23 @@
+package jnr.ffi.util;
+
+import java.lang.annotation.Annotation;
+import java.util.Comparator;
+
+/**
+ * Sorts annotations according to name
+ */
+final class AnnotationNameComparator implements Comparator<Annotation> {
+ static final Comparator<Annotation> INSTANCE = new AnnotationNameComparator();
+
+ public static Comparator<Annotation> getInstance() {
+ return INSTANCE;
+ }
+
+ public int compare(Annotation o1, Annotation o2) {
+ return o1.annotationType().getName().compareTo(o2.annotationType().getName());
+ }
+
+ public boolean equals(Object other) {
+ return other != null && getClass().equals(other.getClass());
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/Annotations.java b/src/main/java/jnr/ffi/util/Annotations.java
new file mode 100644
index 0000000..3d8c875
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/Annotations.java
@@ -0,0 +1,68 @@
+package jnr.ffi.util;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+/**
+ * Utilities for collections of annotations
+ */
+public final class Annotations {
+ public static final Collection<Annotation> EMPTY_ANNOTATIONS = Collections.emptyList();
+
+ private Annotations() {}
+
+ public static Collection<Annotation> sortedAnnotationCollection(Annotation[] annotations) {
+ if (annotations.length > 1) {
+ return sortedAnnotationCollection(Arrays.asList(annotations));
+
+ } else if (annotations.length > 0) {
+ return Collections.singletonList(annotations[0]);
+
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public static Collection<Annotation> sortedAnnotationCollection(Collection<Annotation> annotations) {
+ // If already sorted, or empty, or only one element, no need to sort again
+ if (annotations.size() < 2 || (annotations instanceof SortedSet && ((SortedSet) annotations).comparator() instanceof AnnotationNameComparator)) {
+ return annotations;
+ }
+
+ SortedSet<Annotation> sorted = new TreeSet<Annotation>(AnnotationNameComparator.getInstance());
+ sorted.addAll(annotations);
+
+ return Collections.unmodifiableSortedSet(sorted);
+ }
+
+ public static final Collection<Annotation> mergeAnnotations(Collection<Annotation> a, Collection<Annotation> b) {
+ if (a.isEmpty() && b.isEmpty()) {
+ return EMPTY_ANNOTATIONS;
+
+ } else if (!a.isEmpty() && b.isEmpty()) {
+ return a;
+
+ } else if (a.isEmpty() && !b.isEmpty()) {
+ return b;
+
+ } else {
+ List<Annotation> all = new ArrayList<Annotation>(a);
+ all.addAll(b);
+ return sortedAnnotationCollection(all);
+ }
+ }
+
+ public static final Collection<Annotation> mergeAnnotations(Collection<Annotation>... collections) {
+ int totalLength = 0;
+ for (Collection<Annotation> c : collections) {
+ totalLength += c.size();
+ }
+
+ List<Annotation> all = new ArrayList<Annotation>(totalLength);
+ for (Collection<Annotation> c : collections) {
+ all.addAll(c);
+ }
+
+ return sortedAnnotationCollection(all);
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/BufferUtil.java b/src/main/java/jnr/ffi/util/BufferUtil.java
new file mode 100644
index 0000000..35cf7f6
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/BufferUtil.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.*;
+
+/**
+ *
+ */
+public final class BufferUtil {
+ private BufferUtil() {}
+
+ public static void putString(ByteBuffer buf, Charset charset, String value) {
+ putCharSequence(buf, charset, value);
+ }
+ public static String getString(ByteBuffer buf, Charset charset) {
+ return getCharSequence(buf, charset).toString();
+ }
+ public static void putCharSequence(ByteBuffer buf, Charset charset, CharSequence value) {
+ putCharSequence(buf, charset.newEncoder(), value);
+ }
+ public static void putCharSequence(ByteBuffer buf, CharsetEncoder encoder, CharSequence value) {
+ //
+ // Convert any CharSequence implementor (String, etc) into a native
+ // C string.
+ //
+ encoder.reset().onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ .encode(CharBuffer.wrap(value), buf, true);
+ encoder.flush(buf);
+ final int nulSize = Math.round(encoder.maxBytesPerChar());
+ // NUL terminate the string
+ if (nulSize == 4) {
+ buf.putInt(0);
+ } else if (nulSize == 2) {
+ buf.putShort((short) 0);
+ } else if (nulSize == 1) {
+ buf.put((byte) 0);
+ }
+ }
+ public static CharSequence getCharSequence(ByteBuffer buf, Charset charset) {
+ final ByteBuffer buffer = buf.slice();
+ // Find the NUL terminator and limit to that, so the
+ // StringBuffer/StringBuilder does not have superfluous NUL chars
+ int end = indexOf(buffer, (byte) 0);
+ if (end < 0) {
+ end = buffer.limit();
+ }
+ buffer.position(0).limit(end);
+ return charset.decode(buffer);
+ }
+
+ public static CharSequence getCharSequence(final ByteBuffer buf, final CharsetDecoder decoder) {
+ final ByteBuffer buffer = buf.slice();
+ // Find the NUL terminator and limit to that, so the
+ // StringBuffer/StringBuilder does not have superfluous NUL chars
+ int end = indexOf(buffer, (byte) 0);
+ if (end < 0) {
+ end = buffer.limit();
+ }
+ buffer.position(0).limit(end);
+ try {
+ return decoder.reset().onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE).decode(buffer);
+ } catch (CharacterCodingException ex) {
+ throw new Error("Illegal character data in native string", ex);
+ }
+ }
+
+ /**
+ * Finds the position of a byte relative to the start of the buffer.
+ *
+ * @param buf The ByteBuffer to find the value in
+ * @param value The value to locate
+ * @return The position within the buffer that value is found, or -1 if not
+ * found.
+ */
+ public static int positionOf(ByteBuffer buf, byte value) {
+ if (buf.hasArray()) {
+ final byte[] array = buf.array();
+ final int offset = buf.arrayOffset();
+ final int limit = buf.limit();
+ for (int pos = buf.position(); pos < limit; ++pos) {
+ if (array[offset + pos] == value) {
+ return pos;
+ }
+ }
+
+ } else {
+ final int limit = buf.limit();
+ for (int pos = buf.position(); pos < limit; ++pos) {
+ if (buf.get(pos) == value) {
+ return pos;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ public static int indexOf(ByteBuffer buf, byte value) {
+ if (buf.hasArray()) {
+ byte[] array = buf.array();
+ int begin = buf.arrayOffset() + buf.position();
+ int end = buf.arrayOffset() + buf.limit();
+ for (int offset = 0; offset < end && offset > -1; ++offset) {
+ if (array[begin + offset] == value) {
+ return offset;
+ }
+ }
+ } else {
+ int begin = buf.position();
+ for (int offset = 0; offset < buf.limit(); ++offset) {
+ if (buf.get(begin + offset) == value) {
+ return offset;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public static int indexOf(ByteBuffer buf, int offset, byte value) {
+ if (buf.hasArray()) {
+ byte[] array = buf.array();
+ int begin = buf.arrayOffset() + buf.position() + offset;
+ int end = buf.arrayOffset() + buf.limit();
+ for (int idx = 0; idx < end && idx > -1; ++idx) {
+ if (array[begin + idx] == value) {
+ return idx;
+ }
+ }
+ } else {
+ int begin = buf.position();
+ for (int idx = 0; idx < buf.limit(); ++idx) {
+ if (buf.get(begin + idx) == value) {
+ return idx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public static ByteBuffer slice(final ByteBuffer buffer, final int position) {
+ final ByteBuffer tmp = buffer.duplicate();
+ tmp.position(position);
+ return tmp.slice();
+ }
+ public static ByteBuffer slice(final ByteBuffer buffer, final int position, final int size) {
+ final ByteBuffer tmp = buffer.duplicate();
+ tmp.position(position).limit(position + size);
+ return tmp.slice();
+ }
+
+}
diff --git a/src/main/java/jnr/ffi/util/EnumMapper.java b/src/main/java/jnr/ffi/util/EnumMapper.java
new file mode 100644
index 0000000..579abee
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/EnumMapper.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008-2010 Wayne Meissner
+ *
+ * This file is part of the JNR project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util;
+
+import jnr.ffi.mapper.*;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Provides mapping from Enum values to native integers and vice-versa
+ */
+ at ToNativeConverter.NoContext
+ at FromNativeConverter.NoContext
+public final class EnumMapper {
+
+ private static final class StaticDataHolder {
+ private static volatile Map<Class<? extends Enum>, EnumMapper> MAPPERS = Collections.emptyMap();
+ }
+
+ private final Class<? extends Enum> enumClass;
+ private final Integer[] intValues;
+ private final Long[] longValues;
+ private final Map<Number, Enum> reverseLookupMap = new HashMap<Number, Enum>();
+
+ private EnumMapper(Class<? extends Enum> enumClass) {
+ this.enumClass = enumClass;
+
+ EnumSet<? extends Enum> enums = EnumSet.allOf(enumClass);
+
+ this.intValues = new Integer[enums.size()];
+ this.longValues = new Long[enums.size()];
+ Method intValueMethod = getNumberValueMethod(enumClass, int.class);
+ Method longValueMethod = getNumberValueMethod(enumClass, long.class);
+ for (Enum e : enums) {
+ Number value;
+ if (longValueMethod != null) {
+ value = reflectedNumberValue(e, longValueMethod);
+
+ } else if (intValueMethod != null) {
+ value = reflectedNumberValue(e, intValueMethod);
+
+ } else {
+ value = e.ordinal();
+ }
+ intValues[e.ordinal()] = value.intValue();
+ longValues[e.ordinal()] = value.longValue();
+
+ reverseLookupMap.put(value, e);
+ }
+
+ }
+
+ public static interface IntegerEnum {
+ public int intValue();
+ }
+
+ public static EnumMapper getInstance(Class<? extends Enum> enumClass) {
+ EnumMapper mapper = StaticDataHolder.MAPPERS.get(enumClass);
+ if (mapper != null) {
+ return mapper;
+ }
+
+ return addMapper(enumClass);
+ }
+
+ private static synchronized EnumMapper addMapper(Class<? extends Enum> enumClass) {
+ EnumMapper mapper = new EnumMapper(enumClass);
+
+ Map<Class<? extends Enum>, EnumMapper> tmp
+ = new IdentityHashMap<Class<? extends Enum>, EnumMapper>(StaticDataHolder.MAPPERS);
+ tmp.put(enumClass, mapper);
+
+ StaticDataHolder.MAPPERS = tmp;
+
+ return mapper;
+ }
+
+ private static Method getNumberValueMethod(Class c, Class numberClass) {
+ try {
+ Method m = c.getDeclaredMethod(numberClass.getSimpleName() + "Value");
+ return m != null && numberClass == m.getReturnType() ? m : null;
+
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ private static Number reflectedNumberValue(Enum e, Method m) {
+ try {
+ return (Number) m.invoke(e);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public final Integer integerValue(Enum value) {
+ if (value.getClass() != enumClass) {
+ throw new IllegalArgumentException("enum class mismatch, " + value.getClass());
+ }
+
+ return intValues[value.ordinal()];
+ }
+
+ public final int intValue(Enum value) {
+ return integerValue(value);
+ }
+
+ public final Long longValue(Enum value) {
+ if (value.getClass() != enumClass) {
+ throw new IllegalArgumentException("enum class mismatch, " + value.getClass());
+ }
+
+ return longValues[value.ordinal()];
+ }
+
+ public Enum valueOf(int value) {
+ return reverseLookup(value);
+ }
+
+ public Enum valueOf(long value) {
+ return reverseLookup(value);
+ }
+
+ public Enum valueOf(Number value) {
+ return reverseLookup(value);
+ }
+
+ private Enum reverseLookup(Number value) {
+ Enum e = reverseLookupMap.get(value);
+ return e != null ? e : badValue(value);
+ }
+
+ private Enum badValue(Number value) {
+ //
+ // No value found - try to find the default value for unknown values.
+ // This is useful for enums that aren't fixed in stone and/or where you
+ // don't want to throw an Exception for an unknown value.
+ //
+ try {
+ return Enum.valueOf(enumClass, "__UNKNOWN_NATIVE_VALUE");
+ } catch (IllegalArgumentException ex) {
+ //
+ // No default, so just give up and throw an exception
+ //
+ throw new IllegalArgumentException("No known Enum mapping for value "
+ + value + " of type " + enumClass.getName());
+ }
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/ref/FinalizablePhantomReference.java b/src/main/java/jnr/ffi/util/ref/FinalizablePhantomReference.java
new file mode 100644
index 0000000..0485e7d
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/FinalizablePhantomReference.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref;
+
+import java.lang.ref.PhantomReference;
+
+/**
+ * Phantom reference with a {@code finalizeReferent()} method which a background thread invokes
+ * after the garbage collector reclaims the referent. This is a simpler alternative to using a
+ * {@link java.lang.ref.ReferenceQueue}.
+ *
+ * <p>Unlike a normal phantom reference, this reference will be cleared automatically.
+ *
+ * @author Bob Lee
+ * @since 2.0 (imported from Google Collections Library)
+ */
+public abstract class FinalizablePhantomReference<T> extends PhantomReference<T>
+ implements FinalizableReference {
+ /**
+ * Constructs a new finalizable phantom reference.
+ *
+ * @param referent to phantom reference
+ * @param queue that should finalize the referent
+ */
+ protected FinalizablePhantomReference(T referent, FinalizableReferenceQueue queue) {
+ super(referent, queue.queue);
+ queue.cleanUp();
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/ref/FinalizableReference.java b/src/main/java/jnr/ffi/util/ref/FinalizableReference.java
new file mode 100644
index 0000000..4cc2bc0
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/FinalizableReference.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref;
+
+/**
+ * Implemented by references that have code to run after garbage collection of their referents.
+ *
+ * @see FinalizableReferenceQueue
+ * @author Bob Lee
+ * @since 2.0 (imported from Google Collections Library)
+ */
+public interface FinalizableReference {
+ /**
+ * Invoked on a background thread after the referent has been garbage collected unless security
+ * restrictions prevented starting a background thread, in which case this method is invoked when
+ * new references are created.
+ */
+ void finalizeReferent();
+}
diff --git a/src/main/java/jnr/ffi/util/ref/FinalizableReferenceQueue.java b/src/main/java/jnr/ffi/util/ref/FinalizableReferenceQueue.java
new file mode 100644
index 0000000..c7b12e4
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/FinalizableReferenceQueue.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A reference queue with an associated background thread that dequeues references and invokes
+ * {@link FinalizableReference#finalizeReferent()} on them.
+ *
+ * <p>Keep a strong reference to this object until all of the associated referents have been
+ * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code
+ * finalizeReferent()} on the remaining references.
+ *
+ * @author Bob Lee
+ * @since 2.0 (imported from Google Collections Library)
+ */
+public class FinalizableReferenceQueue {
+ /*
+ * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
+ * map built by MapMaker) no longer has a strong reference to this object, the garbage collector
+ * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
+ * Finalizer to stop.
+ *
+ * If this library is loaded in the system class loader, FinalizableReferenceQueue can load
+ * Finalizer directly with no problems.
+ *
+ * If this library is loaded in an application class loader, it's important that Finalizer not
+ * have a strong reference back to the class loader. Otherwise, you could have a graph like this:
+ *
+ * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader
+ * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance
+ *
+ * Even if no other references to classes from the application class loader remain, the Finalizer
+ * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the
+ * Finalizer running, and as a result, the application class loader can never be reclaimed.
+ *
+ * This means that dynamically loaded web applications and OSGi bundles can't be unloaded.
+ *
+ * If the library is loaded in an application class loader, we try to break the cycle by loading
+ * Finalizer in its own independent class loader:
+ *
+ * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue
+ * -> etc. -> Decoupled class loader -> Finalizer
+ *
+ * Now, Finalizer no longer keeps an indirect strong reference to the static
+ * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed
+ * at which point the Finalizer thread will stop and its decoupled class loader can also be
+ * reclaimed.
+ *
+ * If any of this fails along the way, we fall back to loading Finalizer directly in the
+ * application class loader.
+ */
+
+ private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
+
+ private static final String FINALIZER_CLASS_NAME = "jnr.ffi.util.ref.internal.Finalizer";
+
+ /** Reference to Finalizer.startFinalizer(). */
+ private static final Method startFinalizer;
+ static {
+ Class<?> finalizer = loadFinalizer(
+ new SystemLoader(), new DecoupledLoader(), new DirectLoader());
+ startFinalizer = getStartFinalizer(finalizer);
+ }
+
+ private static final Map<FinalizableReferenceQueue, Boolean> finalizerQueues
+ = Collections.synchronizedMap(new WeakHashMap<FinalizableReferenceQueue, Boolean>());
+
+ /**
+ * The actual reference queue that our background thread will poll.
+ */
+ final ReferenceQueue<Object> queue;
+
+ /**
+ * Whether or not the background thread started successfully.
+ */
+ final boolean threadStarted;
+
+ /**
+ * Constructs a new queue.
+ */
+ @SuppressWarnings("unchecked")
+ public FinalizableReferenceQueue() {
+ // We could start the finalizer lazily, but I'd rather it blow up early.
+ ReferenceQueue<Object> queue;
+ boolean threadStarted = false;
+ try {
+ queue = (ReferenceQueue<Object>)
+ startFinalizer.invoke(null, FinalizableReference.class, this);
+ threadStarted = true;
+ } catch (IllegalAccessException impossible) {
+ throw new AssertionError(impossible); // startFinalizer() is public
+ } catch (Throwable t) {
+ logger.log(Level.INFO, "Failed to start reference finalizer thread."
+ + " Reference cleanup will only occur when new references are created.", t);
+ queue = new ReferenceQueue<Object>();
+ }
+
+ this.queue = queue;
+ this.threadStarted = threadStarted;
+ finalizerQueues.put(this, Boolean.TRUE);
+ }
+
+ /**
+ * Repeatedly dequeues references from the queue and invokes {@link
+ * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a
+ * no-op if the background thread was created successfully.
+ */
+ void cleanUp() {
+ if (!threadStarted) {
+ pollReferenceQueue();
+ }
+ }
+
+ private void pollReferenceQueue() {
+
+ Reference<?> reference;
+ while ((reference = queue.poll()) != null) {
+ /*
+ * This is for the benefit of phantom references. Weak and soft references will have already
+ * been cleared by this point.
+ */
+ reference.clear();
+ try {
+ ((FinalizableReference) reference).finalizeReferent();
+ } catch (Throwable t) {
+ logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
+ }
+ }
+ }
+
+ /**
+ * Iterates through the given loaders until it finds one that can load Finalizer.
+ *
+ * @return Finalizer.class
+ */
+ private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
+ for (FinalizerLoader loader : loaders) {
+ Class<?> finalizer = loader.loadFinalizer();
+ if (finalizer != null) {
+ return finalizer;
+ }
+ }
+
+ throw new AssertionError();
+ }
+
+ /**
+ * Loads Finalizer.class.
+ */
+ interface FinalizerLoader {
+
+ /**
+ * Returns Finalizer.class or null if this loader shouldn't or can't load it.
+ *
+ * @throws SecurityException if we don't have the appropriate privileges
+ */
+ Class<?> loadFinalizer();
+ }
+
+ /**
+ * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path,
+ * we needn't create a separate loader.
+ */
+ static class SystemLoader implements FinalizerLoader {
+ @Override
+ public Class<?> loadFinalizer() {
+ ClassLoader systemLoader;
+ try {
+ systemLoader = ClassLoader.getSystemClassLoader();
+ } catch (SecurityException e) {
+ logger.info("Not allowed to access system class loader.");
+ return null;
+ }
+ if (systemLoader != null) {
+ try {
+ return systemLoader.loadClass(FINALIZER_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ // Ignore. Finalizer is simply in a child class loader.
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
+ * our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
+ * it would prevent our class loader from getting garbage collected.
+ */
+ static class DecoupledLoader implements FinalizerLoader {
+ private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader."
+ + "Loading Finalizer in the current class loader instead. As a result, you will not be able"
+ + "to garbage collect this class loader. To support reclaiming this class loader, either"
+ + "resolve the underlying issue, or move Google Collections to your system class path.";
+
+ @Override
+ public Class<?> loadFinalizer() {
+ try {
+ /*
+ * We use URLClassLoader because it's the only concrete class loader implementation in the
+ * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this
+ * class loader:
+ *
+ * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader
+ *
+ * System class loader will (and must) be the parent.
+ */
+ ClassLoader finalizerLoader = newLoader(getBaseUrl());
+ return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
+ } catch (Exception e) {
+ logger.log(Level.WARNING, LOADING_ERROR, e);
+ return null;
+ }
+ }
+
+ /**
+ * Gets URL for base of path containing Finalizer.class.
+ */
+ URL getBaseUrl() throws IOException {
+ // Find URL pointing to Finalizer.class file.
+ String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
+ URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
+ if (finalizerUrl == null) {
+ throw new FileNotFoundException(finalizerPath);
+ }
+
+ // Find URL pointing to base of class path.
+ String urlString = finalizerUrl.toString();
+ if (!urlString.endsWith(finalizerPath)) {
+ throw new IOException("Unsupported path style: " + urlString);
+ }
+ urlString = urlString.substring(0, urlString.length() - finalizerPath.length());
+ return new URL(finalizerUrl, urlString);
+ }
+
+ /** Creates a class loader with the given base URL as its classpath. */
+ URLClassLoader newLoader(URL base) {
+ return new URLClassLoader(new URL[] {base});
+ }
+ }
+
+ /**
+ * Loads Finalizer directly using the current class loader. We won't be able to garbage collect
+ * this class loader, but at least the world doesn't end.
+ */
+ static class DirectLoader implements FinalizerLoader {
+ @Override
+ public Class<?> loadFinalizer() {
+ try {
+ return Class.forName(FINALIZER_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+
+ /**
+ * Looks up Finalizer.startFinalizer().
+ */
+ static Method getStartFinalizer(Class<?> finalizer) {
+ try {
+ return finalizer.getMethod("startFinalizer", Class.class, Object.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static void cleanUpAll() {
+ try {
+ // Iterate over a new array containing all the queues, so we don't risk a concurrent modification exception
+ for (Object frq : finalizerQueues.keySet().toArray()) {
+ ((FinalizableReferenceQueue) frq).cleanUp();
+ }
+ } catch (Throwable t) {}
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/ref/FinalizableSoftReference.java b/src/main/java/jnr/ffi/util/ref/FinalizableSoftReference.java
new file mode 100644
index 0000000..2d6bf5a
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/FinalizableSoftReference.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * Soft reference with a {@code finalizeReferent()} method which a background thread invokes after
+ * the garbage collector reclaims the referent. This is a simpler alternative to using a {@link
+ * java.lang.ref.ReferenceQueue}.
+ *
+ * @author Bob Lee
+ * @since 2.0 (imported from Google Collections Library)
+ */
+public abstract class FinalizableSoftReference<T> extends SoftReference<T>
+ implements FinalizableReference {
+ /**
+ * Constructs a new finalizable soft reference.
+ *
+ * @param referent to softly reference
+ * @param queue that should finalize the referent
+ */
+ protected FinalizableSoftReference(T referent, FinalizableReferenceQueue queue) {
+ super(referent, queue.queue);
+ queue.cleanUp();
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/ref/FinalizableWeakReference.java b/src/main/java/jnr/ffi/util/ref/FinalizableWeakReference.java
new file mode 100644
index 0000000..e5ac281
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/FinalizableWeakReference.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Weak reference with a {@code finalizeReferent()} method which a background thread invokes after
+ * the garbage collector reclaims the referent. This is a simpler alternative to using a {@link
+ * java.lang.ref.ReferenceQueue}.
+ *
+ * @author Bob Lee
+ * @since 2.0 (imported from Google Collections Library)
+ */
+public abstract class FinalizableWeakReference<T> extends WeakReference<T>
+ implements FinalizableReference {
+ /**
+ * Constructs a new finalizable weak reference.
+ *
+ * @param referent to weakly reference
+ * @param queue that should finalize the referent
+ */
+ protected FinalizableWeakReference(T referent, FinalizableReferenceQueue queue) {
+ super(referent, queue.queue);
+ queue.cleanUp();
+ }
+}
diff --git a/src/main/java/jnr/ffi/util/ref/internal/Finalizer.java b/src/main/java/jnr/ffi/util/ref/internal/Finalizer.java
new file mode 100644
index 0000000..666f02b
--- /dev/null
+++ b/src/main/java/jnr/ffi/util/ref/internal/Finalizer.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jnr.ffi.util.ref.internal;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Thread that finalizes referents. All references should implement
+ * {@code com.google.common.base.FinalizableReference}.
+ *
+ * <p>While this class is public, we consider it to be *internal* and not part
+ * of our published API. It is public so we can access it reflectively across
+ * class loaders in secure environments.
+ *
+ * <p>This class can't depend on other Google Collections code. If we were
+ * to load this class in the same class loader as the rest of
+ * Google Collections, this thread would keep an indirect strong reference
+ * to the class loader and prevent it from being garbage collected. This
+ * poses a problem for environments where you want to throw away the class
+ * loader. For example, dynamically reloading a web application or unloading
+ * an OSGi bundle.
+ *
+ * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
+ * in its own class loader. That way, this class doesn't prevent the main
+ * class loader from getting garbage collected, and this class can detect when
+ * the main class loader has been garbage collected and stop itself.
+ */
+public class Finalizer extends Thread {
+
+ private static final Logger logger
+ = Logger.getLogger(Finalizer.class.getName());
+
+ /** Name of FinalizableReference.class. */
+ private static final String FINALIZABLE_REFERENCE
+ = "jnr.ffi.util.ref.FinalizableReference";
+
+ /**
+ * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
+ * reflectively.
+ *
+ * @param finalizableReferenceClass FinalizableReference.class
+ * @param frq reference to instance of FinalizableReferenceQueue that started
+ * this thread
+ * @return ReferenceQueue which Finalizer will poll
+ */
+ public static ReferenceQueue<Object> startFinalizer(
+ Class<?> finalizableReferenceClass, Object frq) {
+ /*
+ * We use FinalizableReference.class for two things:
+ *
+ * 1) To invoke FinalizableReference.finalizeReferent()
+ *
+ * 2) To detect when FinalizableReference's class loader has to be garbage
+ * collected, at which point, Finalizer can stop running
+ */
+ if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
+ throw new IllegalArgumentException(
+ "Expected " + FINALIZABLE_REFERENCE + ".");
+ }
+
+ Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
+ finalizer.start();
+ return finalizer.queue;
+ }
+
+ private final WeakReference<Class<?>> finalizableReferenceClassReference;
+ private final PhantomReference<Object> frqReference;
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+
+ private static final Field inheritableThreadLocals
+ = getInheritableThreadLocalsField();
+
+ /** Constructs a new finalizer thread. */
+ private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
+ super(Finalizer.class.getName());
+
+ this.finalizableReferenceClassReference
+ = new WeakReference<Class<?>>(finalizableReferenceClass);
+
+ // Keep track of the FRQ that started us so we know when to stop.
+ this.frqReference = new PhantomReference<Object>(frq, queue);
+
+ setDaemon(true);
+ setPriority(Thread.MAX_PRIORITY);
+ // Set the context class loader to null in order to avoid
+ // keeping a strong reference to an application classloader.
+ setContextClassLoader(null);
+
+ try {
+ if (inheritableThreadLocals != null) {
+ inheritableThreadLocals.set(this, null);
+ }
+ } catch (Throwable t) {
+ logger.log(Level.INFO, "Failed to clear thread local values inherited"
+ + " by reference finalizer thread.", t);
+ }
+ }
+
+ /**
+ * Loops continuously, pulling references off the queue and cleaning them up.
+ */
+ @SuppressWarnings("InfiniteLoopStatement")
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ if (!cleanUp(queue.remove())) {
+ break;
+ }
+ } catch (InterruptedException e) { /* ignore */ }
+ }
+ }
+
+ /**
+ * Cleans up a single reference. Catches and logs all throwables.
+ */
+ private boolean cleanUp(Reference<?> reference) {
+ Method finalizeReferentMethod = getFinalizeReferentMethod();
+ if (finalizeReferentMethod == null) {
+ return false;
+ }
+ do {
+ /*
+ * This is for the benefit of phantom references. Weak and soft
+ * references will have already been cleared by this point.
+ */
+ reference.clear();
+
+ if (reference == frqReference) {
+ /*
+ * The client no longer has a reference to the
+ * FinalizableReferenceQueue. We can stop.
+ */
+ return false;
+ }
+
+ try {
+ finalizeReferentMethod.invoke(reference);
+ } catch (Throwable t) {
+ logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
+ }
+
+ /*
+ * Loop as long as we have references available so as not to waste
+ * CPU looking up the Method over and over again.
+ */
+ } while ((reference = queue.poll()) != null);
+ return true;
+ }
+
+ /**
+ * Looks up FinalizableReference.finalizeReferent() method.
+ */
+ private Method getFinalizeReferentMethod() {
+ Class<?> finalizableReferenceClass
+ = finalizableReferenceClassReference.get();
+ if (finalizableReferenceClass == null) {
+ /*
+ * FinalizableReference's class loader was reclaimed. While there's a
+ * chance that other finalizable references could be enqueued
+ * subsequently (at which point the class loader would be resurrected
+ * by virtue of us having a strong reference to it), we should pretty
+ * much just shut down and make sure we don't keep it alive any longer
+ * than necessary.
+ */
+ return null;
+ }
+ try {
+ return finalizableReferenceClass.getMethod("finalizeReferent");
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static Field getInheritableThreadLocalsField() {
+ try {
+ Field inheritableThreadLocals
+ = Thread.class.getDeclaredField("inheritableThreadLocals");
+ inheritableThreadLocals.setAccessible(true);
+ return inheritableThreadLocals;
+ } catch (Throwable t) {
+ logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
+ + " Reference finalizer threads will inherit thread local"
+ + " values.");
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/jnr/ffi/ArrayTest.java b/src/test/java/jnr/ffi/ArrayTest.java
new file mode 100644
index 0000000..033c60d
--- /dev/null
+++ b/src/test/java/jnr/ffi/ArrayTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.lang.*;
+
+import static org.junit.Assert.*;
+
+
+public class ArrayTest {
+ public static interface TestLib {
+// Pointer ptr_return_array_element(Pointer[] array, int index);
+// void ptr_set_array_element(Pointer[] array, int index, Pointer value);
+ byte ptr_ret_int8_t(byte[] p, int offset);
+ short ptr_ret_int16_t(short[] p, int offset);
+ int ptr_ret_int32_t(int[] p, int offset);
+ @LongLong long ptr_ret_int64_t(@LongLong long[] p, int offset);
+ long ptr_ret_int32_t(long[] p, int offset);
+ float ptr_ret_float(float[] p, int offset);
+// Pointer ptr_ret_pointer(Pointer[] p, int offset);
+ double ptr_ret_double(double[] p, int offset);
+ void ptr_set_int8_t(byte[] p, int offset, byte value);
+ void ptr_set_int16_t(short[] p, int offset, short value);
+ void ptr_set_int32_t(int[] p, int offset, int value);
+ void ptr_set_int64_t(@LongLong long[] p, int offset, @LongLong long value);
+ void ptr_set_int32_t(long[] p, int offset, long value);
+ void ptr_set_float(float[] p, int offset, float value);
+ void ptr_set_double(double[] p, int offset, double value);
+// void ptr_set_pointer(Pointer[] p, int offset, Pointer value);
+ Pointer ptr_malloc(int size);
+ void ptr_free(Pointer ptr);
+ }
+ public static interface TestLibInOnly {
+ byte ptr_ret_int8_t(@In byte[] p, int offset);
+ short ptr_ret_int16_t(@In short[] p, int offset);
+ int ptr_ret_int32_t(@In int[] p, int offset);
+ @LongLong long ptr_ret_int64_t(@In @LongLong long[] p, int offset);
+ long ptr_ret_int32_t(@In long[] p, int offset);
+ float ptr_ret_float(@In float[] p, int offset);
+ void ptr_set_int8_t(@In byte[] p, int offset, byte value);
+ void ptr_set_int16_t(@In short[] p, int offset, short value);
+ void ptr_set_int32_t(@In int[] p, int offset, int value);
+ void ptr_set_int64_t(@In @LongLong long[] p, int offset, @LongLong long value);
+ void ptr_set_int32_t(@In long[] p, int offset, long value);
+ void ptr_set_float(@In float[] p, int offset, float value);
+ void ptr_set_double(@In double[] p, int offset, double value);
+// void ptr_set_pointer(@In Pointer[] p, int offset, Pointer value);
+ }
+ public static interface TestLibOutOnly {
+ byte ptr_ret_int8_t(@Out byte[] p, int offset);
+ short ptr_ret_int16_t(@Out short[] p, int offset);
+ int ptr_ret_int32_t(@Out int[] p, int offset);
+ @LongLong long ptr_ret_int64_t(@Out @LongLong long[] p, int offset);
+ long ptr_ret_int32_t(@Out long[] p, int offset);
+ float ptr_ret_float(@Out float[] p, int offset);
+ void ptr_set_int8_t(@Out byte[] p, int offset, byte value);
+ void ptr_set_int16_t(@Out short[] p, int offset, short value);
+ void ptr_set_int32_t(@Out int[] p, int offset, int value);
+ void ptr_set_int64_t(@Out @LongLong long[] p, int offset, @LongLong long value);
+ void ptr_set_int32_t(@Out long[] p, int offset, long value);
+ void ptr_set_float(@Out float[] p, int offset, float value);
+ void ptr_set_double(@Out double[] p, int offset, double value);
+// void ptr_set_pointer(@Out Pointer[] p, int offset, Pointer value);
+ }
+ static TestLib testlib;
+ public ArrayTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void byteByReference() {
+ final byte MAGIC = (byte) 0xfe;
+ byte[] ref = { MAGIC };
+ assertEquals("byte reference not read correctly", MAGIC, testlib.ptr_ret_int8_t(ref, 0));
+ final byte MAGIC2 = (byte) 0xca;
+ testlib.ptr_set_int8_t(ref, 0, MAGIC2);
+ assertEquals("byte reference not written correctly", MAGIC2, ref[0]);
+ }
+ @Test
+ public void shortByReference() {
+ final short MAGIC = (short) 0xfee1;
+ short[] ref = { MAGIC };
+ assertEquals("short reference not read correctly", MAGIC, testlib.ptr_ret_int16_t(ref, 0));
+ final short MAGIC2 = (short) 0xcafe;
+ testlib.ptr_set_int16_t(ref, 0, MAGIC2);
+ assertEquals("short reference not written correctly", MAGIC2, ref[0]);
+ }
+ @Test
+ public void intByReference() {
+ final int MAGIC = (int) 0xfee1dead;
+ int[] ref = { MAGIC };
+ assertEquals("int reference not read correctly", MAGIC, testlib.ptr_ret_int32_t(ref, 0));
+ final int MAGIC2 = (int) 0xcafebabe;
+ testlib.ptr_set_int32_t(ref, 0, MAGIC2);
+ assertEquals("int reference not written correctly", MAGIC2, ref[0]);
+ }
+
+ @Test
+ public void getLongByReference() {
+ final long MAGIC = 0x1234fee1dead6789L;
+ long[] ref = { MAGIC };
+ assertEquals("long reference not read correctly", MAGIC, testlib.ptr_ret_int64_t(ref, 0));
+ }
+
+ @Test
+ public void setLongByReference() {
+
+ final long MAGIC = 0xcafebabe12345678L;
+ long[] ref = { 0L };
+ testlib.ptr_set_int64_t(ref, 0, MAGIC);
+ assertEquals("long reference not written correctly", MAGIC, ref[0]);
+ }
+
+ @Test
+ public void setLong32ByReference() {
+ if (Runtime.getRuntime(testlib).longSize() == 4) {
+
+ final long MAGIC = 0x12345678L;
+ long[] ref = {0L};
+ testlib.ptr_set_int32_t(ref, 0, MAGIC);
+ assertEquals("long reference not written correctly", MAGIC, ref[0]);
+ }
+ }
+
+// @Test
+// public void pointerByReference() {
+// final Pointer MAGIC = new Pointer(0xfee1dead);
+// Pointer[] ref = { MAGIC };
+// assertEquals("Pointer reference not read correctly", MAGIC, testlib.ptr_ret_pointer(ref, 0));
+// final Pointer MAGIC2 = new Pointer(0xcafebabe);
+// testlib.ptr_set_pointer(ref, 0, MAGIC2);
+// assertEquals("Pointer reference not written correctly", MAGIC2, ref[0]);
+// }
+ @Test
+ public void floatByReference() {
+ final float MAGIC = (float) 0xfee1dead;
+ float[] ref = { MAGIC };
+ assertEquals("float reference not read correctly", MAGIC, testlib.ptr_ret_float(ref, 0), 0.00001);
+ final float MAGIC2 = (float) 0xcafebabe;
+ testlib.ptr_set_float(ref, 0, MAGIC2);
+ assertEquals("float reference not written correctly", MAGIC2, ref[0], 0.00001);
+ }
+ @Test
+ public void doubleByReference() {
+ final double MAGIC = 0x1234fee1dead6789L;
+ double[] ref = { MAGIC };
+ assertEquals("double reference not read correctly", MAGIC, testlib.ptr_ret_double(ref, 0), 0.00001);
+ final double MAGIC2 = (double) 0xcafebabe12345678L;
+ testlib.ptr_set_double(ref, 0, MAGIC2);
+ assertEquals("double reference not written correctly", MAGIC2, ref[0], 0d);
+ }
+
+ //@Test
+ public void inOnlyByteByReference() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final byte MAGIC = (byte) 0xfe;
+ byte[] ref = { MAGIC };
+ assertEquals("byte reference not read correctly", MAGIC, lib.ptr_ret_int8_t(ref, 0));
+ final byte MAGIC2 = (byte) 0xca;
+ lib.ptr_set_int8_t(ref, 0, MAGIC2);
+ assertEquals("byte array read from native memory when it should not be", MAGIC, ref[0]);
+ }
+ //@Test
+ public void inOnlyByteArray() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final byte MAGIC = (byte) 0xfe;
+ byte[] ref = new byte[1024];
+ ref[0] = MAGIC;
+ assertEquals("byte array not read correctly", MAGIC, lib.ptr_ret_int8_t(ref, 0));
+ final byte MAGIC2 = (byte) 0xca;
+ lib.ptr_set_int8_t(ref, 0, MAGIC2);
+ assertEquals("byte array read from native memory when it should not be", MAGIC, ref[0]);
+ }
+ //@Test
+ public void outOnlyByteByReference() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final byte MAGIC = (byte) 0xfe;
+ byte[] ref = { MAGIC };
+
+ final byte MAGIC2 = (byte) 0xca;
+ lib.ptr_set_int8_t(ref, 0, MAGIC2);
+ assertEquals("byte reference not copied from native memory", MAGIC2, ref[0]);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/BufferTest.java b/src/test/java/jnr/ffi/BufferTest.java
new file mode 100644
index 0000000..bde65d0
--- /dev/null
+++ b/src/test/java/jnr/ffi/BufferTest.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+
+import java.nio.*;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author wayne
+ */
+public class BufferTest {
+
+ public BufferTest() {
+ }
+ public static interface TestLib {
+ void fillByteBuffer(@Out ByteBuffer buf, byte value, int size);
+ void fillByteBuffer(@Out Buffer buf, byte value, int size);
+// void fillCharBuffer(@Out CharBuffer buf, char value, int size);
+ void fillShortBuffer(@Out ShortBuffer buf, short value, int size);
+ void fillShortBuffer(@Out Buffer buf, short value, int size);
+ void fillIntBuffer(@Out IntBuffer buf, int value, int size);
+ void fillIntBuffer(@Out Buffer buf, int value, int size);
+ void fillLongBuffer(@Out LongBuffer buf, @LongLong long value, int size);
+ void fillLongBuffer(@Out Buffer buf, @LongLong long value, int size);
+ void fillFloatBuffer(@Out FloatBuffer buf, float value, int size);
+ void fillDoubleBuffer(@Out DoubleBuffer buf, double value, int size);
+ void fillByteBuffer(@Out byte[] buf, byte value, int size);
+// void fillCharBuffer(@Out char[] buf, char value, int size);
+ void fillShortBuffer(@Out short[] buf, short value, int size);
+ void fillIntBuffer(@Out int[] buf, int value, int size);
+ void fillLongBuffer(@Out @LongLong long[] buf, @LongLong long value, int size);
+ void fillFloatBuffer(@Out float[] buf, float value, int size);
+ void fillDoubleBuffer(@Out double[] buf, double value, int size);
+ void copyByteBuffer(@Out ByteBuffer dst, @In ByteBuffer src, int size);
+ void copyByteBuffer(@Out ByteBuffer dst, @In byte[] src, int size);
+ void copyByteBuffer(@Out byte[] dst, @In ByteBuffer src, int size);
+ void copyByteBuffer(@Out byte[] dst, @In byte[] src, int size);
+ void copyShortBuffer(@Out ShortBuffer dst, @In ShortBuffer src, int size);
+ void copyShortBuffer(@Out ShortBuffer dst, @In short[] src, int size);
+ void copyShortBuffer(@Out short[] dst, @In ShortBuffer src, int size);
+ void copyShortBuffer(@Out short[] dst, @In short[] src, int size);
+ void copyIntBuffer(@Out IntBuffer dst, @In IntBuffer src, int size);
+ void copyIntBuffer(@Out IntBuffer dst, @In int[] src, int size);
+ void copyIntBuffer(@Out int[] dst, @In IntBuffer src, int size);
+ void copyIntBuffer(@Out int[] dst, @In int[] src, int size);
+ }
+ static TestLib lib;
+ static Runtime runtime;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ lib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(lib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ lib = null;
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ // SMALL sized heap buffers can be done using direct buffers
+ private static final int SMALL = 64;
+
+ // LARGE sized heap buffers are handled via the native code
+ private static final int LARGE = 2048;
+
+ public void testByteBufferArgument(int size) {
+ ByteBuffer buf = ByteBuffer.allocate(size).order(runtime.byteOrder());
+ final byte MAGIC = (byte)0xED;
+ lib.fillByteBuffer(buf, MAGIC, buf.remaining());
+ for (int i=0;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillSmallByteBufferArgument() {
+ testByteBufferArgument(SMALL);
+ }
+ @Test
+ public void fillLargeByteBufferArgument() {
+ testByteBufferArgument(LARGE);
+ }
+ @Test
+ public void fillByteBufferWithOffsetArgument() {
+ ByteBuffer buf = ByteBuffer.allocate(SMALL).order(runtime.byteOrder());
+ final byte MAGIC = (byte)0xED;
+ buf.put((byte)0xDE);
+ lib.fillByteBuffer(buf.slice(), MAGIC, SMALL - 1);
+ assertEquals("Value at position 0 overwritten", (byte)0xde, buf.get(0));
+ for (int i=buf.position();i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillByteBufferSlice() {
+ final int SIZE = SMALL;
+ ByteBuffer buf = ByteBuffer.allocate(SIZE).order(runtime.byteOrder());
+ final byte MAGIC = (byte)0xED;
+ buf.put(0, (byte)0xDE);
+ buf.put(buf.limit() - 1, (byte) 0xDE);
+ ByteBuffer dup = buf.duplicate();
+ dup.position(1).limit(buf.limit() - 1);
+ ByteBuffer slice = dup.slice();
+ lib.fillByteBuffer(slice, MAGIC, slice.capacity());
+ assertEquals("Value at position 0 overwritten", (byte)0xde, buf.get(0));
+ assertEquals("Value at position " + (SIZE - 1) + " overwritten",
+ (byte)0xde, buf.get(SIZE - 1));
+ for (int i = 1; i < buf.capacity() - 1; i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+
+ public void testShortBufferArgument(int size) {
+ ShortBuffer buf = ShortBuffer.allocate(size);
+ final short MAGIC = (short)0xABED;
+ lib.fillShortBuffer(buf, MAGIC, size);
+ for (int i=0;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+
+ @Test
+ public void fillSmallShortBufferArgument() {
+ testShortBufferArgument(SMALL);
+ }
+ @Test
+ public void fillLargeShortBufferArgument() {
+ testShortBufferArgument(LARGE);
+ }
+
+ @Test
+ public void fillShortBufferWithOffsetArgument() {
+ ShortBuffer buf = ShortBuffer.allocate(SMALL);
+ final short MAGIC = (short)0xABED;
+ buf.put((short)0xDEAD);
+ lib.fillShortBuffer(buf.slice(), MAGIC, SMALL - 1);
+ assertEquals("Value at position 0 overwritten", (short)0xdead, buf.get(0));
+ for (int i=1;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillShortBufferSlice() {
+ ShortBuffer buf = ShortBuffer.allocate(SMALL);
+ final short FILL = (short) 0x1234;
+ final short GUARD = (short) 0xdead;
+ buf.put(0, GUARD).put(buf.limit() - 1, GUARD);
+ ShortBuffer dup = buf.duplicate();
+ dup.position(1).limit(buf.limit() - 1);
+ ShortBuffer slice = dup.slice();
+ lib.fillShortBuffer(slice, FILL, slice.capacity());
+ assertEquals("Value at position 0 overwritten", GUARD, buf.get(0));
+ assertEquals("Value at position " + (buf.limit() - 1) + " overwritten",
+ GUARD, buf.get(buf.limit() - 1));
+ for (int i = 1; i < buf.limit() - 1; i++) {
+ assertEquals("Bad value at index " + i, FILL, buf.get(i));
+ }
+ }
+ public void testIntBufferArgument(int size) {
+ IntBuffer buf = IntBuffer.allocate(size);
+ final int MAGIC = 0xABEDCF23;
+ lib.fillIntBuffer(buf, MAGIC, size);
+ for (int i=0;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillSmallIntBufferArgument() {
+ testIntBufferArgument(SMALL);
+ }
+ @Test
+ public void fillLargeIntBufferArgument() {
+ testIntBufferArgument(SMALL);
+ }
+ @Test
+ public void fillIntBufferWithOffsetArgument() {
+ IntBuffer buf = IntBuffer.allocate(SMALL);
+ final int MAGIC = 0xABEDCF23;
+ buf.put(0xdeadbeef);
+ lib.fillIntBuffer(buf.slice(), MAGIC, SMALL - 1);
+ assertEquals("Value at position 0 overwritten", 0xdeadbeef, buf.get(0));
+ for (int i=1;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillIntBufferSlice() {
+ IntBuffer buf = IntBuffer.allocate(SMALL);
+ final int FILL = 0x12345678;
+ final int GUARD = 0xdeadbeef;
+ buf.put(0, GUARD).put(buf.limit() - 1, GUARD);
+ IntBuffer dup = buf.duplicate();
+ dup.position(1).limit(buf.limit() - 1);
+ IntBuffer slice = dup.slice();
+ lib.fillIntBuffer(slice, FILL, slice.capacity());
+ assertEquals("Value at position 0 overwritten", GUARD, buf.get(0));
+ assertEquals("Value at position " + (buf.limit() - 1) + " overwritten",
+ GUARD, buf.get(buf.limit() - 1));
+ for (int i = 1; i < buf.limit() - 1; i++) {
+ assertEquals("Bad value at index " + i, FILL, buf.get(i));
+ }
+ }
+ @Test
+ public void fillLongBufferArgument() {
+ LongBuffer buf = LongBuffer.allocate(SMALL);
+ final long MAGIC = 0x1234567887654321L;
+ lib.fillLongBuffer(buf, MAGIC, SMALL);
+ for (int i=0;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillLongBufferWithOffsetArgument() {
+ LongBuffer buf = LongBuffer.allocate(SMALL);
+ final long MAGIC = 0x1234567887654321L;
+ buf.put(0xdeadbeefL);
+ lib.fillLongBuffer(buf.slice(), MAGIC, SMALL - 1);
+ assertEquals("Value at position 0 overwritten", 0xdeadbeefL, buf.get(0));
+ for (int i=1;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillLongBufferSlice() {
+ LongBuffer buf = LongBuffer.allocate(SMALL);
+ final long GUARD = 0xdeadbeefL;
+ final long FILL = 0x1234567887654321L;
+ buf.put(0, GUARD).put(buf.limit() - 1, GUARD);
+ LongBuffer dup = buf.duplicate();
+ dup.position(1).limit(buf.limit() - 1);
+ LongBuffer slice = dup.slice();
+ lib.fillLongBuffer(dup.slice(), FILL, slice.capacity());
+ assertEquals("Value at position 0 overwritten", GUARD, buf.get(0));
+ assertEquals("Value at position " + (buf.limit() - 1) + " overwritten",
+ GUARD, buf.get(buf.limit() - 1));
+ for (int i = 1; i < buf.limit() - 1; i++) {
+ assertEquals("Bad value at index " + i, FILL, buf.get(i));
+ }
+ }
+ @Test
+ public void fillDirectByteBufferArgument() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL).order(runtime.byteOrder());
+ final byte MAGIC = (byte)0xED;
+ lib.fillByteBuffer(buf, MAGIC, SMALL);
+ for (int i=0;i < buf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+ }
+ }
+ @Test
+ public void fillDirectShortBufferArgument() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL*2).order(runtime.byteOrder());
+ ShortBuffer shortBuf = buf.asShortBuffer();
+ final short MAGIC = (short)0xABED;
+ lib.fillShortBuffer(shortBuf, MAGIC, SMALL);
+ for (int i=0;i < shortBuf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, shortBuf.get(i));
+ }
+ }
+
+ @Test
+ public void fillDirectShortBufferSlice() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL*2).order(runtime.byteOrder());
+ ShortBuffer shortBuf = buf.asShortBuffer();
+ buf.position(2);
+ ShortBuffer sliced = buf.slice().asShortBuffer();
+ final short MAGIC = (short)0xABED;
+ lib.fillShortBuffer(sliced, MAGIC, SMALL - 1);
+ assertEquals("Bad value at index " + 0, 0, shortBuf.get(0));
+ for (int i=1; i < shortBuf.capacity() - 1; i++) {
+ assertEquals("Bad value at index " + i, MAGIC, shortBuf.get(i));
+ }
+ }
+
+ @Test
+ public void fillDirectShortBufferSliceAsBuffer() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL*2).order(runtime.byteOrder());
+ ShortBuffer shortBuf = buf.asShortBuffer();
+ buf.position(2);
+ Buffer sliced = buf.slice().asShortBuffer();
+ final short MAGIC = (short)0xABED;
+ lib.fillShortBuffer(sliced, MAGIC, SMALL - 1);
+ assertEquals("Bad value at index " + 0, 0, shortBuf.get(0));
+ for (int i=1; i < shortBuf.capacity() - 1; i++) {
+ assertEquals("Bad value at index " + i, MAGIC, shortBuf.get(i));
+ }
+ }
+
+ @Test
+ public void fillDirectIntBufferArgument() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL*4).order(runtime.byteOrder());
+ IntBuffer intBuf = buf.asIntBuffer();
+ final int MAGIC = 0xABEDCF23;
+ lib.fillIntBuffer(intBuf, MAGIC, SMALL);
+ for (int i=0;i < intBuf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, intBuf.get(i));
+ }
+ }
+ @Test
+ public void fillDirectLongBufferArgument() {
+ ByteBuffer buf = ByteBuffer.allocateDirect(SMALL*8).order(runtime.byteOrder());
+ LongBuffer longBuf = buf.asLongBuffer();
+ final long MAGIC = 0x1234567887654321L;
+ lib.fillLongBuffer(longBuf, MAGIC, SMALL);
+ for (int i=0;i < longBuf.capacity();i++) {
+ assertEquals("Bad value at index " + i, MAGIC, longBuf.get(i));
+ }
+ }
+ @Test
+ public void copySmallDirectByteBufferToArray() {
+ testCopyByteBufferToArray(ByteBuffer.allocateDirect(SMALL));
+ }
+ @Test
+ public void copyLargeDirectByteBufferToArray() {
+ testCopyByteBufferToArray(ByteBuffer.allocateDirect(LARGE));
+ }
+ @Test
+ public void copySmallHeapByteBufferToArray() {
+ testCopyByteBufferToArray(ByteBuffer.allocate(SMALL));
+ }
+ @Test
+ public void copyLargeHeapByteBufferToArray() {
+ testCopyByteBufferToArray(ByteBuffer.allocate(LARGE));
+ }
+ public void testCopyByteBufferToArray(ByteBuffer src) {
+ final int size = src.capacity();
+ for (int i = 0; i < size; ++i) {
+ src.put(i, (byte) i);
+ }
+ byte[] dst = new byte[size];
+ lib.copyByteBuffer(dst, src, size);
+ for (int i = 0; i < size; ++i) {
+ assertEquals("Bad value at index " + i, (byte) i, dst[i]);
+ }
+ }
+ // FIXME: re-enable when read-only buffers are supported
+// @Test
+ public void copyReadOnlyHeapByteBufferToArray() {
+ final int size = SMALL;
+ ByteBuffer src = ByteBuffer.allocate(size);
+ for (int i = 0; i < size; ++i) {
+ src.put(i, (byte) i);
+ }
+ byte[] dst = new byte[size];
+ lib.copyByteBuffer(dst, src.asReadOnlyBuffer(), size);
+ for (int i = 0; i < size; ++i) {
+ assertEquals("Bad value at index " + i, (byte) i, dst[i]);
+ }
+ }
+// @Test
+// public void pinnedByteBuffer() {
+// ByteBuffer buf = ByteBuffer.allocate(LARGE);
+// Function fillByteBuffer = NativeLibrary.getInstance("jffitest").getFunction("fillByteBuffer");
+// NativeInvoker nativeInvoker = FFINativeInvoker.getInvoker(fillByteBuffer, NativeInvoker.VOID);
+// CallBuilder cb = CallBuilder.create(nativeInvoker);
+// final byte MAGIC = (byte)0xED;
+// cb.addPinned(buf).addInt8(MAGIC).addInt32(buf.remaining()).invokeVoid();
+// for (int i=0;i < buf.capacity();i++) {
+// assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
+// }
+// }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/DelegateTest.java b/src/test/java/jnr/ffi/DelegateTest.java
new file mode 100644
index 0000000..f6b26d2
--- /dev/null
+++ b/src/test/java/jnr/ffi/DelegateTest.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.types.u_int32_t;
+import org.junit.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+/**
+ *
+ */
+public class DelegateTest {
+
+ public DelegateTest() {
+ }
+ private static TestLib lib;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ lib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ public static interface TestLib {
+ public static interface CallableVrV {
+ @Delegate public void call();
+ }
+ void testClosureVrV(CallableVrV closure);
+ public static interface CallableVrB {
+ @Delegate public byte call();
+ }
+ byte testClosureVrB(CallableVrB closure);
+ public static interface CallableVrS {
+ @Delegate public short call();
+ }
+ short testClosureVrS(CallableVrS closure);
+ public static interface CallableVrI {
+ @Delegate public int call();
+ }
+ int testClosureVrI(CallableVrI closure);
+ public static interface CallableVrL {
+ @Delegate public @LongLong long call();
+ }
+ @LongLong long testClosureVrL(CallableVrL closure);
+ public interface CallableVrF {
+ @Delegate public float call();
+ }
+ float testClosureVrF(CallableVrF closure);
+ public interface CallableVrD {
+ @Delegate public double call();
+ }
+ double testClosureVrD(CallableVrD closure);
+
+ public interface CallableBrV {
+ @Delegate public void call(byte a1);
+ }
+ void testClosureBrV(CallableBrV closure, byte a1);
+
+ public interface CallableSrV {
+ @Delegate public void call(short a1);
+ }
+ void testClosureSrV(CallableSrV closure, short a1);
+
+ public interface CallableIrV {
+ @Delegate public void call(int a1);
+ }
+ void testClosureIrV(CallableIrV closure, int a1);
+
+ public interface CallableIrVBoxed {
+ @Delegate public void call(@u_int32_t Long a1);
+ }
+ void testClosureIrV(CallableIrVBoxed closure, @u_int32_t long a1);
+
+ public interface CallableErV {
+ @Delegate public void call(@u_int32_t EnumTest.TestEnum a1);
+ }
+ void testClosureIrV(CallableErV closure, @u_int32_t EnumTest.TestEnum a1);
+
+ public static interface CallableVrE {
+ @Delegate public EnumTest.TestEnum call();
+ }
+ int testClosureVrI(CallableVrE closure);
+
+ public class ClosureStruct extends Struct {
+ public final Function<CallableIrV> function = function(CallableIrV.class);
+ public ClosureStruct(Runtime runtime) {
+ super(runtime);
+ }
+ }
+
+ void testStructClosureIrV(ClosureStruct closure, int a1);
+
+// void testClosureBrV(Callable closure, byte a1);
+// void testClosureSrV(Callable closure, short a1);
+
+// void testClosureLrV(Callable closure, long a1);
+// void testClosureFrV(Callable closure, float a1);
+// void testClosureDrV(Callable closure, double a1);
+//
+// // closures with small-big-small arguments
+// void testClosureBSBrV(Callable closure, byte a1, short a2, byte a3);
+// void testClosureBIBrV(Callable closure, byte a1, int a2, byte a3);
+// void testClosureBLBrV(Callable closure, byte a1, long a2, byte a3);
+// void testClosureBFBrV(Callable closure, byte a1, float a2, byte a3);
+// void testClosureBDBrV(Callable closure, byte a1, double a2, byte a3);
+//
+// void testClosureSBSrV(Callable closure, short a1, byte a2, short a3);
+// void testClosureSISrV(Callable closure, short a1, int a2, short a3);
+// void testClosureSLSrV(Callable closure, short a1, long a2, short a3);
+// void testClosureSFSrV(Callable closure, short a1, float a2, short a3);
+// void testClosureSDSrV(Callable closure, short a1, double a2, short a3);
+//
+// // Now big-smaller-smaller
+// void testClosureLSBrV(Callable closure, long a1, short a2, byte a3);
+// // big-smaller-small
+// void testClosureLBSrV(Callable closure, long a1, byte a2, short a3);
+ public interface ReusableCallable {
+ @Delegate public void call(int a1);
+ }
+ Pointer ret_pointer(ReusableCallable callable);
+ public CallableVrV ret_pointer(CallableVrV callable);
+ public CallableIrV ret_pointer(CallableIrV callable);
+ }
+ @Test
+ public void closureVrV() {
+ final boolean[] called = { false };
+ final TestLib.CallableVrV closure = new TestLib.CallableVrV() {
+
+ public void call() {
+ called[0] = true;
+ }
+ };
+ lib.testClosureVrV(closure);
+ assertTrue("Callable not called", called[0]);
+ }
+ @Test
+ public void closureVrB() {
+ final boolean[] called = { false };
+ final byte MAGIC = (byte) 0xfe;
+ TestLib.CallableVrB closure = new TestLib.CallableVrB() {
+
+ public byte call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ byte retVal = lib.testClosureVrB(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal);
+ }
+ @Test
+ public void closureVrS() {
+ final boolean[] called = { false };
+ final short MAGIC = (short) 0xfee1;
+ TestLib.CallableVrS closure = new TestLib.CallableVrS() {
+
+ public short call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ short retVal = lib.testClosureVrS(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal);
+ }
+ @Test
+ public void closureVrI() {
+ final boolean[] called = { false };
+ final int MAGIC = (int) 0xfee1dead;
+ TestLib.CallableVrI closure = new TestLib.CallableVrI() {
+
+ public int call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ int retVal = lib.testClosureVrI(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal);
+ }
+ @Test
+ public void closureVrL() {
+ final boolean[] called = { false };
+ final long MAGIC = 0xfee1deadcafebabeL;
+ TestLib.CallableVrL closure = new TestLib.CallableVrL() {
+
+ public long call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ long retVal = lib.testClosureVrL(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal);
+ }
+ @Test
+ public void closureVrF() {
+ final boolean[] called = { false };
+ final float MAGIC = (float) 0xfee1dead;
+ TestLib.CallableVrF closure = new TestLib.CallableVrF() {
+
+ public float call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ float retVal = lib.testClosureVrF(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal, 0f);
+ }
+ @Test
+ public void closureVrD() {
+ final boolean[] called = { false };
+ final double MAGIC = (double) 0xfee1dead;
+ TestLib.CallableVrD closure = new TestLib.CallableVrD() {
+
+ public double call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ double retVal = lib.testClosureVrD(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC, retVal, 0d);
+ }
+ @Test
+ public void closureBrV() {
+ final boolean[] called = { false };
+ final byte[] val = { 0 };
+ final byte MAGIC = (byte) 0xde;
+ TestLib.CallableBrV closure = new TestLib.CallableBrV() {
+
+ public void call(byte a1) {
+ called[0] = true;
+ val[0] = a1;
+ }
+ };
+ lib.testClosureBrV(closure, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+ @Test
+ public void closureSrV() {
+ final boolean[] called = { false };
+ final short[] val = { 0 };
+ final short MAGIC = (short) 0xdead;
+ TestLib.CallableSrV closure = new TestLib.CallableSrV() {
+
+ public void call(short a1) {
+ called[0] = true;
+ val[0] = a1;
+ }
+ };
+ lib.testClosureSrV(closure, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+
+ @Test
+ public void closureIrV() {
+ final boolean[] called = { false };
+ final int[] val = { 0 };
+ final int MAGIC = 0xdeadbeef;
+ TestLib.CallableIrV closure = new TestLib.CallableIrV() {
+
+ public void call(int a1) {
+ called[0] = true;
+ val[0] = a1;
+ }
+ };
+ lib.testClosureIrV(closure, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+
+ @Test
+ public void closureStructIrV() {
+ final boolean[] called = { false };
+ final int[] val = { 0 };
+ final int MAGIC = 0xdeadbeef;
+ TestLib.CallableIrV closure = new TestLib.CallableIrV() {
+
+ public void call(int a1) {
+ called[0] = true;
+ val[0] = a1;
+ }
+ };
+ TestLib.ClosureStruct s = new TestLib.ClosureStruct(Runtime.getRuntime(lib));
+ s.function.set(closure);
+ lib.testStructClosureIrV(s, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+
+
+ @Test
+ public void closureIrVBoxed() {
+ final boolean[] called = { false };
+ final int[] val = { 0 };
+ final int MAGIC = 0xdeadbeef;
+ TestLib.CallableIrVBoxed closure = new TestLib.CallableIrVBoxed() {
+
+ public void call(Long a1) {
+ called[0] = true;
+ val[0] = a1.intValue();
+ }
+ };
+ lib.testClosureIrV(closure, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+
+ @Test
+ public void closureErV() {
+ final boolean[] called = { false };
+ final EnumTest.TestEnum[] val = { null };
+ final EnumTest.TestEnum MAGIC = EnumTest.TestEnum.C;
+ TestLib.CallableErV closure = new TestLib.CallableErV() {
+
+ public void call(EnumTest.TestEnum a1) {
+ called[0] = true;
+ val[0] = a1;
+ }
+ };
+ lib.testClosureIrV(closure, MAGIC);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+ }
+
+ @Test
+ public void closureVrE() {
+ final boolean[] called = { false };
+ final EnumTest.TestEnum MAGIC = EnumTest.TestEnum.C;
+ TestLib.CallableVrE closure = new TestLib.CallableVrE() {
+
+ public EnumTest.TestEnum call() {
+ called[0] = true;
+ return MAGIC;
+ }
+ };
+ int retVal = lib.testClosureVrI(closure);
+ assertTrue("Callable not called", called[0]);
+ assertEquals("Incorrect return value from closure", MAGIC.intValue(), retVal);
+ }
+
+ @Test
+ public void reuseClosure() {
+ TestLib.ReusableCallable closure = new TestLib.ReusableCallable() {
+
+ public void call(int a1) {}
+ };
+ Pointer p1 = lib.ret_pointer(closure);
+ Pointer p2 = lib.ret_pointer(closure);
+ assertEquals("not same native address for Callable instance", p1, p2);
+ }
+
+ @Test public void allocateMany() {
+ for (int i = 0; i < 100000; i++) {
+ lib.ret_pointer(new TestLib.ReusableCallable() {
+ public void call(int a1) {}
+ });
+ }
+// assertEquals("not same native address for Callable instance", p1, p2);
+ }
+
+ @Test public void callFunctionPointerVrV() {
+ final boolean[] called = { false };
+ TestLib.CallableVrV javaClosure = new TestLib.CallableVrV() {
+ @Override
+ public void call() {
+ called[0] = true;
+ }
+ };
+
+ TestLib.CallableVrV callable = lib.ret_pointer(javaClosure);
+ callable.call();
+ assertTrue(called[0]);
+ }
+
+ @Test public void callFunctionPointerIrV() {
+ final boolean[] called = { false };
+ final int[] values = { 0 };
+ final int MAGIC = 0xdeadbeef;
+ TestLib.CallableIrV javaClosure = new TestLib.CallableIrV() {
+ @Override
+ public void call(int i) {
+ called[0] = true;
+ values[0] = i;
+ }
+ };
+
+ TestLib.CallableIrV callable = lib.ret_pointer(javaClosure);
+ callable.call(MAGIC);
+ assertEquals(MAGIC, values[0]);
+ assertTrue(called[0]);
+
+ }
+
+
+// @Test
+// public void closureLrV() {
+// final boolean[] called = { false };
+// final long[] val = { 0 };
+// final long MAGIC = 0xfee1deadcafebabeL;
+// Callable closure = new Callable() {
+//
+// public void invoke(long a1) {
+// called[0] = true;
+// val[0] = a1;
+// }
+// };
+// lib.testClosureLrV(closure, MAGIC);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", MAGIC, val[0]);
+// }
+// @Test
+// public void closureFrV() {
+// final boolean[] called = { false };
+// final float[] val = { 0 };
+// final float MAGIC = (float) 0xdeadbeef;
+// Callable closure = new Callable() {
+//
+// public void invoke(float a1) {
+// called[0] = true;
+// val[0] = a1;
+// }
+// };
+// lib.testClosureFrV(closure, MAGIC);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", MAGIC, val[0], 0f);
+// }
+// @Test
+// public void closureDrV() {
+// final boolean[] called = { false };
+// final double[] val = { 0 };
+// final double MAGIC = (double) 0xfee1deadcafebabeL;
+// Callable closure = new Callable() {
+//
+// public void invoke(double a1) {
+// called[0] = true;
+// val[0] = a1;
+// }
+// };
+// lib.testClosureDrV(closure, MAGIC);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", MAGIC, val[0], 0d);
+// }
+// @Test
+// public void closureBSBrV() {
+// final boolean[] called = { false };
+// final byte[] v1 = { 0 };
+// final short[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final byte A1 = (byte) 0x11;
+// final short A2 = (short) 0xfee1;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(byte a1, short a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureBSBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureBIBrV() {
+// final boolean[] called = { false };
+// final byte[] v1 = { 0 };
+// final int[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final byte A1 = (byte) 0x11;
+// final int A2 = (int) 0xfee1dead;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(byte a1, int a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureBIBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureBLBrV() {
+// final boolean[] called = { false };
+// final byte[] v1 = { 0 };
+// final long[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final byte A1 = (byte) 0x11;
+// final long A2 = (long) 0xfee1deadcafebabeL;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(byte a1, long a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureBLBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureBFBrV() {
+// final boolean[] called = { false };
+// final byte[] v1 = { 0 };
+// final float[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final byte A1 = (byte) 0x11;
+// final float A2 = (float) 0xfee1dead;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(byte a1, float a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureBFBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0], 0f);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureBDBrV() {
+// final boolean[] called = { false };
+// final byte[] v1 = { 0 };
+// final double[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final byte A1 = (byte) 0x11;
+// final double A2 = (double) 0xfee1deadcafebabeL;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(byte a1, double a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureBDBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0], 0d);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureSBSrV() {
+// final boolean[] called = { false };
+// final short[] v1 = { 0 };
+// final byte[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final short A1 = (short) 0x1111;
+// final byte A2 = (byte) 0xfe;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(short a1, byte a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureSBSrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureSISrV() {
+// final boolean[] called = { false };
+// final short[] v1 = { 0 };
+// final int[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final short A1 = (short) 0x1111;
+// final int A2 = (int) 0xfee1dead;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(short a1, int a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureSISrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+//
+// @Test
+// public void closureSLSrV() {
+// final boolean[] called = { false };
+// final short[] v1 = { 0 };
+// final long[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final short A1 = (short) 0x1111;
+// final long A2 = (long) 0xfee1deadcafebabeL;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(short a1, long a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureSLSrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureSFSrV() {
+// final boolean[] called = { false };
+// final short[] v1 = { 0 };
+// final float[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final short A1 = (short) 0x1111;
+// final float A2 = (float) 0xfee1dead;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(short a1, float a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureSFSrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0], 0f);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureSDSrV() {
+// final boolean[] called = { false };
+// final short[] v1 = { 0 };
+// final double[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final short A1 = (short) 0x1111;
+// final double A2 = (double) 0xfee1deadcafebabeL;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(short a1, double a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureSDSrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0], 0d);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureLSBrV() {
+// final boolean[] called = { false };
+// final long[] v1 = { 0 };
+// final short[] v2 = { 0 };
+// final byte[] v3 = { 0 };
+// final long A1 = (long) 0xfee1deadcafebabeL;
+// final short A2 = (short) 0x1111;
+// final byte A3 = (byte) 0x22;
+// Callable closure = new Callable() {
+//
+// public void invoke(long a1, short a2, byte a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureLSBrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+// @Test
+// public void closureLBSrV() {
+// final boolean[] called = { false };
+// final long[] v1 = { 0 };
+// final byte[] v2 = { 0 };
+// final short[] v3 = { 0 };
+// final long A1 = (long) 0xfee1deadcafebabeL;
+// final byte A2 = (byte) 0x11;
+// final short A3 = (short) 0x2222;
+// Callable closure = new Callable() {
+//
+// public void invoke(long a1, byte a2, short a3) {
+// called[0] = true;
+// v1[0] = a1;
+// v2[0] = a2;
+// v3[0] = a3;
+// }
+// };
+// lib.testClosureLBSrV(closure, A1, A2, A3);
+// assertTrue("Callable not called", called[0]);
+// assertEquals("Wrong value passed to closure", A1, v1[0]);
+// assertEquals("Wrong value passed to closure", A2, v2[0]);
+// assertEquals("Wrong value passed to closure", A3, v3[0]);
+// }
+}
diff --git a/src/test/java/jnr/ffi/EnumTest.java b/src/test/java/jnr/ffi/EnumTest.java
new file mode 100644
index 0000000..730ac88
--- /dev/null
+++ b/src/test/java/jnr/ffi/EnumTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.util.EnumMapper;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class EnumTest {
+
+ public EnumTest() {
+ }
+ public static enum TestEnum implements EnumMapper.IntegerEnum {
+ A(1),
+ B(2),
+ C(3),
+ Z(100);
+ TestEnum(int value) {
+ this.value = value;
+ }
+
+ public int intValue() {
+ return value;
+ }
+ private final int value;
+ }
+
+ public static enum BitField {
+ A(0x1),
+ B(0x2),
+ C(0x4),
+ D(0x8);
+
+ BitField(int value) {
+ this.value = value;
+ }
+
+ public int intValue() {
+ return value;
+ }
+ private final int value;
+ }
+
+ public static interface TestLib {
+ public int ret_int32_t(TestEnum e);
+ public int add_int32_t(TestEnum i1, TestEnum i2);
+ public int ret_int32_t(Set<TestEnum> enumSet);
+ }
+
+ public static interface ReturnEnumLib {
+ public TestEnum ret_int32_t(int e);
+ public Set<BitField> ret_int32_t(Set<BitField> bitfield);
+ public TestEnum ret_int32_t(TestEnum e);
+ public TestEnum add_int32_t(int i1, int i2);
+ public TestEnum add_int32_t(TestEnum i1, TestEnum i2);
+ }
+ static TestLib testlib;
+ static ReturnEnumLib retenum;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ retenum = TstUtil.loadTestLib(ReturnEnumLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ @Test
+ public void enumArgument() {
+ assertEquals("Wrong value returned for enum", TestEnum.Z.intValue(), testlib.ret_int32_t(TestEnum.Z));
+ assertEquals("Wrong value returned for enum", TestEnum.C.intValue(), testlib.add_int32_t(TestEnum.A, TestEnum.B));
+ }
+ @Test
+ public void returnEnum() {
+ assertEquals("Wrong value returned for enum", TestEnum.Z, retenum.ret_int32_t(TestEnum.Z.intValue()));
+ assertEquals("Wrong value returned for enum", TestEnum.C, retenum.add_int32_t(1, 2));
+ }
+
+ @Test
+ public void enumSetParameter() {
+ assertEquals(TestEnum.A.intValue() | TestEnum.B.intValue(), testlib.ret_int32_t(EnumSet.of(TestEnum.A, TestEnum.B)));
+ }
+
+ @Test
+ public void enumSetResult() {
+ EnumSet<BitField> MAGIC = EnumSet.of(BitField.A, BitField.B);
+ assertEquals(MAGIC, retenum.ret_int32_t(MAGIC));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/GlobalVariableTest.java b/src/test/java/jnr/ffi/GlobalVariableTest.java
new file mode 100644
index 0000000..4d914f4
--- /dev/null
+++ b/src/test/java/jnr/ffi/GlobalVariableTest.java
@@ -0,0 +1,71 @@
+package jnr.ffi;
+
+
+import jnr.ffi.annotations.Delegate;
+import jnr.ffi.types.u_int32_t;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class GlobalVariableTest {
+ public static interface ClosureIrV {
+ @Delegate void call(int value);
+ }
+
+ public static interface TestLib {
+ @u_int32_t Variable<Long> gvar_s32();
+ int gvar_s32_get();
+ void gvar_s32_set(int value);
+ Variable<ClosureIrV> gvar_pointer();
+ Pointer gvar_pointer_get();
+ }
+
+ @Test
+ public void testIntegerVariableSet() {
+ TestLib lib = TstUtil.loadTestLib(TestLib.class);
+ Variable<Long> var = lib.gvar_s32();
+ final long MAGIC = 0xdeadbeef;
+ var.set(MAGIC);
+ assertEquals(MAGIC, lib.gvar_s32_get());
+ }
+
+ @Test
+ public void testIntegerVariableGet() {
+ TestLib lib = TstUtil.loadTestLib(TestLib.class);
+ Variable<Long> var = lib.gvar_s32();
+ final int MAGIC = 0xdeadbeef;
+ lib.gvar_s32_set(MAGIC);
+ assertEquals(MAGIC, var.get().intValue());
+ }
+
+ @Test
+ public void testCallbackVariableSet() {
+ TestLib lib = TstUtil.loadTestLib(TestLib.class);
+ Variable<ClosureIrV> var = lib.gvar_pointer();
+ var.set(new ClosureIrV() {
+ public void call(int value) {
+ }
+ });
+ assertNotNull(lib.gvar_pointer_get());
+ }
+
+// @Test public void testCallbackVariableGet() {
+// TestLib lib = TstUtil.loadTestLib(TestLib.class);
+// Variable<ClosureIrV> var = lib.gvar_pointer();
+// final boolean[] called = { false };
+// final int[] values = { 0 };
+//
+// var.set(new ClosureIrV() {
+// public void call(int value) {
+// called[0] = true;
+// values[0] = value;
+// }
+// });
+// assertNotNull(lib.gvar_pointer_get());
+// assertNotNull(var.get());
+// var.get().call(0xdeadbeef);
+// assertTrue(called[0]);
+// assertEquals(0xdeadbeef, values[0]);
+// }
+}
diff --git a/src/test/java/jnr/ffi/InvocationTest.java b/src/test/java/jnr/ffi/InvocationTest.java
new file mode 100644
index 0000000..04f10a8
--- /dev/null
+++ b/src/test/java/jnr/ffi/InvocationTest.java
@@ -0,0 +1,29 @@
+package jnr.ffi;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ */
+public class InvocationTest {
+ public static interface TestLib {
+ int ret_int32_t(int i);
+ }
+
+ static TestLib testlib;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @Test
+ public void hammer() throws Throwable {
+ for (int i = 0; i < 1000000; i++) {
+ assertEquals(i, testlib.ret_int32_t(i));
+ }
+ }
+}
diff --git a/src/test/java/jnr/ffi/LastErrorTest.java b/src/test/java/jnr/ffi/LastErrorTest.java
new file mode 100644
index 0000000..2a09da8
--- /dev/null
+++ b/src/test/java/jnr/ffi/LastErrorTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.SaveError;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class LastErrorTest {
+ public static interface TestLib {
+ @SaveError
+ int setLastError(int error);
+ }
+
+ public LastErrorTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test public void testLastError() {
+ TestLib lib = TstUtil.loadTestLib(TestLib.class);
+ Runtime runtime = Runtime.getRuntime(lib);
+
+ final int MAGIC = 0xdeadbeef;
+ lib.setLastError(MAGIC);
+ assertEquals("Wrong errno value", MAGIC, runtime.getLastError());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/LibraryLoaderTest.java b/src/test/java/jnr/ffi/LibraryLoaderTest.java
new file mode 100644
index 0000000..1682a91
--- /dev/null
+++ b/src/test/java/jnr/ffi/LibraryLoaderTest.java
@@ -0,0 +1,33 @@
+package jnr.ffi;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class LibraryLoaderTest {
+ public static interface TestLib {
+ int setLastError(int error);
+ }
+
+ @Test public void loadShouldNotThrowExceptions() {
+ try {
+ LibraryLoader.create(TestLib.class).load("non-existant-library");
+ } catch (Throwable t) {
+ fail("load raised exception " + t);
+ }
+ }
+
+ @Test(expected = UnsatisfiedLinkError.class)
+ public void failImmediatelyShouldThrowULE() {
+ LibraryLoader.create(TestLib.class).failImmediately().load("non-existant-library");
+ }
+
+ @Test(expected = UnsatisfiedLinkError.class)
+ public void invocationOnFailedLoadShouldThrowULE() {
+ TestLib lib = LibraryLoader.create(TestLib.class).failImmediately().load("non-existant-library");
+ lib.setLastError(0);
+ }
+}
diff --git a/src/test/java/jnr/ffi/LibraryTest.java b/src/test/java/jnr/ffi/LibraryTest.java
new file mode 100644
index 0000000..36253e1
--- /dev/null
+++ b/src/test/java/jnr/ffi/LibraryTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Test library locating/loading
+ */
+public class LibraryTest {
+
+ public LibraryTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ public static interface TestLib {
+ int setLastError(int error);
+ }
+ @Test public void loadTestLib() {
+ TestLib lib = TstUtil.loadTestLib(TestLib.class);
+ assertNotNull("Could not load libtest", lib);
+ // This just forces the library to really load and call a function
+ lib.setLastError(0);
+ }
+
+ @Test(expected = UnsatisfiedLinkError.class)
+ public void badLibrarynameShouldThrowULE() {
+ Library.loadLibrary(TestLib.class, "non-existant-library");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/MemoryIOTest.java b/src/test/java/jnr/ffi/MemoryIOTest.java
new file mode 100644
index 0000000..75b6fce
--- /dev/null
+++ b/src/test/java/jnr/ffi/MemoryIOTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import java.nio.ByteBuffer;
+
+import jnr.ffi.annotations.LongLong;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+/**
+ *
+ * @author wayne
+ */
+public class MemoryIOTest {
+ public static interface TestLib {
+ byte ptr_ret_int8_t(Pointer p, int offset);
+ short ptr_ret_int16_t(Pointer p, int offset);
+ int ptr_ret_int32_t(Pointer p, int offset);
+ @LongLong long ptr_ret_int64_t(Pointer p, int offset);
+ float ptr_ret_float(Pointer p, int offset);
+ double ptr_ret_double(Pointer p, int offset);
+
+ void ptr_set_int8_t(Pointer p, int offset, byte value);
+ void ptr_set_int16_t(Pointer p, int offset, short value);
+ void ptr_set_int32_t(Pointer p, int offset, int value);
+ void ptr_set_int64_t(Pointer p, int offset, @LongLong long value);
+ void ptr_set_float(Pointer p, int offset, float value);
+ void ptr_set_double(Pointer p, int offset, double value);
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+
+ public MemoryIOTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ private Pointer direct(int size) {
+ return Memory.allocateDirect(runtime, size);
+ }
+
+ private Pointer heap(int size) {
+ return Memory.allocate(runtime, size);
+ }
+
+ private Pointer buffer(int size) {
+ return wrap(ByteBuffer.allocate(size).order(runtime.byteOrder()));
+ }
+
+ private static Pointer wrap(ByteBuffer buffer) {
+ return Pointer.wrap(runtime, buffer);
+ }
+ private static Pointer allocateDirect(int size) {
+ return Memory.allocateDirect(runtime, size);
+ }
+ private void testPutByte(Pointer io, int size) {
+ for (int i = 0; i < size; ++i) {
+ io.putByte(i, (byte) (i + 5));
+ assertEquals("Incorrect value at offset " + i, (byte) (i + 5), testlib.ptr_ret_int8_t(io, i));
+ }
+ }
+ private void testGetByte(Pointer io, int size) {
+ for (int i = 0; i < size; ++i) {
+ testlib.ptr_set_int8_t(io, i, (byte) (i + 5));
+ assertEquals("Incorrect value at offset " + i, (byte) (i + 5), io.getByte(i));
+ }
+ }
+ private void testPutShort(Pointer io, int size) {
+ for (int i = 0; i <= size - 2; ++i) {
+ io.putShort(i, (short) i);
+ assertEquals("Incorrect value at offset " + i, (short) i, testlib.ptr_ret_int16_t(io, i));
+ }
+ }
+ private void testGetShort(Pointer io, int size) {
+ for (int i = 0; i <= size - 2; ++i) {
+ testlib.ptr_set_int16_t(io, i, (short) i);
+ assertEquals("Incorrect value at offset " + i, (short) i, io.getShort(i));
+ }
+ }
+ private void testPutInt(Pointer io, int size) {
+ for (int i = 0; i <= size - 4; ++i) {
+ io.putInt(i, i);
+ assertEquals("Incorrect value at offset " + i, i, testlib.ptr_ret_int32_t(io, i));
+ }
+ }
+ private void testGetInt(Pointer io, int size) {
+ for (int i = 0; i <= size - 4; ++i) {
+ testlib.ptr_set_int32_t(io, i, i);
+ assertEquals("Incorrect value at offset " + i, i, io.getInt(i));
+ }
+ }
+ private void testPutLongLong(Pointer io, int size) {
+ for (int i = 0; i <= size - 8; ++i) {
+ io.putLongLong(i, i);
+ assertEquals("Incorrect value at offset " + i, (long) i, testlib.ptr_ret_int64_t(io, i));
+ }
+ }
+ private void testGetLongLong(Pointer io, int size) {
+ for (int i = 0; i <= size - 8; ++i) {
+ testlib.ptr_set_int64_t(io, i, i);
+ assertEquals("Incorrect value at offset " + i, (long) i, io.getLongLong(i));
+ }
+ }
+ private void testPutFloat(Pointer io, int size) {
+ for (int i = 0; i <= size - (Float.SIZE / 8); ++i) {
+ io.putFloat(i, i);
+ assertEquals("Incorrect value at offset " + i, (float) i, testlib.ptr_ret_float(io, i), 0.00001);
+ }
+ }
+ private void testGetFloat(Pointer io, int size) {
+ for (int i = 0; i <= size - (Float.SIZE / 8); ++i) {
+ testlib.ptr_set_float(io, i, (float) i);
+ assertEquals("Incorrect value at offset " + i, (float) i, io.getFloat(i), 0.00001);
+ }
+ }
+ private void testPutDouble(Pointer io, int size) {
+ for (int i = 0; i <= size - (Double.SIZE / 8); ++i) {
+ io.putDouble(i, (double) i);
+ assertEquals("Incorrect value at offset " + i, (double) i, testlib.ptr_ret_double(io, i), 0d);
+ }
+ }
+ private void testGetDouble(Pointer io, int size) {
+ for (int i = 0; i <= size - (Double.SIZE / 8); ++i) {
+ testlib.ptr_set_double(io, i, (double) i);
+ assertEquals("Incorrect value at offset " + i, (double) i, io.getDouble(i), 0d);
+ }
+ }
+
+
+
+ @Test public void testHeapMemoryIOPutByte() {
+ final int SIZE = 16;
+ testPutByte(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOGetByte() {
+ final int SIZE = 16;
+ testGetByte(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOPutShort() {
+ final int SIZE = 16;
+ testPutShort(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOGetShort() {
+ final int SIZE = 16;
+ testGetShort(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOPutInt() {
+ final int SIZE = 16;
+ testPutInt(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOGetInt() {
+ final int SIZE = 16;
+ testGetInt(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOPutLong() {
+ final int SIZE = 16;
+ Pointer memory = heap(SIZE);
+ testPutLongLong(memory, SIZE);
+ }
+
+ @Test public void testHeapMemoryIOGetLongLong() {
+ final int SIZE = 16;
+ testGetLongLong(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOPutFloat() {
+ final int SIZE = 16;
+ Pointer memory = heap(SIZE);
+ testPutFloat(memory, SIZE);
+
+ }
+
+ @Test public void testHeapMemoryIOGetFloat() {
+ final int SIZE = 16;
+ testGetFloat(heap(SIZE), SIZE);
+ }
+
+ @Test public void testHeapMemoryIOPutDouble() {
+ final int SIZE = 16;
+ Pointer memory = heap(SIZE);
+ testPutDouble(memory, SIZE);
+ }
+
+ @Test public void testHeapMemoryIOGetDouble() {
+ final int SIZE = 16;
+ testGetDouble(heap(SIZE), SIZE);
+ }
+ @Test
+ public void testNegativeBoundedIO() {
+ final int SIZE = 16;
+ Pointer memio = allocateDirect(SIZE);
+ try {
+ memio.putByte(-1, (byte) 0);
+ fail("Should have thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+
+ }
+ }
+ @Test
+ public void testOverflowBoundedIO() {
+ final int SIZE = 16;
+ Pointer memio = allocateDirect(SIZE);
+ try {
+ memio.putByte(16, (byte) 0);
+ fail("Should have thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+
+ }
+ }
+
+
+ @Test public void testDirectMemoryIOPutByte() {
+ final int SIZE = 16;
+ testPutByte(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOGetByte() {
+ final int SIZE = 16;
+ testGetByte(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOPutShort() {
+ final int SIZE = 16;
+ testPutShort(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOGetShort() {
+ final int SIZE = 16;
+ testGetShort(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOPutInt() {
+ final int SIZE = 16;
+ testPutInt(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOGetInt() {
+ final int SIZE = 16;
+ testGetInt(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOPutLongLong() {
+ final int SIZE = 16;
+ testPutLongLong(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOGetLongLong() {
+ final int SIZE = 16;
+ testGetLongLong(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOPutFloat() {
+ final int SIZE = 16;
+ testPutFloat(direct(SIZE), SIZE);
+
+ }
+ @Test public void testDirectMemoryIOGetFloat() {
+ final int SIZE = 16;
+ testGetFloat(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOPutDouble() {
+ final int SIZE = 16;
+ testPutDouble(direct(SIZE), SIZE);
+ }
+ @Test public void testDirectMemoryIOGetDouble() {
+ final int SIZE = 16;
+ testGetDouble(direct(SIZE), SIZE);
+ }
+
+
+ @Test public void testBufferIOPutByte() {
+ final int SIZE = 16;
+ testPutByte(buffer(SIZE), SIZE);
+ }
+
+ @Test public void testBufferIOGetByte() {
+ final int SIZE = 16;
+ testGetByte(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOPutShort() {
+ final int SIZE = 16;
+ testPutShort(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOGetShort() {
+ final int SIZE = 16;
+ testGetShort(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOPutInt() {
+ final int SIZE = 16;
+ testPutInt(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOGetInt() {
+ final int SIZE = 16;
+ testGetInt(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOPutLongLong() {
+ final int SIZE = 16;
+ testPutLongLong(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOGetLongLong() {
+ final int SIZE = 16;
+ testGetLongLong(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOPutFloat() {
+ final int SIZE = 16;
+ testPutFloat(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOGetFloat() {
+ final int SIZE = 16;
+ testGetFloat(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOPutDouble() {
+ final int SIZE = 16;
+ testPutDouble(buffer(SIZE), SIZE);
+ }
+ @Test public void testBufferIOGetDouble() {
+ final int SIZE = 16;
+ testGetDouble(buffer(SIZE), SIZE);
+ }
+ @Test
+ public void testNegativeBufferIO() {
+ final int SIZE = 16;
+ Pointer memio = wrap(ByteBuffer.allocate(SIZE));
+ try {
+ memio.putByte(-1, (byte) 0);
+ fail("Should have thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+
+ }
+ }
+ @Test
+ public void testOverflowBufferIO() {
+ final int SIZE = 16;
+ Pointer memio = wrap(ByteBuffer.allocate(SIZE));
+ try {
+ memio.putByte(16, (byte) 0);
+ fail("Should have thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+
+ }
+ }
+ @Test public void transferDirectToHeap() throws Exception {
+ ByteBuffer buf = ByteBuffer.allocate(1024);
+ Pointer dst = Pointer.wrap(runtime, buf);
+ Pointer src = Memory.allocateDirect(runtime, 1024);
+ byte[] MAGIC = "MAGIC".getBytes();
+ src.put(0, MAGIC, 0, MAGIC.length);
+ src.transferTo(0, dst, 0, MAGIC.length);
+ for (int i = 0; i < MAGIC.length; ++i) {
+ assertEquals("Wrong byte at index " + i, MAGIC[i], dst.getByte(i));
+ }
+ for (int i = 0; i < MAGIC.length; ++i) {
+ assertEquals("Wrong byte at index " + i, MAGIC[i], buf.get(i));
+ }
+ }
+ @Test public void transferDirectToDirect() throws Exception {
+ Pointer dst = Memory.allocateDirect(runtime, 1024);
+ Pointer src = Memory.allocateDirect(runtime, 1024);
+ final byte[] MAGIC = "MAGIC".getBytes();
+ final int SRCOFF = 100;
+ final int DSTOFF = 123;
+ src.put(SRCOFF, MAGIC, 0, MAGIC.length);
+ src.transferTo(SRCOFF, dst, DSTOFF, MAGIC.length);
+ for (int i = 0; i < MAGIC.length; ++i) {
+ assertEquals("Wrong byte at index " + i, MAGIC[i], dst.getByte(DSTOFF + i));
+ }
+ }
+
+ @Test public void manyTransientAllocations() {
+ for (int i = 0; i < 100000; i++) {
+ Memory.allocate(runtime, 4);
+ }
+ }
+}
diff --git a/src/test/java/jnr/ffi/NumberTest.java b/src/test/java/jnr/ffi/NumberTest.java
new file mode 100644
index 0000000..8a41872
--- /dev/null
+++ b/src/test/java/jnr/ffi/NumberTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import java.util.Random;
+
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.types.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class NumberTest {
+
+ public NumberTest() {
+ }
+
+ public static interface TestLib {
+
+ public byte add_int8_t(byte i1, byte i2);
+
+ public short add_int16_t(short i1, short i2);
+ public short add_int16_t(Short i1, short i2);
+
+ public int add_int32_t(int i1, int i2);
+ public @LongLong long add_int64_t(@LongLong long i1, @LongLong long i2);
+ public long add_long(long i1, long i2);
+ public Long add_long(Long i1, Long i2);
+ public NativeLong add_long(NativeLong i1, NativeLong i2);
+ public NativeLong sub_long(NativeLong i1, NativeLong i2);
+ public NativeLong mul_long(NativeLong i1, NativeLong i2);
+ public NativeLong div_long(NativeLong i1, NativeLong i2);
+ public float add_float(float f1, float f2);
+ public float sub_float(float f1, float f2);
+ public float mul_float(float f1, float f2);
+ public float div_float(float f1, float f2);
+ public double add_double(double f1, double f2);
+ public double sub_double(double f1, double f2);
+ public double mul_double(double f1, double f2);
+ public double div_double(double f1, double f2);
+ public @int32_t long ret_int32_t(@int32_t long l);
+ public @u_int32_t long ret_uint32_t(@u_int32_t long l);
+ public @pid_t int ret_int32_t(@pid_t int l);
+ }
+ static TestLib testlib;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testByteAddition() throws Exception {
+ for (int i = 0; i <= 255; ++i) {
+ byte i1 = (byte) i;
+ byte i2 = (byte) 0xde;
+ assertEquals("byte addition failed", (byte) (i1 + i2), testlib.add_int8_t(i1, i2));
+ }
+ }
+ @Test
+ public void testShortAddition() throws Exception {
+ for (int i = 0; i <= 0xffff; ++i) {
+ short i1 = (short) i;
+ short i2 = (short) 0xdead;
+ assertEquals("byte addition failed", (short) (i1 + i2), testlib.add_int16_t(i1, i2));
+ }
+ }
+ static interface FloatOp {
+ public float j(float f1, float f2);
+ public float n(float f1, float f2);
+ }
+ private void testFloat(FloatOp op) throws Exception {
+ float f1 = 1.0f;
+ float f2 = (float) 0xdeadbeef;
+ assertEquals("float " + op + " failed", op.j(f1, f2), op.n(f1, f2), 0.001);
+ for (int i = 0; i < 0xffff; ++i) {
+ f1 = (float) i;
+ assertEquals("float + " + op + " failed", op.j(f1, f2), op.n(f1, f2), 0.001);
+ }
+ Random random = new Random();
+ for (int i = 0; i < 0xffff; ++i) {
+ f1 = random.nextFloat();
+ f2 = random.nextFloat();
+ float expected = op.j(f1, f2);
+ float result = op.n(f1, f2);
+ if (expected != result) {
+ fail(String.format("float " + op + "(%f, %f) failed - expected: %f, received: %f",
+ f1, f2, expected, result));
+ }
+ }
+ }
+ @Test
+ public void testFloatAddition() throws Exception {
+ testFloat(new FloatOp() {
+
+ public float j(float f1, float f2) {
+ return f1 + f2;
+ }
+
+ public float n(float f1, float f2) {
+ return testlib.add_float(f1, f2);
+ }
+ @Override
+ public String toString() { return "add"; }
+ });
+ }
+ @Test
+ public void testFloatSubtraction() throws Exception {
+ testFloat(new FloatOp() {
+
+ public float j(float f1, float f2) {
+ return f1 - f2;
+ }
+
+ public float n(float f1, float f2) {
+ return testlib.sub_float(f1, f2);
+ }
+ @Override
+ public String toString() { return "subtract"; }
+ });
+ }
+ @Test
+ public void testFloatMultiplication() throws Exception {
+ testFloat(new FloatOp() {
+
+ public float j(float f1, float f2) {
+ return f1 * f2;
+ }
+
+ public float n(float f1, float f2) {
+ return testlib.mul_float(f1, f2);
+ }
+ public String toString() { return "multiply"; }
+ });
+
+ }
+
+ @Test
+ public void testFloatDivision() throws Exception {
+ testFloat(new FloatOp() {
+
+ public float j(float f1, float f2) {
+ return f1 / f2;
+ }
+
+ public float n(float f1, float f2) {
+ return testlib.div_float(f1, f2);
+ }
+ @Override
+ public String toString() { return "divide"; }
+ });
+
+ }
+ static interface DoubleOp {
+ public double j(double f1, double f2);
+ public double n(double f1, double f2);
+ }
+ private void testDouble(DoubleOp op) throws Exception {
+ double f1 = 1.0f;
+ double f2 = (double) 0xdeadbeef;
+ assertEquals("double " + op + " failed", op.j(f1, f2), op.n(f1, f2), 0.001);
+ for (int i = 0; i < 0xffff; ++i) {
+ f1 = (float) i;
+ assertEquals("double + " + op + " failed", op.j(f1, f2), op.n(f1, f2), 0.001);
+ }
+ Random random = new Random();
+ for (int i = 0; i < 0xffff; ++i) {
+ f1 = random.nextFloat();
+ f2 = random.nextFloat();
+ double expected = op.j(f1, f2);
+ double result = op.n(f1, f2);
+ if (expected != result) {
+ fail(String.format("double " + op + "(%f, %f) failed - expected: %f, received: %f",
+ f1, f2, expected, result));
+ }
+ }
+ }
+ @Test
+ public void testDoubleAddition() throws Exception {
+ testDouble(new DoubleOp() {
+
+ public double j(double f1, double f2) {
+ return f1 + f2;
+ }
+
+ public double n(double f1, double f2) {
+ return testlib.add_double(f1, f2);
+ }
+ @Override
+ public String toString() { return "add"; }
+ });
+ }
+ @Test
+ public void testDoubleSubtraction() throws Exception {
+ testDouble(new DoubleOp() {
+
+ public double j(double f1, double f2) {
+ return f1 - f2;
+ }
+
+ public double n(double f1, double f2) {
+ return testlib.sub_double(f1, f2);
+ }
+ @Override
+ public String toString() { return "subtract"; }
+ });
+ }
+ @Test
+ public void testDoubleMultiplication() throws Exception {
+ testDouble(new DoubleOp() {
+
+ public double j(double f1, double f2) {
+ return f1 * f2;
+ }
+
+ public double n(double f1, double f2) {
+ return testlib.mul_double(f1, f2);
+ }
+ @Override
+ public String toString() { return "multiply"; }
+ });
+
+ }
+ @Test
+ public void testDoubleDivision() throws Exception {
+ testDouble(new DoubleOp() {
+
+ public double j(double f1, double f2) {
+ return f1 / f2;
+ }
+
+ public double n(double f1, double f2) {
+ return testlib.div_double(f1, f2);
+ }
+ @Override
+ public String toString() { return "divide"; }
+ });
+
+ }
+ static interface NativeLongOp {
+ public long j(long f1, long f2);
+ public long n(long f1, long f2);
+ }
+ private void testNativeLong(NativeLongOp op) throws Exception {
+ long i1 = 1;
+ long i2 = 2;
+ assertEquals("NativeLong " + op + " failed", op.j(i1, i2), op.n(i1, i2));
+ for (int i = 0; i < 0xffff; ++i) {
+ assertEquals("NativeLong + " + op + " failed", op.j(i, i2), op.n(i, i2));
+ }
+ }
+ @Test
+ public void NativeLong_valueOf() {
+ for (int i = -1000; i < 1000; ++i) {
+ assertEquals("Incorrect value from valueOf(" + i+ ")", i, NativeLong.valueOf(i).intValue());
+ }
+ for (long i = -1000; i < 1000; ++i) {
+ assertEquals("Incorrect value from valueOf(" + i+ ")", i, NativeLong.valueOf(i).longValue());
+ }
+ }
+
+ @Test
+ public void testNativeLongAddition() throws Exception {
+ testNativeLong(new NativeLongOp() {
+
+ public long j(long i1, long i2) {
+ return i1 + i2;
+ }
+
+ public long n(long i1, long i2) {
+ return testlib.add_long(NativeLong.valueOf(i1), NativeLong.valueOf(i2)).longValue();
+ }
+ });
+ }
+
+ @Test
+ public void testPrimitiveLongAddition() throws Exception {
+ testNativeLong(new NativeLongOp() {
+
+ public long j(long i1, long i2) {
+ return i1 + i2;
+ }
+
+ public long n(long i1, long i2) {
+ return testlib.add_long(i1, i2);
+ }
+ });
+ }
+
+ @Test
+ public void testBoxedLongAddition() throws Exception {
+ testNativeLong(new NativeLongOp() {
+
+ public long j(long i1, long i2) {
+ return i1 + i2;
+ }
+
+ public long n(long i1, long i2) {
+ return testlib.add_long(new Long(i1), new Long(i2));
+ }
+ });
+ }
+
+ @Test public void testSignExtension() throws Exception {
+ assertEquals("upper 32 bits not set to 1", 0xffffffffdeadbeefL, testlib.ret_int32_t(0x1eefdeadbeefL));
+ }
+
+ @Test public void testZeroExtension() throws Exception {
+ assertEquals("upper 32 bits not set to zero", 0xdeadbeefL, testlib.ret_uint32_t(0xfee1deadbeefL));
+ }
+}
diff --git a/src/test/java/jnr/ffi/ObjectReferenceManagerTest.java b/src/test/java/jnr/ffi/ObjectReferenceManagerTest.java
new file mode 100644
index 0000000..b3f9077
--- /dev/null
+++ b/src/test/java/jnr/ffi/ObjectReferenceManagerTest.java
@@ -0,0 +1,42 @@
+package jnr.ffi;
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ObjectReferenceManagerTest {
+
+ @Test public void sameObjectReturned() {
+ ObjectReferenceManager<String> referenceManager = ObjectReferenceManager.newInstance(Runtime.getSystemRuntime());
+ String bar = "bar";
+ Pointer ptr = referenceManager.add(bar);
+ assertSame(bar, referenceManager.get(ptr));
+ }
+
+ @Test public void differentPointerReturnedForSameObject() {
+ ObjectReferenceManager<String> referenceManager = ObjectReferenceManager.newInstance(Runtime.getSystemRuntime());
+ String bar = "bar";
+ Pointer ptr = referenceManager.add(bar);
+ Pointer ptr2 = referenceManager.add(bar);
+ assertSame(bar, referenceManager.get(ptr));
+ assertSame(bar, referenceManager.get(ptr2));
+ assertNotSame(ptr, ptr2);
+ }
+
+ @Test public void remove() {
+ ObjectReferenceManager<String> referenceManager = ObjectReferenceManager.newInstance(Runtime.getSystemRuntime());
+ assertTrue(referenceManager.remove(referenceManager.add("bar")));
+ }
+
+ @Test public void referenceEqualityOnly() {
+ ObjectReferenceManager<String> referenceManager = ObjectReferenceManager.newInstance(Runtime.getSystemRuntime());
+ String bar = "bar";
+ String bar2 = new String("bar");
+ Pointer ptr = referenceManager.add(bar);
+ Pointer ptr2 = referenceManager.add(bar2);
+ assertNotSame(ptr, ptr2);
+ assertNotEquals(ptr, ptr2);
+ assertSame(bar, referenceManager.get(ptr));
+ assertSame(bar2, referenceManager.get(ptr2));
+ }
+}
diff --git a/src/test/java/jnr/ffi/PointerTest.java b/src/test/java/jnr/ffi/PointerTest.java
new file mode 100644
index 0000000..ac6afbf
--- /dev/null
+++ b/src/test/java/jnr/ffi/PointerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.*;
+import jnr.ffi.types.int32_t;
+import jnr.ffi.types.int8_t;
+import jnr.ffi.types.size_t;
+import org.junit.*;
+
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ */
+public class PointerTest {
+
+ public PointerTest() {
+ }
+ public static interface TestLib {
+ Pointer ptr_return_array_element(@In Pointer[] array, int index);
+ void ptr_set_array_element(@Out Pointer[] array, int index, Pointer value);
+ byte ptr_ret_int8_t(Pointer p, int offset);
+ byte ptr_ret_int8_t(Address p, int offset);
+ short ptr_ret_int16_t(Pointer p, int offset);
+ int ptr_ret_int32_t(Pointer p, int offset);
+ @LongLong long ptr_ret_int64_t(Pointer p, int offset);
+ float ptr_ret_float(Pointer p, int offset);
+ double ptr_ret_double(Pointer p, int offset);
+ void ptr_set_int8_t(Pointer p, int offset, @int8_t byte value);
+ void ptr_set_int16_t(Pointer p, int offset, short value);
+ void ptr_set_int32_t(Pointer p, int offset, @int32_t int value);
+ void ptr_set_int32_t(Pointer p, int offset, @int32_t long value);
+ void ptr_set_int64_t(Pointer p, int offset, @LongLong long value);
+ void ptr_set_float(Pointer p, int offset, float value);
+ void ptr_set_double(Pointer p, int offset, double value);
+ void ptr_reverse_l5(Pointer p1, Pointer p2, Pointer p3, Pointer p4, Pointer p5);
+ void ptr_reverse_l6(Pointer p1, Pointer p2, Pointer p3, Pointer p4, Pointer p5, Pointer p6);
+
+ public static final class Foo extends Struct {
+ public final UnsignedLong l1 = new UnsignedLong();
+ public final UnsignedLong l2 = new UnsignedLong();
+ public final UnsignedLong l3 = new UnsignedLong();
+
+ public Foo(Runtime runtime) {
+ super(runtime);
+ }
+ }
+ int fill_struct_from_longs(@size_t long l1, @size_t long l2, @Out Foo[] foo, @size_t long l3);
+
+ Pointer ptr_malloc(@size_t int size);
+ void ptr_free(Pointer ptr);
+ }
+ static TestLib testlib;
+ static Runtime runtime;
+ public static interface Libc {
+ Pointer calloc(int nmemb, int size);
+ Pointer malloc(int size);
+ void free(Pointer ptr);
+ void cfree(Pointer ptr);
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+// libc = Library.loadLibrary("c", Libc.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+// @Test
+// public void testGetPointerArrayArgument() throws Exception {
+//
+// Pointer MAGIC0 = new Pointer(0xdeadbeef);
+// Pointer MAGIC1 = new Pointer(0xcafebabe);
+// Pointer[] array = { MAGIC0, MAGIC1 };
+//
+// assertEquals("Incorrect Pointer at index 0", MAGIC0,
+// testlib.ptr_return_array_element(array, 0));
+// assertEquals("Incorrect Pointer at index 1", MAGIC1,
+// testlib.ptr_return_array_element(array, 1));
+// }
+// @Test
+// public void testSetPointerArrayArgument() throws Exception {
+//
+// Pointer MAGIC0 = new Pointer(0xdeadbeef);
+// Pointer MAGIC1 = new Pointer(0xcafebabe);
+// Pointer[] array = { MAGIC0, MAGIC1 };
+//
+// testlib.ptr_set_array_element(array, 0, MAGIC1);
+// testlib.ptr_set_array_element(array, 1, MAGIC0);
+// assertEquals("Pointer at index 0 not set", MAGIC1, array[0]);
+// assertEquals("Pointer at index 1 not set", MAGIC0, array[1]);
+// }
+//
+// @Test
+// public void testLongPointerValue() throws Exception {
+// long MAGIC0 = 0xdeadbeefL | (Address.SIZE == 64 ? (0xfee1deadL << 32) : 0L);
+// assertEquals("Pointer value not equal", MAGIC0, new Pointer(MAGIC0).nativeAddress());
+// }
+ static final int SIZE = 128;
+ @Test
+ public void testPointerSetByte() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ byte MAGIC = (byte) 0xFE;
+ for (int i = 0; i < SIZE; ++i) {
+ p.putByte(i, MAGIC);
+ assertEquals("Byte not set at offset " + i, MAGIC, testlib.ptr_ret_int8_t(p, i));
+ }
+ }
+ @Test
+ public void testPointerSetShort() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ short MAGIC = (short) 0xFEE1;
+ for (int i = 0; i < (SIZE - 1); ++i) {
+ p.putShort(i, MAGIC);
+ assertEquals("Short not set at offset " + i, MAGIC, testlib.ptr_ret_int16_t(p, i));
+ }
+ }
+ @Test
+ public void testPointerSetInt() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ int MAGIC = (int) 0xFEE1DEAD;
+ for (int i = 0; i < (SIZE - 3); ++i) {
+ p.putInt(i, MAGIC);
+ assertEquals("Integer not set at offset " + i, MAGIC, testlib.ptr_ret_int32_t(p, i));
+ }
+ }
+ @Test
+ public void testPointerSetLongLong() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ long MAGIC = 0xFEE1DEADABCDEF12L;
+ final long l = MAGIC;
+ byte[] bytes = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? new byte[]{
+ (byte) (l >>> 56), (byte) (l >>> 48), (byte) (l >>> 40),
+ (byte) (l >>> 32), (byte) (l >>> 24), (byte) (l >>> 16),
+ (byte) (l >>> 8), (byte) (l >>> 0)
+ }
+ : new byte[]{
+ (byte) (l >>> 0), (byte) (l >>> 8), (byte) (l >>> 16),
+ (byte) (l >>> 24), (byte) (l >>> 32), (byte) (l >>> 40),
+ (byte) (l >>> 48), (byte) (l >>> 56)
+ };
+
+ for (int i = 0; i < (SIZE - 7); ++i) {
+ p.putLongLong(i, MAGIC);
+ for (int idx = 0; idx < 8; ++idx) {
+ assertEquals("incorrect byte value at offset= " + i + " idx=" + idx, bytes[idx], p.getByte(i + idx));
+ }
+ assertEquals("Long not set at offset " + i, MAGIC, testlib.ptr_ret_int64_t(p, i));
+ }
+ }
+ @Test
+ public void testPointerSetFloat() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ float MAGIC = (float) 0xFEE1DEADABCDEF12L;
+ for (int i = 0; i < (SIZE - 7); ++i) {
+ p.putFloat(i, MAGIC);
+ assertEquals("Float not set at offset " + i, MAGIC, testlib.ptr_ret_float(p, i), 0.00001);
+ }
+ }
+ @Test
+ public void testPointerSetDouble() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ double MAGIC = (double) 0xFEE1DEADABCDEF12L;
+
+ long l = Double.doubleToRawLongBits(MAGIC);
+ byte[] bytes = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? new byte[]{
+ (byte) (l >>> 56), (byte) (l >>> 48), (byte) (l >>> 40),
+ (byte) (l >>> 32), (byte) (l >>> 24), (byte) (l >>> 16),
+ (byte) (l >>> 8), (byte) (l >>> 0)
+ }
+ : new byte[]{
+ (byte) (l >>> 0), (byte) (l >>> 8), (byte) (l >>> 16),
+ (byte) (l >>> 24), (byte) (l >>> 32), (byte) (l >>> 40),
+ (byte) (l >>> 48), (byte) (l >>> 56)
+ };
+
+ p.putDouble(0, MAGIC);
+ for (int i = 0; i < 8; ++i) {
+ assertEquals("incorrect byte value at idx=" + i, bytes[i], p.getByte(i));
+ }
+ for (int i = 0; i < (SIZE - 7); ++i) {
+ p.putDouble(i, MAGIC);
+ assertEquals("Double not set at offset " + i, MAGIC, testlib.ptr_ret_double(p, i), 0.0001E16);
+ }
+ }
+ @Test
+ public void testPointerGetByte() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ byte MAGIC = (byte) 0xFE;
+ for (int i = 0; i < SIZE; ++i) {
+ testlib.ptr_set_int8_t(p, i, MAGIC);
+ assertEquals("Byte not set at offset " + i, MAGIC, p.getByte(i));
+ }
+ }
+ @Test
+ public void testPointerGetShort() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ short MAGIC = (short) 0xFEE1;
+ for (int i = 0; i < SIZE - 1; ++i) {
+ testlib.ptr_set_int16_t(p, i, MAGIC);
+ assertEquals("Short not set at offset " + i, MAGIC, p.getShort(i));
+ }
+ }
+ @Test
+ public void testPointerGetInt() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ int MAGIC = (int) 0xFEE1DEAD;
+ for (int i = 0; i < SIZE - 3; ++i) {
+ testlib.ptr_set_int32_t(p, i, MAGIC);
+ assertEquals("Integer not set at offset " + i, MAGIC, p.getInt(i));
+ }
+ }
+ @Test
+ public void testPointerGetLongLong() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ long MAGIC = 0xFEE1DEADABCDEF12L;
+ for (int i = 0; i < SIZE - 7; ++i) {
+ testlib.ptr_set_int64_t(p, i, MAGIC);
+ assertEquals("Long not set at offset " + i, MAGIC, p.getLongLong(i));
+ }
+ }
+ @Test
+ public void testPointerGetFloat() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ float MAGIC = (float) 0xFEE1DEADABCDEF12L;
+ for (int i = 0; i < (SIZE - 7); ++i) {
+ testlib.ptr_set_float(p, i, MAGIC);
+ assertEquals("Float not set at offset " + i, MAGIC, p.getFloat(i), 0.0001);
+ }
+ }
+ @Test
+ public void testPointerGetDouble() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ double MAGIC = (double) 0xFEE1DEADABCDEF12L;
+ for (int i = 0; i < (SIZE - 7); ++i) {
+ testlib.ptr_set_double(p, i, MAGIC);
+ assertEquals("Double not set at offset " + i, MAGIC, p.getDouble(i), 0.00001);
+ }
+ }
+ @Test
+ public void testMalloc() {
+ Pointer[] pointers = new Pointer[1024];
+ for (int i = 0; i < pointers.length; ++i) {
+ pointers[i] = testlib.ptr_malloc(SIZE);
+ }
+ for (int i = 0; i < pointers.length; ++i) {
+ testlib.ptr_free(pointers[i]);
+ }
+ }
+
+ @Test
+ public void testP5() {
+ Pointer[] p = new Pointer[5];
+ long[] v = { 1, 2, 3, 4, 5 };
+ for (int i = 0; i < p.length; ++i) {
+ if ((i % 2) == 0) {
+ p[i] = Memory.allocate(Runtime.getRuntime(testlib), 8);
+ } else {
+ p[i] = Memory.allocateDirect(Runtime.getRuntime(testlib), 8);
+ }
+ p[i].putLongLong(0, v[i]);
+ }
+ testlib.ptr_reverse_l5(p[0], p[1], p[2], p[3], p[4]);
+ for (int i = 0; i < p.length; ++i) {
+ assertEquals("not same value for pointer " + (i + 1), v[v.length - i - 1], p[i].getLongLong(0));
+ }
+ }
+
+ @Test
+ public void testP6() {
+ Pointer[] p = new Pointer[6];
+ long[] v = { 1, 2, 3, 4, 5, 6};
+ for (int i = 0; i < p.length; ++i) {
+ if ((i % 2) == 0) {
+ p[i] = Memory.allocate(Runtime.getRuntime(testlib), 8);
+ } else {
+ p[i] = Memory.allocateDirect(Runtime.getRuntime(testlib), 8);
+ }
+ p[i].putLongLong(0, v[i]);
+ }
+ testlib.ptr_reverse_l6(p[0], p[1], p[2], p[3], p[4], p[5]);
+ for (int i = 0; i < p.length; ++i) {
+ assertEquals("not same value for pointer " + (i + 1), v[v.length - i - 1], p[i].getLongLong(0));
+ }
+ }
+
+ @Test public void nullTerminatedStringArray() {
+ Runtime runtime = Runtime.getRuntime(testlib);
+ Pointer[] array = new Pointer[10];
+ String[] in = new String[array.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Memory.allocateDirect(runtime, 128);
+ array[i].putString(0, in[i] = Integer.toString(i), 128, Charset.defaultCharset());
+ }
+ Pointer memory = Memory.allocateDirect(runtime, (2 * array.length + 1) * runtime.addressSize(), true);
+ memory.put(array.length * runtime.addressSize(), array, 0, array.length);
+ String[] out = memory.getNullTerminatedStringArray(array.length * runtime.addressSize());
+ assertArrayEquals(in, out);
+ }
+
+ @Test public void nullTerminatedPointerArray() {
+ Runtime runtime = Runtime.getRuntime(testlib);
+ Pointer[] array = new Pointer[10];
+ String[] in = new String[array.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Memory.allocateDirect(runtime, 128);
+ array[i].putString(0, in[i] = Integer.toString(i), 128, Charset.defaultCharset());
+ }
+ Pointer memory = Memory.allocateDirect(runtime, (2 * array.length + 1) * runtime.addressSize(), true);
+ memory.put(array.length * runtime.addressSize(), array, 0, array.length);
+ Pointer[] out = memory.getNullTerminatedPointerArray(array.length * runtime.addressSize());
+ assertArrayEquals(array, out);
+ }
+
+ @Test
+ public void testAddressSetByte() {
+
+ Pointer p = testlib.ptr_malloc(SIZE);
+ byte MAGIC = (byte) 0xFE;
+ for (int i = 0; i < SIZE; ++i) {
+ p.putByte(i, MAGIC);
+ assertEquals("Byte not set at offset " + i, MAGIC, testlib.ptr_ret_int8_t(Address.valueOf(p.address()), i));
+ }
+ }
+
+ @Test
+ public void pointerArrayGetElement() {
+ Pointer[] ary = new Pointer[10];
+ Pointer p1 = ary[0] = runtime.getMemoryManager().newPointer(0xdeadbeef & runtime.addressMask());
+ Pointer p2 = ary[9] = runtime.getMemoryManager().newPointer(0xfee1dead & runtime.addressMask());
+ assertEquals(p1, testlib.ptr_return_array_element(ary, 0));
+ assertEquals(p2, testlib.ptr_return_array_element(ary, 9));
+ }
+
+ @Test
+ public void pointerArraySetElement() {
+ Pointer[] ary = new Pointer[10];
+ Pointer p1 = runtime.getMemoryManager().newPointer(0xdeadbeef & runtime.addressMask());
+ Pointer p2 = runtime.getMemoryManager().newPointer(0xfee1dead & runtime.addressMask());
+ testlib.ptr_set_array_element(ary, 0, p1);
+ assertEquals(p1, ary[0]);
+ testlib.ptr_set_array_element(ary, 9, p2);
+ assertEquals(p2, ary[9]);
+ }
+
+ @Test public void mixObjectsAndPrimitives() {
+ TestLib.Foo[] structs = Struct.arrayOf(runtime, TestLib.Foo.class, 1);
+ TestLib.Foo foo = structs[0];
+
+ testlib.fill_struct_from_longs(0xdeadL, 0xbeefL, structs, 0x1eefcafe);
+ assertEquals(0xdeadL, foo.l1.get());
+ assertEquals(0xbeefL, foo.l2.get());
+ assertEquals(0x1eefcafeL, foo.l3.get());
+ }
+// @Test
+// public void testLibcMalloc() {
+// Pointer p = libc.malloc(SIZE);
+// libc.free(p);
+// }
+}
diff --git a/src/test/java/jnr/ffi/ResultConverterTest.java b/src/test/java/jnr/ffi/ResultConverterTest.java
new file mode 100644
index 0000000..a3a75ac
--- /dev/null
+++ b/src/test/java/jnr/ffi/ResultConverterTest.java
@@ -0,0 +1,161 @@
+package jnr.ffi;
+
+import jnr.ffi.mapper.*;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class ResultConverterTest {
+ public static final class TestType {
+ public final String str;
+
+ public TestType(String str) {
+ this.str = str;
+ }
+
+ }
+
+ public static interface TestLib {
+ TestType strdup(CharSequence cs);
+ void cfree(Pointer ptr);
+ }
+
+ public static interface Libc {
+ Pointer calloc(int nmemb, int size);
+ Pointer malloc(int size);
+ void free(Pointer ptr);
+ void cfree(Pointer ptr);
+ }
+
+ public static final class TestTypeResultConverter implements FromNativeConverter<TestType, Pointer> {
+
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+
+ public TestType fromNative(Pointer nativeValue, FromNativeContext context) {
+ return new TestType(nativeValue.getString(0));
+ }
+ }
+
+ static final TypeMapper mapper = new TypeMapper() {
+
+ public FromNativeConverter getFromNativeConverter(Class type) {
+ if (TestType.class == type) {
+ return new TestTypeResultConverter();
+ }
+ return null;
+ }
+
+ public ToNativeConverter getToNativeConverter(Class type) {
+ return null;
+ }
+ };
+ static TestLib testlib;
+ static Runtime runtime;
+
+ public ResultConverterTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ Map<LibraryOption, Object> options = new HashMap<LibraryOption, Object>();
+ options.put(LibraryOption.TypeMapper, mapper);
+ System.setProperty("jaffl.compiler.dump", "true");
+ testlib = TstUtil.loadTestLib(TestLib.class, options);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ @Test public void testCustomResult() {
+ final String MAGIC = "test";
+ TestType t = testlib.strdup(MAGIC);
+ assertNotNull(t);
+ assertEquals("contents not set", MAGIC, t.str);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface PosixError {
+
+ }
+
+ @FromNativeConverter.NoContext
+ public static final class PosixErrorConverter implements FromNativeConverter<Integer, Integer> {
+ public final Integer fromNative(Integer nativeValue, FromNativeContext context) {
+ if (nativeValue < 0) {
+ throw new RuntimeException("posix error!");
+ }
+
+ return nativeValue;
+ }
+
+ public Class<Integer> nativeType() {
+ return Integer.class;
+ }
+ }
+
+ static final SignatureTypeMapper posixTypeMapper = new AbstractSignatureTypeMapper() {
+
+ public FromNativeConverter getFromNativeConverter(SignatureType type, FromNativeContext context) {
+ if (int.class == type.getDeclaredType()) {
+ for (Annotation a : context.getAnnotations()) {
+ if (a instanceof PosixError) {
+ return new PosixErrorConverter();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public ToNativeConverter getToNativeConverter(SignatureType type, ToNativeContext context) {
+ return null;
+ }
+ };
+
+ public static interface Posix {
+ @PosixError int ret_int32_t(int v);
+ }
+
+ @Test public void testPosixError() {
+ System.setProperty("jnr.ffi.compile.dump", "true");
+ Map<LibraryOption, Object> options = new HashMap<LibraryOption, Object>();
+ options.put(LibraryOption.TypeMapper, posixTypeMapper);
+ Posix posix = TstUtil.loadTestLib(Posix.class, options);
+ assertEquals(1, posix.ret_int32_t(1));
+ try {
+ posix.ret_int32_t(-1);
+ } catch (RuntimeException re) {
+ System.out.println(re);
+ }
+ }
+}
diff --git a/src/test/java/jnr/ffi/StringArrayTest.java b/src/test/java/jnr/ffi/StringArrayTest.java
new file mode 100644
index 0000000..8e56ad9
--- /dev/null
+++ b/src/test/java/jnr/ffi/StringArrayTest.java
@@ -0,0 +1,86 @@
+package jnr.ffi;
+
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import java.nio.charset.Charset;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class StringArrayTest {
+
+ public StringArrayTest() {
+ }
+
+ public static interface TestLib {
+ String ptr_return_array_element(@In String[] array, int index);
+ void ptr_set_array_element(@Out String[] array, int index, Pointer value);
+ String ptr_return_array_element(@In CharSequence[] array, int index);
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ @Test public void lastElementOfStringArrayShouldBeNull() {
+ String[] strings = { "test" };
+ String result = testlib.ptr_return_array_element(strings, 1);
+ assertNull("last element of string array was not null", result);
+ }
+
+ @Test public void lastElementOfCharSequenceArrayShouldBeNull() {
+ CharSequence[] strings = { "test" };
+ String result = testlib.ptr_return_array_element(strings, 1);
+ assertNull("last element of string array was not null", result);
+ }
+
+
+ @Test public void firstElementOfStringArrayShouldNotBeNull() {
+ final String MAGIC = "test";
+ String[] strings = { MAGIC };
+ assertNotNull(testlib.ptr_return_array_element(strings, 0));
+ }
+
+ @Test public void firstElementOfStringArrayShouldEqualOriginalValue() {
+ final String MAGIC = "test";
+ String[] strings = { MAGIC };
+ assertEquals(MAGIC, testlib.ptr_return_array_element(strings, 0));
+ }
+
+ @Test public void elementsSetByNativeCodeShouldBeReloaded() {
+ final String MAGIC = "test";
+ String[] strings = new String[1];
+ Pointer ptr = Memory.allocateDirect(runtime, 1024);
+ ptr.putString(0, MAGIC, 1024, Charset.defaultCharset());
+ testlib.ptr_set_array_element(strings, 0, ptr);
+ assertEquals(MAGIC, strings[0]);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/StringTest.java b/src/test/java/jnr/ffi/StringTest.java
new file mode 100644
index 0000000..2fa55ff
--- /dev/null
+++ b/src/test/java/jnr/ffi/StringTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi;
+
+import jnr.ffi.annotations.In;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class StringTest {
+
+ public StringTest() {
+ }
+ public static interface TestLib {
+ boolean string_equals(String s1, String s2);
+ boolean string_equals(CharSequence s1, byte[] s2);
+ void string_set(StringBuffer dst, CharSequence src);
+ void string_set(StringBuilder dst, CharSequence src);
+ void string_concat(StringBuilder dst, CharSequence src);
+ void string_concat(StringBuffer dst, CharSequence src);
+ String ptr_return_array_element(@In String[] array, int index);
+ }
+ static TestLib testlib;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+
+ @Test
+ public void testReadOnlyString() {
+ String MAGIC = "deadbeef\u0000";
+ assertTrue("String did not equal byte array", testlib.string_equals(MAGIC, MAGIC.getBytes()));
+ assertTrue("StringBuffer did not equal byte array", testlib.string_equals(new StringBuffer(MAGIC), MAGIC.getBytes()));
+ assertTrue("StringBuilder did not equal byte array", testlib.string_equals(new StringBuilder(MAGIC), MAGIC.getBytes()));
+ }
+
+ @Test
+ public void testSetStringBuffer() {
+ String MAGIC = "deadbeef";
+ StringBuffer buffer = new StringBuffer(1024);
+ testlib.string_set(buffer, MAGIC);
+ assertEquals("StringBuffer was not set", MAGIC, buffer.toString());
+ }
+ @Test
+ public void testSetStringBuilder() {
+ String MAGIC = "deadbeef";
+ StringBuilder buffer = new StringBuilder(1024);
+ testlib.string_set(buffer, MAGIC);
+ assertEquals("StringBuilder was not set", MAGIC, buffer.toString());
+ }
+ @Test
+ public void testStringBufferAppend() {
+ String ORIG = "test ";
+ String MAGIC = "deadbeef";
+ StringBuffer buffer = new StringBuffer(1024);
+ buffer.append(ORIG);
+ testlib.string_concat(buffer, MAGIC);
+ assertEquals("StringBuilder was not set", ORIG + MAGIC, buffer.toString());
+ }
+ @Test
+ public void testStringBuilderAppend() {
+ String ORIG = "test ";
+ String MAGIC = "deadbeef";
+ StringBuilder buffer = new StringBuilder(1024);
+ buffer.append(ORIG);
+ testlib.string_concat(buffer, MAGIC);
+ assertEquals("StringBuilder was not set", ORIG + MAGIC, buffer.toString());
+ }
+
+ @Test public void testStringParams() {
+ assertTrue("strings should be equal", testlib.string_equals("test", "test"));
+ assertFalse("strings should not be equal", testlib.string_equals("test", "deadbeef"));
+ }
+
+ @Test public void stringResult() {
+ final String MAGIC = "deadbeef";
+ assertEquals(MAGIC, testlib.ptr_return_array_element(new String[] { MAGIC }, 0));
+ }
+}
diff --git a/src/test/java/jnr/ffi/TstUtil.java b/src/test/java/jnr/ffi/TstUtil.java
new file mode 100644
index 0000000..cb71998
--- /dev/null
+++ b/src/test/java/jnr/ffi/TstUtil.java
@@ -0,0 +1,44 @@
+package jnr.ffi;
+
+import jnr.ffi.provider.FFIProvider;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Map;
+
+public final class TstUtil {
+ private TstUtil() {}
+ private static FFIProvider provider;
+ private static String libname = "test";
+
+ public static void setProvider(FFIProvider provider) {
+ TstUtil.provider = provider;
+ }
+
+ public static void setPath(String path) {
+ TstUtil.libname = path;
+ }
+
+ public static interface HelperLib {
+ Pointer ptr_from_buffer(ByteBuffer buf);
+ }
+
+ public static <T> T loadTestLib(Class<T> interfaceClass) {
+ final Map<LibraryOption, ?> options = Collections.emptyMap();
+ return loadTestLib(interfaceClass, options);
+ }
+ public static <T> T loadTestLib(Class<T> interfaceClass, Map<LibraryOption, ?> options) {
+ LibraryLoader<T> loader = (provider != null ? provider : FFIProvider.getSystemProvider()).createLibraryLoader(interfaceClass);
+
+ loader.library(libname);
+ for (Map.Entry<LibraryOption, ?> option : options.entrySet()) {
+ loader.option(option.getKey(), option.getValue());
+ }
+
+ return loader.load();
+ }
+
+ public static Pointer getDirectBufferPointer(ByteBuffer buf) {
+ return TstUtil.loadTestLib(HelperLib.class).ptr_from_buffer(buf);
+ }
+}
diff --git a/src/test/java/jnr/ffi/TypeDefinitionTest.java b/src/test/java/jnr/ffi/TypeDefinitionTest.java
new file mode 100644
index 0000000..2781634
--- /dev/null
+++ b/src/test/java/jnr/ffi/TypeDefinitionTest.java
@@ -0,0 +1,57 @@
+package jnr.ffi;
+
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.types.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class TypeDefinitionTest {
+
+ public TypeDefinitionTest() {
+
+
+ }
+ public static interface TestLib {
+ public @int8_t int add_int8_t(@int8_t int i1, @int8_t int i2);
+ public @int8_t int add_int8_t(@int8_t Integer i1, @int8_t int i2);
+ public @int8_t Long add_int8_t(@int8_t Long i1, @int8_t Integer i2);
+ public int add_uint8_t(@u_int8_t int i1, @u_int8_t int i2);
+ public int ret_uint8_t(@u_int8_t int i1);
+ public @u_int32_t long ret_uint32_t(@u_int32_t long i1);
+ public @u_int32_t short ret_uint32_t(@u_int32_t byte i1);
+ public void ret_long(@size_t int i1);
+ }
+
+ static TestLib testlib;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ testlib = null;
+ }
+
+ @Test public void doNothing() {
+ }
+
+ @Test public void returnUnsigned8() {
+ int i1 = 0xdead0001;
+ // when passed to the native function, only the lowest 8 bits are passed
+ assertEquals("incorrect value returned", 1, testlib.ret_uint8_t(i1));
+ }
+ @Test public void addUnsigned8() {
+ int i1 = 0xdead0001;
+ int i2 = 0xbeef0002;
+ // when passed to the native function, only the lowest 8 bits are passed
+ assertEquals("did not add correctly", 3, testlib.add_uint8_t(i1, i2));
+ }
+}
diff --git a/src/test/java/jnr/ffi/byref/AddressByReferenceTest.java b/src/test/java/jnr/ffi/byref/AddressByReferenceTest.java
new file mode 100644
index 0000000..6db5e2a
--- /dev/null
+++ b/src/test/java/jnr/ffi/byref/AddressByReferenceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007, 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.TstUtil;
+import jnr.ffi.Address;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class AddressByReferenceTest {
+ public AddressByReferenceTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static interface TestLib {
+ Address ptr_ret_pointer(AddressByReference p, int offset);
+ void ptr_set_pointer(AddressByReference p, int offset, Address value);
+ }
+ public static interface TestLibInOnly {
+ Address ptr_ret_pointer(@In AddressByReference p, int offset);
+ void ptr_set_pointer(@In AddressByReference p, int offset, Address value);
+ }
+ public static interface TestLibOutOnly {
+ Address ptr_ret_pointer(@Out AddressByReference p, int offset);
+ void ptr_set_pointer(@Out AddressByReference p, int offset, Address value);
+ }
+
+ @Test public void inOnlyReferenceSet() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final Address MAGIC = Address.valueOf(0xdeadbeef);
+ AddressByReference ref = new AddressByReference(MAGIC);
+ assertEquals("Wrong value passed", MAGIC, lib.ptr_ret_pointer(ref, 0));
+ }
+
+ @Test public void inOnlyIntReferenceNotWritten() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final Address MAGIC = Address.valueOf(0xdeadbeefL);
+ AddressByReference ref = new AddressByReference(MAGIC);
+ lib.ptr_set_pointer(ref, 0, Address.valueOf(0));
+ assertEquals("Int reference written when it should not be", MAGIC, ref.getValue());
+ }
+
+ @Test public void outOnlyIntReferenceNotRead() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final Address MAGIC = Address.valueOf(0xdeadbeef);
+ AddressByReference ref = new AddressByReference(MAGIC);
+ assertTrue("Reference value passed to native code when it should not be", !MAGIC.equals(lib.ptr_ret_pointer(ref, 0)));
+ }
+
+ @Test public void outOnlyIntReferenceGet() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final Address MAGIC = Address.valueOf(0xdeadbeef);
+ AddressByReference ref = new AddressByReference(Address.valueOf(0));
+ lib.ptr_set_pointer(ref, 0, MAGIC);
+ assertEquals("Reference value not set", MAGIC, ref.getValue());
+ }
+}
diff --git a/src/test/java/jnr/ffi/byref/ByteByReferenceTest.java b/src/test/java/jnr/ffi/byref/ByteByReferenceTest.java
new file mode 100644
index 0000000..2415de4
--- /dev/null
+++ b/src/test/java/jnr/ffi/byref/ByteByReferenceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.byref;
+
+import jnr.ffi.TstUtil;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class ByteByReferenceTest {
+ public ByteByReferenceTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static interface TestLib {
+ byte ptr_ret_int8_t(ByteByReference p, int offset);
+ void ptr_set_int_t(ByteByReference p, int offset, byte value);
+ }
+
+ public static interface TestLibInOnly {
+ byte ptr_ret_int8_t(@In ByteByReference p, int offset);
+ void ptr_set_int8_t(@In ByteByReference p, int offset, byte value);
+ }
+
+ public static interface TestLibOutOnly {
+ byte ptr_ret_int8_t(@Out ByteByReference p, int offset);
+ void ptr_set_int8_t(@Out ByteByReference p, int offset, byte value);
+ }
+
+ @Test public void inOnlyReferenceSet() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final byte MAGIC = (byte) 0xef;
+ ByteByReference ref = new ByteByReference(MAGIC);
+ assertEquals("Wrong value passed", MAGIC, lib.ptr_ret_int8_t(ref, 0));
+ }
+
+ @Test public void inOnlyByteReferenceNotWritten() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final byte MAGIC = (byte) 0xef;
+ ByteByReference ref = new ByteByReference(MAGIC);
+ lib.ptr_set_int8_t(ref, 0, (byte) 0);
+ assertEquals("Int reference written when it should not be", Byte.valueOf(MAGIC), ref.getValue());
+ }
+
+ @Test public void outOnlyByteReferenceNotRead() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final byte MAGIC = (byte) 0xef;
+ ByteByReference ref = new ByteByReference(MAGIC);
+ assertTrue("Reference value passed to native code when it should not be", MAGIC != lib.ptr_ret_int8_t(ref, 0));
+ }
+
+ @Test public void outOnlyByteReferenceGet() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final byte MAGIC = (byte) 0xef;
+ ByteByReference ref = new ByteByReference((byte) 0);
+ lib.ptr_set_int8_t(ref, 0, MAGIC);
+ assertEquals("Reference value not set", Byte.valueOf(MAGIC), ref.getValue());
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/byref/IntByReferenceTest.java b/src/test/java/jnr/ffi/byref/IntByReferenceTest.java
new file mode 100644
index 0000000..e6b474b
--- /dev/null
+++ b/src/test/java/jnr/ffi/byref/IntByReferenceTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+package jnr.ffi.byref;
+
+import jnr.ffi.TstUtil;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class IntByReferenceTest {
+ public IntByReferenceTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static interface TestLib {
+ int ptr_ret_int32_t(IntByReference p, int offset);
+ void ptr_set_int32_t(IntByReference p, int offset, int value);
+ }
+ public static interface TestLibInOnly {
+ int ptr_ret_int32_t(@In IntByReference p, int offset);
+ void ptr_set_int32_t(@In IntByReference p, int offset, int value);
+ }
+ public static interface TestLibOutOnly {
+ int ptr_ret_int32_t(@Out IntByReference p, int offset);
+ void ptr_set_int32_t(@Out IntByReference p, int offset, int value);
+ }
+
+ @Test public void inOnlyIntReferenceSet() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final int MAGIC = 0xdeadbeef;
+ IntByReference ref = new IntByReference(MAGIC);
+ assertEquals("Wrong value passed", MAGIC, lib.ptr_ret_int32_t(ref, 0));
+ }
+ @Test public void inOnlyIntReferenceNotWritten() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ final int MAGIC = 0xdeadbeef;
+ IntByReference ref = new IntByReference(MAGIC);
+ lib.ptr_set_int32_t(ref, 0, 0);
+ assertEquals("Int reference written when it should not be", Integer.valueOf(MAGIC), ref.getValue());
+ }
+ @Test public void outOnlyIntReferenceNotRead() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final int MAGIC = 0xdeadbeef;
+ IntByReference ref = new IntByReference(MAGIC);
+ assertTrue("Reference value passed to native code when it should not be", MAGIC != lib.ptr_ret_int32_t(ref, 0));
+ }
+ @Test public void outOnlyIntReferenceGet() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ final int MAGIC = 0xdeadbeef;
+ IntByReference ref = new IntByReference(0);
+ lib.ptr_set_int32_t(ref, 0, MAGIC);
+ assertEquals("Reference value not set", Integer.valueOf(MAGIC), ref.getValue());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/byref/PointerByReferenceTest.java b/src/test/java/jnr/ffi/byref/PointerByReferenceTest.java
new file mode 100644
index 0000000..b42fdc9
--- /dev/null
+++ b/src/test/java/jnr/ffi/byref/PointerByReferenceTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2007, 2008, 2010 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.byref;
+
+
+import jnr.ffi.Library;
+import jnr.ffi.Memory;
+import jnr.ffi.Pointer;
+import jnr.ffi.Runtime;
+import jnr.ffi.TstUtil;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class PointerByReferenceTest {
+ public PointerByReferenceTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ public static interface TestLib {
+ Pointer ptr_ret_pointer(PointerByReference p, int offset);
+ void ptr_set_pointer(PointerByReference p, int offset, Pointer value);
+ }
+
+ public static interface TestLibInOnly {
+ Pointer ptr_ret_pointer(@In PointerByReference p, int offset);
+ void ptr_set_pointer(@In PointerByReference p, int offset, Pointer value);
+ }
+
+ public static interface TestLibOutOnly {
+ Pointer ptr_ret_pointer(@Out PointerByReference p, int offset);
+ void ptr_set_pointer(@Out PointerByReference p, int offset, Pointer value);
+ }
+
+
+ @Test public void inOnlyReferenceSet() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ Runtime runtime = Runtime.getRuntime(lib);
+
+ final Pointer MAGIC = Memory.allocateDirect(runtime, 123);
+
+ PointerByReference ref = new PointerByReference(MAGIC);
+ assertEquals("Wrong value passed", MAGIC, lib.ptr_ret_pointer(ref, 0));
+ }
+
+ @Test public void inOnlyIntReferenceNotWritten() {
+ TestLibInOnly lib = TstUtil.loadTestLib(TestLibInOnly.class);
+ Runtime runtime = Runtime.getRuntime(lib);
+
+ final Pointer MAGIC = Memory.allocateDirect(runtime, 123);
+
+ PointerByReference ref = new PointerByReference(MAGIC);
+
+ lib.ptr_set_pointer(ref, 0, null);
+ assertEquals("Int reference written when it should not be", MAGIC, ref.getValue());
+ }
+
+ @Test public void outOnlyIntReferenceNotRead() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ Runtime runtime = Runtime.getRuntime(lib);
+
+ final Pointer MAGIC = Memory.allocateDirect(runtime, 123);
+
+ PointerByReference ref = new PointerByReference(MAGIC);
+ assertNotSame("Reference value passed to native code when it should not be", MAGIC, lib.ptr_ret_pointer(ref, 0));
+ }
+
+ @Test public void outOnlyIntReferenceGet() {
+ TestLibOutOnly lib = TstUtil.loadTestLib(TestLibOutOnly.class);
+ Runtime runtime = Runtime.getRuntime(lib);
+
+ final Pointer MAGIC = Memory.allocateDirect(runtime, 123);
+
+
+ PointerByReference ref = new PointerByReference(Memory.allocateDirect(runtime, 1));
+ lib.ptr_set_pointer(ref, 0, MAGIC);
+ assertEquals("Reference value not set", MAGIC, ref.getValue());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/mapper/AnnotatedMappedTypeTest.java b/src/test/java/jnr/ffi/mapper/AnnotatedMappedTypeTest.java
new file mode 100644
index 0000000..f784106
--- /dev/null
+++ b/src/test/java/jnr/ffi/mapper/AnnotatedMappedTypeTest.java
@@ -0,0 +1,57 @@
+package jnr.ffi.mapper;
+
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.provider.converters.EnumSetConverter;
+import jnr.ffi.types.size_t;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static junit.framework.TestCase.*;
+
+public class AnnotatedMappedTypeTest {
+ public static final class CustomPointer {
+ private final Pointer pointer;
+
+ public CustomPointer(Pointer pointer) {
+ this.pointer = pointer;
+ }
+
+ @ToNativeConverter.ToNative(nativeType = jnr.ffi.Pointer.class)
+ public static Pointer toNative(CustomPointer value, ToNativeContext context) {
+ return value != null ? value.pointer : null;
+ }
+
+ @FromNativeConverter.FromNative(nativeType = jnr.ffi.Pointer.class)
+ public static CustomPointer fromNative(Pointer value, FromNativeContext context) {
+ return value != null ? new CustomPointer(value) : null;
+ }
+ }
+
+ public static interface TestLib {
+ CustomPointer ptr_malloc(@size_t int size);
+ void ptr_free(CustomPointer ptr);
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @Test public void returnsInstanceOfCorrectClass() {
+ assertSame(CustomPointer.class, testlib.ptr_malloc(1).getClass());
+ }
+
+ @Test public void toNative() {
+ testlib.ptr_free(testlib.ptr_malloc(1));
+ }
+}
diff --git a/src/test/java/jnr/ffi/mapper/CachingTypeMapperTest.java b/src/test/java/jnr/ffi/mapper/CachingTypeMapperTest.java
new file mode 100644
index 0000000..e168658
--- /dev/null
+++ b/src/test/java/jnr/ffi/mapper/CachingTypeMapperTest.java
@@ -0,0 +1,231 @@
+package jnr.ffi.mapper;
+
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.provider.converters.EnumSetConverter;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+
+public class CachingTypeMapperTest {
+ public static interface Lib {
+ public static enum Enum1 {
+ A, B;
+ }
+ public static enum Enum2 {
+ A, B;
+ }
+ public int ret_int();
+ public Set<Enum1> ret_enumset1a();
+ public Set<Enum1> ret_enumset1b();
+ public Set<Enum2> ret_enumset2a();
+ public Set<Enum2> ret_enumset2b();
+ public Set<Integer> ret_intset();
+ public void enumset_param(Set<Enum1> enums);
+ public void annotated_params(@In long[] in, @Out long[] out);
+ public void int_array_params(int[] in, int[] out);
+ public void intset_param(Set<Integer> bitfield);
+ }
+
+ private static Method getLibMethod(String name, Class... parameterTypes) {
+ try {
+ return Lib.class.getMethod(name, parameterTypes);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ @ToNativeConverter.Cacheable
+ private static final class LongArrayParameterConverter implements ToNativeConverter<long[], Pointer> {
+ @Override
+ public Pointer toNative(long[] value, ToNativeContext context) {
+ return null;
+ }
+
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+ }
+
+ // Do not mark as cacheable
+ private static final class IntArrayParameterConverter implements ToNativeConverter<int[], Pointer> {
+ @Override
+ public Pointer toNative(int[] value, ToNativeContext context) {
+ return null;
+ }
+
+ @Override
+ public Class<Pointer> nativeType() {
+ return Pointer.class;
+ }
+ }
+
+ private static final class TestTypeMapper implements SignatureTypeMapper {
+ public FromNativeConverter getFromNativeConverter(SignatureType type, FromNativeContext context) {
+ FromNativeConverter converter;
+ if (type.getDeclaredType() == Set.class && (converter = EnumSetConverter.getFromNativeConverter(type, context)) != null) {
+ return converter;
+ } else {
+ return null;
+ }
+ }
+
+ public ToNativeConverter getToNativeConverter(SignatureType type, ToNativeContext context) {
+ ToNativeConverter converter;
+ if (type.getDeclaredType() == Set.class && (converter = EnumSetConverter.getToNativeConverter(type, context)) != null) {
+ return converter;
+
+ } else if (long[].class == type.getDeclaredType()) {
+ return new LongArrayParameterConverter();
+
+ } else if (int[].class == type.getDeclaredType()) {
+ return new IntArrayParameterConverter();
+
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ return FromNativeTypes.create(getFromNativeConverter(type, context));
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ return ToNativeTypes.create(getToNativeConverter(type, context));
+ }
+ }
+
+ private static final class CountingTypeMapper implements SignatureTypeMapper {
+ private final Map<Class, Integer> fromConverterStats = new HashMap<Class, Integer>();
+ private final Map<Class, Integer> toConverterStats = new HashMap<Class, Integer>();
+ private final SignatureTypeMapper typeMapper;
+
+ CountingTypeMapper(SignatureTypeMapper typeMapper) {
+ this.typeMapper = typeMapper;
+ }
+
+ private void incrementCount(Map<Class, Integer> stats, Class type) {
+ Integer count = stats.get(type);
+ stats.put(type, count != null ? count + 1 : 1);
+ }
+
+ @Override
+ public FromNativeType getFromNativeType(SignatureType type, FromNativeContext context) {
+ incrementCount(fromConverterStats, type.getDeclaredType());
+ return typeMapper.getFromNativeType(type, context);
+ }
+
+ @Override
+ public ToNativeType getToNativeType(SignatureType type, ToNativeContext context) {
+ incrementCount(toConverterStats, type.getDeclaredType());
+ return typeMapper.getToNativeType(type, context);
+ }
+
+ public int getFromNativeCount(Class type) {
+ Integer count = fromConverterStats.get(type);
+ return count != null ? count : 0;
+ }
+
+ public int getToNativeCount(Class type) {
+ Integer count = toConverterStats.get(type);
+ return count != null ? count : 0;
+ }
+ }
+
+ private SignatureTypeMapper defaultTypeMapper;
+ @Before
+ public void setUp() {
+ defaultTypeMapper = new CachingTypeMapper(new TestTypeMapper());
+ }
+
+ private ToNativeConverter getToNativeConverter(SignatureTypeMapper typeMapper, Method m, int parameterIndex) {
+ ToNativeContext toNativeContext = new MethodParameterContext(Runtime.getSystemRuntime(), m, parameterIndex);
+ SignatureType signatureType = DefaultSignatureType.create(m.getParameterTypes()[parameterIndex], toNativeContext);
+ ToNativeType toNativeType = typeMapper.getToNativeType(signatureType, toNativeContext);
+ return toNativeType != null ? toNativeType.getToNativeConverter() : null;
+ }
+
+ private FromNativeConverter getFromNativeConverter(SignatureTypeMapper typeMapper, Method m) {
+ FromNativeContext fromNativeContext = new MethodResultContext(Runtime.getSystemRuntime(), m);
+ SignatureType signatureType = DefaultSignatureType.create(m.getReturnType(), fromNativeContext);
+ FromNativeType fromNativeType = typeMapper.getFromNativeType(signatureType, fromNativeContext);
+ return fromNativeType != null ? fromNativeType.getFromNativeConverter() : null;
+ }
+
+
+ @Test public void intReturnHasNoConverter() {
+ assertNull(getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_int")));
+ }
+
+ @Test public void sameResultTypeHasSameConverter() {
+ FromNativeConverter converter;
+ assertNotNull(converter = getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset1a")));
+ assertSame(converter, getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset1b")));
+ }
+
+ @Test public void differentEnumSet() {
+ FromNativeConverter converter1;
+ assertNotNull(converter1 = getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset1a")));
+ assertSame(converter1, getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset1b")));
+ FromNativeConverter converter2;
+ assertNotNull(converter2 = getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset2a")));
+ assertSame(converter2, getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_enumset2b")));
+ assertNotSame(converter1, converter2);
+ }
+
+
+ @Test public void integerSet() {
+ assertNull(getFromNativeConverter(defaultTypeMapper, getLibMethod("ret_intset")));
+ }
+
+
+ @Test public void differentAnnotations() {
+ ToNativeConverter converter1, converter2;
+ Method m = getLibMethod("annotated_params", long[].class, long[].class);
+ assertNotNull(converter1 = getToNativeConverter(defaultTypeMapper, m, 0));
+ assertNotNull(converter2 = getToNativeConverter(defaultTypeMapper, m, 1));
+ assertNotSame(converter1, converter2);
+ }
+
+ @Test public void uncacheableConverter() {
+ CountingTypeMapper counter;
+ SignatureTypeMapper typeMapper = new CachingTypeMapper(counter = new CountingTypeMapper(new TestTypeMapper()));
+ ToNativeConverter converter1, converter2;
+ Method m = getLibMethod("int_array_params", int[].class, int[].class);
+ assertNotNull(converter1 = getToNativeConverter(typeMapper, m, 0));
+ assertEquals(1, counter.getToNativeCount(int[].class));
+ assertNotNull(converter2 = getToNativeConverter(typeMapper, m, 1));
+ assertEquals(2, counter.getToNativeCount(int[].class));
+ assertNotSame(converter1, converter2);
+ }
+
+ @Test public void converterIsCached() {
+ CountingTypeMapper counter;
+ SignatureTypeMapper typeMapper = new CachingTypeMapper(counter = new CountingTypeMapper(new TestTypeMapper()));
+ FromNativeConverter converter1;
+ assertNotNull(converter1 = getFromNativeConverter(typeMapper, getLibMethod("ret_enumset1a")));
+ assertEquals(1, counter.getFromNativeCount(Set.class));
+ assertSame(converter1, getFromNativeConverter(typeMapper, getLibMethod("ret_enumset1b")));
+ assertEquals(1, counter.getFromNativeCount(Set.class));
+ FromNativeConverter converter2;
+ assertNotNull(converter2 = getFromNativeConverter(typeMapper, getLibMethod("ret_enumset2a")));
+ assertEquals(2, counter.getFromNativeCount(Set.class));
+ assertSame(converter2, getFromNativeConverter(typeMapper, getLibMethod("ret_enumset2b")));
+ assertNotSame(converter1, converter2);
+ assertEquals(2, counter.getFromNativeCount(Set.class));
+ }
+
+}
diff --git a/src/test/java/jnr/ffi/struct/AlignmentTest.java b/src/test/java/jnr/ffi/struct/AlignmentTest.java
new file mode 100644
index 0000000..6a5354d
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/AlignmentTest.java
@@ -0,0 +1,66 @@
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.struct.AlignmentTest.TestLib.PointerStruct;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class AlignmentTest {
+ public static interface TestLib {
+ class PointerStruct extends Struct {
+
+ public final Signed8 s8 = new Signed8();
+ public final Pointer p = new Pointer();
+
+ public PointerStruct() {
+ super(runtime);
+ }
+ }
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+ public AlignmentTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+
+
+ @Test public void alignPointer() throws Throwable {
+ PointerStruct s = new PointerStruct();
+ final int SIZE = runtime.addressSize() == 4 ? 8 : 16;
+ assertEquals("Incorrect pointer field alignment", SIZE, Struct.size(s));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/struct/ArrayTest.java b/src/test/java/jnr/ffi/struct/ArrayTest.java
new file mode 100644
index 0000000..4aa453b
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/ArrayTest.java
@@ -0,0 +1,94 @@
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.In;
+import jnr.ffi.provider.AbstractArrayMemoryIO;
+import jnr.ffi.provider.DelegatingMemoryIO;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+public class ArrayTest {
+ public static interface TestLib {
+ byte ptr_ret_int8_t(@In s8[] s, int index);
+ class PointerStruct extends Struct {
+
+ public final Signed8 s8 = new Signed8();
+ public final Pointer p = new Pointer();
+
+ public PointerStruct(Runtime runtime) {
+ super(runtime);
+ }
+ }
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+ public ArrayTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static final class s8 extends Struct {
+ public final Signed8 s8 = new Signed8();
+
+ public s8(Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+ public static final class s32 extends Struct {
+ public final Signed8 s8 = new Signed8();
+ public s32(Runtime runtime) {
+ super(runtime);
+ }
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ @Test public void s8Array() {
+ s8[] array = Struct.arrayOf(runtime, s8.class, 10);
+ assertEquals("Array length incorrect", 10, array.length);
+ for (int i = 0; i < array.length; ++i) {
+ assertNotNull("Memory not allocated for array member", Struct.getMemory(array[i]));
+ }
+ Pointer ptr = ((DelegatingMemoryIO) Struct.getMemory(array[0])).getDelegatedMemoryIO();
+ for (int i = 0; i < array.length; ++i) {
+ assertSame("Different backing memory", ptr, ((DelegatingMemoryIO) Struct.getMemory(array[i])).getDelegatedMemoryIO());
+ }
+ if (ptr instanceof AbstractArrayMemoryIO) {
+ assertEquals("Incorrect size", array.length, ((AbstractArrayMemoryIO)ptr).length());
+ }
+ for (int i = 0; i < array.length; ++i) {
+ array[i].s8.set((byte) i);
+ }
+ for (int i = 0; i < array.length; ++i) {
+ assertEquals("Incorrect value written to native memory at index " + i,
+ (byte) i, testlib.ptr_ret_int8_t(array, i));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/struct/AsciiStringFieldTest.java b/src/test/java/jnr/ffi/struct/AsciiStringFieldTest.java
new file mode 100644
index 0000000..21a5e5f
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/AsciiStringFieldTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.Library;
+import jnr.ffi.Struct;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.annotations.Pinned;
+import jnr.ffi.annotations.Transient;
+import jnr.ffi.Runtime;
+import jnr.ffi.TstUtil;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class AsciiStringFieldTest {
+ public AsciiStringFieldTest() {
+ }
+ public class StringFieldStruct extends Struct {
+ public final String string = new AsciiString(32);
+
+ public StringFieldStruct() {
+ super(runtime);
+ }
+
+ public StringFieldStruct(Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+ public static interface TestLib {
+ // This makes use of the string being the first field in the struct
+ int string_equals(@Pinned @In @Transient StringFieldStruct s1, String s2);
+ int copyByteBuffer(@Pinned @Out StringFieldStruct dst, @In byte[] src, int len);
+ int copyByteBuffer(@Pinned @Out byte[] dst, @Pinned @In @Transient StringFieldStruct src, int len);
+ int copyByteBuffer(@Pinned @Out StringBuilder dst, @Pinned @In @Transient StringFieldStruct src, int len);
+ }
+ static TestLib testlib;
+ static Runtime runtime;
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void stringFieldFirstInStruct() {
+ StringFieldStruct s = new StringFieldStruct();
+ final String MAGIC = "test";
+ s.string.set(MAGIC);
+ StringBuilder tmp = new StringBuilder(s.string.length());
+ testlib.copyByteBuffer(tmp, s, s.string.length());
+ assertEquals("String not put into struct correctly", MAGIC, tmp.toString());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/struct/EnumTest.java b/src/test/java/jnr/ffi/struct/EnumTest.java
new file mode 100644
index 0000000..11fed01
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/EnumTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.util.EnumMapper;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class EnumTest {
+
+ public EnumTest() {
+ }
+ public enum TestEnum {
+ ZERO,
+ B,
+ MAGIC
+ }
+ public class struct1 extends Struct {
+ public final Enum8<TestEnum> b = new Enum8<TestEnum>(TestEnum.class);
+ public final Enum16<TestEnum> s = new Enum16<TestEnum>(TestEnum.class);
+ public final Enum32<TestEnum> i = new Enum32<TestEnum>(TestEnum.class);
+ public final Enum64<TestEnum> i64 = new Enum64<TestEnum>(TestEnum.class);
+ public final EnumLong<TestEnum> l = new EnumLong<TestEnum>(TestEnum.class);
+
+ public struct1() {
+ super(runtime);
+ }
+
+ }
+ public static interface TestLib {
+ byte struct_field_Signed8(struct1 s);
+ short struct_field_Signed16(struct1 s);
+ int struct_field_Signed32(struct1 s);
+ long struct_field_Signed64(struct1 s);
+ float struct_field_Float32(struct1 s);
+ double struct_field_Float64(struct1 s);
+ short struct_align_Signed16(Int16Align s);
+ int struct_align_Signed32(Int32Align s);
+ long struct_align_Signed64(Int64Align s);
+ NativeLong struct_align_SignedLong(LongAlign s);
+// float struct_align_Float32(Float32Align s);
+// double struct_align_Float64(Float64Align s);
+// void struct_set_string(struct1 s, String string);
+ }
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ // TODO add test methods here.
+ // The methods must be annotated with annotation @Test. For example:
+ //
+ // @Test
+ // public void hello() {}
+ public static class Int16Align extends Struct {
+ public final Enum8<TestEnum> first = new Enum8<TestEnum>(TestEnum.class);
+ public final Enum16<TestEnum> s = new Enum16<TestEnum>(TestEnum.class);
+
+ public Int16Align() {
+ super(runtime);
+ }
+
+ }
+ public static class Int32Align extends Struct {
+ public final Enum8<TestEnum> first = new Enum8<TestEnum>(TestEnum.class);
+ public final Enum32<TestEnum> i = new Enum32<TestEnum>(TestEnum.class);
+
+ public Int32Align() {
+ super(runtime);
+ }
+ }
+ public static class Int64Align extends Struct {
+ public final Enum8<TestEnum> first = new Enum8<TestEnum>(TestEnum.class);
+ public final Enum64<TestEnum> l = new Enum64<TestEnum>(TestEnum.class);
+
+ public Int64Align() {
+ super(runtime);
+ }
+ }
+
+ public static class LongAlign extends Struct {
+ public final Enum8<TestEnum> first = new Enum8<TestEnum>(TestEnum.class);
+ public final EnumLong<TestEnum> l = new EnumLong<TestEnum>(TestEnum.class);
+
+ public LongAlign() {
+ super(runtime);
+ }
+
+ }
+ @Test public void testInt8InitialValue() {
+ struct1 s = new struct1();
+ assertEquals("default value not zero", TestEnum.ZERO, s.b.get());
+ }
+ @Test public void testInt8Set() {
+ struct1 s = new struct1();
+ final TestEnum MAGIC = TestEnum.MAGIC;
+ s.b.set(MAGIC);
+ assertEquals("Byte value not set correctly", MAGIC, s.b.get());
+ }
+ @Test
+ public void byteField() {
+ final byte MAGIC = (byte) EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ struct1 s = new struct1();
+ s.b.set(TestEnum.MAGIC);
+
+ assertEquals("byte field not set", MAGIC, testlib.struct_field_Signed8(s));
+ s.b.set(TestEnum.ZERO);
+ assertEquals("byte field not cleared", (byte) 0, testlib.struct_field_Signed8(s));
+ }
+ @Test
+ public void shortField() {
+ final short MAGIC = (short) EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ struct1 s = new struct1();
+ s.s.set(TestEnum.MAGIC);
+
+ assertEquals("short field not set", MAGIC, testlib.struct_field_Signed16(s));
+ s.s.set(TestEnum.ZERO);
+ assertEquals("short field not cleared", (short) 0, testlib.struct_field_Signed16(s));
+ }
+ @Test
+ public void intField() {
+ final int MAGIC = EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ struct1 s = new struct1();
+ s.i.set(TestEnum.MAGIC);
+
+ assertEquals("int field not set", MAGIC, testlib.struct_field_Signed32(s));
+ s.i.set(TestEnum.ZERO);
+ assertEquals("int field not cleared", 0, testlib.struct_field_Signed32(s));
+ }
+ @Test
+ public void int64Field() {
+ final long MAGIC = EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ struct1 s = new struct1();
+ s.i64.set(TestEnum.MAGIC);
+
+ assertEquals("long field not set", MAGIC, testlib.struct_field_Signed64(s));
+ s.i64.set(TestEnum.ZERO);
+ assertEquals("long field not cleared", 0L, testlib.struct_field_Signed64(s));
+ }
+ @Test
+ public void alignInt16Field() {
+ final short MAGIC = (short) EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ Int16Align s = new Int16Align();
+ s.s.set(TestEnum.MAGIC);
+
+ assertEquals("short field not aligned", MAGIC, testlib.struct_align_Signed16(s));
+ }
+ @Test
+ public void alignSigned32Field() {
+ final int MAGIC = (int) EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ Int32Align s = new Int32Align();
+ s.i.set(TestEnum.MAGIC);
+
+ assertEquals("int field not aligned", MAGIC, testlib.struct_align_Signed32(s));
+ }
+ @Test
+ public void alignSigned64Field() {
+ final long MAGIC = EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC);
+ Int64Align s = new Int64Align();
+ s.l.set(TestEnum.MAGIC);
+
+ assertEquals("long field not aligned", MAGIC, testlib.struct_align_Signed64(s));
+ }
+ @Test
+ public void alignSignedLongField() {
+ final NativeLong MAGIC = new NativeLong(EnumMapper.getInstance(TestEnum.class).intValue(TestEnum.MAGIC));
+ LongAlign s = new LongAlign();
+ s.l.set(TestEnum.MAGIC);
+
+ assertEquals("native long field not aligned", MAGIC, testlib.struct_align_SignedLong(s));
+ }
+}
diff --git a/src/test/java/jnr/ffi/struct/PaddingTest.java b/src/test/java/jnr/ffi/struct/PaddingTest.java
new file mode 100644
index 0000000..a2ea714
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/PaddingTest.java
@@ -0,0 +1,58 @@
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.struct.PaddingTest.TestLib.LongPadding;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+public class PaddingTest {
+ static TestLib testlib;
+ static Runtime runtime;
+
+ public static interface TestLib {
+
+ static final class LongPadding extends Struct {
+
+ public final Signed8 s8 = new Signed8();
+ public final Padding pad = new Padding(NativeType.SLONG, 3);
+
+ public LongPadding() {
+ super(runtime);
+ }
+ }
+ }
+ public PaddingTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test public void longPadding() throws Throwable {
+ Type longType = runtime.findType(NativeType.SLONG);
+ final int SIZE = longType.alignment() + (longType.size() * 3);
+ assertEquals("incorrect size", SIZE, Struct.size(new LongPadding()));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/struct/StructLayoutTest.java b/src/test/java/jnr/ffi/struct/StructLayoutTest.java
new file mode 100644
index 0000000..60be4ad
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/StructLayoutTest.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.LongLong;
+import org.junit.*;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ */
+public class StructLayoutTest {
+
+ public StructLayoutTest() {
+ }
+
+ public static interface TestLib {
+ byte struct_field_Signed8(Pointer s);
+ short struct_field_Signed16(Pointer s);
+ int struct_field_Signed32(Pointer s);
+ @LongLong long struct_field_Signed64(Pointer s);
+ float struct_field_Float32(Pointer s);
+ double struct_field_Float64(Pointer s);
+ short struct_align_Signed16(Pointer s);
+ int struct_align_Signed32(Pointer s);
+ @LongLong long struct_align_Signed64(Pointer s);
+ NativeLong struct_align_SignedLong(Pointer s);
+ Pointer struct_make_struct(byte b, short s, int i, @LongLong long ll, float f, double d);
+// float struct_align_Float32(Float32Align s);
+// double struct_align_Float64(Float64Align s);
+// void struct_set_string(struct1 s, String string);
+ }
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static class struct1 extends StructLayout {
+ public final Signed8 b = new Signed8();
+ public final Signed16 s = new Signed16();
+ public final Signed32 i = new Signed32();
+ public final Signed64 i64 = new Signed64();
+ public final SignedLong l = new SignedLong();
+ public final Float f = new Float();
+ public final Double d = new Double();
+
+ public struct1() {
+ super(runtime);
+ }
+
+
+ }
+ public static class Int16Align extends StructLayout {
+ public final Signed8 first = new Signed8();
+ public final Signed16 s = new Signed16();
+
+ public Int16Align() {
+ super(runtime);
+ }
+
+ }
+ public static class Int32Align extends StructLayout {
+ public final Signed8 first = new Signed8();
+ public final Signed32 i = new Signed32();
+
+ public Int32Align() {
+ super(runtime);
+ }
+
+ }
+ public static class Int64Align extends StructLayout {
+ public final Signed8 first = new Signed8();
+ public final Signed64 l = new Signed64();
+
+ public Int64Align() {
+ super(runtime);
+ }
+
+ }
+ public static class LongAlign extends StructLayout {
+ public final Signed8 first = new Signed8();
+ public final SignedLong l = new SignedLong();
+
+ public LongAlign() {
+ super(runtime);
+ }
+
+ }
+
+ @Test public void testInt8InitialValue() {
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ assertEquals("default value not zero", (byte) 0, s.b.get(ptr));
+ }
+
+ @Test public void testInt8Set() {
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ final byte MAGIC = (byte) 0xfe;
+ s.b.set(ptr, MAGIC);
+ assertEquals("Byte value not set correctly", MAGIC, s.b.get(ptr));
+ }
+
+ @Test
+ public void byteField() {
+ final byte MAGIC = (byte) 0xbe;
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.b.set(ptr, MAGIC);
+
+ assertEquals("byte field not set", MAGIC, testlib.struct_field_Signed8(ptr));
+ s.b.set(ptr, (byte) 0);
+ assertEquals("byte field not cleared", (byte) 0, testlib.struct_field_Signed8(ptr));
+ }
+
+ @Test
+ public void shortField() {
+ final short MAGIC = (short) 0xbeef;
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.s.set(ptr, MAGIC);
+
+ assertEquals("short field not set", MAGIC, testlib.struct_field_Signed16(ptr));
+ s.s.set(ptr, (short) 0);
+ assertEquals("short field not cleared", (short) 0, testlib.struct_field_Signed16(ptr));
+ }
+
+ @Test
+ public void intField() {
+ final int MAGIC = 0xdeadbeef;
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.i.set(ptr, MAGIC);
+
+ assertEquals("int field not set", MAGIC, testlib.struct_field_Signed32(ptr));
+ s.i.set(ptr, 0);
+ assertEquals("int field not cleared", 0, testlib.struct_field_Signed32(ptr));
+ }
+ @Test
+ public void int64Field() {
+ final long MAGIC = 0x1234deadbeef5678L;
+ struct1 s = new struct1();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.i64.set(ptr, MAGIC);
+
+ assertEquals("long field not set", MAGIC, testlib.struct_field_Signed64(ptr));
+ s.i64.set(ptr, 0);
+ assertEquals("long field not cleared", 0L, testlib.struct_field_Signed64(ptr));
+ }
+ @Test
+ public void alignInt16Field() {
+ final short MAGIC = (short) 0xbeef;
+ Int16Align s = new Int16Align();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.s.set(ptr, MAGIC);
+
+ assertEquals("short field not aligned", MAGIC, testlib.struct_align_Signed16(ptr));
+ }
+ @Test
+ public void alignSigned32Field() {
+ final int MAGIC = (int) 0xdeadbeef;
+ Int32Align s = new Int32Align();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.i.set(ptr, MAGIC);
+
+ assertEquals("int field not aligned", MAGIC, testlib.struct_align_Signed32(ptr));
+ }
+ @Test
+ public void alignSigned64Field() {
+ final long MAGIC = 0x1234deadbeef5678L;
+ Int64Align s = new Int64Align();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.l.set(ptr, MAGIC);
+
+ assertEquals("long field not aligned", MAGIC, testlib.struct_align_Signed64(ptr));
+ }
+ @Test
+ public void alignSignedLongField() {
+ final NativeLong MAGIC = new NativeLong(0xdeadbeef);
+ LongAlign s = new LongAlign();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ s.l.set(ptr, MAGIC);
+
+ assertEquals("native long field not aligned", MAGIC, testlib.struct_align_SignedLong(ptr));
+ }
+ @Test
+ public void returnStructAddress() throws Throwable {
+ final byte B = 0x11;
+ final short S = 0x2222;
+ final int I = 0x33333333;
+ final long L = 0x4444444444444444L;
+ final float F = (float) 0x55555555;
+ final double D = (double) 0x6666666666666666L;
+ struct1 s = new struct1();
+ Pointer ptr = testlib.struct_make_struct(B, S, I, L, F, D);
+ assertEquals("Incorrect byte value in struct", B, s.b.get(ptr));
+ assertEquals("Incorrect short value in struct", S, s.s.get(ptr));
+ assertEquals("Incorrect int value in struct", I, s.i.get(ptr));
+ assertEquals("Incorrect int64 value in struct", L, s.i64.get(ptr));
+ assertEquals("Incorrect float value in struct", F, s.f.get(ptr), 0.0001);
+ assertEquals("Incorrect double value in struct", D, s.d.get(ptr), 0.0001);
+
+ }
+ private static final class ArrayTest extends StructLayout {
+ public final Signed8[] byteArray = array(new Signed8[8]);
+
+ public ArrayTest() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void arrayMember() {
+ ArrayTest s = new ArrayTest();
+ assertEquals("First element not at correct offset", 0L, s.byteArray[0].offset());
+ assertEquals("Second element not at correct offset", 1L, s.byteArray[1].offset());
+ assertEquals("Last element not at correct offset", 7L, s.byteArray[7].offset());
+ }
+ private static final class Unsigned8Test extends StructLayout {
+ public final Unsigned8 u8 = new Unsigned8();
+
+ public Unsigned8Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned8() {
+ Unsigned8Test s = new Unsigned8Test();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ final short MAGIC = (short) Byte.MAX_VALUE + 1;
+ s.u8.set(ptr, MAGIC);
+ assertEquals("Incorrect unsigned byte value", MAGIC, s.u8.shortValue(ptr));
+ }
+ private static final class Unsigned16Test extends Struct {
+ public final Unsigned16 u16 = new Unsigned16();
+
+ public Unsigned16Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned16() {
+ Unsigned16Test s = new Unsigned16Test();
+ final int MAGIC = (int) Short.MAX_VALUE + 1;
+ s.u16.set(MAGIC);
+ assertEquals("Incorrect unsigned short value", MAGIC, s.u16.intValue());
+ }
+ private static final class Unsigned32Test extends Struct {
+ public final Unsigned32 u32 = new Unsigned32();
+
+ public Unsigned32Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned32() {
+ Unsigned32Test s = new Unsigned32Test();
+ final long MAGIC = (long) Integer.MAX_VALUE + 1;
+ s.u32.set(MAGIC);
+ assertEquals("Incorrect unsigned int value", MAGIC, s.u32.longValue());
+ }
+
+ private static final class Unsigned64Test extends StructLayout {
+ public final Unsigned64 u64 = new Unsigned64();
+
+ public Unsigned64Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned64() {
+ Unsigned64Test s = new Unsigned64Test();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ final long MAGIC = Long.MAX_VALUE;
+ s.u64.set(ptr, MAGIC);
+ assertEquals("Incorrect unsigned long long value", MAGIC, s.u64.longValue(ptr));
+ // Just make sure that an Unsigned64 doesn't do anything weird with negative values
+ s.u64.set(ptr, Long.MIN_VALUE);
+ assertEquals("Incorrect unsigned long long value", Long.MIN_VALUE, s.u64.longValue(ptr));
+ }
+
+ private static final class UnsignedLongTest extends StructLayout {
+ public final UnsignedLong ul = new UnsignedLong();
+
+ public UnsignedLongTest() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsignedLong() {
+ UnsignedLongTest s = new UnsignedLongTest();
+ Pointer ptr = Memory.allocate(runtime, s.size());
+ final long MAGIC = (long) Integer.MAX_VALUE;
+ s.ul.set(ptr, MAGIC);
+ assertEquals("Incorrect unsigned long value", MAGIC, s.ul.longValue(ptr));
+ }
+
+ private class InnerStruct extends StructLayout {
+ public final Signed8 s8 = new Signed8();
+
+ public InnerStruct() {
+ super(runtime);
+ }
+
+ }
+ private class InnerTest extends StructLayout {
+ public final Signed32 i32 = new Signed32();
+ public final InnerStruct s = inner(new InnerStruct());
+
+ public InnerTest() {
+ super(runtime);
+ }
+
+ }
+ @Test public void innerStruct() {
+ InnerTest t = new InnerTest();
+ Pointer ptr = Memory.allocate(runtime, t.size());
+ ptr.putInt(0, 0xdeadbeef);
+ ptr.putByte(4, (byte) 0x12);
+ assertEquals("incorrect inner struct field value", (byte) 0x12, t.s.s8.get(ptr));
+ }
+
+ static final class LongPadding extends StructLayout {
+
+ public final Signed8 s8 = new Signed8();
+ public final Padding pad = new Padding(NativeType.SLONG, 3);
+
+ public LongPadding() {
+ super(runtime);
+ }
+ }
+
+ @Test public void longPadding() throws Throwable {
+ Type longType = runtime.findType(NativeType.SLONG);
+ final int SIZE = longType.alignment() + (longType.size() * 3);
+ assertEquals("incorrect size", SIZE, new LongPadding().size());
+ }
+
+ static final class TailPadding extends StructLayout {
+
+ public final SignedLong sl = new SignedLong();
+ public final Signed8 s8 = new Signed8();
+
+
+ public TailPadding() {
+ super(runtime);
+ }
+ }
+
+ @Test public void tailPadding() throws Throwable {
+ Type longType = runtime.findType(NativeType.SLONG);
+ assertEquals("incorrect size", longType.size() * 2, new TailPadding().size());
+ assertEquals("incorrect alignment", longType.alignment(), new TailPadding().alignment());
+ }
+}
diff --git a/src/test/java/jnr/ffi/struct/StructureTest.java b/src/test/java/jnr/ffi/struct/StructureTest.java
new file mode 100644
index 0000000..10d8887
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/StructureTest.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.*;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.LongLong;
+import jnr.ffi.TstUtil;
+import jnr.ffi.types.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static jnr.ffi.TypeAlias.*;
+
+/**
+ *
+ */
+public class StructureTest {
+
+ public StructureTest() {
+ }
+
+ public static interface TestLib {
+ byte struct_field_Signed8(struct1 s);
+ short struct_field_Signed16(struct1 s);
+ int struct_field_Signed32(struct1 s);
+ @LongLong long struct_field_Signed64(struct1 s);
+ float struct_field_Float32(struct1 s);
+ double struct_field_Float64(struct1 s);
+ short struct_align_Signed16(Int16Align s);
+ int struct_align_Signed32(Int32Align s);
+ @LongLong long struct_align_Signed64(Int64Align s);
+ NativeLong struct_align_SignedLong(LongAlign s);
+ struct1 struct_make_struct(byte b, short s, int i, @LongLong long ll, float f, double d);
+// float struct_align_Float32(Float32Align s);
+// double struct_align_Float64(Float64Align s);
+// void struct_set_string(struct1 s, String string);
+ }
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ public static class struct1 extends Struct {
+ public final Signed8 b = new Signed8();
+ public final Signed16 s = new Signed16();
+ public final Signed32 i = new Signed32();
+ public final Signed64 i64 = new Signed64();
+ public final SignedLong l = new SignedLong();
+ public final Float f = new Float();
+ public final Double d = new Double();
+
+ public struct1(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+
+ }
+ public static class Int16Align extends Struct {
+ public final Signed8 first = new Signed8();
+ public final Signed16 s = new Signed16();
+
+ public Int16Align(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+ public static class Int32Align extends Struct {
+ public final Signed8 first = new Signed8();
+ public final Signed32 i = new Signed32();
+
+ public Int32Align(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+ public static class Int64Align extends Struct {
+ public final Signed8 first = new Signed8();
+ public final Signed64 l = new Signed64();
+
+ public Int64Align(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+ public static class LongAlign extends Struct {
+ public final Signed8 first = new Signed8();
+ public final SignedLong l = new SignedLong();
+
+ public LongAlign(jnr.ffi.Runtime runtime) {
+ super(runtime);
+ }
+
+ }
+
+ @Test public void testInt8InitialValue() {
+ struct1 s = new struct1(runtime);
+ assertEquals("default value not zero", (byte) 0, s.b.get());
+ }
+
+ @Test public void testInt8Set() {
+ struct1 s = new struct1(runtime);
+ final byte MAGIC = (byte) 0xfe;
+ s.b.set(MAGIC);
+ assertEquals("Byte value not set correctly", MAGIC, s.b.get());
+ }
+
+ @Test
+ public void byteField() {
+ final byte MAGIC = (byte) 0xbe;
+ struct1 s = new struct1(runtime);
+ s.b.set(MAGIC);
+
+ assertEquals("byte field not set", MAGIC, testlib.struct_field_Signed8(s));
+ s.b.set((byte) 0);
+ assertEquals("byte field not cleared", (byte) 0, testlib.struct_field_Signed8(s));
+ }
+
+ @Test
+ public void shortField() {
+ final short MAGIC = (short) 0xbeef;
+ struct1 s = new struct1(runtime);
+ s.s.set(MAGIC);
+
+ assertEquals("short field not set", MAGIC, testlib.struct_field_Signed16(s));
+ s.s.set((short) 0);
+ assertEquals("short field not cleared", (short) 0, testlib.struct_field_Signed16(s));
+ }
+
+ @Test
+ public void intField() {
+ final int MAGIC = 0xdeadbeef;
+ struct1 s = new struct1(runtime);
+ s.i.set(MAGIC);
+
+ assertEquals("int field not set", MAGIC, testlib.struct_field_Signed32(s));
+ s.i.set(0);
+ assertEquals("int field not cleared", 0, testlib.struct_field_Signed32(s));
+ }
+ @Test
+ public void int64Field() {
+ final long MAGIC = 0x1234deadbeef5678L;
+ struct1 s = new struct1(runtime);
+ s.i64.set(MAGIC);
+
+ assertEquals("long field not set", MAGIC, testlib.struct_field_Signed64(s));
+ s.i64.set(0);
+ assertEquals("long field not cleared", 0L, testlib.struct_field_Signed64(s));
+ }
+ @Test
+ public void alignInt16Field() {
+ final short MAGIC = (short) 0xbeef;
+ Int16Align s = new Int16Align(runtime);
+ s.s.set(MAGIC);
+
+ assertEquals("short field not aligned", MAGIC, testlib.struct_align_Signed16(s));
+ }
+ @Test
+ public void alignSigned32Field() {
+ final int MAGIC = (int) 0xdeadbeef;
+ Int32Align s = new Int32Align(runtime);
+ s.i.set(MAGIC);
+
+ assertEquals("int field not aligned", MAGIC, testlib.struct_align_Signed32(s));
+ }
+ @Test
+ public void alignSigned64Field() {
+ final long MAGIC = 0x1234deadbeef5678L;
+ Int64Align s = new Int64Align(runtime);
+ s.l.set(MAGIC);
+
+ assertEquals("long field not aligned", MAGIC, testlib.struct_align_Signed64(s));
+ }
+ @Test
+ public void alignSignedLongField() {
+ final NativeLong MAGIC = new NativeLong(0xdeadbeef);
+ LongAlign s = new LongAlign(runtime);
+ s.l.set(MAGIC);
+
+ assertEquals("native long field not aligned", MAGIC, testlib.struct_align_SignedLong(s));
+ }
+ @Test
+ public void returnStructAddress() throws Throwable {
+ final byte B = 0x11;
+ final short S = 0x2222;
+ final int I = 0x33333333;
+ final long L = 0x4444444444444444L;
+ final float F = (float) 0x55555555;
+ final double D = (double) 0x6666666666666666L;
+ struct1 s = testlib.struct_make_struct(B, S, I, L, F, D);
+ assertEquals("Incorrect byte value in struct", B, s.b.get());
+ assertEquals("Incorrect short value in struct", S, s.s.get());
+ assertEquals("Incorrect int value in struct", I, s.i.get());
+ assertEquals("Incorrect int64 value in struct", L, s.i64.get());
+ assertEquals("Incorrect float value in struct", F, s.f.get(), 0.0001);
+ assertEquals("Incorrect double value in struct", D, s.d.get(), 0.0001);
+
+ }
+ private static final class ArrayTest extends Struct {
+ public final Signed8[] byteArray = array(new Signed8[8]);
+
+ public ArrayTest() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void arrayMember() {
+ ArrayTest s = new ArrayTest();
+ assertEquals("First element not at correct offset", 0L, s.byteArray[0].offset());
+ assertEquals("Second element not at correct offset", 1L, s.byteArray[1].offset());
+ assertEquals("Last element not at correct offset", 7L, s.byteArray[7].offset());
+ }
+ private static final class Unsigned8Test extends Struct {
+ public final Unsigned8 u8 = new Unsigned8();
+
+ public Unsigned8Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned8() {
+ Unsigned8Test s = new Unsigned8Test();
+ final short MAGIC = (short) Byte.MAX_VALUE + 1;
+ s.u8.set(MAGIC);
+ assertEquals("Incorrect unsigned byte value", MAGIC, s.u8.shortValue());
+ }
+ private static final class Unsigned16Test extends Struct {
+ public final Unsigned16 u16 = new Unsigned16();
+
+ public Unsigned16Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned16() {
+ Unsigned16Test s = new Unsigned16Test();
+ final int MAGIC = (int) Short.MAX_VALUE + 1;
+ s.u16.set(MAGIC);
+ assertEquals("Incorrect unsigned short value", MAGIC, s.u16.intValue());
+ }
+ private static final class Unsigned32Test extends Struct {
+ public final Unsigned32 u32 = new Unsigned32();
+
+ public Unsigned32Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned32() {
+ Unsigned32Test s = new Unsigned32Test();
+ final long MAGIC = (long) Integer.MAX_VALUE + 1;
+ s.u32.set(MAGIC);
+ assertEquals("Incorrect unsigned int value", MAGIC, s.u32.longValue());
+ }
+
+ private static final class Unsigned64Test extends Struct {
+ public final Unsigned64 u64 = new Unsigned64();
+
+ public Unsigned64Test() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsigned64() {
+ Unsigned64Test s = new Unsigned64Test();
+ final long MAGIC = Long.MAX_VALUE;
+ s.u64.set(MAGIC);
+ assertEquals("Incorrect unsigned long long value", MAGIC, s.u64.longValue());
+ // Just make sure that an Unsigned64 doesn't do anything weird with negative values
+ s.u64.set(Long.MIN_VALUE);
+ assertEquals("Incorrect unsigned long long value", Long.MIN_VALUE, s.u64.longValue());
+ }
+
+ private static final class UnsignedLongTest extends Struct {
+ public final UnsignedLong ul = new UnsignedLong();
+
+ public UnsignedLongTest() {
+ super(runtime);
+ }
+
+ }
+ @Test
+ public void unsignedLong() {
+ UnsignedLongTest s = new UnsignedLongTest();
+ final long MAGIC = (long) Integer.MAX_VALUE;
+ s.ul.set(MAGIC);
+ assertEquals("Incorrect unsigned long value", MAGIC, s.ul.longValue());
+ }
+
+ private class InnerStruct extends Struct {
+ public final Signed8 s8 = new Signed8();
+
+ public InnerStruct() {
+ super(runtime);
+ }
+
+ }
+ private class InnerTest extends Struct {
+ public final Signed32 i32 = new Signed32();
+ public final InnerStruct s = inner(new InnerStruct());
+
+ public InnerTest() {
+ super(runtime);
+ }
+
+ }
+ @Test public void innerStruct() {
+ InnerTest t = new InnerTest();
+ Pointer io = Struct.getMemory(t);
+ io.putInt(0, 0xdeadbeef);
+ io.putByte(4, (byte) 0x12);
+ assertEquals("incorrect inner struct field value", (byte) 0x12, t.s.s8.get());
+ }
+
+ private class TypeTest extends Struct {
+ class Fixnum extends NumberField {
+ Fixnum(TypeAlias type) {
+ super(NativeType.SINT);
+ }
+
+ @Override
+ public void set(Number value) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int intValue() {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ }
+
+ class Fubar extends NumberField {
+ Fubar(Class<?> type) {
+ super(NativeType.SINT);
+ }
+
+ @Override
+ public void set(Number value) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public int intValue() {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ }
+ public final Fixnum i = new Fixnum(ssize_t);
+ public final Fubar fubar = new Fubar(ssize_t.class);
+
+ private TypeTest(Runtime runtime) {
+ super(runtime);
+ }
+ }
+
+ public static interface TestLib2 {
+ @ssize_t long write(int fd, Pointer buf, @size_t long len);
+ }
+
+}
diff --git a/src/test/java/jnr/ffi/struct/UTF8StringFieldTest.java b/src/test/java/jnr/ffi/struct/UTF8StringFieldTest.java
new file mode 100644
index 0000000..0d3495e
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/UTF8StringFieldTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.Struct;
+import jnr.ffi.TstUtil;
+import jnr.ffi.Library;
+import jnr.ffi.Runtime;
+import jnr.ffi.annotations.In;
+import jnr.ffi.annotations.Out;
+import jnr.ffi.annotations.Pinned;
+import jnr.ffi.annotations.Transient;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author wayne
+ */
+public class UTF8StringFieldTest {
+ public UTF8StringFieldTest() {
+ }
+
+ public static class StringFieldStruct extends Struct {
+ public final UTF8String string = new UTF8String(32);
+
+ public StringFieldStruct() {
+ super(runtime);
+ }
+
+ }
+ public static interface TestLib {
+ // This makes use of the string being the first field in the struct
+ int string_equals(@Pinned @In @Transient StringFieldStruct s1, String s2);
+ int copyByteBuffer(@Pinned @Out StringFieldStruct dst, @In byte[] src, int len);
+ int copyByteBuffer(@Pinned @Out byte[] dst, @Pinned @In @Transient StringFieldStruct src, int len);
+ int copyByteBuffer(@Pinned @Out StringBuilder dst, @Pinned @In @Transient StringFieldStruct src, int len);
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+ @Test public void stringFieldFirstInStruct() {
+ StringFieldStruct s = new StringFieldStruct();
+ final String MAGIC = "test";
+ s.string.set(MAGIC);
+ StringBuilder tmp = new StringBuilder(s.string.length());
+ testlib.copyByteBuffer(tmp, s, s.string.length());
+ assertEquals("String not put into struct correctly", MAGIC, tmp.toString());
+ }
+
+ public static final class SockaddrUnix extends Struct {
+ private final Signed8 sun_len = new Signed8();
+ private final Signed8 sun_family = new Signed8();
+ private final UTF8String sun_path = new UTF8String(100);
+
+ public SockaddrUnix() {
+ super(runtime);
+ }
+ }
+
+ @Test public void testSockaddrUnix() {
+ final int SUN_LEN = 1;
+ final int SUN_FAM = 2;
+ final String SUN_PATH = "test";
+
+ SockaddrUnix s = new SockaddrUnix();
+ s.sun_len.set(SUN_LEN);
+ s.sun_family.set(SUN_FAM);
+ s.sun_path.set(SUN_PATH);
+ assertEquals("Incorrect sun_len value", SUN_LEN, s.sun_len.intValue());
+ assertEquals("Incorrect sun_fam value", SUN_FAM, s.sun_family.intValue());
+ assertEquals("Incorrect sun_path value", SUN_PATH, s.sun_path.toString());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/jnr/ffi/struct/UnionTest.java b/src/test/java/jnr/ffi/struct/UnionTest.java
new file mode 100644
index 0000000..03c8d7b
--- /dev/null
+++ b/src/test/java/jnr/ffi/struct/UnionTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 Wayne Meissner
+ *
+ * This file is part of jffi.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 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 Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package jnr.ffi.struct;
+
+import jnr.ffi.Library;
+import jnr.ffi.Runtime;
+import jnr.ffi.TstUtil;
+import java.nio.ByteOrder;
+
+import jnr.ffi.Union;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class UnionTest {
+ public static interface TestLib {
+
+ }
+
+ public UnionTest() {
+ }
+
+ static TestLib testlib;
+ static Runtime runtime;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ testlib = TstUtil.loadTestLib(TestLib.class);
+ runtime = Runtime.getRuntime(testlib);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ public static final class union extends Union {
+ public final Signed8 s8 = new Signed8();
+ public final Unsigned8 u8 = new Unsigned8();
+ public final Signed16 s16 = new Signed16();
+ public final Unsigned16 u16 = new Unsigned16();
+ public final Signed32 s32 = new Signed32();
+ public final Unsigned32 u32 = new Unsigned32();
+ public final Signed64 s64 = new Signed64();
+ public final Unsigned64 u64 = new Unsigned64();
+
+ public union() {
+ super(runtime);
+ }
+
+
+ }
+ @Test public void offsetTest() {
+ union u = new union();
+ assertEquals("Not at offset 0", 0L, u.s8.offset());
+ assertEquals("Not at offset 0", 0L, u.u8.offset());
+ assertEquals("Not at offset 0", 0L, u.s16.offset());
+ assertEquals("Not at offset 0", 0L, u.u16.offset());
+ assertEquals("Not at offset 0", 0L, u.s32.offset());
+ assertEquals("Not at offset 0", 0L, u.s32.offset());
+ assertEquals("Not at offset 0", 0L, u.s64.offset());
+ assertEquals("Not at offset 0", 0L, u.u64.offset());
+ }
+ @Test public void s8s16() {
+ union u = new union();
+ final int MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xef00 : 0x00ef;
+ u.s16.set((short) MAGIC);
+ assertEquals("Wrong value", (byte) 0xef, u.s8.get());
+ }
+
+ @Test public void s8s32() {
+ union u = new union();
+ final int MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xef000000 : 0x000000ef;
+ u.s32.set(MAGIC);
+ assertEquals("Wrong value", (byte) 0xef, u.s8.get());
+ }
+
+ @Test public void s16s32() {
+ union u = new union();
+ final int MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xdead0000 : 0x0000dead;
+ u.s32.set(MAGIC);
+ assertEquals("Wrong value", (short) 0xdead, u.s16.get());
+ }
+ @Test public void s8s64() {
+ union u = new union();
+ final long MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xef00000000000000L : 0xefL;
+ u.s64.set(MAGIC);
+ assertEquals("Wrong value", (byte) 0xef, u.s8.get());
+ }
+
+ @Test public void s16s64() {
+ union u = new union();
+ final long MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xbeef000000000000L : 0xbeefL;
+ u.s64.set(MAGIC);
+ assertEquals("Wrong value", (short) 0xbeef, u.s16.get());
+ }
+
+ @Test public void s32s64() {
+ union u = new union();
+ final long MAGIC = runtime.byteOrder().equals(ByteOrder.BIG_ENDIAN)
+ ? 0xdeadbeef00000000L : 0xdeadbeefL;
+ u.s64.set(MAGIC);
+ assertEquals("Wrong value", 0xdeadbeef, u.s32.get());
+ }
+
+}
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jnr-ffi.git
More information about the pkg-java-commits
mailing list