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);