From ad659f25d2bdca8b5062a3ad6b4288f661f6770f Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Wed, 9 Aug 2017 13:40:49 +0200
Subject: [PATCH] allow RIntVector and RLogicalVector contents to live in
 native space

---
 .../impl/nfi/TruffleNFI_UpCallsRFFIImpl.java  | 88 +++++++++++++++----
 .../r/ffi/impl/upcalls/FFIWrapNode.java       |  6 ++
 .../fficall/src/truffle_nfi/Rinternals.c      |  6 +-
 .../r/runtime/data/NativeDataAccess.java      | 77 ++++++++++++++++
 .../truffle/r/runtime/data/RIntVector.java    | 56 +++++++++---
 .../r/runtime/data/RLogicalVector.java        | 54 +++++++++---
 6 files changed, 239 insertions(+), 48 deletions(-)

diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
index eca0ce803c..627f2be72d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
@@ -22,17 +22,33 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guaranteeInstanceOf;
+
+import java.nio.charset.StandardCharsets;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl;
+import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter;
+import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_C.StringWrapper;
 import com.oracle.truffle.r.ffi.impl.upcalls.FFIUnwrapNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.CEntry;
@@ -41,6 +57,8 @@ import com.oracle.truffle.r.runtime.ffi.DLL.DotSymbol;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
+import sun.misc.Unsafe;
+
 public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
 
     private static final String SETSYMBOL_SIGNATURE = "(pointer, sint32, pointer, sint32): pointer";
@@ -71,35 +89,69 @@ public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
         return CharSXPWrapper.create(TruffleNFI_Utils.getString(address, len));
     }
 
+    @MessageResolution(receiverType = VectorWrapper.class)
+    public static class VectorWrapperMR {
+
+        @Resolve(message = "IS_POINTER")
+        public abstract static class IntVectorWrapperNativeIsPointerNode extends Node {
+            protected Object access(@SuppressWarnings("unused") VectorWrapper receiver) {
+                return true;
+            }
+        }
+
+        @Resolve(message = "AS_POINTER")
+        public abstract static class IntVectorWrapperNativeAsPointerNode extends Node {
+            protected long access(VectorWrapper receiver) {
+                RVector<?> v = receiver.vector;
+                if (v instanceof RIntVector) {
+                    return ((RIntVector) v).allocateNativeContents();
+                } else if (v instanceof RLogicalVector) {
+                    return ((RLogicalVector) v).allocateNativeContents();
+                } else {
+                    throw RInternalError.shouldNotReachHere();
+                }
+            }
+        }
+
+        @CanResolve
+        public abstract static class VectorWrapperCheck extends Node {
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof VectorWrapper;
+            }
+        }
+    }
+
+    public static final class VectorWrapper implements TruffleObject {
+
+        private final RVector<?> vector;
+
+        public VectorWrapper(RVector<?> vector) {
+            this.vector = vector;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return VectorWrapperMRForeign.ACCESS;
+        }
+    }
+
     @Override
     public Object INTEGER(Object x) {
-        long arrayAddress = TruffleNFI_NativeArray.findArray(x);
-        if (arrayAddress == 0) {
-            Object array = super.INTEGER(x);
-            arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.INTSXP);
-        } else {
-            TruffleNFI_Call.returnArrayExisting(SEXPTYPE.INTSXP, arrayAddress);
-        }
-        return x;
+        // also handles LOGICAL
+        assert x instanceof RIntVector || x instanceof RLogicalVector;
+        return new VectorWrapper(guaranteeInstanceOf(x, RVector.class));
     }
 
     @Override
     public Object LOGICAL(Object x) {
-        long arrayAddress = TruffleNFI_NativeArray.findArray(x);
-        if (arrayAddress == 0) {
-            Object array = super.LOGICAL(x);
-            arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.LGLSXP);
-        } else {
-            TruffleNFI_Call.returnArrayExisting(SEXPTYPE.LGLSXP, arrayAddress);
-        }
-        return x;
-
+        return new VectorWrapper(guaranteeInstanceOf(x, RLogicalVector.class));
     }
 
     @Override
     public Object REAL(Object x) {
         long arrayAddress = TruffleNFI_NativeArray.findArray(x);
         if (arrayAddress == 0) {
+            System.out.println("getting REAL contents");
             Object array = super.REAL(x);
             arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.REALSXP);
         } else {
@@ -113,6 +165,7 @@ public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     public Object RAW(Object x) {
         long arrayAddress = TruffleNFI_NativeArray.findArray(x);
         if (arrayAddress == 0) {
+            System.out.println("getting RAW contents");
             Object array = super.RAW(x);
             arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.RAWSXP);
         } else {
@@ -125,6 +178,7 @@ public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     public Object R_CHAR(Object x) {
         long arrayAddress = TruffleNFI_NativeArray.findArray(x);
         if (arrayAddress == 0) {
+            System.out.println("getting R_CHAR contents");
             CharSXPWrapper charSXP = (CharSXPWrapper) x;
             Object array = charSXP.getContents().getBytes();
             arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.CHARSXP);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIWrapNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIWrapNode.java
index d32cf9f1a9..bbf83e4735 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIWrapNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIWrapNode.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.ffi.impl.upcalls;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_UpCallsRFFIImpl.VectorWrapper;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDouble;
@@ -93,6 +94,11 @@ public abstract class FFIWrapNode extends Node {
         return value;
     }
 
+    @Specialization
+    protected static Object wrap(VectorWrapper value) {
+        return value;
+    }
+
     @Fallback
     protected static Object wrap(Object value) {
         System.out.println("invalid wrapping: " + value.getClass().getSimpleName());
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
index bc24bc1441..d5c0f76695 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
@@ -98,13 +98,11 @@ void return_FREE(void *address) {
 }
 
 int *INTEGER(SEXP x) {
-	((call_INTEGER) callbacks[INTEGER_x])(x);
-	return return_int;
+	return ((call_INTEGER) callbacks[INTEGER_x])(x);
 }
 
 int *LOGICAL(SEXP x){
-	((call_LOGICAL) callbacks[LOGICAL_x])(x);
-	return return_int;
+	return ((call_LOGICAL) callbacks[LOGICAL_x])(x);
 }
 
 double *REAL(SEXP x){
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 9953847018..442e4ae68f 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
@@ -37,6 +37,7 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -79,11 +80,19 @@ public final class NativeDataAccess {
     private static final class NativeMirror {
         private final long id;
         private long dataAddress;
+        private long length;
 
         NativeMirror() {
             this.id = counter.incrementAndGet();
         }
 
+        void allocateNative(Object source, long sourceLength, int len, int elementBase, int elementSize) {
+            assert dataAddress == 0;
+            dataAddress = UnsafeAdapter.UNSAFE.allocateMemory(sourceLength * elementSize);
+            UnsafeAdapter.UNSAFE.copyMemory(source, elementBase, null, dataAddress, sourceLength * elementSize);
+            this.length = len;
+        }
+
         @Override
         protected void finalize() throws Throwable {
             super.finalize();
@@ -148,4 +157,72 @@ public final class NativeDataAccess {
         }
         return result;
     }
+
+    static long getDataLength(RVector<?> vector) {
+        return ((NativeMirror) vector.getNativeMirror()).length;
+    }
+
+    static int getIntData(RVector<?> vector, int index) {
+        long address = ((NativeMirror) vector.getNativeMirror()).dataAddress;
+        assert address != 0;
+        return UnsafeAdapter.UNSAFE.getInt(address + index * Unsafe.ARRAY_INT_INDEX_SCALE);
+    }
+
+    static void setIntData(RVector<?> vector, int index, int value) {
+        long address = ((NativeMirror) vector.getNativeMirror()).dataAddress;
+        assert address != 0;
+        UnsafeAdapter.UNSAFE.putInt(address + index * Unsafe.ARRAY_INT_INDEX_SCALE, value);
+    }
+
+    static double getDoubleData(RVector<?> vector, int index) {
+        long address = ((NativeMirror) vector.getNativeMirror()).dataAddress;
+        assert address != 0;
+        return UnsafeAdapter.UNSAFE.getDouble(address + index * Unsafe.ARRAY_INT_INDEX_SCALE);
+    }
+
+    static void setDoubleData(RVector<?> vector, int index, double value) {
+        long address = ((NativeMirror) vector.getNativeMirror()).dataAddress;
+        assert address != 0;
+        UnsafeAdapter.UNSAFE.putDouble(address + index * Unsafe.ARRAY_INT_INDEX_SCALE, value);
+    }
+
+    static long allocateNativeContents(RLogicalVector vector, byte[] data, int length) {
+        NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
+        assert mirror != null;
+        assert mirror.dataAddress == 0 ^ data == null;
+        if (mirror.dataAddress == 0) {
+            // System.out.println(String.format("allocating native for logical vector %16x",
+            // mirror.id));
+            int[] intArray = new int[data.length];
+            for (int i = 0; i < data.length; i++) {
+                intArray[i] = RRuntime.logical2int(data[i]);
+            }
+            ((NativeMirror) vector.getNativeMirror()).allocateNative(intArray, data.length, length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
+        }
+        return mirror.dataAddress;
+    }
+
+    static long allocateNativeContents(RIntVector vector, int[] data, int length) {
+        NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
+        assert mirror != null;
+        assert mirror.dataAddress == 0 ^ data == null;
+        if (mirror.dataAddress == 0) {
+            // System.out.println(String.format("allocating native for int vector %16x",
+            // mirror.id));
+            ((NativeMirror) vector.getNativeMirror()).allocateNative(data, data.length, length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
+        }
+        return mirror.dataAddress;
+    }
+
+    static long allocateNativeContents(RDoubleVector vector, double[] data, int length) {
+        NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
+        assert mirror != null;
+        assert mirror.dataAddress == 0 ^ data == null;
+        if (mirror.dataAddress == 0) {
+            // System.out.println(String.format("allocating native for double vector %16x",
+            // mirror.id));
+            ((NativeMirror) vector.getNativeMirror()).allocateNative(data, data.length, length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        }
+        return mirror.dataAddress;
+    }
 }
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 face5053d9..dd06073146 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
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public final class RIntVector extends RVector<int[]> implements RAbstractIntVector {
 
-    private final int[] data;
+    private int[] data;
 
     RIntVector(int[] data, boolean complete) {
         super(complete);
@@ -68,24 +68,29 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
 
     @Override
     public int[] getInternalStore() {
+        assert data != null;
         return data;
     }
 
     @Override
     public int getDataAt(int index) {
-        return data[index];
+        return data == null ? NativeDataAccess.getIntData(this, index) : data[index];
     }
 
     @Override
     public int getDataAt(Object store, int index) {
         assert data == store;
-        return ((int[]) store)[index];
+        return store == null ? NativeDataAccess.getIntData(this, index) : ((int[]) store)[index];
     }
 
     @Override
     public void setDataAt(Object store, int index, int value) {
         assert data == store;
-        ((int[]) store)[index] = value;
+        if (store == null) {
+            NativeDataAccess.setIntData(this, index, value);
+        } else {
+            ((int[]) store)[index] = value;
+        }
     }
 
     @Override
@@ -108,7 +113,7 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
 
     @Override
     public int getLength() {
-        return data.length;
+        return data == null ? (int) NativeDataAccess.getDataLength(this) : data.length;
     }
 
     @Override
@@ -119,8 +124,8 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
     @Override
     public boolean verify() {
         if (isComplete()) {
-            for (int x : data) {
-                if (x == RRuntime.INT_NA) {
+            for (int i = 0; i < getLength(); i++) {
+                if (getDataAt(i) == RRuntime.INT_NA) {
                     return false;
                 }
             }
@@ -130,6 +135,7 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
 
     @Override
     public int[] getDataCopy() {
+        assert data != null;
         return Arrays.copyOf(data, data.length);
     }
 
@@ -139,6 +145,7 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
      */
     @Override
     public int[] getDataWithoutCopying() {
+        assert data != null;
         return data;
     }
 
@@ -147,13 +154,17 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
         return RDataFactory.createIntVector(data, isComplete(), newDimensions);
     }
 
-    public RIntVector updateDataAt(int i, int right, NACheck valueNACheck) {
+    public RIntVector updateDataAt(int index, int value, NACheck valueNACheck) {
         assert !this.isShared();
-        data[i] = right;
-        if (valueNACheck.check(right)) {
+        if (data == null) {
+            NativeDataAccess.setIntData(this, index, value);
+        } else {
+            data[index] = value;
+        }
+        if (valueNACheck.check(value)) {
             setComplete(false);
         }
-        assert !isComplete() || !RRuntime.isNA(right);
+        assert !isComplete() || !RRuntime.isNA(value);
         return this;
     }
 
@@ -178,12 +189,14 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
     }
 
     private int[] copyResizedData(int size, boolean fillNA) {
+        assert data != null;
         int[] newData = Arrays.copyOf(data, size);
         return resizeData(newData, this.data, this.getLength(), fillNA);
     }
 
     @Override
     protected RIntVector internalCopyResized(int size, boolean fillNA, int[] dimensions) {
+        assert data != null;
         boolean isComplete = isComplete() && ((data.length >= size) || !fillNA);
         return RDataFactory.createIntVector(copyResizedData(size, fillNA), isComplete, dimensions);
     }
@@ -201,6 +214,11 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
     @Override
     public void transferElementSameType(int toIndex, RAbstractVector fromVector, int fromIndex) {
         RAbstractIntVector other = (RAbstractIntVector) fromVector;
+        if (data == null) {
+            NativeDataAccess.setIntData(this, toIndex, other.getDataAt(fromIndex));
+        } else {
+            data[toIndex] = other.getDataAt(fromIndex);
+        }
         data[toIndex] = other.getDataAt(fromIndex);
     }
 
@@ -210,7 +228,19 @@ public final class RIntVector extends RVector<int[]> implements RAbstractIntVect
     }
 
     @Override
-    public void setElement(int i, Object value) {
-        data[i] = (int) value;
+    public void setElement(int index, Object value) {
+        if (data == null) {
+            NativeDataAccess.setIntData(this, index, (int) value);
+        } else {
+            data[index] = (int) value;
+        }
+    }
+
+    public long allocateNativeContents() {
+        try {
+            return NativeDataAccess.allocateNativeContents(this, data, getLength());
+        } finally {
+            data = null;
+        }
     }
 }
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 5f04723085..b83a438aba 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
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public final class RLogicalVector extends RVector<byte[]> implements RAbstractLogicalVector {
 
-    private final byte[] data;
+    private byte[] data;
 
     RLogicalVector(byte[] data, boolean complete) {
         super(complete);
@@ -70,23 +70,29 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
 
     @Override
     public byte[] getInternalStore() {
+        assert data != null;
         return data;
     }
 
     @Override
     public void setDataAt(Object store, int index, byte value) {
         assert data == store;
-        ((byte[]) store)[index] = value;
+        if (store == null) {
+            NativeDataAccess.setIntData(this, index, RRuntime.logical2int(value));
+        } else {
+            ((byte[]) store)[index] = value;
+        }
     }
 
     @Override
     public byte getDataAt(Object store, int index) {
         assert data == store;
-        return ((byte[]) store)[index];
+        return data == null ? (byte) NativeDataAccess.getIntData(this, index) : data[index];
     }
 
     @Override
     protected RLogicalVector internalCopy() {
+        assert data != null;
         return new RLogicalVector(Arrays.copyOf(data, data.length), isComplete());
     }
 
@@ -105,7 +111,7 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
 
     @Override
     public int getLength() {
-        return data.length;
+        return data == null ? (int) NativeDataAccess.getDataLength(this) : data.length;
     }
 
     @Override
@@ -116,8 +122,8 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
     @Override
     public boolean verify() {
         if (isComplete()) {
-            for (byte b : data) {
-                if (b == RRuntime.LOGICAL_NA) {
+            for (int i = 0; i < getLength(); i++) {
+                if (getDataAt(i) == RRuntime.LOGICAL_NA) {
                     return false;
                 }
             }
@@ -126,17 +132,21 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
     }
 
     @Override
-    public byte getDataAt(int i) {
-        return data[i];
+    public byte getDataAt(int index) {
+        return data == null ? (byte) NativeDataAccess.getIntData(this, index) : data[index];
     }
 
-    private RLogicalVector updateDataAt(int index, byte right, NACheck valueNACheck) {
+    private RLogicalVector updateDataAt(int index, byte value, NACheck valueNACheck) {
         assert !this.isShared();
-        data[index] = right;
-        if (valueNACheck.check(right)) {
+        if (data == null) {
+            NativeDataAccess.setIntData(this, index, RRuntime.logical2int(value));
+        } else {
+            data[index] = value;
+        }
+        if (valueNACheck.check(value)) {
             setComplete(false);
         }
-        assert !isComplete() || !RRuntime.isNA(right);
+        assert !isComplete() || !RRuntime.isNA(value);
         return this;
     }
 
@@ -147,6 +157,7 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
     }
 
     private byte[] copyResizedData(int size, boolean fillNA) {
+        assert data != null;
         byte[] newData = Arrays.copyOf(data, size);
         if (size > this.getLength()) {
             if (fillNA) {
@@ -164,7 +175,7 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
 
     @Override
     protected RLogicalVector internalCopyResized(int size, boolean fillNA, int[] dimensions) {
-        boolean isComplete = isComplete() && ((data.length >= size) || !fillNA);
+        boolean isComplete = isComplete() && ((getLength() >= size) || !fillNA);
         return RDataFactory.createLogicalVector(copyResizedData(size, fillNA), isComplete, dimensions);
     }
 
@@ -176,11 +187,16 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
     @Override
     public void transferElementSameType(int toIndex, RAbstractVector fromVector, int fromIndex) {
         RAbstractLogicalVector other = (RAbstractLogicalVector) fromVector;
-        data[toIndex] = other.getDataAt(fromIndex);
+        if (data == null) {
+            NativeDataAccess.setIntData(this, toIndex, RRuntime.logical2int(other.getDataAt(fromIndex)));
+        } else {
+            data[toIndex] = other.getDataAt(fromIndex);
+        }
     }
 
     @Override
     public byte[] getDataCopy() {
+        assert data != null;
         return Arrays.copyOf(data, data.length);
     }
 
@@ -190,11 +206,13 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
      */
     @Override
     public byte[] getDataWithoutCopying() {
+        assert data != null;
         return data;
     }
 
     @Override
     public RLogicalVector copyWithNewDimensions(int[] newDimensions) {
+        assert data != null;
         return RDataFactory.createLogicalVector(data, isComplete(), newDimensions);
     }
 
@@ -207,4 +225,12 @@ public final class RLogicalVector extends RVector<byte[]> implements RAbstractLo
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    public long allocateNativeContents() {
+        try {
+            return NativeDataAccess.allocateNativeContents(this, data, getLength());
+        } finally {
+            data = null;
+        }
+    }
 }
-- 
GitLab