diff --git a/java/src/main/java/ai/rapids/cudf/ContiguousTable.java b/java/src/main/java/ai/rapids/cudf/ContiguousTable.java
index 94e44fa9d79..87a3f5f0ddf 100644
--- a/java/src/main/java/ai/rapids/cudf/ContiguousTable.java
+++ b/java/src/main/java/ai/rapids/cudf/ContiguousTable.java
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2019-2020, NVIDIA CORPORATION.
+ * Copyright (c) 2019-2021, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,63 +18,96 @@
package ai.rapids.cudf;
-import java.util.Arrays;
+import java.nio.ByteBuffer;
/**
* A table that is backed by a single contiguous device buffer. This makes transfers of the data
* much simpler.
*/
public final class ContiguousTable implements AutoCloseable {
- private Table table;
+ private long metadataHandle = 0;
+ private Table table = null;
private DeviceMemoryBuffer buffer;
+ private ByteBuffer metadataBuffer = null;
+ private final long rowCount;
- //Will be called from JNI
- static ContiguousTable fromContiguousColumnViews(long[] columnViewAddresses,
- long address, long lengthInBytes, long rmmBufferAddress) {
- Table table = null;
- ColumnVector[] vectors = new ColumnVector[columnViewAddresses.length];
- DeviceMemoryBuffer buffer = DeviceMemoryBuffer.fromRmm(address, lengthInBytes, rmmBufferAddress);
- try {
- for (int i = 0; i < vectors.length; i++) {
- vectors[i] = ColumnVector.fromViewWithContiguousAllocation(columnViewAddresses[i], buffer);
- }
- table = new Table(vectors);
- ContiguousTable ret = new ContiguousTable(table, buffer);
- buffer = null;
- table = null;
- return ret;
- } finally {
- if (buffer != null) {
- buffer.close();
- }
-
- for (int i = 0; i < vectors.length; i++) {
- if (vectors[i] != null) {
- vectors[i].close();
- }
- }
-
- if (table != null) {
- table.close();
- }
- }
+ // This method is invoked by JNI
+ static ContiguousTable fromPackedTable(long metadataHandle,
+ long dataAddress,
+ long dataLength,
+ long rmmBufferAddress,
+ long rowCount) {
+ DeviceMemoryBuffer buffer = DeviceMemoryBuffer.fromRmm(dataAddress, dataLength, rmmBufferAddress);
+ return new ContiguousTable(metadataHandle, buffer, rowCount);
}
+ /** Construct a contiguous table instance given a table and the device buffer backing it. */
ContiguousTable(Table table, DeviceMemoryBuffer buffer) {
+ this.metadataHandle = createPackedMetadata(table.getNativeView(),
+ buffer.getAddress(), buffer.getLength());
this.table = table;
this.buffer = buffer;
+ this.rowCount = table.getRowCount();
}
- public Table getTable() {
+ /**
+ * Construct a contiguous table
+ * @param metadataHandle address of the cudf packed_table host-based metadata instance
+ * @param buffer buffer containing the packed table data
+ * @param rowCount number of rows in the table
+ */
+ ContiguousTable(long metadataHandle, DeviceMemoryBuffer buffer, long rowCount) {
+ this.metadataHandle = metadataHandle;
+ this.buffer = buffer;
+ this.rowCount = rowCount;
+ }
+
+ /**
+ * Returns the number of rows in the table. This accessor avoids manifesting
+ * the Table instance if only the row count is needed.
+ */
+ public long getRowCount() {
+ return rowCount;
+ }
+
+ /** Get the table instance, reconstructing it from the metadata if necessary. */
+ public synchronized Table getTable() {
+ if (table == null) {
+ table = Table.fromPackedTable(getMetadataDirectBuffer(), buffer);
+ }
return table;
}
+ /** Get the device buffer backing the contiguous table data. */
public DeviceMemoryBuffer getBuffer() {
return buffer;
}
+ /**
+ * Get the byte buffer containing the host metadata describing the schema and layout of the
+ * contiguous table.
+ *
+ * NOTE: This is a direct byte buffer that is backed by the underlying native metadata instance
+ * and therefore is only valid to be used while this contiguous table instance is valid.
+ * Attempts to cache and access the resulting buffer after this instance has been destroyed
+ * will result in undefined behavior including the possibility of segmentation faults
+ * or data corruption.
+ */
+ public ByteBuffer getMetadataDirectBuffer() {
+ if (metadataBuffer == null) {
+ metadataBuffer = createMetadataDirectBuffer(metadataHandle);
+ }
+ return metadataBuffer.asReadOnlyBuffer();
+ }
+
+ /** Close the contiguous table instance and its underlying resources. */
@Override
public void close() {
+ if (metadataHandle != 0) {
+ closeMetadata(metadataHandle);
+ metadataHandle = 0;
+ }
+
if (table != null) {
table.close();
table = null;
@@ -85,4 +118,13 @@ public void close() {
buffer = null;
}
}
+
+ // create packed metadata for a table backed by a single data buffer
+ private static native long createPackedMetadata(long tableView, long dataAddress, long dataSize);
+
+ // create a DirectByteBuffer for the packed table metadata
+ private static native ByteBuffer createMetadataDirectBuffer(long metadataHandle);
+
+ // release the native metadata resources for a packed table
+ private static native void closeMetadata(long metadataHandle);
}
diff --git a/java/src/main/java/ai/rapids/cudf/Table.java b/java/src/main/java/ai/rapids/cudf/Table.java
index da4c446d9f7..0637ae6de1e 100644
--- a/java/src/main/java/ai/rapids/cudf/Table.java
+++ b/java/src/main/java/ai/rapids/cudf/Table.java
@@ -27,6 +27,7 @@
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -117,6 +118,11 @@ ColumnVector[] getColumns() {
return columns;
}
+ /** Return the native table view handle for this table */
+ long getNativeView() {
+ return nativeHandle;
+ }
+
/**
* Return the {@link ColumnVector} at the specified index. If you want to keep a reference to
* the column around past the life time of the table, you will need to increment the reference
@@ -503,7 +509,9 @@ private static native long[] repeatColumnCount(long tableHandle,
private static native long[] explode(long tableHandle, int index);
- private native long createCudfTableView(long[] nativeColumnViewHandles);
+ private static native long createCudfTableView(long[] nativeColumnViewHandles);
+
+ private static native long[] columnViewsFromPacked(ByteBuffer metadata, long dataAddress);
/////////////////////////////////////////////////////////////////////////////
// TABLE CREATION APIs
@@ -1796,6 +1804,50 @@ public static Table convertFromRows(ColumnVector vec, DType ... schema) {
return new Table(convertFromRows(vec.getNativeView(), types, scale));
}
+ /**
+ * Construct a table from a packed representation.
+ * @param metadata host-based metadata for the table
+ * @param data GPU data buffer for the table
+ * @return table which is zero-copy reconstructed from the packed-form
+ */
+ public static Table fromPackedTable(ByteBuffer metadata, DeviceMemoryBuffer data) {
+ // Ensure the metadata buffer is direct so it can be passed to JNI
+ ByteBuffer directBuffer = metadata;
+ if (!directBuffer.isDirect()) {
+ directBuffer = ByteBuffer.allocateDirect(metadata.remaining());
+ directBuffer.put(metadata);
+ directBuffer.flip();
+ }
+
+ long[] columnViewAddresses = columnViewsFromPacked(directBuffer, data.getAddress());
+ ColumnVector[] columns = new ColumnVector[columnViewAddresses.length];
+ Table result = null;
+ try {
+ for (int i = 0; i < columns.length; i++) {
+ columns[i] = ColumnVector.fromViewWithContiguousAllocation(columnViewAddresses[i], data);
+ columnViewAddresses[i] = 0;
+ }
+ result = new Table(columns);
+ } catch (Throwable t) {
+ for (int i = 0; i < columns.length; i++) {
+ if (columns[i] != null) {
+ columns[i].close();
+ }
+ if (columnViewAddresses[i] != 0) {
+ ColumnView.deleteColumnView(columnViewAddresses[i]);
+ }
+ }
+ throw t;
+ }
+
+ // close columns to leave the resulting table responsible for freeing underlying columns
+ for (ColumnVector column : columns) {
+ column.close();
+ }
+
+ return result;
+ }
+
/////////////////////////////////////////////////////////////////////////////
// HELPER CLASSES
/////////////////////////////////////////////////////////////////////////////
diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt
index 6d658b6d80b..614ff155c44 100755
--- a/java/src/main/native/CMakeLists.txt
+++ b/java/src/main/native/CMakeLists.txt
@@ -308,6 +308,7 @@ set(SOURCE_FILES
"src/CudaJni.cpp"
"src/ColumnVectorJni.cpp"
"src/ColumnViewJni.cpp"
+ "src/ContiguousTableJni.cpp"
"src/HostMemoryBufferNativeUtilsJni.cpp"
"src/NvcompJni.cpp"
"src/NvtxRangeJni.cpp"
diff --git a/java/src/main/native/src/ContiguousTableJni.cpp b/java/src/main/native/src/ContiguousTableJni.cpp
new file mode 100644
index 00000000000..352256af450
--- /dev/null
+++ b/java/src/main/native/src/ContiguousTableJni.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2021, NVIDIA CORPORATION.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cudf_jni_apis.hpp"
+
+namespace {
+
+#define CONTIGUOUS_TABLE_CLASS "ai/rapids/cudf/ContiguousTable"
+#define CONTIGUOUS_TABLE_FACTORY_SIG(param_sig) "(" param_sig ")L" CONTIGUOUS_TABLE_CLASS ";"
+
+jclass Contiguous_table_jclass;
+jmethodID From_packed_table_method;
+
+} // anonymous namespace
+
+namespace cudf {
+namespace jni {
+
+bool cache_contiguous_table_jni(JNIEnv *env) {
+ jclass cls = env->FindClass(CONTIGUOUS_TABLE_CLASS);
+ if (cls == nullptr) {
+ return false;
+ }
+
+ From_packed_table_method =
+ env->GetStaticMethodID(cls, "fromPackedTable", CONTIGUOUS_TABLE_FACTORY_SIG("JJJJJ"));
+ if (From_packed_table_method == nullptr) {
+ return false;
+ }
+
+ // Convert local reference to global so it cannot be garbage collected.
+ Contiguous_table_jclass = static_cast(env->NewGlobalRef(cls));
+ if (Contiguous_table_jclass == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+void release_contiguous_table_jni(JNIEnv *env) {
+ if (Contiguous_table_jclass != nullptr) {
+ env->DeleteGlobalRef(Contiguous_table_jclass);
+ Contiguous_table_jclass = nullptr;
+ }
+}
+
+jobject contiguous_table_from(JNIEnv *env, cudf::packed_columns &split, long row_count) {
+ jlong metadata_address = reinterpret_cast(split.metadata_.get());
+ jlong data_address = reinterpret_cast(split.gpu_data->data());
+ jlong data_size = static_cast(split.gpu_data->size());
+ jlong rmm_buffer_address = reinterpret_cast(split.gpu_data.get());
+
+ jobject contig_table_obj = env->CallStaticObjectMethod(
+ Contiguous_table_jclass, From_packed_table_method, metadata_address, data_address, data_size,
+ rmm_buffer_address, row_count);
+
+ if (contig_table_obj != nullptr) {
+ split.metadata_.release();
+ split.gpu_data.release();
+ }
+
+ return contig_table_obj;
+}
+
+native_jobjectArray contiguous_table_array(JNIEnv *env, jsize length) {
+ return native_jobjectArray(
+ env, env->NewObjectArray(length, Contiguous_table_jclass, nullptr));
+}
+
+} // namespace jni
+} // namespace cudf
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ContiguousTable_createPackedMetadata(
+ JNIEnv *env, jclass, jlong j_table, jlong j_buffer_addr, jlong j_buffer_length) {
+ JNI_NULL_CHECK(env, j_table, "input table is null", 0);
+ try {
+ cudf::jni::auto_set_device(env);
+ auto table = reinterpret_cast(j_table);
+ auto data_addr = reinterpret_cast(j_buffer_addr);
+ auto data_size = static_cast(j_buffer_length);
+ auto metadata_ptr =
+ new cudf::packed_columns::metadata(cudf::pack_metadata(*table, data_addr, data_size));
+ return reinterpret_cast(metadata_ptr);
+ }
+ CATCH_STD(env, 0);
+}
+
+JNIEXPORT jobject JNICALL Java_ai_rapids_cudf_ContiguousTable_createMetadataDirectBuffer(
+ JNIEnv *env, jclass, jlong j_metadata_ptr) {
+ JNI_NULL_CHECK(env, j_metadata_ptr, "metadata is null", nullptr);
+ try {
+ auto metadata = reinterpret_cast(j_metadata_ptr);
+ return env->NewDirectByteBuffer(const_cast(metadata->data()), metadata->size());
+ }
+ CATCH_STD(env, nullptr);
+}
+
+JNIEXPORT void JNICALL Java_ai_rapids_cudf_ContiguousTable_closeMetadata(JNIEnv *env, jclass,
+ jlong j_metadata_ptr) {
+ JNI_NULL_CHECK(env, j_metadata_ptr, "metadata is null", );
+ try {
+ auto metadata = reinterpret_cast(j_metadata_ptr);
+ delete metadata;
+ }
+ CATCH_STD(env, );
+}
+
+} // extern "C"
diff --git a/java/src/main/native/src/CudfJni.cpp b/java/src/main/native/src/CudfJni.cpp
index 0c560833bb1..928e167c4da 100644
--- a/java/src/main/native/src/CudfJni.cpp
+++ b/java/src/main/native/src/CudfJni.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2020, NVIDIA CORPORATION.
+ * Copyright (c) 2019-2021, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
#include
#include
-#include "jni_utils.hpp"
+#include "cudf_jni_apis.hpp"
namespace {
@@ -45,74 +45,6 @@ constexpr bool is_ptds_enabled{true};
constexpr bool is_ptds_enabled{false};
#endif
-static jclass Contiguous_table_jclass;
-static jmethodID From_contiguous_column_views;
-
-#define CONTIGUOUS_TABLE_CLASS "ai/rapids/cudf/ContiguousTable"
-#define CONTIGUOUS_TABLE_FACTORY_SIG(param_sig) "(" param_sig ")L" CONTIGUOUS_TABLE_CLASS ";"
-
-static bool cache_contiguous_table_jni(JNIEnv *env) {
- jclass cls = env->FindClass(CONTIGUOUS_TABLE_CLASS);
- if (cls == nullptr) {
- return false;
- }
-
- From_contiguous_column_views = env->GetStaticMethodID(cls, "fromContiguousColumnViews",
- CONTIGUOUS_TABLE_FACTORY_SIG("[JJJJ"));
- if (From_contiguous_column_views == nullptr) {
- return false;
- }
-
- // Convert local reference to global so it cannot be garbage collected.
- Contiguous_table_jclass = static_cast(env->NewGlobalRef(cls));
- if (Contiguous_table_jclass == nullptr) {
- return false;
- }
- return true;
-}
-
-static void release_contiguous_table_jni(JNIEnv *env) {
- if (Contiguous_table_jclass != nullptr) {
- env->DeleteGlobalRef(Contiguous_table_jclass);
- Contiguous_table_jclass = nullptr;
- }
-}
-
-jobject contiguous_table_from(JNIEnv *env, cudf::contiguous_split_result &split) {
- jlong address = reinterpret_cast(split.all_data->data());
- jlong size = static_cast(split.all_data->size());
- jlong buff_address = reinterpret_cast(split.all_data.get());
- int num_columns = split.table.num_columns();
- cudf::jni::native_jlongArray views(env, num_columns);
- for (int i = 0; i < num_columns; i++) {
- // TODO Exception handling is not ideal, if no exceptions are thrown ownership of the new cv
- // is passed to java. If an exception is thrown we need to free it, but this needs to be
- // coordinated with the java side because one column may have changed ownership while
- // another may not have. We don't want to double free the view so for now we just let it
- // leak because it should be a small amount of host memory.
- //
- // In the ideal case we would keep the view where it is at, and pass in a pointer to it
- // That pointer would then be copied when java takes ownership of it, but that adds an
- // extra JNI call that I would like to avoid for performance reasons.
- cudf::column_view *cv = new cudf::column_view(split.table.column(i));
- views[i] = reinterpret_cast(cv);
- }
-
- views.commit();
- jobject ret = env->CallStaticObjectMethod(Contiguous_table_jclass, From_contiguous_column_views,
- views.get_jArray(), address, size, buff_address);
-
- if (ret != nullptr) {
- split.all_data.release();
- }
- return ret;
-}
-
-native_jobjectArray contiguous_table_array(JNIEnv *env, jsize length) {
- return native_jobjectArray(
- env, env->NewObjectArray(length, Contiguous_table_jclass, nullptr));
-}
-
static jclass Host_memory_buffer_jclass;
static jmethodID Host_buffer_allocate;
static jfieldID Host_buffer_address;
diff --git a/java/src/main/native/src/TableJni.cpp b/java/src/main/native/src/TableJni.cpp
index 20afe12baf9..30222452804 100644
--- a/java/src/main/native/src/TableJni.cpp
+++ b/java/src/main/native/src/TableJni.cpp
@@ -606,6 +606,38 @@ JNIEXPORT void JNICALL Java_ai_rapids_cudf_Table_deleteCudfTable(JNIEnv *env, jc
CATCH_STD(env, );
}
+JNIEXPORT jlongArray JNICALL Java_ai_rapids_cudf_Table_columnViewsFromPacked(JNIEnv *env, jclass,
+ jobject buffer_obj,
+ jlong j_data_address) {
+ // The GPU data address can be null when the table is empty, so it is not null-checked here.
+ JNI_NULL_CHECK(env, buffer_obj, "metadata is null", nullptr);
+ try {
+ cudf::jni::auto_set_device(env);
+ void const *metadata_address = env->GetDirectBufferAddress(buffer_obj);
+ JNI_NULL_CHECK(env, metadata_address, "metadata buffer address is null", nullptr);
+ cudf::table_view table = cudf::unpack(static_cast(metadata_address),
+ reinterpret_cast(j_data_address));
+ cudf::jni::native_jlongArray views(env, table.num_columns());
+ for (int i = 0; i < table.num_columns(); i++) {
+ // TODO Exception handling is not ideal, if no exceptions are thrown ownership of the new cv
+ // is passed to Java. If an exception is thrown we need to free it, but this needs to be
+ // coordinated with the Java side because one column may have changed ownership while
+ // another may not have. We don't want to double free the view so for now we just let it
+ // leak because it should be a small amount of host memory.
+ //
+ // In the ideal case we would keep the view where it is at, and pass in a pointer to it
+ // That pointer would then be copied when Java takes ownership of it, but that adds an
+ // extra JNI call that I would like to avoid for performance reasons.
+ cudf::column_view *cv = new cudf::column_view(table.column(i));
+ views[i] = reinterpret_cast(cv);
+ }
+ views.commit();
+
+ return views.get_jArray();
+ }
+ CATCH_STD(env, nullptr);
+}
+
JNIEXPORT jlongArray JNICALL Java_ai_rapids_cudf_Table_orderBy(JNIEnv *env, jclass j_class_object,
jlong j_input_table,
jlongArray j_sort_keys_columns,
@@ -1817,11 +1849,12 @@ JNIEXPORT jobjectArray JNICALL Java_ai_rapids_cudf_Table_contiguousSplit(JNIEnv
std::vector indices(n_split_indices.data(),
n_split_indices.data() + n_split_indices.size());
- std::vector result = cudf::contiguous_split(*n_table, indices);
+ std::vector result = cudf::contiguous_split(*n_table, indices);
cudf::jni::native_jobjectArray n_result =
cudf::jni::contiguous_table_array(env, result.size());
for (int i = 0; i < result.size(); i++) {
- n_result.set(i, cudf::jni::contiguous_table_from(env, result[i]));
+ n_result.set(i, cudf::jni::contiguous_table_from(env, result[i].data,
+ result[i].table.num_rows()));
}
return n_result.wrapped();
}
diff --git a/java/src/main/native/src/cudf_jni_apis.hpp b/java/src/main/native/src/cudf_jni_apis.hpp
index a7d955b2bbf..76c7e91d335 100644
--- a/java/src/main/native/src/cudf_jni_apis.hpp
+++ b/java/src/main/native/src/cudf_jni_apis.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2020, NVIDIA CORPORATION.
+ * Copyright (c) 2019-2021, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,12 +23,23 @@
namespace cudf {
namespace jni {
+jlongArray convert_table_for_return(JNIEnv *env, std::unique_ptr &table_result);
+
+//
+// ContiguousTable APIs
+//
+
+bool cache_contiguous_table_jni(JNIEnv *env);
-jobject contiguous_table_from(JNIEnv *env, cudf::contiguous_split_result &split);
+void release_contiguous_table_jni(JNIEnv *env);
+
+jobject contiguous_table_from(JNIEnv *env, cudf::packed_columns &split, long row_count);
native_jobjectArray contiguous_table_array(JNIEnv *env, jsize length);
-jlongArray convert_table_for_return(JNIEnv *env, std::unique_ptr &table_result);
+//
+// HostMemoryBuffer APIs
+//
/**
* Allocate a HostMemoryBuffer
diff --git a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java
index 7806bd1797b..cb1f792b99e 100644
--- a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java
+++ b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java
@@ -3737,6 +3737,12 @@ void testConcatListsOfLists() {
void testContiguousSplitConstructor() {
try (Table tmp = new Table.TestBuilder().column(1, 2).column(3, 4).build();
ContiguousTable ct = tmp.contiguousSplit()[0]) {
+ // table should not be referencing the device buffer yet
+ assertEquals(1, ct.getBuffer().getRefCount());
+
+ // get the table to force it to be instantiated
+ Table ignored = ct.getTable();
+
// one reference for the device buffer itself, two more for the column using it
assertEquals(3, ct.getBuffer().getRefCount());
}
diff --git a/java/src/test/java/ai/rapids/cudf/TableTest.java b/java/src/test/java/ai/rapids/cudf/TableTest.java
index 35be427d0c8..bb9e5e40cb9 100644
--- a/java/src/test/java/ai/rapids/cudf/TableTest.java
+++ b/java/src/test/java/ai/rapids/cudf/TableTest.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
@@ -1672,9 +1673,13 @@ void testContiguousSplit() {
.build()) {
splits = t1.contiguousSplit(2, 5, 9);
assertEquals(4, splits.length);
+ assertEquals(2, splits[0].getRowCount());
assertEquals(2, splits[0].getTable().getRowCount());
+ assertEquals(3, splits[1].getRowCount());
assertEquals(3, splits[1].getTable().getRowCount());
+ assertEquals(4, splits[2].getRowCount());
assertEquals(4, splits[2].getTable().getRowCount());
+ assertEquals(1, splits[3].getRowCount());
assertEquals(1, splits[3].getTable().getRowCount());
} finally {
if (splits != null) {
@@ -1697,9 +1702,13 @@ void testContiguousSplitWithStrings() {
.build()) {
splits = t1.contiguousSplit(2, 5, 9);
assertEquals(4, splits.length);
+ assertEquals(2, splits[0].getRowCount());
assertEquals(2, splits[0].getTable().getRowCount());
+ assertEquals(3, splits[1].getRowCount());
assertEquals(3, splits[1].getTable().getRowCount());
+ assertEquals(4, splits[2].getRowCount());
assertEquals(4, splits[2].getTable().getRowCount());
+ assertEquals(1, splits[3].getRowCount());
assertEquals(1, splits[3].getTable().getRowCount());
} finally {
if (splits != null) {
@@ -2164,6 +2173,25 @@ void testSerializationRoundTripSliced() throws IOException {
}
}
+ @Test
+ void testSerializationReconstructFromMetadata() throws IOException {
+ try (Table t = buildTestTable()) {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ JCudfSerialization.writeToStream(t, bout, 0, t.getRowCount());
+ ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+ try (JCudfSerialization.TableAndRowCountPair trcp = JCudfSerialization.readTableFrom(bin)) {
+ ContiguousTable contigTable = trcp.getContiguousTable();
+ DeviceMemoryBuffer oldbuf = contigTable.getBuffer();
+ try (DeviceMemoryBuffer newbuf = oldbuf.sliceWithCopy(0, oldbuf.getLength())) {
+ ByteBuffer metadata = contigTable.getMetadataDirectBuffer();
+ try (Table newTable = Table.fromPackedTable(metadata, newbuf)) {
+ assertTablesAreEqual(t, newTable);
+ }
+ }
+ }
+ }
+ }
+
@Test
void testValidityFill() {
byte[] buff = new byte[2];