Introduction to the

There are many types of mapping in JNA, library mapping, function mapping, function parameter and return value mapping, libary mapping to functions is relatively easy, as we have covered in previous articles. For type mapping, there are so many types in JAVA, So here we will isolate the type mapping of JNA.

The nature of type mapping

We mentioned earlier that there are two methods in JNA to map JAVA methods to native Libary methods. One method is called Interface Mapping and the other is called Direct Mapping.

But have we thought about the nature of these two mappings?

For example, native has a method. How do we pass method parameters in JAVA code to Native method and convert the return value of native method into the return type of JAVA function?

The answer is serialization.

Because essentially all interactions are binary interactions. The simplest case for converting a JAVA type to a Native type is that the underlying data length of the JAVA type is the same as that of the Native type. In this way, data conversion is easier.

Let’s look at the mapping and length relationship between JAVA types and Native types:

C Type The meaning of Native type Java Type
char 8-bit integer byte
wchar_t Platform specific char
short 16 – bit integer short
int 32 – bit integer int
int boolean flag boolean
enum Enumerated type int (usually)
long long, __int64 The 64 – bit integer long
float 32 – bit floating point number float
double The 64 – bit floating point number double
pointer (e.g. void*) Platform specific Buffer Pointer
pointer (e.g. void*), array Platform specific

[] (Primitive array)

All of the above JAVA types are JDK native (except Pointer).

In addition to JAVA’s built-in type mapping, JNA also defines some data types that can be mapped to native types:

C Type The meaning of Native type Java Type
long Platform dependent (32-bit or 64-bit integer) NativeLong
const char* String (native encoding or jna.encoding) String
const wchar_t* String (unicode) WString
char** String array String[]
wchar_t** String Arrays (Unicode) WString[]
void** An array of Pointers Pointer[]
struct* struct Structure pointer and structure Structure
union The structure of the body Union
struct[] Struct array Structure[]
void (*FP)() Function Pointers (Java or Native) Callback
pointer ( *) Pointer to the PointerType
other Integer types IntegerType
other Custom mapping type NativeMapped

TypeMapper

In addition to the defined mapping, you can also use TypeMapper to convert parameter types.

public interface TypeMapper { FromNativeConverter getFromNativeConverter(Class<? > javaType); ToNativeConverter getToNativeConverter(Class<? > javaType); }Copy the code

TypeMapper is an interface that defines two Converter methods, getFromNativeConverter and getToNativeConverter.

If you want to use TypeMapper you need to implement it and these two methods will do. Let’s take a look at the official W32APITypeMapper:

 TypeConverter stringConverter = new TypeConverter() {
                @Override
                public Object toNative(Object value, ToNativeContext context) {
                    if (value == null)
                        return null;
                    if (value instanceof String[]) {
                        return new StringArray((String[])value, true);
                    }
                    return new WString(value.toString());
                }
                @Override
                public Object fromNative(Object value, FromNativeContext context) {
                    if (value == null)
                        return null;
                    return value.toString();
                }
                @Override
                public Class<?> nativeType() {
                    return WString.class;
                }
            };
            addTypeConverter(String.class, stringConverter);
            addToNativeConverter(String[].class, stringConverter);
Copy the code

First, define a TypeConverter, in which three methods toNative, fromNative and nativeType are implemented. In this example, the native type is WString and the JAVA type is String. And this TypeConverter is the FromNativeConverter and ToNativeConverter that will eventually be used.

Now that you have a typeMapper, how do you use it? The easiest way to do this is to add it to the third parameter of native.load, as follows:

 TestLibrary lib = Native.load("testlib", TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper));
Copy the code

NativeMapped

The TypeMapper needs to be passed in when the Native. Load method is called to provide the conversion relationship between JAVA types and Native types. The TypeMapper can be thought of as an external maintainer of the type conversion relationship.

If you can maintain a conversion relationship outside of a JAVA type, can you maintain the conversion relationship inside the JAVA type itself? The answer is yes, we simply implemented NativeMapped interface in the JAVA type where the conversion type relationship was implemented.

(NativeMapped interface)

public interface NativeMapped { Object fromNative(Object nativeValue, FromNativeContext context); Object toNative(); Class<? > nativeType(); }Copy the code

You can see that the methods to be implemented in NativeMapped were basically the same as in FromNativeConverter and ToNativeConverter.

It’s no use saying NativeMapped. (Apped!) (” NativeMapped “) (” NativeMapped “)

    public enum TestEnum implements NativeMapped {
        VALUE1, VALUE2;

        @Override
        public Object fromNative(Object nativeValue, FromNativeContext context) {
            return values()[(Integer) nativeValue];
        }

        @Override
        public Object toNative() {
            return ordinal();
        }

        @Override
        public Class<?> nativeType() {
            return Integer.class;
        }
    }
Copy the code

This class implements conversions from Integer to TestEnum enumeration.

To use the TestEnum class, define an interface:


    public static interface EnumerationTestLibrary extends Library {
        TestEnum returnInt32Argument(TestEnum arg);
    }
Copy the code

The specific call logic is as follows:

EnumerationTestLibrary lib = Native.load("testlib", EnumerationTestLibrary.class);
assertEquals("Enumeration improperly converted", TestEnum.VALUE1, lib.returnInt32Argument(TestEnum.VALUE1));
assertEquals("Enumeration improperly converted", TestEnum.VALUE2, lib.returnInt32Argument(TestEnum.VALUE2));
Copy the code

It was no longer necessary to specify TypeMapper because NativeMapped contained the conversion information.

Note that testlib is used here. This testlib is compiled from the JNA Native module. If you’re on a MAC, you can copy the JNA code and run Ant Native to get it. Copy libtestlib.dylib to the Resources directory in your project and darwin-aarch64 or Darwin-x86 will do.

Have not the classmate, can contact me.

conclusion

This article explained the type mapping rules in JNA and the methods for customizing type mappings.

The code for this article: github.com/ddean2009/l…

This article is available at www.flydean.com

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!