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 #30 #254

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
70 changes: 61 additions & 9 deletions src/main/java/jnr/ffi/Struct.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
import jnr.ffi.provider.ParameterFlags;
import jnr.ffi.provider.jffi.ArrayMemoryIO;
import jnr.ffi.util.EnumMapper;
import jnr.ffi.Platform;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
* Representation of C structures in java.
Expand Down Expand Up @@ -2291,7 +2293,7 @@ public Enum(Class<T> enumClass) {

abstract public class String extends AbstractMember {
protected final Charset charset;
protected final int length;
protected int length;

protected String(int size, int align, int length, Charset cs) {
super(size, align);
Expand All @@ -2318,8 +2320,8 @@ public final java.lang.String toString() {
}

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
public UTFString(int lengthInBytes, Charset cs) {
super(lengthInBytes * 8, 8, lengthInBytes, cs); // FIXME: This won't work for non-ASCII

}
protected jnr.ffi.Pointer getStringMemory() {
Expand Down Expand Up @@ -2347,6 +2349,12 @@ public AsciiString(int size) {
}
}

public class WString extends UTFString {
public WString(int sizeInChars) {
super(sizeInChars * getWideCharWidthInBytes(), getCharset());
}
}

public class UTFStringRef extends String {
private jnr.ffi.Pointer valueHolder;

Expand All @@ -2369,16 +2377,29 @@ public final java.lang.String get() {
}

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 {
if(value == null) {
this.valueHolder = null;
getMemory().putAddress(offset(), 0);
return;
}

value += "\0";
byte[] bytes = value.getBytes(charset);
if(bytes.length > length || valueHolder == null) {
valueHolder = getRuntime().getMemoryManager().allocateDirect(bytes.length);
length = bytes.length;
getMemory().putPointer(offset(), valueHolder);
}
valueHolder.put(0, bytes, 0, bytes.length);
}

public void reAllocate(int sizeBytes) {
valueHolder = getRuntime().getMemoryManager().allocateDirect(sizeBytes);
length = sizeBytes;
getMemory().putPointer(offset(), valueHolder);
}

//TODO: implement bool isNull()
}

public class UTF8StringRef extends UTFStringRef {
Expand All @@ -2399,6 +2420,37 @@ public AsciiStringRef() {
}
}

public class WStringRef extends UTFStringRef {
public WStringRef(int sizeInChars) {
super(sizeInChars * getWideCharWidthInBytes(), getCharset());
}

public WStringRef() {
super(Integer.MAX_VALUE, getCharset());
}

public final void setMaxLength(int sizeInChars) {
length = sizeInChars * getWideCharWidthInBytes();
}

@Override
public void reAllocate(int sizeInChars){
super.reAllocate(sizeInChars * getWideCharWidthInBytes());
}
}

private static Charset getCharset() {
if(Platform.getPlatform().getOS() == Platform.OS.WINDOWS) {
return StandardCharsets.UTF_16LE;
} else {
return Charset.forName("UTF-32LE");//unless -fshort-wchar is used for compiling native libs
}
}

private static int getWideCharWidthInBytes() {
return Platform.getPlatform().getOS() == Platform.OS.WINDOWS ? 2 : 4;
}

/**
* Specialized padding fields for structs. Use this instead of arrays of other
* members for more efficient struct construction.
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/jnr/ffi/provider/converters/StringUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import java.util.Arrays;
import java.util.Collection;

final class StringUtil {
final public class StringUtil {
private StringUtil() {}

static CharsetEncoder getEncoder(Charset charset, ThreadLocal<Reference<CharsetEncoder>> localEncoder) {
Expand Down Expand Up @@ -121,7 +121,7 @@ static void throwException(CoderResult result) {
private static final Charset UTF16LE = Charset.forName("UTF-16LE");
private static final Charset UTF16BE = Charset.forName("UTF-16BE");

static int terminatorWidth(Charset charset) {
public static int terminatorWidth(Charset charset) {
if (charset.equals(UTF8) || charset.equals(USASCII) || charset.equals(ISO8859_1)) {
return 1;

Expand Down
39 changes: 37 additions & 2 deletions src/main/java/jnr/ffi/provider/jffi/DirectMemoryIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import jnr.ffi.Runtime;
import jnr.ffi.provider.AbstractMemoryIO;
import jnr.ffi.provider.DelegatingMemoryIO;
import jnr.ffi.provider.converters.StringUtil;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
Expand Down Expand Up @@ -186,8 +187,42 @@ public String getString(long offset) {


public String getString(long offset, int maxLength, Charset cs) {
final byte[] bytes = IO.getZeroTerminatedByteArray(address() + offset, maxLength);
return cs.decode(ByteBuffer.wrap(bytes)).toString();
long baseAddress = address() + offset;
int nullTermSize = StringUtil.terminatorWidth(cs);
if(nullTermSize == 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting throughout should match the rest of the code, which is intended to match typical Java coding conventions:

  • four space indentation for blocks of code, 8-space indentation for line continuation
  • space between keywords like if and while and their parenthesized conditionals
  • spaces around operators like + and %

It's nitpicky but if we don't maintain consistent code formatting then we end up with future commits and PRs that have lots of unrelated changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, will take care of that

final byte[] bytes = IO.getZeroTerminatedByteArray(baseAddress, maxLength);
return cs.decode(ByteBuffer.wrap(bytes)).toString();
} else {
if(address() == 0) {
return null;
}

int nullTerminatedLen = 0;
int matchingBytesCount = 0;
int i = 0;
while(i < maxLength) {
if(IO.getByte(baseAddress+i) == 0) {
matchingBytesCount++;
i++;
} else {
matchingBytesCount = 0;
i += nullTermSize - (i%nullTermSize);//jump to start of next character
continue;
}
if(matchingBytesCount == nullTermSize) {
nullTerminatedLen = i-nullTermSize;//trim to the last byte just before null terminator
break;
}
}

if(nullTerminatedLen == 0) {
return "";
}

byte[] bytes = new byte[nullTerminatedLen];
IO.getByteArray(baseAddress, bytes, 0, nullTerminatedLen);
return new String(bytes, 0, bytes.length, cs);
}
}

public void putString(long offset, String string, int maxLength, Charset cs) {
Expand Down
46 changes: 45 additions & 1 deletion src/main/java/jnr/ffi/util/BufferUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;

import jnr.ffi.provider.converters.StringUtil;

/**
*
*/
Expand Down Expand Up @@ -64,7 +66,8 @@ 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);
final byte[] nullCharBytes = new byte[StringUtil.terminatorWidth(charset)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can cache the three known lengths and not reallocate this byte[] every time?

int end = indexOf(buffer, nullCharBytes) - nullCharBytes.length;
if (end < 0) {
end = buffer.limit();
}
Expand Down Expand Up @@ -141,6 +144,47 @@ public static int indexOf(ByteBuffer buf, byte value) {
return -1;
}

public static int indexOf(ByteBuffer buf, byte[] value) {
int matchingBytesCount = 0;
int offset = 0;
if (buf.hasArray()) {
byte[] array = buf.array();
int begin = buf.arrayOffset() + buf.position();
int end = buf.arrayOffset() + buf.limit();
while(offset < end && offset > -1) {
if(array[begin + offset] == value[matchingBytesCount]) {
matchingBytesCount++;
offset++;
} else {
matchingBytesCount = 0;
offset += value.length - (offset%value.length);//jump to start of next character
continue;
}

if(matchingBytesCount == value.length) {
return offset;
}
}
} else {
int begin = buf.position();
while(offset < buf.limit()) {
if(buf.get(begin + offset) == value[matchingBytesCount]) {
matchingBytesCount++;
offset++;
} else {
matchingBytesCount = 0;
offset += value.length - (offset%value.length);//jump to start of next character
continue;
}

if(matchingBytesCount == value.length) {
return offset;
}
}
}
return -1;
}

public static int indexOf(ByteBuffer buf, int offset, byte value) {
if (buf.hasArray()) {
byte[] array = buf.array();
Expand Down