Home  >  Article  >  Java  >  Java's JNA type mapping attention to details and usage

Java's JNA type mapping attention to details and usage

王林
王林forward
2023-04-18 17:07:031323browse

String

The first is the mapping of String. String in JAVA actually corresponds to two native types: const char* and const wchar_t*. By default String will be converted to char*.

char is an ANSI data type, and wchar_t is a Unicode character data type, also called a wide character.

If JAVA's unicode characters are to be converted into a char array, some encoding operations need to be performed. If jna.encoding is set, the set encoding method will be used for encoding. By default, the encoding method is "UTF8".

If it is WString, Unicode values ​​can be copied directly to WString without any encoding.

Let’s look at a simple example first:

char* returnStringArgument(char *arg) {
  return arg;
}

wchar_t* returnWStringArgument(wchar_t *arg) {
  return arg;
}

The above native code can be mapped to:

String returnStringArgument(String s);
WString returnWStringArgument(WString s);

Let’s look at a different example. If the definition of the native method is like this :

int getString(char* buffer, int bufsize);

int getUnicodeString(wchar_t* buffer, int bufsize);

We have defined two methods, the parameters of which are char* and wchar_t*.

Next, let’s take a look at how to define method mapping in JAVA:

// Mapping A:
int getString(byte[] buf, int bufsize);
// Mapping B:
int getUnicodeString(char[] buf, int bufsize);

The following is the specific use:

byte[] buf = new byte[256];
int len = getString(buf, buf.length);
String normalCString = Native.toString(buf);
String embeddedNULs = new String(buf, 0, len);

Some students may ask, since in JAVA String can be converted into char*, why do we need to use byte array here?

This is because the getString method needs to modify the contents of the incoming char array, but because String is immutable, String cannot be used directly here. We need to use a byte array.

Then we use Native.toString(byte[]) to convert the byte array into a JAVA string.

Look at another return value situation:

// Example A: Returns a C string directly
const char* getString();
// Example B: Returns a wide character C string directly
const wchar_t* getString();

Generally, if the native method returns string directly, we can use String for mapping:

// Mapping A
String getString();
// Mapping B
WString getString();

If the native code After allocating memory space for String, we'd better use Pointer in JNA as the return value, so that we can release the occupied space at some point in the future, as shown below:

Pointer getString();

Buffers, Memory, Arrays and Pointers

When do you need to use Buffers and Memory?

Generally, if an array of basic data is passed as a parameter to a function, you can use the basic class directly in JAVA array instead. But if the native method still needs to access the array after the method returns (save the pointer to the array), in this case it is not appropriate to use the array of the base class. In this case, we need to use ByteBuffers or Memory .

We know that arrays in JAVA have lengths, but for native methods, the returned array is actually a pointer to the array, and we do not know the length of the returned array, so if the native method If an array pointer is returned, it is inappropriate to use arrays for mapping in JAVA code. In this case, you need to use Pointer.

Pointer represents a pointer. Let’s look at the example of Pointer first. The first is the native code:

void* returnPointerArgument(void *arg) {
  return arg;
}

void* returnPointerArrayElement(void* args[], int which) {
  return args[which];
}

The next is the JAVA mapping:

Pointer returnPointerArgument(Pointer p);
Pointer returnPointerArrayElement(Pointer[] args, int which);

In addition to the basic Pointer, you can also customize the typed Pointer, which is PointerType. You only need to inherit PointerType, as shown below:

public static class TestPointerType extends PointerType {
            public TestPointerType() { }
            public TestPointerType(Pointer p) { super(p); }
        }
TestPointerType returnPointerArrayElement(TestPointerType[] args, int which);

Look at the string again Array:

char* returnStringArrayElement(char* args[], int which) {
  return args[which];
}
wchar_t* returnWideStringArrayElement(wchar_t* args[], int which) {
  return args[which];
}

The corresponding JAVA mapping is as follows:

String returnStringArrayElement(String[] args, int which);

WString returnWideStringArrayElement(WString[] args, int which);

Corresponding to Buffer, JAVA NIO provides many types of buffers, such as ByteBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer and DoubleBuffer, etc. . Here we take ByteBuffer as an example to see the specific usage.

First look at the native code:

int32_t fillInt8Buffer(int8_t *buf, int len, char value) {
  int i;

  for (i=0;i < len;i++) {
    buf[i] = value;
  }
  return len;
}

The buffer is filled here. Obviously, this buffer needs to be used later, so here It is not appropriate to use an array. We can choose to use ByteBuffer:

int fillInt8Buffer(ByteBuffer buf, int len, byte value);

and then see how to use it:

TestLibrary lib = Native.load("testlib", TestLibrary.class);

        ByteBuffer buf  = ByteBuffer.allocate(1024).order(ByteOrder.nativeOrder());
        final byte MAGIC = (byte)0xED;
        lib.fillInt8Buffer(buf, 1024, MAGIC);
        for (int i=0;i < buf.capacity();i++) {
            assertEquals("Bad value at index " + i, MAGIC, buf.get(i));
        }

Variable parameters

For native and JAVA itself , all support variable parameters. Let’s take an example. In the native method:

int32_t addVarArgs(const char *fmt, ...) {
  va_list ap;
  int32_t sum = 0;
  va_start(ap, fmt);

  while (*fmt) {
    switch (*fmt++) {
    case &#39;d&#39;:
      sum += va_arg(ap, int32_t);
      break;
    case &#39;l&#39;:
      sum += (int) va_arg(ap, int64_t);
      break;
    case &#39;s&#39;: // short (promoted to &#39;int&#39; when passed through &#39;...&#39;) 
    case &#39;c&#39;: // byte/char (promoted to &#39;int&#39; when passed through &#39;...&#39;)
      sum += (int) va_arg(ap, int);
      break;
    case &#39;f&#39;: // float (promoted to ‘double&#39; when passed through ‘...&#39;)
    case &#39;g&#39;: // double
      sum += (int) va_arg(ap, double);
      break;
    default:
      break;
    }
  }
  va_end(ap);
  return sum;
}

The corresponding JAVA method mapping is as follows:

public int addVarArgs(String fmt, Number... args);

The corresponding calling code is as follows:

int arg1 = 1;
int arg2 = 2;
assertEquals("32-bit integer varargs not added correctly", arg1 + arg2,
                     lib.addVarArgs("dd", arg1, arg2));

The above is the detailed content of Java's JNA type mapping attention to details and usage. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete