package org.jruby.ext.ffi.jffi;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import org.jruby.Ruby;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.ext.ffi.CallbackInfo;
import org.jruby.ext.ffi.NativeType;
import org.jruby.ext.ffi.StructLayout;
import org.jruby.ext.ffi.Type;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
public final class FFIUtil {
private static final com.kenai.jffi.MemoryIO IO = com.kenai.jffi.MemoryIO.getInstance();
private FFIUtil() {}
private static final Map<NativeType, com.kenai.jffi.Type> typeMap = buildTypeMap();
private static final Map<NativeType, com.kenai.jffi.Type> buildTypeMap() {
Map<NativeType, com.kenai.jffi.Type> m = new EnumMap<NativeType, com.kenai.jffi.Type>(NativeType.class);
m.put(NativeType.VOID, com.kenai.jffi.Type.VOID);
m.put(NativeType.BOOL, com.kenai.jffi.Type.UINT8);
m.put(NativeType.CHAR, com.kenai.jffi.Type.SCHAR);
m.put(NativeType.SHORT, com.kenai.jffi.Type.SSHORT);
m.put(NativeType.INT, com.kenai.jffi.Type.SINT);
m.put(NativeType.LONG, com.kenai.jffi.Type.SLONG);
m.put(NativeType.LONG_LONG, com.kenai.jffi.Type.SLONG_LONG);
m.put(NativeType.UCHAR, com.kenai.jffi.Type.UCHAR);
m.put(NativeType.USHORT, com.kenai.jffi.Type.USHORT);
m.put(NativeType.UINT, com.kenai.jffi.Type.UINT);
m.put(NativeType.ULONG, com.kenai.jffi.Type.ULONG);
m.put(NativeType.ULONG_LONG, com.kenai.jffi.Type.ULONG_LONG);
m.put(NativeType.FLOAT, com.kenai.jffi.Type.FLOAT);
m.put(NativeType.DOUBLE, com.kenai.jffi.Type.DOUBLE);
m.put(NativeType.POINTER, com.kenai.jffi.Type.POINTER);
m.put(NativeType.BUFFER_IN, com.kenai.jffi.Type.POINTER);
m.put(NativeType.BUFFER_OUT, com.kenai.jffi.Type.POINTER);
m.put(NativeType.BUFFER_INOUT, com.kenai.jffi.Type.POINTER);
m.put(NativeType.STRING, com.kenai.jffi.Type.POINTER);
m.put(NativeType.TRANSIENT_STRING, com.kenai.jffi.Type.POINTER);
return m;
}
static final com.kenai.jffi.Type getFFIType(Type type) {
Object jffiType;
if ((jffiType = type.getFFIHandle()) instanceof com.kenai.jffi.Type) {
return (com.kenai.jffi.Type) jffiType;
}
return cacheFFIType(type);
}
private static com.kenai.jffi.Type cacheFFIType(Type type) {
Object ffiType;
synchronized (type) {
if ((ffiType = type.getFFIHandle()) instanceof com.kenai.jffi.Type) {
return (com.kenai.jffi.Type) ffiType;
}
if (type instanceof Type.Builtin || type instanceof CallbackInfo) {
ffiType = FFIUtil.getFFIType(type.getNativeType());
} else if (type instanceof org.jruby.ext.ffi.StructLayout) {
ffiType = FFIUtil.newStruct((org.jruby.ext.ffi.StructLayout) type);
} else if (type instanceof org.jruby.ext.ffi.StructByValue) {
ffiType = FFIUtil.newStruct(((org.jruby.ext.ffi.StructByValue) type).getStructLayout());
} else if (type instanceof org.jruby.ext.ffi.Type.Array) {
ffiType = FFIUtil.newArray((org.jruby.ext.ffi.Type.Array) type);
} else if (type instanceof org.jruby.ext.ffi.MappedType) {
ffiType = FFIUtil.getFFIType(((org.jruby.ext.ffi.MappedType) type).getRealType());
} else {
return null;
}
type.setFFIHandle(ffiType);
}
return (com.kenai.jffi.Type) ffiType;
}
static final com.kenai.jffi.Type getFFIType(NativeType type) {
return typeMap.get(type);
}
static final com.kenai.jffi.Aggregate newStruct(org.jruby.ext.ffi.StructLayout layout) {
if (layout.isUnion()) {
com.kenai.jffi.Type[] alignmentTypes = {
com.kenai.jffi.Type.SINT8,
com.kenai.jffi.Type.SINT16,
com.kenai.jffi.Type.SINT32,
com.kenai.jffi.Type.SINT64,
com.kenai.jffi.Type.FLOAT,
com.kenai.jffi.Type.DOUBLE,
com.kenai.jffi.Type.LONGDOUBLE,
};
com.kenai.jffi.Type alignmentType = null;
for (com.kenai.jffi.Type t : alignmentTypes) {
if (t.alignment() == layout.getNativeAlignment()) {
alignmentType = t;
break;
}
}
if (alignmentType == null) {
throw layout.getRuntime().newRuntimeError("cannot discern base alignment type for union of alignment "
+ layout.getNativeAlignment());
}
com.kenai.jffi.Type[] fields = new com.kenai.jffi.Type[layout.getNativeSize() / alignmentType.size()];
Arrays.fill(fields, alignmentType);
return com.kenai.jffi.Struct.newStruct(fields);
} else {
Collection<StructLayout.Member> structMembers = layout.getMembers();
java.util.List<com.kenai.jffi.Type> fields = new java.util.ArrayList<com.kenai.jffi.Type>();
for (StructLayout.Member m : structMembers) {
com.kenai.jffi.Type fieldType;
fieldType = FFIUtil.getFFIType(m.type());
if (fieldType == null) {
throw layout.getRuntime().newTypeError("unsupported Struct field type " + m);
}
if (fieldType.size() > 0) fields.add(fieldType);
}
return com.kenai.jffi.Struct.newStruct(fields.toArray(new com.kenai.jffi.Type[fields.size()]));
}
}
static com.kenai.jffi.Array newArray(org.jruby.ext.ffi.Type.Array arrayType) {
com.kenai.jffi.Type componentType = FFIUtil.getFFIType(arrayType.getComponentType());
if (componentType == null) {
throw arrayType.getRuntime().newTypeError("unsupported array element type " + arrayType.getComponentType());
}
return com.kenai.jffi.Array.newArray(componentType, arrayType.length());
}
static final IRubyObject getString(Ruby runtime, long address) {
if (address == 0) {
return runtime.getNil();
}
byte[] bytes = IO.getZeroTerminatedByteArray(address);
if (bytes.length == 0) {
return RubyString.newEmptyString(runtime);
}
RubyString s = RubyString.newStringNoCopy(runtime, bytes);
s.setTaint(true);
return s;
}
}