Skip to content

Commit

Permalink
feat(binding/c): Add blocking_reader for C binding (#3259)
Browse files Browse the repository at this point in the history
* Add blocking_read_with_buffer for C binding

* Fix clippy warning

* Add opendal_operator_blocking_reader

* Fix memory leak
  • Loading branch information
jiaoew1991 authored Oct 14, 2023
1 parent 8e098eb commit 8679cae
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 0 deletions.
77 changes: 77 additions & 0 deletions bindings/c/include/opendal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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().
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`.
*
Expand Down Expand Up @@ -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
Expand Down
67 changes: 67 additions & 0 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`.
Expand Down
11 changes: 11 additions & 0 deletions bindings/c/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
}
60 changes: 60 additions & 0 deletions bindings/c/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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) };
}
}
}
12 changes: 12 additions & 0 deletions bindings/c/tests/bdd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 8679cae

Please sign in to comment.