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 20d82324 feat(bindings/C): Initially support stat in C binding (#2249)
20d82324 is described below
commit 20d82324b4eabe307316a3567a09f731c32a2ea2
Author: xyJi <[email protected]>
AuthorDate: Wed May 10 16:28:54 2023 +0800
feat(bindings/C): Initially support stat in C binding (#2249)
* basic support of stat
Signed-off-by: Ji-Xinyou <[email protected]>
* content length
Signed-off-by: Ji-Xinyou <[email protected]>
* more docs
Signed-off-by: Ji-Xinyou <[email protected]>
* format basicio.c
Signed-off-by: Ji-Xinyou <[email protected]>
* typo
Signed-off-by: Ji-Xinyou <[email protected]>
* no abbreviation for meta
Signed-off-by: Ji-Xinyou <[email protected]>
---------
Signed-off-by: Ji-Xinyou <[email protected]>
---
bindings/c/include/opendal.h | 88 ++++++++++++++++++++++++++++++++++++++++----
bindings/c/src/lib.rs | 59 +++++++++++++++++++++--------
bindings/c/src/result.rs | 11 +++++-
bindings/c/src/types.rs | 83 +++++++++++++++++++++++++++++++++++++----
bindings/c/tests/basicio.c | 26 +++++++++++--
5 files changed, 231 insertions(+), 36 deletions(-)
diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index f4ad4704..2d4e82fd 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -111,6 +111,17 @@ typedef enum opendal_code {
*/
typedef struct BlockingOperator BlockingOperator;
+/*
+ Metadata carries all metadata associated with an path.
+
+ # Notes
+
+ mode and content_length are required metadata that all services
+ should provide during `stat` operation. But in `list` operation,
+ a.k.a., `Entry`'s content length could be `None`.
+ */
+typedef struct Metadata Metadata;
+
/*
The [`opendal_operator_ptr`] owns a pointer to a [`od::BlockingOperator`].
It is also the key struct that OpenDAL's APIs access the real
@@ -155,6 +166,27 @@ typedef struct opendal_result_is_exist {
enum opendal_code code;
} opendal_result_is_exist;
+/*
+ Metadata carries all metadata associated with an path.
+
+ # Notes
+
+ mode and content_length are required metadata that all services
+ should provide during `stat` operation. But in `list` operation,
+ a.k.a., `Entry`'s content length could be NULL.
+ */
+typedef const struct Metadata *opendal_metadata;
+
+/*
+ The result type for [`opendal_operator_stat()`], the meta contains the
metadata
+ of the path, the code represents whether the stat operation is successful.
Note
+ that the operation could be successful even if the path does not exist.
+ */
+typedef struct opendal_result_stat {
+ opendal_metadata meta;
+ enum opendal_code code;
+} opendal_result_stat;
+
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
@@ -172,18 +204,13 @@ extern "C" {
*/
opendal_operator_ptr opendal_operator_new(const char *scheme);
-/*
- Free the allocated operator pointed by [`opendal_operator_ptr`]
- */
-void opendal_operator_free(opendal_operator_ptr op_ptr);
-
/*
Write the data into the path blockingly by operator, returns the error code
OPENDAL_OK
if succeeds, others otherwise
# Safety
- It is [safe] under two cases below
+ 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.
@@ -202,7 +229,7 @@ enum opendal_code
opendal_operator_blocking_write(opendal_operator_ptr op_ptr,
# Safety
- It is [safe] under two cases below
+ 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.
@@ -223,7 +250,7 @@ struct opendal_result_read
opendal_operator_blocking_read(opendal_operator_ptr o
# Safety
- It is [safe] under two cases below
+ 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.
@@ -234,11 +261,56 @@ struct opendal_result_read
opendal_operator_blocking_read(opendal_operator_ptr o
struct opendal_result_is_exist opendal_operator_is_exist(opendal_operator_ptr
op_ptr,
const char *path);
+/*
+ Stat the path, return its metadata.
+
+ If the operation succeeds, no matter the path exists or not,
+ the error code should be opendal_code::OPENDAL_OK. Otherwise,
+ the field `meata` is filled with a NULL pointer, and the error code
+ is set correspondingly.
+
+ # 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
+ */
+struct opendal_result_stat opendal_operator_stat(opendal_operator_ptr op_ptr,
const char *path);
+
+/*
+ Free the allocated operator pointed by [`opendal_operator_ptr`]
+ */
+void opendal_operator_free(const opendal_operator_ptr *self);
+
/*
Frees the heap memory used by the [`opendal_bytes`]
*/
void opendal_bytes_free(const struct opendal_bytes *self);
+/*
+ Free the allocated metadata
+ */
+void opendal_metadata_free(const opendal_metadata *self);
+
+/*
+ Return the content_length of the metadata
+ */
+uint64_t opendal_metadata_content_length(const opendal_metadata *self);
+
+/*
+ Return whether the path represents a file
+ */
+bool opendal_metadata_is_file(const opendal_metadata *self);
+
+/*
+ Return whether the path represents a directory
+ */
+bool opendal_metadata_is_dir(const opendal_metadata *self);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index c248502e..649154ea 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -28,8 +28,8 @@ use std::str::FromStr;
use ::opendal as od;
use error::opendal_code;
-use result::opendal_result_is_exist;
-use result::opendal_result_read;
+use result::{opendal_result_is_exist, opendal_result_read,
opendal_result_stat};
+use types::opendal_metadata;
use crate::types::opendal_bytes;
use crate::types::opendal_operator_ptr;
@@ -74,22 +74,12 @@ pub unsafe extern "C" fn opendal_operator_new(scheme:
*const c_char) -> opendal_
opendal_operator_ptr::from(op)
}
-/// Free the allocated operator pointed by [`opendal_operator_ptr`]
-#[no_mangle]
-pub extern "C" fn opendal_operator_free(op_ptr: opendal_operator_ptr) {
- if op_ptr.is_null() {
- return;
- }
- let _ = unsafe { Box::from_raw(op_ptr.get_ref_mut()) };
- // dropped
-}
-
/// Write the data into the path blockingly by operator, returns the error
code OPENDAL_OK
/// if succeeds, others otherwise
///
/// # Safety
///
-/// It is [safe] under two cases below
+/// 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.
///
@@ -120,7 +110,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write(
///
/// # Safety
///
-/// It is [safe] under two cases below
+/// 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.
///
@@ -163,7 +153,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read(
///
/// # Safety
///
-/// It is [safe] under two cases below
+/// 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.
///
@@ -192,3 +182,42 @@ pub unsafe extern "C" fn opendal_operator_is_exist(
},
}
}
+
+/// Stat the path, return its metadata.
+///
+/// If the operation succeeds, no matter the path exists or not,
+/// the error code should be opendal_code::OPENDAL_OK. Otherwise,
+/// the field `meata` is filled with a NULL pointer, and the error code
+/// is set correspondingly.
+///
+/// # 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
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_stat(
+ op_ptr: opendal_operator_ptr,
+ path: *const c_char,
+) -> opendal_result_stat {
+ 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.stat(path) {
+ Ok(m) => opendal_result_stat {
+ meta: opendal_metadata::from_metadata(m),
+ code: opendal_code::OPENDAL_OK,
+ },
+ Err(err) => opendal_result_stat {
+ meta: opendal_metadata::null(),
+ code: opendal_code::from_opendal_error(err),
+ },
+ }
+}
diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs
index 6f869973..36591c30 100644
--- a/bindings/c/src/result.rs
+++ b/bindings/c/src/result.rs
@@ -21,7 +21,7 @@
//! we are defining all Result types here
use crate::error::opendal_code;
-use crate::types::opendal_bytes;
+use crate::types::{opendal_bytes, opendal_metadata};
/// The Rust-like Result type of opendal C binding, it contains
/// the data that the read operation returns and a error code
@@ -41,3 +41,12 @@ pub struct opendal_result_is_exist {
pub is_exist: bool,
pub code: opendal_code,
}
+
+/// The result type for [`opendal_operator_stat()`], the meta contains the
metadata
+/// of the path, the code represents whether the stat operation is successful.
Note
+/// that the operation could be successful even if the path does not exist.
+#[repr(C)]
+pub struct opendal_result_stat {
+ pub meta: opendal_metadata,
+ pub code: opendal_code,
+}
diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs
index b8752fa0..5bbebb02 100644
--- a/bindings/c/src/types.rs
+++ b/bindings/c/src/types.rs
@@ -30,6 +30,17 @@ pub struct opendal_operator_ptr {
ptr: *const od::BlockingOperator,
}
+impl opendal_operator_ptr {
+ /// Free the allocated operator pointed by [`opendal_operator_ptr`]
+ #[no_mangle]
+ pub extern "C" fn opendal_operator_free(&self) {
+ if self.is_null() {
+ return;
+ }
+ let _ = unsafe { Box::from_raw(self.ptr as *mut od::BlockingOperator)
};
+ }
+}
+
impl opendal_operator_ptr {
/// Creates an OperatorPtr will nullptr, indicating this
[`opendal_operator_ptr`]
/// is invalid. The `transparent` layout also guarantees that if the
@@ -50,14 +61,6 @@ impl opendal_operator_ptr {
pub(crate) fn get_ref(&self) -> &od::BlockingOperator {
unsafe { &*(self.ptr) }
}
-
- /// Returns a mutable reference to the underlying [`od::BlockingOperator`].
- /// Note that this should be only used when the operator is being freed
- #[allow(clippy::mut_from_ref)]
- pub(crate) fn get_ref_mut(&self) -> &mut od::BlockingOperator {
- let ptr_mut = self.ptr as *mut od::BlockingOperator;
- unsafe { &mut (*ptr_mut) }
- }
}
#[allow(clippy::from_over_into)]
@@ -111,3 +114,67 @@ impl Into<bytes::Bytes> for opendal_bytes {
bytes::Bytes::from_static(slice)
}
}
+
+/// Metadata carries all metadata associated with an path.
+///
+/// # Notes
+///
+/// mode and content_length are required metadata that all services
+/// should provide during `stat` operation. But in `list` operation,
+/// a.k.a., `Entry`'s content length could be NULL.
+#[repr(transparent)]
+pub struct opendal_metadata {
+ pub inner: *const od::Metadata,
+}
+
+impl opendal_metadata {
+ /// Free the allocated metadata
+ #[no_mangle]
+ pub extern "C" fn opendal_metadata_free(&self) {
+ if self.inner.is_null() {
+ return;
+ }
+ let _ = unsafe { Box::from_raw(self.inner as *mut od::Metadata) };
+ }
+
+ /// Return the content_length of the metadata
+ #[no_mangle]
+ pub extern "C" fn opendal_metadata_content_length(&self) -> u64 {
+ // Safety: the inner should never be null once constructed
+ // The use-after-free is undefined behavior
+ unsafe { (*self.inner).content_length() }
+ }
+
+ /// Return whether the path represents a file
+ #[no_mangle]
+ pub extern "C" fn opendal_metadata_is_file(&self) -> bool {
+ // Safety: the inner should never be null once constructed
+ // The use-after-free is undefined behavior
+ unsafe { (*self.inner).is_file() }
+ }
+
+ /// Return whether the path represents a directory
+ #[no_mangle]
+ pub extern "C" fn opendal_metadata_is_dir(&self) -> bool {
+ // Safety: the inner should never be null once constructed
+ // The use-after-free is undefined behavior
+ unsafe { (*self.inner).is_dir() }
+ }
+}
+
+impl opendal_metadata {
+ /// Return a null metadata
+ pub(crate) fn null() -> Self {
+ Self {
+ inner: std::ptr::null(),
+ }
+ }
+
+ /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible
+ /// [`opendal_metadata`]
+ pub(crate) fn from_metadata(m: od::Metadata) -> Self {
+ Self {
+ inner: Box::leak(Box::new(m)),
+ }
+ }
+}
diff --git a/bindings/c/tests/basicio.c b/bindings/c/tests/basicio.c
index 765669f2..36524594 100644
--- a/bindings/c/tests/basicio.c
+++ b/bindings/c/tests/basicio.c
@@ -27,13 +27,12 @@
// * A valid ptr is given
// * The blocking write operation is successful
// * The blocking read operation is successful and works as expected
-void test_operator_rw(opendal_operator_ptr ptr)
+void test_operator_rw(opendal_operator_ptr ptr, char* path)
{
// have to be valid ptr
assert(ptr);
// write some contents by the operator, must be successful
- char path[] = "test";
char content[] = "Hello World";
const opendal_bytes data = {
.len = sizeof(content) - 1,
@@ -55,17 +54,36 @@ void test_operator_rw(opendal_operator_ptr ptr)
opendal_bytes_free(r.data);
}
+void test_operator_stat(opendal_operator_ptr ptr, char* path)
+{
+ assert(ptr);
+ opendal_result_stat r = opendal_operator_stat(ptr, path);
+ assert(r.code == OPENDAL_OK);
+
+ opendal_metadata meta = r.meta;
+ assert(meta);
+
+ assert(opendal_metadata_is_file(&meta));
+
+ uint64_t content_length = opendal_metadata_content_length(&meta);
+ assert(content_length == 11);
+
+ opendal_metadata_free(&meta);
+}
+
int main(int argc, char* argv[])
{
// construct the memory operator
char scheme1[] = "memory";
+ char path[] = "test";
opendal_operator_ptr p1 = opendal_operator_new(scheme1);
assert(p1);
- test_operator_rw(p1);
+ test_operator_rw(p1, path);
+ test_operator_stat(p1, path);
// free the operator
- opendal_operator_free(p1);
+ opendal_operator_free(&p1);
return 0;
}