Skip to content

Commit

Permalink
feat(bindings/c): add is_exist operation on operator
Browse files Browse the repository at this point in the history
* Add is_exist operation
* Update some rustdocs

Signed-off-by: Ji-Xinyou <[email protected]>
  • Loading branch information
xyjixyjixyji committed Apr 10, 2023
1 parent 391a56c commit 4a7a333
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 37 deletions.
53 changes: 44 additions & 9 deletions bindings/c/include/opendal.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,22 @@ typedef enum opendal_code {
typedef struct BlockingOperator BlockingOperator;

/*
The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`].
The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`].
It is also the key struct that OpenDAL's APIs access the real
operator's memory. The use of OperatorPtr is zero cost, it
only returns a reference of the underlying Operator.
The [`OperatorPtr`] also has a transparent layout, allowing you
The [`opendal_operator_ptr`] also has a transparent layout, allowing you
to check its validity by native boolean operator.
e.g. you could check by (!ptr) on a opendal_operator_ptr type
e.g. you could check by (!ptr) on a [`opendal_operator_ptr`]
*/
typedef const struct BlockingOperator *opendal_operator_ptr;

/*
The [`Bytes`] type is a C-compatible substitute for [`Bytes`]
The [`opendal_bytes`] type is a C-compatible substitute for [`Vec`]
in Rust, it will not be deallocated automatically like what
has been done in Rust. Instead, you have to call [`opendal_free_bytes`]
to free the heap memory to avoid memory leak.
The field `data` should not be modified since it might causes
the reallocation of the Vector.
*/
typedef struct opendal_bytes {
const uint8_t *data;
Expand All @@ -147,6 +145,16 @@ typedef struct opendal_result_read {
enum opendal_code code;
} opendal_result_read;

/*
The result type for [`opendal_operator_is_exist()`], the field `is_exist`
contains whether the path exists, and the field `code` contains the
corresponding error code.
*/
typedef struct opendal_result_is_exist {
bool is_exist;
enum opendal_code code;
} opendal_result_is_exist;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
Expand Down Expand Up @@ -178,7 +186,10 @@ void opendal_operator_free(opendal_operator_ptr op_ptr);
It is [safe] under two cases below
* The memory pointed to by `path` must contain a valid nul terminator at the end of
the string.
* The `path` points to NULL, this function simply returns you false
# Panic
* If the `path` points to NULL, this function panics
*/
enum opendal_code opendal_operator_blocking_write(opendal_operator_ptr op_ptr,
const char *path,
Expand All @@ -194,15 +205,39 @@ enum opendal_code opendal_operator_blocking_write(opendal_operator_ptr op_ptr,
It is [safe] under two cases below
* The memory pointed to by `path` must contain a valid nul terminator at the end of
the string.
* The `path` points to NULL, this function simply returns you a nullptr
# Panic
* If the `path` points to NULL, this function panics
*/
struct opendal_result_read opendal_operator_blocking_read(opendal_operator_ptr op_ptr,
const char *path);

/*
Check whether the path exists.
If the operation succeeds, no matter the path exists or not,
the error code should be opendal_code::OPENDAL_OK. Otherwise,
the field `is_exist` is filled with false, and the error code
is set correspondingly.
# Safety
It is [safe] under two 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
*/
struct opendal_result_is_exist opendal_operator_is_exist(opendal_operator_ptr op_ptr,
const char *path);

/*
Frees the heap memory used by the [`opendal_bytes`]
*/
void opendal_bytes_free(const struct opendal_bytes *vec);
void opendal_bytes_free(const struct opendal_bytes *self);

#ifdef __cplusplus
} // extern "C"
Expand Down
58 changes: 50 additions & 8 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::types::{opendal_bytes, opendal_operator_ptr};

use ::opendal as od;
use error::opendal_code;
use result::opendal_result_read;
use result::{opendal_result_is_exist, opendal_result_read};

/// Returns a result type [`opendal_result_op`], with operator_ptr. If the construction succeeds
/// the error is nullptr, otherwise it contains the error information.
Expand Down Expand Up @@ -90,15 +90,18 @@ pub extern "C" fn opendal_operator_free(op_ptr: opendal_operator_ptr) {
/// It is [safe] under two cases below
/// * The memory pointed to by `path` must contain a valid nul terminator at the end of
/// the string.
/// * The `path` points to NULL, this function simply returns you false
///
/// # Panic
///
/// * If the `path` points to NULL, this function panics
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_blocking_write(
op_ptr: opendal_operator_ptr,
path: *const c_char,
bytes: opendal_bytes,
) -> opendal_code {
if path.is_null() {
return opendal_code::OPENDAL_ERROR;
panic!("The path given is pointing at NULL");
}

let op = op_ptr.get_ref();
Expand All @@ -118,17 +121,17 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
/// It is [safe] under two cases below
/// * The memory pointed to by `path` must contain a valid nul terminator at the end of
/// the string.
/// * The `path` points to NULL, this function simply returns you a nullptr
///
/// # Panic
///
/// * If the `path` points to NULL, this function panics
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_blocking_read(
op_ptr: opendal_operator_ptr,
path: *const c_char,
) -> opendal_result_read {
if path.is_null() {
return opendal_result_read {
data: std::ptr::null_mut(),
code: opendal_code::OPENDAL_ERROR,
};
panic!("The path given is pointing at NULL");
}

let op = op_ptr.get_ref();
Expand All @@ -148,3 +151,42 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
},
}
}

/// Check whether the path exists.
///
/// If the operation succeeds, no matter the path exists or not,
/// the error code should be opendal_code::OPENDAL_OK. Otherwise,
/// the field `is_exist` is filled with false, and the error code
/// is set correspondingly.
///
/// # Safety
///
/// It is [safe] under two 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
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_is_exist(
op_ptr: opendal_operator_ptr,
path: *const c_char,
) -> opendal_result_is_exist {
if path.is_null() {
panic!("The path given is pointing at NULL");
}

let op = op_ptr.get_ref();
let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
match op.is_exist(path) {
Ok(e) => opendal_result_is_exist {
is_exist: e,
code: opendal_code::OPENDAL_OK,
},
Err(err) => opendal_result_is_exist {
is_exist: false,
code: opendal_code::from_opendal_error(err),
},
}
}
9 changes: 9 additions & 0 deletions bindings/c/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,12 @@ pub struct opendal_result_read {
pub data: *mut opendal_bytes,
pub code: opendal_code,
}

/// The result type for [`opendal_operator_is_exist()`], the field `is_exist`
/// contains whether the path exists, and the field `code` contains the
/// corresponding error code.
#[repr(C)]
pub struct opendal_result_is_exist {
pub is_exist: bool,
pub code: opendal_code,
}
38 changes: 18 additions & 20 deletions bindings/c/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@

use ::opendal as od;

/// The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`].
/// The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`].
/// It is also the key struct that OpenDAL's APIs access the real
/// operator's memory. The use of OperatorPtr is zero cost, it
/// only returns a reference of the underlying Operator.
///
/// The [`OperatorPtr`] also has a transparent layout, allowing you
/// The [`opendal_operator_ptr`] also has a transparent layout, allowing you
/// to check its validity by native boolean operator.
/// e.g. you could check by (!ptr) on a opendal_operator_ptr type
/// e.g. you could check by (!ptr) on a [`opendal_operator_ptr`]
#[repr(transparent)]
pub struct opendal_operator_ptr {
ptr: *const od::BlockingOperator,
}

impl opendal_operator_ptr {
/// Creates an OperatorPtr will nullptr, indicating this [`OperatorPtr`]
/// Creates an OperatorPtr will nullptr, indicating this [`opendal_operator_ptr`]
/// is invalid. The `transparent` layout also guarantees that if the
/// underlying field `ptr` is a nullptr, the [`OperatorPtr`] has the
/// underlying field `ptr` is a nullptr, the [`opendal_operator_ptr`] has the
/// same layout as the nullptr.
pub(crate) fn null() -> Self {
Self {
Expand All @@ -46,7 +46,7 @@ impl opendal_operator_ptr {
self.ptr.is_null()
}

/// Returns a reference to the underlying [`BlockingOperator`]
/// Returns a reference to the underlying [`od::BlockingOperator`]
pub(crate) fn get_ref(&self) -> &od::BlockingOperator {
unsafe { &*(self.ptr) }
}
Expand Down Expand Up @@ -74,26 +74,34 @@ impl From<&mut od::BlockingOperator> for opendal_operator_ptr {
}
}

/// The [`Bytes`] type is a C-compatible substitute for [`Bytes`]
/// The [`opendal_bytes`] type is a C-compatible substitute for [`Vec`]
/// in Rust, it will not be deallocated automatically like what
/// has been done in Rust. Instead, you have to call [`opendal_free_bytes`]
/// to free the heap memory to avoid memory leak.
/// The field `data` should not be modified since it might causes
/// the reallocation of the Vector.
#[repr(C)]
pub struct opendal_bytes {
pub data: *const u8,
pub len: usize,
}

impl opendal_bytes {
/// Construct a [`Vector`] from the Rust [`Vec`] of bytes
/// Construct a [`opendal_bytes`] from the Rust [`Vec`] of bytes
pub(crate) fn from_vec(vec: Vec<u8>) -> Self {
let data = vec.as_ptr() as *const u8;
let len = vec.len();
std::mem::forget(vec); // To avoid deallocation of the vec.
Self { data, len }
}

/// Frees the heap memory used by the [`opendal_bytes`]
#[no_mangle]
pub extern "C" fn opendal_bytes_free(&self) {
unsafe {
// this deallocates the vector by reconstructing the vector and letting
// it be dropped when its out of scope
Vec::from_raw_parts(self.data as *mut u8, self.len, self.len);
}
}
}

#[allow(clippy::from_over_into)]
Expand All @@ -103,13 +111,3 @@ impl Into<bytes::Bytes> for opendal_bytes {
bytes::Bytes::from_static(slice)
}
}

/// Frees the heap memory used by the [`opendal_bytes`]
#[no_mangle]
pub extern "C" fn opendal_bytes_free(vec: *const opendal_bytes) {
unsafe {
// this deallocates the vector by reconstructing the vector and letting
// it be dropped when its out of scope
Vec::from_raw_parts((*vec).data as *mut u8, (*vec).len, (*vec).len);
}
}

0 comments on commit 4a7a333

Please sign in to comment.