diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java index f8c9740e0670919634015cd2f65032f6d833872d..9c6878788b8834752ea58a49efe18da2e8c57e16 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java @@ -499,4 +499,23 @@ public final class NativeDataAccess { } return mirror.dataAddress; } + + public static void setNativeContents(RObject obj, long address, int length) { + assert obj.getNativeMirror() != null; + if (noDoubleNative.isValid() && obj instanceof RDoubleVector) { + noDoubleNative.invalidate(); + } else if (noComplexNative.isValid() && obj instanceof RComplexVector) { + noComplexNative.invalidate(); + } else if (noIntNative.isValid() && obj instanceof RIntVector) { + noIntNative.invalidate(); + } else if (noRawNative.isValid() && obj instanceof RRawVector) { + noRawNative.invalidate(); + } else if (noLogicalNative.isValid() && obj instanceof RLogicalVector) { + noLogicalNative.invalidate(); + } + NativeMirror mirror = (NativeMirror) obj.getNativeMirror(); + mirror.dataAddress = address; + mirror.length = length; + + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java index 5aba159019d3194b3c76929e6bd35575d961e451..a3cedbc0057006849cc31cfcfe2c7b37561b7d37 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java @@ -49,12 +49,23 @@ public final class RComplexVector extends RVector<double[]> implements RAbstract initDimsNamesDimNames(dims, names, dimNames); } + private RComplexVector() { + super(false); + } + + static RComplexVector fromNative(long address, int length) { + RComplexVector result = new RComplexVector(); + NativeDataAccess.asPointer(result); + NativeDataAccess.setNativeContents(result, address, length); + return result; + } + @Override protected RComplexVector internalCopy() { if (data != null) { return new RComplexVector(Arrays.copyOf(data, data.length), this.isComplete()); } else { - return new RComplexVector(NativeDataAccess.copyDoubleNativeData(getNativeMirror()), this.isComplete(), null); + return new RComplexVector(NativeDataAccess.copyDoubleNativeData(getNativeMirror()), this.isComplete()); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java index 619bc2a75a2caff43fd491cb19def9571921693b..47e1c9d20c8c08cbce0b211a20673d7701fbd8a3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java @@ -56,6 +56,10 @@ public final class RDataFactory { public static final boolean INCOMPLETE_VECTOR = false; public static final boolean COMPLETE_VECTOR = true; + public static RIntVector createIntVectorFromNative(long address, int length) { + return traceDataCreated(RIntVector.fromNative(address, length)); + } + public static RIntVector createIntVector(int length) { return createIntVector(length, false); } @@ -88,6 +92,10 @@ public final class RDataFactory { return traceDataCreated(new RIntVector(data, complete, dims, names, dimNames)); } + public static RDoubleVector createDoubleVectorFromNative(long address, int length) { + return traceDataCreated(RDoubleVector.fromNative(address, length)); + } + public static RDoubleVector createDoubleVector(int length) { return createDoubleVector(length, false); } @@ -144,6 +152,10 @@ public final class RDataFactory { return traceDataCreated(new RRawVector(data, dims, names, dimNames)); } + public static RComplexVector createComplexVectorFromNative(long address, int length) { + return traceDataCreated(RComplexVector.fromNative(address, length)); + } + public static RComplexVector createComplexVector(int length) { return createComplexVector(length, false); } @@ -217,6 +229,10 @@ public final class RDataFactory { return traceDataCreated(new RStringVector(data, complete, dims, names, dimNames)); } + public static RLogicalVector createLogicalVectorFromNative(long address, int length) { + return traceDataCreated(RLogicalVector.fromNative(address, length)); + } + public static RLogicalVector createLogicalVector(int length) { return createLogicalVector(length, false); } @@ -317,6 +333,10 @@ public final class RDataFactory { return traceDataCreated(RComplex.valueOf(realPart, imaginaryPart)); } + public static RRawVector createRawVectorFromNative(long address, int length) { + return traceDataCreated(RRawVector.fromNative(address, length)); + } + public static RRaw createRaw(byte value) { return traceDataCreated(new RRaw(value)); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java index c5bfb51de748e6c643f1ae338c3ff02a26a07935..cc84373f42bcae0a9f153044ac545d4700966526 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java @@ -48,6 +48,18 @@ public final class RDoubleVector extends RVector<double[]> implements RAbstractD initDimsNamesDimNames(dims, names, dimNames); } + private RDoubleVector() { + super(false); + } + + static RDoubleVector fromNative(long address, int length) { + RDoubleVector result = new RDoubleVector(); + NativeDataAccess.asPointer(result); + NativeDataAccess.setNativeContents(result, address, length); + assert result.data == null; + return result; + } + @Override public RAbstractVector castSafe(RType type, ConditionProfile isNAProfile, boolean keepAttributes) { switch (type) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java index a974ca745be12443883289e745d08a044b9efeb2..93d5547fc5ab9987b3dbae7d60c5ccc388ce8e47 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java @@ -48,6 +48,17 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect initDimsNamesDimNames(dims, names, dimNames); } + private RIntVector() { + super(false); + } + + static RIntVector fromNative(long address, int length) { + RIntVector result = new RIntVector(); + NativeDataAccess.asPointer(result); + NativeDataAccess.setNativeContents(result, address, length); + return result; + } + @Override public RAbstractVector castSafe(RType type, ConditionProfile isNAProfile, boolean keepAttributes) { switch (type) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java index a96573dad51fa7e73df8b3f471c9b3453dfa84f7..8d87a76756d9dc101d4767d56b9be4051fd25f88 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java @@ -48,6 +48,17 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo initDimsNamesDimNames(dims, names, dimNames); } + private RLogicalVector() { + super(false); + } + + static RLogicalVector fromNative(long address, int length) { + RLogicalVector result = new RLogicalVector(); + NativeDataAccess.asPointer(result); + NativeDataAccess.setNativeContents(result, address, length); + return result; + } + @Override public RAbstractVector castSafe(RType type, ConditionProfile isNAProfile, boolean keepAttributes) { switch (type) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java index 8e12c79aab3b58aa77d22c69cd248f678434a241..1bd4e1e70c5ef768f5993f0f60e56e204d934ba5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java @@ -48,6 +48,17 @@ public final class RRawVector extends RVector<byte[]> implements RAbstractRawVec initDimsNamesDimNames(dims, names, dimNames); } + private RRawVector() { + super(true); + } + + static RRawVector fromNative(long address, int length) { + RRawVector result = new RRawVector(); + NativeDataAccess.asPointer(result); + NativeDataAccess.setNativeContents(result, address, length); + return result; + } + @Override public RAbstractVector castSafe(RType type, ConditionProfile isNAProfile, boolean keepAttributes) { switch (type) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java index 16e53cf26ee6b3014a75e21ccc4c48369289a3bf..27ad924f7b5e9d8e71877be62886206c25a54d7d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java @@ -94,6 +94,10 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement */ public abstract ArrayT getInternalManagedData(); + public boolean hasNativeMemoryData() { + return getInternalManagedData() == null; + } + /** * Intended for external calls where a mutable copy is needed. */ diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java index 37e2afc0d46ce0a0e101c3cdb02cd334b9916bca..13bb121163eed6b1467ba03786b12851a5dca641 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java @@ -36,6 +36,7 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.data.NativeDataAccess; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RComplex; import com.oracle.truffle.r.runtime.data.RComplexVector; @@ -54,6 +55,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.nodes.RBaseNode; import sun.misc.Unsafe; @@ -75,7 +77,7 @@ class TemporaryWrapperMR { protected Object access(TemporaryWrapper receiver) { long address = receiver.address; if (profile.profile(address == 0)) { - receiver.address = address = receiver.allocate(); + return receiver.asPointer(); } return address; } @@ -117,13 +119,12 @@ abstract class TemporaryWrapper implements TruffleObject { protected long address; protected RAbstractAtomicVector vector; + protected boolean reuseVector = false; TemporaryWrapper(RAbstractAtomicVector vector) { this.vector = vector; } - public abstract long allocate(); - public Object read(long index) { throw RInternalError.unimplemented("read at " + index); } @@ -137,34 +138,64 @@ abstract class TemporaryWrapper implements TruffleObject { return TemporaryWrapperMRForeign.ACCESS; } + public long asPointer() { + // if the vector is temporary, we can re-use it. We turn it into native memory backed + // vector, keep it so and reuse it as the result. + if (vector instanceof RVector<?> && ((RVector<?>) vector).isTemporary()) { + NativeDataAccess.asPointer(vector); + reuseVector = true; + address = allocateNative((RVector<?>) vector); + } else { + reuseVector = false; + address = allocate(vector); + } + return address; + } + public final RAbstractAtomicVector cleanup() { - if (address == 0) { + if (address == 0 || reuseVector) { return vector; } else { - return copyBack(); + return copyBack(address, vector); } } - protected abstract RAbstractAtomicVector copyBack(); + protected abstract long allocate(RAbstractVector vector); - protected static <ArrayT> ArrayT reuseData(RVector<ArrayT> vec) { - // Note: maybe we can reuse non-shared vectors too? - return vec.isTemporary() ? vec.getInternalManagedData() : null; - } + protected abstract long allocateNative(RVector<?> vector); + + protected abstract RAbstractAtomicVector copyBack(long address, RAbstractVector vector); } +// TODO: fortran only takes a pointer to the first string final class StringWrapper extends TemporaryWrapper { StringWrapper(RAbstractStringVector vector) { super(vector); } + @Override + public long asPointer() { + address = allocate(vector); + return address; + } + + @Override + protected long allocateNative(RVector<?> vector) { + throw RInternalError.shouldNotReachHere(); + } + @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { + // We allocate contiguous memory that we'll use to store both the array of pointers (char**) + // and the arrays of characters (char*). Given vector of size N, we allocate memory for N + // adresses (long) and after those we put individual strings character by character, the + // pointers from the first segment of this memory will be pointing to the starts of those + // strings. RAbstractStringVector v = (RAbstractStringVector) vector; int length = v.getLength(); - int size = length * 8; + int size = length * Long.BYTES; byte[][] bytes = new byte[length][]; for (int i = 0; i < length; i++) { String element = v.getDataAt(i); @@ -172,7 +203,7 @@ final class StringWrapper extends TemporaryWrapper { size += bytes[i].length + 1; } long memory = UnsafeAdapter.UNSAFE.allocateMemory(size); - long ptr = memory + length * 8; // start of the actual character data + long ptr = memory + length * Long.BYTES; // start of the actual character data for (int i = 0; i < length; i++) { UnsafeAdapter.UNSAFE.putLong(memory + i * 8, ptr); UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length); @@ -185,12 +216,10 @@ final class StringWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RStringVector copyBack() { + protected RStringVector copyBack(long address, RAbstractVector vector) { RStringVector result = ((RAbstractStringVector) vector).materialize(); - String[] data = reuseData(result); - if (data == null) { - data = new String[result.getLength()]; - } + boolean reuseResult = result.isTemporary() && !result.hasNativeMemoryData(); + String[] data = reuseResult ? result.getInternalManagedData() : new String[result.getLength()]; for (int i = 0; i < data.length; i++) { long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8); int length = 0; @@ -202,7 +231,13 @@ final class StringWrapper extends TemporaryWrapper { data[i] = new String(bytes, StandardCharsets.US_ASCII); } UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createStringVector(data, true); + if (reuseResult) { + return result; + } else { + RStringVector newResult = RDataFactory.createStringVector(data, true); + newResult.copyAttributesFrom(result); + return newResult; + } } } @@ -214,7 +249,7 @@ final class IntWrapper extends TemporaryWrapper { @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { RAbstractIntVector v = (RAbstractIntVector) vector; int length = v.getLength(); long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE); @@ -226,15 +261,16 @@ final class IntWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RIntVector copyBack() { - RIntVector result = ((RAbstractIntVector) vector).materialize(); - int[] data = reuseData(result); - if (data == null) { - data = new int[vector.getLength()]; - } - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_INT_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_INT_INDEX_SCALE); - UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createIntVector(data, false); + protected long allocateNative(RVector<?> vector) { + return ((RIntVector) vector).allocateNativeContents(); + } + + @Override + @TruffleBoundary + protected RIntVector copyBack(long address, RAbstractVector vector) { + RIntVector result = RDataFactory.createIntVectorFromNative(address, vector.getLength()); + result.copyAttributesFrom(vector); + return result; } } @@ -246,7 +282,7 @@ final class LogicalWrapper extends TemporaryWrapper { @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { RAbstractLogicalVector v = (RAbstractLogicalVector) vector; int length = v.getLength(); long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE); @@ -258,18 +294,16 @@ final class LogicalWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RLogicalVector copyBack() { - RLogicalVector result = ((RAbstractLogicalVector) vector).materialize(); - byte[] data = reuseData(result); - if (data == null) { - data = new byte[result.getLength()]; - } - int length = vector.getLength(); - for (int i = 0; i < length; i++) { - data[i] = RRuntime.int2logical(UnsafeAdapter.UNSAFE.getInt(address + (i * Unsafe.ARRAY_INT_INDEX_SCALE))); - } - UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createLogicalVector(data, false); + protected long allocateNative(RVector<?> vector) { + return ((RLogicalVector) vector).allocateNativeContents(); + } + + @Override + @TruffleBoundary + protected RLogicalVector copyBack(long address, RAbstractVector vector) { + RLogicalVector result = RDataFactory.createLogicalVectorFromNative(address, vector.getLength()); + result.copyAttributesFrom(vector); + return result; } } @@ -281,7 +315,7 @@ final class DoubleWrapper extends TemporaryWrapper { @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { RAbstractDoubleVector v = (RAbstractDoubleVector) vector; int length = v.getLength(); long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); @@ -293,15 +327,16 @@ final class DoubleWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RDoubleVector copyBack() { - RDoubleVector result = ((RAbstractDoubleVector) vector).materialize(); - double[] data = reuseData(result); - if (data == null) { - data = new double[vector.getLength()]; - } - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); - UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createDoubleVector(data, false); + protected long allocateNative(RVector<?> vector) { + return ((RDoubleVector) vector).allocateNativeContents(); + } + + @Override + @TruffleBoundary + protected RDoubleVector copyBack(long address, RAbstractVector vector) { + RDoubleVector result = RDataFactory.createDoubleVectorFromNative(address, vector.getLength()); + result.copyAttributesFrom(vector); + return result; } } @@ -313,7 +348,7 @@ final class ComplexWrapper extends TemporaryWrapper { @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { RAbstractComplexVector v = (RAbstractComplexVector) vector; int length = v.getLength(); long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2); @@ -327,15 +362,16 @@ final class ComplexWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RComplexVector copyBack() { - RComplexVector result = ((RAbstractComplexVector) vector).materialize(); - double[] data = reuseData(result); - if (data == null) { - data = new double[result.getLength() * 2]; - } - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * 2 * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); - UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createComplexVector(data, false); + protected long allocateNative(RVector<?> vector) { + return ((RComplexVector) vector).allocateNativeContents(); + } + + @Override + @TruffleBoundary + protected RComplexVector copyBack(long address, RAbstractVector vector) { + RComplexVector result = RDataFactory.createComplexVectorFromNative(address, vector.getLength()); + result.copyAttributesFrom(vector); + return result; } } @@ -347,7 +383,7 @@ final class RawWrapper extends TemporaryWrapper { @Override @TruffleBoundary - public long allocate() { + public long allocate(RAbstractVector vector) { RAbstractRawVector v = (RAbstractRawVector) vector; int length = v.getLength(); long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_BYTE_INDEX_SCALE); @@ -359,20 +395,30 @@ final class RawWrapper extends TemporaryWrapper { @Override @TruffleBoundary - protected RRawVector copyBack() { - RRawVector result = ((RAbstractRawVector) vector).materialize(); - byte[] data = reuseData(result); - if (data == null) { - data = new byte[result.getLength()]; - } - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_BYTE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_BYTE_INDEX_SCALE); - UnsafeAdapter.UNSAFE.freeMemory(address); - return RDataFactory.createRawVector(data); + protected long allocateNative(RVector<?> vector) { + return ((RRawVector) vector).allocateNativeContents(); + } + + @Override + @TruffleBoundary + protected RRawVector copyBack(long address, RAbstractVector vector) { + RRawVector result = RDataFactory.createRawVectorFromNative(address, vector.getLength()); + result.copyAttributesFrom(vector); + return result; } } /** - * Support for the {.C} and {.Fortran} calls. + * Support for the {.C} and {.Fortran} calls. Arguments of these calls are only arrays of primitive + * types, in the case character vectors, only the first string. The vectors coming from the R side + * are duplicated (if not temporary) with all their attributes and then the pointer to the data of + * the new fresh vectors is passed to the function. The result is a list of all those new vectors + * (or the original vectors if they are temporary). + * + * Note: seems that symbols in GnuR may declare: expected types of their args (and other types + * should be coerced), whether an argument is only input (RNull is in its place in the result list) + * and whether the argument value must always be copied. We do not implement those as they do not + * seem necessary? */ public interface CRFFI {