diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index 51d66aadb3c0..044a451b5fa9 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 edaef1161f8b..79abaeda5b55 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 aa95c1190b01..610e6d43e5ea 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 dbc0aa68ee9d..19c9365ca220 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 76d5047faf0a..e5a94d2afd2b 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);