Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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