Skip to content

Commit

Permalink
Fix usage of Library.OPTION_STRING_ENCODING property in functions
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasblaesing committed Feb 2, 2023
1 parent 195ebb4 commit 0c74703
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Features

Bug Fixes
---------

* [#1501](https://github.com/java-native-access/jna/pulls/pulls): `Library.OPTION_STRING_ENCODING` is ignore for string arguments function calls - [@matthiasblaesing](https://github.com/matthiasblaesing).

Release (5.13.0)
================
Expand Down
31 changes: 31 additions & 0 deletions native/testlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ extern "C" {
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#if !defined(_WIN32_WCE)
#include <errno.h>
#endif
Expand Down Expand Up @@ -1078,6 +1080,35 @@ returnLastElementOfComponentsDSDAL(DemoStructureDifferentArrayLengths ts, int de
return result;
}

/**
* Copy the input char array to the output char array. The caller is responsible
* to allocate a correctly sized buffer.
*/
EXPORT size_t copyString(char* input, char* output) {
size_t len = strlen(input) + 1;
memcpy(output, input, len);
return len;
}

/**
* Copy the input array of char arrays to the output char array. The caller is
* responsible to allocate a correctly sized buffer.
*/
EXPORT size_t copyStringArray(char** input, char* output) {
size_t len = 0;
for(int i = 0;; i++) {
char* currInput = input[i];
if(currInput == NULL) {
break;
}
size_t localLen = strlen(currInput) + 1;
memcpy(output, currInput, localLen);
output += localLen;
len += localLen;
}
return len;
}

#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion src/com/sun/jna/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ private Object convertArgument(Object[] args, int index,
// than in native code so that the values will be valid until
// this method returns.
// Convert String to native pointer (const)
return new NativeString((String)arg, false).getPointer();
return new NativeString((String)arg, encoding).getPointer();
} else if (arg instanceof WString) {
// Convert WString to native pointer (const)
return new NativeString(arg.toString(), true).getPointer();
Expand Down
140 changes: 140 additions & 0 deletions test/com/sun/jna/FunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
*/
package com.sun.jna;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Collections;
import junit.framework.TestCase;
import org.junit.Assert;

/** Exercise the {@link Function} class.
*
Expand All @@ -32,6 +36,29 @@
//@SuppressWarnings("unused")
public class FunctionTest extends TestCase {

private NativeLibrary libUTF8;
private NativeLibrary libLatin1;
private TestLibUTF8 libUTF8Direct;
private TestLibLatin1 libLatin1Direct;
private TestLib libUTF8Interface;
private TestLib libLatin1Interface;

@Override
protected void setUp() {
libUTF8 = NativeLibrary.getInstance("testlib",
Collections.singletonMap(Library.OPTION_STRING_ENCODING, "UTF-8"));
libLatin1 = NativeLibrary.getInstance("testlib",
Collections.singletonMap(Library.OPTION_STRING_ENCODING, "ISO-8859-1"));
Native.register(TestLibUTF8.class, libUTF8);
Native.register(TestLibLatin1.class, libLatin1);
libUTF8Direct = new TestLibUTF8();
libLatin1Direct = new TestLibLatin1();
libUTF8Interface = Native.load("testlib", TestLib.class,
Collections.singletonMap(Library.OPTION_STRING_ENCODING, "UTF-8"));
libLatin1Interface = Native.load("testlib", TestLib.class,
Collections.singletonMap(Library.OPTION_STRING_ENCODING, "ISO-8859-1"));
}

public void testTooManyArgs() {
NativeLibrary lib = NativeLibrary.getInstance(Platform.C_LIBRARY_NAME);
Function f = lib.getFunction("printf");
Expand All @@ -58,8 +85,121 @@ public void testUnsupportedReturnType() {
}
}

public void testStringEncodingArgument() throws UnsupportedEncodingException {
// String with german umlauts
String input = "Hallo äöüß";
byte[] result = new byte[32];
Arrays.fill(result, (byte) 0);
libUTF8Interface.copyString(input, result);
Assert.assertArrayEquals(toByteArray(input, "UTF-8", 32), result);
Arrays.fill(result, (byte) 0);
libLatin1Interface.copyString(input, result);
Assert.assertArrayEquals(toByteArray(input, "ISO-8859-1", 32), result);

// String array with german umlauts
String[] inputArray = new String[]{"1Hallo äöüß1", "2Hallo äöüß2"};
result = new byte[64];
Arrays.fill(result, (byte) 0);
libUTF8Interface.copyStringArray(inputArray, result);
Assert.assertArrayEquals(toByteArray(inputArray, "UTF-8", 64), result);
Arrays.fill(result, (byte) 0);
libLatin1Interface.copyStringArray(inputArray, result);
Assert.assertArrayEquals(toByteArray(inputArray, "ISO-8859-1", 64), result);
}

public void testStringEncodingArgumentDirect() throws UnsupportedEncodingException {
// String with german umlauts
String input = "Hallo äöüß";
byte[] result = new byte[32];
Arrays.fill(result, (byte) 0);
libUTF8Direct.copyString(input, result);
Assert.assertArrayEquals(toByteArray(input, "UTF-8", 32), result);
Arrays.fill(result, (byte) 0);
libLatin1Direct.copyString(input, result);
Assert.assertArrayEquals(toByteArray(input, "ISO-8859-1", 32), result);
}

public void testStringReturn() throws UnsupportedEncodingException {
// String with german umlauts
String input = "Hallo äöüß";

String result;
Memory mem = new Memory(32);
mem.clear();
mem.write(0, input.getBytes("UTF-8"), 0, input.getBytes("UTF-8").length);
result = libUTF8Interface.returnStringArgument(mem);
assertEquals(input, result);
mem.clear();
mem.write(0, input.getBytes("ISO-8859-1"), 0, input.getBytes("ISO-8859-1").length);
result = libLatin1Interface.returnStringArgument(mem);
assertEquals(input, result);
}

public void testStringReturnDirect() throws UnsupportedEncodingException {
// String with german umlauts
String input = "Hallo äöüß";

String result;
Memory mem = new Memory(32);
mem.clear();
mem.write(0, input.getBytes("UTF-8"), 0, input.getBytes("UTF-8").length);
result = libUTF8Direct.returnStringArgument(mem);
assertEquals(input, result);
mem.clear();
mem.write(0, input.getBytes("ISO-8859-1"), 0, input.getBytes("ISO-8859-1").length);
result = libLatin1Direct.returnStringArgument(mem);
assertEquals(input, result);
}

private byte[] toByteArray(String input, String encoding, int targetLength) throws UnsupportedEncodingException {
byte[] result = new byte[targetLength];
byte[] encoded = input.getBytes(encoding);
System.arraycopy(encoded, 0, result, 0, encoded.length);
return result;
}

private byte[] toByteArray(String[] input, String encoding, int targetLength) throws UnsupportedEncodingException {
byte[] result = new byte[targetLength];
int offset = 0;
for(String currInput: input) {
byte[] encoded = currInput.getBytes(encoding);
System.arraycopy(encoded, 0, result, offset, encoded.length);
offset += encoded.length;
offset++;
}
return result;
}

public static void main(java.lang.String[] argList) {
junit.textui.TestRunner.run(FunctionTest.class);
}

private static class TestLibUTF8 implements Library {
native String returnStringArgument(Pointer input);
native SizeT copyString(String input, byte[] output);
}

private static class TestLibLatin1 implements Library {
native String returnStringArgument(Pointer input);
native SizeT copyString(String input, byte[] output);
}

private interface TestLib extends Library {
public String returnStringArgument(Pointer input);
public SizeT copyString(String input, byte[] output);
public SizeT copyStringArray(String[] input, byte[] output);
}

private static class SizeT extends IntegerType {
public static final int SIZE = Native.SIZE_T_SIZE;

public SizeT() {
this(0);
}

public SizeT(long value) {
super(SIZE, value, true);
}

}
}

0 comments on commit 0c74703

Please sign in to comment.