Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix usage of encoding property for Function #1501

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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/pull/1501): `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);
}

}
}