This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 8679cae87 feat(binding/c): Add blocking_reader for C binding (#3259)
8679cae87 is described below

commit 8679cae872a7d3c62c1e48bba2d5271a2f4af4b6
Author: Enwei Jiao <[email protected]>
AuthorDate: Sat Oct 14 12:28:23 2023 +0800

    feat(binding/c): Add blocking_reader for C binding (#3259)
    
    * Add blocking_read_with_buffer for C binding
    
    * Fix clippy warning
    
    * Add opendal_operator_blocking_reader
    
    * Fix memory leak
---
 bindings/c/include/opendal.h | 77 ++++++++++++++++++++++++++++++++++++++++++++
 bindings/c/src/lib.rs        | 67 ++++++++++++++++++++++++++++++++++++++
 bindings/c/src/result.rs     | 11 +++++++
 bindings/c/src/types.rs      | 60 ++++++++++++++++++++++++++++++++++
 bindings/c/tests/bdd.cpp     | 12 +++++++
 5 files changed, 227 insertions(+)

diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 51d66aadb..044a451b5 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -144,6 +144,12 @@ typedef struct BlockingLister BlockingLister;
  */
 typedef struct BlockingOperator BlockingOperator;
 
+/**
+ * BlockingReader is designed to read data from given path in an blocking
+ * manner.
+ */
+typedef struct BlockingReader BlockingReader;
+
 /**
  * Entry returned by [`Lister`] or [`BlockingLister`] to represent a path and 
it's relative metadata.
  *
@@ -320,6 +326,21 @@ typedef struct opendal_result_read {
   struct opendal_error *error;
 } opendal_result_read;
 
+typedef struct opendal_reader {
+  struct BlockingReader *inner;
+} opendal_reader;
+
+/**
+ * \brief The result type returned by opendal_operator_reader().
+ * The result type for opendal_operator_reader(), the field `reader` contains 
the reader
+ * of the path, which is an iterator of the objects under the path. the field 
`code` represents
+ * whether the stat operation is successful.
+ */
+typedef struct opendal_result_reader {
+  struct opendal_reader *reader;
+  struct opendal_error *error;
+} opendal_result_reader;
+
 /**
  * \brief The result type returned by opendal_operator_is_exist().
  *
@@ -421,6 +442,11 @@ typedef struct opendal_list_entry {
   struct Entry *inner;
 } opendal_list_entry;
 
+typedef struct opendal_result_reader_read {
+  uintptr_t size;
+  struct opendal_error *error;
+} opendal_result_reader_read;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -560,6 +586,51 @@ struct opendal_error 
*opendal_operator_blocking_write(const struct opendal_opera
 struct opendal_result_read opendal_operator_blocking_read(const struct 
opendal_operator_ptr *ptr,
                                                           const char *path);
 
+/**
+ * \brief Blockingly read the data from `path`.
+ *
+ * Read the data out from `path` blockingly by operator, returns
+ * an opendal_result_read with error code.
+ *
+ * @param ptr The opendal_operator_ptr created previously
+ * @param path The path you want to read the data out
+ * @param buffer The buffer you want to read the data into
+ * @param buffer_len The length of the buffer
+ * @see opendal_operator_ptr
+ * @see opendal_result_read
+ * @see opendal_code
+ * @return Returns opendal_code
+ *
+ * \note If the read operation succeeds, the returned opendal_bytes is newly 
allocated on heap.
+ * After your usage of that, please call opendal_bytes_free() to free the 
space.
+ *
+ * # Example
+ *
+ * Following is an example
+ * ```C
+ * // ... you have write "Hello, World!" to path "/testpath"
+ *
+ * int length = 13;
+ * unsigned char buffer[length];
+ * opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, 
"testpath", buffer, length);
+ * assert(r == OPENDAL_OK);
+ * // assert buffer == "Hello, World!"
+ *
+ * ```
+ *
+ * # Safety
+ *
+ * It is **safe** under the cases below
+ * * The memory pointed to by `path` must contain a valid nul terminator at 
the end of
+ *   the string.
+ *
+ * # Panic
+ *
+ * * If the `path` points to NULL, this function panics, i.e. exits with 
information
+ */
+struct opendal_result_reader opendal_operator_blocking_reader(const struct 
opendal_operator_ptr *ptr,
+                                                              const char 
*path);
+
 /**
  * \brief Blockingly delete the object in `path`.
  *
@@ -904,6 +975,12 @@ char *opendal_list_entry_name(const struct 
opendal_list_entry *self);
  */
 void opendal_list_entry_free(struct opendal_list_entry *ptr);
 
+struct opendal_result_reader_read opendal_reader_read(const struct 
opendal_reader *self,
+                                                      uint8_t *buf,
+                                                      uintptr_t len);
+
+void opendal_reader_free(struct opendal_reader *ptr);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index edaef1161..79abaeda5 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -38,7 +38,9 @@ use error::opendal_error;
 use once_cell::sync::Lazy;
 use result::opendal_result_list;
 use result::opendal_result_operator_new;
+use result::opendal_result_reader;
 use types::opendal_blocking_lister;
+use types::opendal_reader;
 
 use crate::result::opendal_result_is_exist;
 use crate::result::opendal_result_read;
@@ -293,6 +295,71 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
     }
 }
 
+/// \brief Blockingly read the data from `path`.
+///
+/// Read the data out from `path` blockingly by operator, returns
+/// an opendal_result_read with error code.
+///
+/// @param ptr The opendal_operator_ptr created previously
+/// @param path The path you want to read the data out
+/// @param buffer The buffer you want to read the data into
+/// @param buffer_len The length of the buffer
+/// @see opendal_operator_ptr
+/// @see opendal_result_read
+/// @see opendal_code
+/// @return Returns opendal_code
+///
+/// \note If the read operation succeeds, the returned opendal_bytes is newly 
allocated on heap.
+/// After your usage of that, please call opendal_bytes_free() to free the 
space.
+///
+/// # Example
+///
+/// Following is an example
+/// ```C
+/// // ... you have write "Hello, World!" to path "/testpath"
+///
+/// int length = 13;
+/// unsigned char buffer[length];
+/// opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, 
"testpath", buffer, length);
+/// assert(r == OPENDAL_OK);
+/// // assert buffer == "Hello, World!"
+///
+/// ```
+///
+/// # Safety
+///
+/// It is **safe** under the cases below
+/// * The memory pointed to by `path` must contain a valid nul terminator at 
the end of
+///   the string.
+///
+/// # Panic
+///
+/// * If the `path` points to NULL, this function panics, i.e. exits with 
information
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_blocking_reader(
+    ptr: *const opendal_operator_ptr,
+    path: *const c_char,
+) -> opendal_result_reader {
+    if path.is_null() {
+        panic!("The path given is pointing at NULL");
+    }
+    let op = (*ptr).as_ref();
+    let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
+    match op.reader(path) {
+        Ok(reader) => opendal_result_reader {
+            reader: Box::into_raw(Box::new(opendal_reader::new(reader))),
+            error: std::ptr::null_mut(),
+        },
+        Err(e) => {
+            let e = Box::new(opendal_error::from_opendal_error(e));
+            opendal_result_reader {
+                reader: std::ptr::null_mut(),
+                error: Box::into_raw(e),
+            }
+        }
+    }
+}
+
 /// \brief Blockingly delete the object in `path`.
 ///
 /// Delete the object in `path` blockingly by `op_ptr`.
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index aa95c1190..610e6d43e 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -25,6 +25,7 @@ use crate::types::opendal_blocking_lister;
 use crate::types::opendal_bytes;
 use crate::types::opendal_metadata;
 use crate::types::opendal_operator_ptr;
+use crate::types::opendal_reader;
 
 /// \brief The result type returned by opendal_operator_new() operation.
 ///
@@ -97,3 +98,13 @@ pub struct opendal_result_list {
     /// The error, if ok, it is null
     pub error: *mut opendal_error,
 }
+
+/// \brief The result type returned by opendal_operator_reader().
+/// The result type for opendal_operator_reader(), the field `reader` contains 
the reader
+/// of the path, which is an iterator of the objects under the path. the field 
`code` represents
+/// whether the stat operation is successful.
+#[repr(C)]
+pub struct opendal_result_reader {
+    pub reader: *mut opendal_reader,
+    pub error: *mut opendal_error,
+}
diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs
index dbc0aa68e..19c9365ca 100644
--- a/bindings/c/src/types.rs
+++ b/bindings/c/src/types.rs
@@ -17,10 +17,14 @@
 
 use std::collections::HashMap;
 use std::ffi::CString;
+use std::io::Read;
 use std::os::raw::c_char;
 
 use ::opendal as od;
 
+use crate::error::opendal_code;
+use crate::error::opendal_error;
+
 /// \brief Used to access almost all OpenDAL APIs. It represents a
 /// operator that provides the unified interfaces provided by OpenDAL.
 ///
@@ -408,3 +412,59 @@ impl opendal_list_entry {
         }
     }
 }
+
+#[repr(C)]
+pub struct opendal_reader {
+    inner: *mut od::BlockingReader,
+}
+
+#[repr(C)]
+pub struct opendal_result_reader_read {
+    pub size: usize,
+    pub error: *mut opendal_error,
+}
+
+impl opendal_reader {
+    pub(crate) fn new(reader: od::BlockingReader) -> Self {
+        Self {
+            inner: Box::into_raw(Box::new(reader)),
+        }
+    }
+
+    #[no_mangle]
+    pub unsafe extern "C" fn opendal_reader_read(
+        &self,
+        buf: *mut u8,
+        len: usize,
+    ) -> opendal_result_reader_read {
+        if buf.is_null() {
+            panic!("The buffer given is pointing at NULL");
+        }
+        let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) };
+        let r = (*self.inner).read(buf);
+        match r {
+            Ok(n) => opendal_result_reader_read {
+                size: n,
+                error: std::ptr::null_mut(),
+            },
+            Err(e) => {
+                let e = Box::new(opendal_error::manual_error(
+                    opendal_code::OPENDAL_UNEXPECTED,
+                    e.to_string(),
+                ));
+                opendal_result_reader_read {
+                    size: 0,
+                    error: Box::into_raw(e),
+                }
+            }
+        }
+    }
+
+    #[no_mangle]
+    pub unsafe extern "C" fn opendal_reader_free(ptr: *mut opendal_reader) {
+        if !ptr.is_null() {
+            let _ = unsafe { Box::from_raw((*ptr).inner) };
+            let _ = unsafe { Box::from_raw(ptr) };
+        }
+    }
+}
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index 76d5047fa..e5a94d2af 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -87,6 +87,18 @@ TEST_F(OpendalBddTest, FeatureTest)
         EXPECT_EQ(this->content[i], (char)(r.data->data[i]));
     }
 
+    // The blocking file "test" must have content "Hello, World!" and read 
into buffer
+    int length = this->content.length();
+    unsigned char buffer[this->content.length()];
+    opendal_result_reader reader = opendal_operator_blocking_reader(this->p, 
this->path.c_str());
+    EXPECT_EQ(reader.error, nullptr);
+    auto rst = opendal_reader_read(reader.reader, buffer, length);
+    EXPECT_EQ(rst.size, length);
+    for (int i = 0; i < this->content.length(); i++) {
+        EXPECT_EQ(this->content[i], buffer[i]);
+    }
+    opendal_reader_free(reader.reader);
+
     // The blocking file should be deleted
     error = opendal_operator_blocking_delete(this->p, this->path.c_str());
     EXPECT_EQ(error, nullptr);

Reply via email to