[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 [![Build Status](https://travis-ci.org/jnr/jnr-ffi.png)](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