felipecrv commented on code in PR #38799:
URL: https://github.com/apache/arrow/pull/38799#discussion_r1399591330


##########
dev/archery/archery/integration/tester_rust.py:
##########
@@ -117,3 +128,102 @@ def flight_request(self, port, json_path=None, 
scenario_name=None):
         if self.debug:
             log(' '.join(cmd))
         run_cmd(cmd)
+
+    def make_c_data_exporter(self):
+        return RustCDataExporter(self.debug, self.args)
+
+    def make_c_data_importer(self):
+        return RustCDataImporter(self.debug, self.args)
+
+
+_rust_c_data_entrypoints = """
+    const char* arrow_rs_cdata_integration_export_schema_from_json(
+        const char* json_path, uintptr_t out);
+    const char* arrow_rs_cdata_integration_import_schema_and_compare_to_json(
+        const char* json_path, uintptr_t c_schema);
+
+    const char* arrow_rs_cdata_integration_export_batch_from_json(
+        const char* json_path, int num_batch, uintptr_t out);
+    const char* arrow_rs_cdata_integration_import_batch_and_compare_to_json(
+        const char* json_path, int num_batch, uintptr_t c_array);
+
+    void arrow_rs_free_error(const char*);
+    """
+
+
[email protected]_cache
+def _load_ffi(ffi, lib_path=_INTEGRATION_DLL):
+    ffi.cdef(_rust_c_data_entrypoints)
+    dll = ffi.dlopen(lib_path)
+    return dll
+
+
+class _CDataBase:
+
+    def __init__(self, debug, args):
+        self.debug = debug
+        self.args = args
+        self.ffi = cdata.ffi()
+        self.dll = _load_ffi(self.ffi)
+
+    def _pointer_to_int(self, c_ptr):
+        return self.ffi.cast('uintptr_t', c_ptr)
+
+    def _check_rust_error(self, rs_error):
+        """
+        Check a `const char*` error return from an integration entrypoint.
+
+        A null means success, a non-empty string is an error message.
+        The string is dynamically allocated on the Rust side.
+        """
+        assert self.ffi.typeof(rs_error) is self.ffi.typeof("const char*")
+        if rs_error != self.ffi.NULL:
+            try:
+                error = self.ffi.string(rs_error).decode(
+                    'utf8', errors='replace')
+                raise RuntimeError(
+                    f"Rust C Data Integration call failed: {error}")
+            finally:
+                self.dll.arrow_rs_free_error(rs_error)
+
+
+class RustCDataExporter(CDataExporter, _CDataBase):
+
+    def export_schema_from_json(self, json_path, c_schema_ptr):
+        rs_error = self.dll.arrow_rs_cdata_integration_export_schema_from_json(
+            str(json_path).encode(), self._pointer_to_int(c_schema_ptr))
+        self._check_rust_error(rs_error)
+
+    def export_batch_from_json(self, json_path, num_batch, c_array_ptr):
+        rs_error = self.dll.arrow_rs_cdata_integration_export_batch_from_json(
+            str(json_path).encode(), num_batch,
+            self._pointer_to_int(c_array_ptr))
+        self._check_rust_error(rs_error)
+
+    @property
+    def supports_releasing_memory(self):
+        return True
+
+    def record_allocation_state(self):
+        # FIXME is it possible to measure the amount of Rust-allocated memory?

Review Comment:
   Do you want to add your name or an issue ID to this or is it fine to keep it 
as is in these scripts?



##########
dev/archery/archery/integration/tester_rust.py:
##########
@@ -117,3 +128,102 @@ def flight_request(self, port, json_path=None, 
scenario_name=None):
         if self.debug:
             log(' '.join(cmd))
         run_cmd(cmd)
+
+    def make_c_data_exporter(self):
+        return RustCDataExporter(self.debug, self.args)
+
+    def make_c_data_importer(self):
+        return RustCDataImporter(self.debug, self.args)
+
+
+_rust_c_data_entrypoints = """
+    const char* arrow_rs_cdata_integration_export_schema_from_json(
+        const char* json_path, uintptr_t out);
+    const char* arrow_rs_cdata_integration_import_schema_and_compare_to_json(
+        const char* json_path, uintptr_t c_schema);
+
+    const char* arrow_rs_cdata_integration_export_batch_from_json(
+        const char* json_path, int num_batch, uintptr_t out);
+    const char* arrow_rs_cdata_integration_import_batch_and_compare_to_json(
+        const char* json_path, int num_batch, uintptr_t c_array);
+
+    void arrow_rs_free_error(const char*);
+    """
+
+
[email protected]_cache
+def _load_ffi(ffi, lib_path=_INTEGRATION_DLL):
+    ffi.cdef(_rust_c_data_entrypoints)
+    dll = ffi.dlopen(lib_path)
+    return dll
+
+
+class _CDataBase:
+
+    def __init__(self, debug, args):
+        self.debug = debug
+        self.args = args
+        self.ffi = cdata.ffi()
+        self.dll = _load_ffi(self.ffi)
+
+    def _pointer_to_int(self, c_ptr):
+        return self.ffi.cast('uintptr_t', c_ptr)
+
+    def _check_rust_error(self, rs_error):
+        """
+        Check a `const char*` error return from an integration entrypoint.
+
+        A null means success, a non-empty string is an error message.
+        The string is dynamically allocated on the Rust side.
+        """
+        assert self.ffi.typeof(rs_error) is self.ffi.typeof("const char*")
+        if rs_error != self.ffi.NULL:
+            try:
+                error = self.ffi.string(rs_error).decode(
+                    'utf8', errors='replace')
+                raise RuntimeError(
+                    f"Rust C Data Integration call failed: {error}")
+            finally:
+                self.dll.arrow_rs_free_error(rs_error)

Review Comment:
   Cand `self.ffi.string(..)` fail and if that happens is it safe to pass 
`rs_error` to `arrow_rs_free_error` in the finally block?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to