From 54d1024c8b92d995e8a198a650fb44c6a9871f5b Mon Sep 17 00:00:00 2001 From: Ji-Xinyou Date: Sat, 14 Oct 2023 01:01:31 -0400 Subject: [PATCH 01/16] chores --- bindings/c/Makefile | 1 + bindings/c/README.md | 4 +-- bindings/c/examples/basic.c | 4 +-- bindings/c/examples/error_handle.c | 2 +- bindings/c/include/opendal.h | 52 +++++++++++++++--------------- bindings/c/src/lib.rs | 22 ++++++------- bindings/c/src/result.rs | 4 +-- bindings/c/src/types.rs | 19 ++++++----- bindings/c/tests/bdd.cpp | 10 +++--- bindings/c/tests/error_msg.cpp | 2 +- bindings/c/tests/list.cpp | 6 ++-- 11 files changed, 65 insertions(+), 61 deletions(-) diff --git a/bindings/c/Makefile b/bindings/c/Makefile index e10f47bdf4b0..245d4facbb11 100644 --- a/bindings/c/Makefile +++ b/bindings/c/Makefile @@ -32,6 +32,7 @@ all: build test examples .PHONY: format format: + cargo fmt find . -name '*.cpp' -exec clang-format -i --style=WebKit --verbose {} \; find . -name '*.c' -exec clang-format -i --style=WebKit --verbose {} \; diff --git a/bindings/c/README.md b/bindings/c/README.md index f89cead26666..13c88797adbe 100644 --- a/bindings/c/README.md +++ b/bindings/c/README.md @@ -24,11 +24,11 @@ int main() }; /* Write this into path "/testpath" */ - opendal_error *error = opendal_operator_blocking_write(op, "/testpath", data); + opendal_error *error = opendal_operator_write(op, "/testpath", data); assert(error == NULL); /* We can read it out, make sure the data is the same */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); opendal_bytes* read_bytes = r.data; assert(r.error == NULL); assert(read_bytes->len == 24); diff --git a/bindings/c/examples/basic.c b/bindings/c/examples/basic.c index 88071847c345..c5019a901742 100644 --- a/bindings/c/examples/basic.c +++ b/bindings/c/examples/basic.c @@ -37,11 +37,11 @@ int main() }; /* Write this into path "/testpath" */ - opendal_error* error = opendal_operator_blocking_write(op, "/testpath", data); + opendal_error* error = opendal_operator_write(op, "/testpath", data); assert(error == NULL); /* We can read it out, make sure the data is the same */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); opendal_bytes* read_bytes = r.data; assert(r.error == NULL); assert(read_bytes->len == 24); diff --git a/bindings/c/examples/error_handle.c b/bindings/c/examples/error_handle.c index 3bb3308e7978..99f5966ff9e7 100644 --- a/bindings/c/examples/error_handle.c +++ b/bindings/c/examples/error_handle.c @@ -32,7 +32,7 @@ int main() opendal_operator_ptr* op = result.operator_ptr; /* The read is supposed to fail */ - opendal_result_read r = opendal_operator_blocking_read(op, "/testpath"); + opendal_result_read r = opendal_operator_read(op, "/testpath"); assert(r.error != NULL); assert(r.error->code == OPENDAL_NOT_FOUND); diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index 044a451b5fa9..d6b839dcd185 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -405,17 +405,17 @@ typedef struct opendal_result_stat { * * Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() */ typedef struct opendal_blocking_lister { struct BlockingLister *inner; } opendal_blocking_lister; /** - * \brief The result type returned by opendal_operator_blocking_list(). + * \brief The result type returned by opendal_operator_list(). * - * The result type for opendal_operator_blocking_list(), the field `lister` contains the lister + * The result type for opendal_operator_list(), the field `lister` contains the lister * of the path, which is an iterator of the objects under the path. the field `error` represents * whether the stat operation is successful. If successful, the `error` field is null. */ @@ -433,8 +433,8 @@ typedef struct opendal_result_list { /** * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() * @see opendal_list_entry_path() * @see opendal_list_entry_name() */ @@ -521,7 +521,7 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; * * // now you can write! - * opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", bytes); + * opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); * * // Assert that this succeeds * assert(err == NULL); @@ -539,9 +539,9 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_blocking_write(const struct opendal_operator_ptr *ptr, - const char *path, - struct opendal_bytes bytes); +struct opendal_error *opendal_operator_write(const struct opendal_operator_ptr *ptr, + const char *path, + struct opendal_bytes bytes); /** * \brief Blockingly read the data from `path`. @@ -566,7 +566,7 @@ struct opendal_error *opendal_operator_blocking_write(const struct opendal_opera * ```C * // ... you have write "Hello, World!" to path "/testpath" * - * opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath"); + * opendal_result_read r = opendal_operator_read(ptr, "testpath"); * assert(r.error == NULL); * * opendal_bytes *bytes = r.data; @@ -583,8 +583,8 @@ struct opendal_error *opendal_operator_blocking_write(const struct opendal_opera * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_read opendal_operator_blocking_read(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_result_read opendal_operator_read(const struct opendal_operator_ptr *ptr, + const char *path); /** * \brief Blockingly read the data from `path`. @@ -612,7 +612,7 @@ struct opendal_result_read opendal_operator_blocking_read(const struct opendal_o * * int length = 13; * unsigned char buffer[length]; - * opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, "testpath", buffer, length); + * opendal_code r = opendal_operator_read_with_buffer(ptr, "testpath", buffer, length); * assert(r == OPENDAL_OK); * // assert buffer == "Hello, World!" * @@ -628,8 +628,8 @@ struct opendal_result_read opendal_operator_blocking_read(const struct opendal_o * * * 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); +struct opendal_result_reader opendal_operator_reader(const struct opendal_operator_ptr *ptr, + const char *path); /** * \brief Blockingly delete the object in `path`. @@ -652,12 +652,12 @@ struct opendal_result_reader opendal_operator_blocking_reader(const struct opend * // prepare your data * char* data = "Hello, World!"; * opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; - * opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", bytes); + * opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); * * assert(error == NULL); * * // now you can delete! - * opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath"); + * opendal_error *error = opendal_operator_delete(ptr, "/testpath"); * * // Assert that this succeeds * assert(error == NULL); @@ -673,8 +673,8 @@ struct opendal_result_reader opendal_operator_blocking_reader(const struct opend * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_blocking_delete(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_error *opendal_operator_delete(const struct opendal_operator_ptr *ptr, + const char *path); /** * \brief Check whether the path exists. @@ -780,7 +780,7 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * ```C * // You have written some data into some files path "root/dir1" * // Your opendal_operator_ptr was called ptr - * opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1"); + * opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); * assert(l.error == ERROR); * * opendal_blocking_lister *lister = l.lister; @@ -809,8 +809,8 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_list opendal_operator_blocking_list(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_result_list opendal_operator_list(const struct opendal_operator_ptr *ptr, + const char *path); /** * \brief Frees the opendal_error, ok to call on NULL @@ -940,8 +940,8 @@ void opendal_operator_options_free(const struct opendal_operator_options *option * Lister is an iterator of the objects under its path, this method is the same as * calling next() on the iterator * - * For examples, please see the comment section of opendal_operator_blocking_list() - * @see opendal_operator_blocking_list() + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() */ struct opendal_list_entry *opendal_lister_next(const struct opendal_blocking_lister *self); @@ -975,7 +975,7 @@ 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, +struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *reader, uint8_t *buf, uintptr_t len); diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 79abaeda5b55..f1abc64e6270 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -189,7 +189,7 @@ pub unsafe extern "C" fn opendal_operator_new( /// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; /// /// // now you can write! -/// opendal_error *err = opendal_operator_blocking_write(ptr, "/testpath", bytes); +/// opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); /// /// // Assert that this succeeds /// assert(err == NULL); @@ -207,7 +207,7 @@ pub unsafe extern "C" fn opendal_operator_new( /// /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] -pub unsafe extern "C" fn opendal_operator_blocking_write( +pub unsafe extern "C" fn opendal_operator_write( ptr: *const opendal_operator_ptr, path: *const c_char, bytes: opendal_bytes, @@ -249,7 +249,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write( /// ```C /// // ... you have write "Hello, World!" to path "/testpath" /// -/// opendal_result_read r = opendal_operator_blocking_read(ptr, "testpath"); +/// opendal_result_read r = opendal_operator_read(ptr, "testpath"); /// assert(r.error == NULL); /// /// opendal_bytes *bytes = r.data; @@ -266,7 +266,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_write( /// /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] -pub unsafe extern "C" fn opendal_operator_blocking_read( +pub unsafe extern "C" fn opendal_operator_read( ptr: *const opendal_operator_ptr, path: *const c_char, ) -> opendal_result_read { @@ -320,7 +320,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read( /// /// int length = 13; /// unsigned char buffer[length]; -/// opendal_code r = opendal_operator_blocking_read_with_buffer(ptr, "testpath", buffer, length); +/// opendal_code r = opendal_operator_read_with_buffer(ptr, "testpath", buffer, length); /// assert(r == OPENDAL_OK); /// // assert buffer == "Hello, World!" /// @@ -336,7 +336,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_read( /// /// * 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( +pub unsafe extern "C" fn opendal_operator_reader( ptr: *const opendal_operator_ptr, path: *const c_char, ) -> opendal_result_reader { @@ -380,12 +380,12 @@ pub unsafe extern "C" fn opendal_operator_blocking_reader( /// // prepare your data /// char* data = "Hello, World!"; /// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; -/// opendal_error *error = opendal_operator_blocking_write(ptr, "/testpath", bytes); +/// opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); /// /// assert(error == NULL); /// /// // now you can delete! -/// opendal_error *error = opendal_operator_blocking_delete(ptr, "/testpath"); +/// opendal_error *error = opendal_operator_delete(ptr, "/testpath"); /// /// // Assert that this succeeds /// assert(error == NULL); @@ -401,7 +401,7 @@ pub unsafe extern "C" fn opendal_operator_blocking_reader( /// /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] -pub unsafe extern "C" fn opendal_operator_blocking_delete( +pub unsafe extern "C" fn opendal_operator_delete( ptr: *const opendal_operator_ptr, path: *const c_char, ) -> *mut opendal_error { @@ -565,7 +565,7 @@ pub unsafe extern "C" fn opendal_operator_stat( /// ```C /// // You have written some data into some files path "root/dir1" /// // Your opendal_operator_ptr was called ptr -/// opendal_result_list l = opendal_operator_blocking_list(ptr, "root/dir1"); +/// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); /// assert(l.error == ERROR); /// /// opendal_blocking_lister *lister = l.lister; @@ -594,7 +594,7 @@ pub unsafe extern "C" fn opendal_operator_stat( /// /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] -pub unsafe extern "C" fn opendal_operator_blocking_list( +pub unsafe extern "C" fn opendal_operator_list( ptr: *const opendal_operator_ptr, path: *const c_char, ) -> opendal_result_list { diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index 610e6d43e5ea..886c82e0e7b9 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -86,9 +86,9 @@ pub struct opendal_result_stat { pub error: *mut opendal_error, } -/// \brief The result type returned by opendal_operator_blocking_list(). +/// \brief The result type returned by opendal_operator_list(). /// -/// The result type for opendal_operator_blocking_list(), the field `lister` contains the lister +/// The result type for opendal_operator_list(), the field `lister` contains the lister /// of the path, which is an iterator of the objects under the path. the field `error` represents /// whether the stat operation is successful. If successful, the `error` field is null. #[repr(C)] diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index 19c9365ca220..1de7d134501d 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -314,8 +314,8 @@ impl opendal_operator_options { /// /// Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) /// -/// For examples, please see the comment section of opendal_operator_blocking_list() -/// @see opendal_operator_blocking_list() +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() #[repr(C)] pub struct opendal_blocking_lister { inner: *mut od::BlockingLister, @@ -333,8 +333,8 @@ impl opendal_blocking_lister { /// Lister is an iterator of the objects under its path, this method is the same as /// calling next() on the iterator /// - /// For examples, please see the comment section of opendal_operator_blocking_list() - /// @see opendal_operator_blocking_list() + /// For examples, please see the comment section of opendal_operator_list() + /// @see opendal_operator_list() #[no_mangle] pub unsafe extern "C" fn opendal_lister_next(&self) -> *mut opendal_list_entry { let e = (*self.inner).next(); @@ -360,8 +360,8 @@ impl opendal_blocking_lister { /// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister /// -/// For examples, please see the comment section of opendal_operator_blocking_list() -/// @see opendal_operator_blocking_list() +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() /// @see opendal_list_entry_path() /// @see opendal_list_entry_name() #[repr(C)] @@ -433,15 +433,18 @@ impl opendal_reader { #[no_mangle] pub unsafe extern "C" fn opendal_reader_read( - &self, + reader: *const 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); + + let inner = unsafe { &mut *(*reader).inner }; + let r = inner.read(buf); match r { Ok(n) => opendal_result_reader_read { size: n, diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp index e5a94d2afd2b..84288e5f0880 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -61,7 +61,7 @@ TEST_F(OpendalBddTest, FeatureTest) .data = (uint8_t*)this->content.c_str(), .len = this->content.length(), }; - opendal_error* error = opendal_operator_blocking_write(this->p, this->path.c_str(), data); + opendal_error* error = opendal_operator_write(this->p, this->path.c_str(), data); EXPECT_EQ(error, nullptr); // The blocking file "test" should exist @@ -80,7 +80,7 @@ TEST_F(OpendalBddTest, FeatureTest) opendal_metadata_free(meta); // The blocking file "test" must have content "Hello, World!" - struct opendal_result_read r = opendal_operator_blocking_read(this->p, this->path.c_str()); + struct opendal_result_read r = opendal_operator_read(this->p, this->path.c_str()); EXPECT_EQ(r.error, nullptr); EXPECT_EQ(r.data->len, this->content.length()); for (int i = 0; i < r.data->len; i++) { @@ -90,7 +90,7 @@ TEST_F(OpendalBddTest, FeatureTest) // 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()); + opendal_result_reader reader = opendal_operator_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); @@ -100,14 +100,14 @@ TEST_F(OpendalBddTest, FeatureTest) opendal_reader_free(reader.reader); // The blocking file should be deleted - error = opendal_operator_blocking_delete(this->p, this->path.c_str()); + error = opendal_operator_delete(this->p, this->path.c_str()); EXPECT_EQ(error, nullptr); e = opendal_operator_is_exist(this->p, this->path.c_str()); EXPECT_EQ(e.error, nullptr); EXPECT_FALSE(e.is_exist); // The deletion operation should be idempotent - error = opendal_operator_blocking_delete(this->p, this->path.c_str()); + error = opendal_operator_delete(this->p, this->path.c_str()); EXPECT_EQ(error, nullptr); opendal_bytes_free(r.data); diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp index 3d06f4532dc5..4ef2687c1f56 100644 --- a/bindings/c/tests/error_msg.cpp +++ b/bindings/c/tests/error_msg.cpp @@ -49,7 +49,7 @@ TEST_F(OpendalErrorTest, ErrorReadTest) { // Initialize a operator for "memory" backend, with no options // The read is supposed to fail - opendal_result_read r = opendal_operator_blocking_read(this->p, "/testpath"); + opendal_result_read r = opendal_operator_read(this->p, "/testpath"); ASSERT_NE(r.error, nullptr); ASSERT_EQ(r.error->code, OPENDAL_NOT_FOUND); diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index 7b003e6a61fb..18bc0e8163f7 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -65,11 +65,11 @@ TEST_F(OpendalListTest, ListDirTest) }; // write must succeed - EXPECT_EQ(opendal_operator_blocking_write(this->p, path.c_str(), data), + EXPECT_EQ(opendal_operator_write(this->p, path.c_str(), data), nullptr); // list must succeed since the write succeeded - opendal_result_list l = opendal_operator_blocking_list(this->p, (dname + "/").c_str()); + opendal_result_list l = opendal_operator_list(this->p, (dname + "/").c_str()); EXPECT_EQ(l.error, nullptr); opendal_blocking_lister* lister = l.lister; @@ -104,7 +104,7 @@ TEST_F(OpendalListTest, ListDirTest) EXPECT_TRUE(found); // delete - EXPECT_EQ(opendal_operator_blocking_delete(this->p, path.c_str()), + EXPECT_EQ(opendal_operator_delete(this->p, path.c_str()), nullptr); opendal_lister_free(lister); From 99500512d9023de80c4f0ab6f033bef300135e17 Mon Sep 17 00:00:00 2001 From: Ji-Xinyou Date: Sat, 14 Oct 2023 01:23:11 -0400 Subject: [PATCH 02/16] rename opendal_lister as well --- bindings/c/include/opendal.h | 20 ++++++++++---------- bindings/c/src/lib.rs | 10 +++++----- bindings/c/src/result.rs | 4 ++-- bindings/c/src/types.rs | 12 ++++++------ bindings/c/tests/list.cpp | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index d6b839dcd185..1ece0cbd1199 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -408,9 +408,9 @@ typedef struct opendal_result_stat { * For examples, please see the comment section of opendal_operator_list() * @see opendal_operator_list() */ -typedef struct opendal_blocking_lister { +typedef struct opendal_lister { struct BlockingLister *inner; -} opendal_blocking_lister; +} opendal_lister; /** * \brief The result type returned by opendal_operator_list(). @@ -423,7 +423,7 @@ typedef struct opendal_result_list { /** * The lister, used for further listing operations */ - struct opendal_blocking_lister *lister; + struct opendal_lister *lister; /** * The error, if ok, it is null */ @@ -431,7 +431,7 @@ typedef struct opendal_result_list { } opendal_result_list; /** - * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister + * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister * * For examples, please see the comment section of opendal_operator_list() * @see opendal_operator_list() @@ -763,12 +763,12 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * \brief Blockingly list the objects in `path`. * * List the object in `path` blockingly by `op_ptr`, return a result with a - * opendal_blocking_lister. Users should call opendal_lister_next() on the + * opendal_lister. Users should call opendal_lister_next() on the * lister. * * @param ptr The opendal_operator_ptr created previously * @param path The designated path you want to delete - * @see opendal_blocking_lister + * @see opendal_lister * @return Returns opendal_result_list, containing a lister and an opendal_error. * If the operation succeeds, the `lister` field would holds a valid lister and * the `error` field should hold nullptr. Otherwise the `lister`` will contain a @@ -783,7 +783,7 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); * assert(l.error == ERROR); * - * opendal_blocking_lister *lister = l.lister; + * opendal_lister *lister = l.lister; * opendal_list_entry *entry; * * while ((entry = opendal_lister_next(lister)) != NULL) { @@ -943,12 +943,12 @@ void opendal_operator_options_free(const struct opendal_operator_options *option * For examples, please see the comment section of opendal_operator_list() * @see opendal_operator_list() */ -struct opendal_list_entry *opendal_lister_next(const struct opendal_blocking_lister *self); +struct opendal_list_entry *opendal_lister_next(const struct opendal_lister *self); /** - * \brief Free the heap-allocated metadata used by opendal_blocking_lister + * \brief Free the heap-allocated metadata used by opendal_lister */ -void opendal_lister_free(const struct opendal_blocking_lister *p); +void opendal_lister_free(const struct opendal_lister *p); /** * \brief Path of entry. diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index f1abc64e6270..421b3587cfac 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -39,7 +39,7 @@ 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_lister; use types::opendal_reader; use crate::result::opendal_result_is_exist; @@ -548,12 +548,12 @@ pub unsafe extern "C" fn opendal_operator_stat( /// \brief Blockingly list the objects in `path`. /// /// List the object in `path` blockingly by `op_ptr`, return a result with a -/// opendal_blocking_lister. Users should call opendal_lister_next() on the +/// opendal_lister. Users should call opendal_lister_next() on the /// lister. /// /// @param ptr The opendal_operator_ptr created previously /// @param path The designated path you want to delete -/// @see opendal_blocking_lister +/// @see opendal_lister /// @return Returns opendal_result_list, containing a lister and an opendal_error. /// If the operation succeeds, the `lister` field would holds a valid lister and /// the `error` field should hold nullptr. Otherwise the `lister`` will contain a @@ -568,7 +568,7 @@ pub unsafe extern "C" fn opendal_operator_stat( /// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); /// assert(l.error == ERROR); /// -/// opendal_blocking_lister *lister = l.lister; +/// opendal_lister *lister = l.lister; /// opendal_list_entry *entry; /// /// while ((entry = opendal_lister_next(lister)) != NULL) { @@ -606,7 +606,7 @@ pub unsafe extern "C" fn opendal_operator_list( let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.lister(path) { Ok(lister) => opendal_result_list { - lister: Box::into_raw(Box::new(opendal_blocking_lister::new(lister))), + lister: Box::into_raw(Box::new(opendal_lister::new(lister))), error: std::ptr::null_mut(), }, diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index 886c82e0e7b9..37bf3cccb148 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -21,8 +21,8 @@ //! we are defining all Result types here use crate::error::opendal_error; -use crate::types::opendal_blocking_lister; use crate::types::opendal_bytes; +use crate::types::opendal_lister; use crate::types::opendal_metadata; use crate::types::opendal_operator_ptr; use crate::types::opendal_reader; @@ -94,7 +94,7 @@ pub struct opendal_result_stat { #[repr(C)] pub struct opendal_result_list { /// The lister, used for further listing operations - pub lister: *mut opendal_blocking_lister, + pub lister: *mut opendal_lister, /// The error, if ok, it is null pub error: *mut opendal_error, } diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index 1de7d134501d..0510d92d83c6 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -317,11 +317,11 @@ impl opendal_operator_options { /// For examples, please see the comment section of opendal_operator_list() /// @see opendal_operator_list() #[repr(C)] -pub struct opendal_blocking_lister { +pub struct opendal_lister { inner: *mut od::BlockingLister, } -impl opendal_blocking_lister { +impl opendal_lister { pub(crate) fn new(lister: od::BlockingLister) -> Self { Self { inner: Box::into_raw(Box::new(lister)), @@ -348,17 +348,17 @@ impl opendal_blocking_lister { } } - /// \brief Free the heap-allocated metadata used by opendal_blocking_lister + /// \brief Free the heap-allocated metadata used by opendal_lister #[no_mangle] - pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_blocking_lister) { + pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_lister) { unsafe { let _ = Box::from_raw((*p).inner); - let _ = Box::from_raw(p as *mut opendal_blocking_lister); + let _ = Box::from_raw(p as *mut opendal_lister); } } } -/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_blocking_lister +/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister /// /// For examples, please see the comment section of opendal_operator_list() /// @see opendal_operator_list() diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index 18bc0e8163f7..61876c744ecc 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -72,7 +72,7 @@ TEST_F(OpendalListTest, ListDirTest) opendal_result_list l = opendal_operator_list(this->p, (dname + "/").c_str()); EXPECT_EQ(l.error, nullptr); - opendal_blocking_lister* lister = l.lister; + opendal_lister* lister = l.lister; // start checking the lister's result bool found = false; From ae0859181fff6189f431d45b3a197b03c5c778f2 Mon Sep 17 00:00:00 2001 From: Ji-Xinyou Date: Mon, 16 Oct 2023 01:28:16 -0400 Subject: [PATCH 03/16] addresses few comments --- bindings/c/README.md | 5 +- bindings/c/examples/basic.c | 2 +- bindings/c/examples/error_handle.c | 2 +- bindings/c/include/opendal.h | 108 +++++++++++++++++------------ bindings/c/src/lib.rs | 80 +++++++++++---------- bindings/c/src/result.rs | 31 +++++++-- bindings/c/src/types.rs | 55 +++++++++------ bindings/c/tests/bdd.cpp | 2 +- bindings/c/tests/error_msg.cpp | 2 +- bindings/c/tests/list.cpp | 10 ++- 10 files changed, 177 insertions(+), 120 deletions(-) diff --git a/bindings/c/README.md b/bindings/c/README.md index 13c88797adbe..4440d1d47cda 100644 --- a/bindings/c/README.md +++ b/bindings/c/README.md @@ -14,8 +14,9 @@ A simple read and write example int main() { /* Initialize a operator for "memory" backend, with no options */ - opendal_operator_ptr op = opendal_operator_new("memory", 0); - assert(op.ptr != NULL); + opendal_result_operator_new result = opendal_operator_new("memory", 0); + assert(result.operator_ptr != NULL); + assert(result.error == NULL); /* Prepare some data to be written */ opendal_bytes data = { diff --git a/bindings/c/examples/basic.c b/bindings/c/examples/basic.c index c5019a901742..c6d018b42f2a 100644 --- a/bindings/c/examples/basic.c +++ b/bindings/c/examples/basic.c @@ -28,7 +28,7 @@ int main() assert(result.operator_ptr != NULL); assert(result.error == NULL); - opendal_operator_ptr* op = result.operator_ptr; + opendal_operator* op = result.operator_ptr; /* Prepare some data to be written */ opendal_bytes data = { diff --git a/bindings/c/examples/error_handle.c b/bindings/c/examples/error_handle.c index 99f5966ff9e7..28610d519616 100644 --- a/bindings/c/examples/error_handle.c +++ b/bindings/c/examples/error_handle.c @@ -29,7 +29,7 @@ int main() assert(result.operator_ptr != NULL); assert(result.error == NULL); - opendal_operator_ptr* op = result.operator_ptr; + opendal_operator* op = result.operator_ptr; /* The read is supposed to fail */ opendal_result_read r = opendal_operator_read(op, "/testpath"); diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index 1ece0cbd1199..c0aa19f78b51 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -214,19 +214,19 @@ typedef struct Metadata Metadata; * @see opendal_operator_new This function construct the operator * @see opendal_operator_free This function frees the heap memory of the operator * - * \note The opendal_operator_ptr actually owns a pointer to + * \note The opendal_operator actually owns a pointer to * a opendal::BlockingOperator, which is inside the Rust core code. * * \remark You may use the field `ptr` to check whether this is a NULL * operator. */ -typedef struct opendal_operator_ptr { +typedef struct opendal_operator { /** * The pointer to the opendal::BlockingOperator in the Rust code. * Only touch this on judging whether it is NULL. */ const struct BlockingOperator *ptr; -} opendal_operator_ptr; +} opendal_operator; /** * \brief opendal_bytes carries raw-bytes with its length @@ -281,16 +281,16 @@ typedef struct opendal_error { * valid pointer with error code and error message. * * @see opendal_operator_new() - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_error */ typedef struct opendal_result_operator_new { - struct opendal_operator_ptr *operator_ptr; + struct opendal_operator *operator_ptr; struct opendal_error *error; } opendal_result_operator_new; /** - * \brief The configuration for the initialization of opendal_operator_ptr. + * \brief The configuration for the initialization of opendal_operator. * * \note This is also a heap-allocated struct, please free it after you use it * @@ -442,6 +442,29 @@ typedef struct opendal_list_entry { struct Entry *inner; } opendal_list_entry; +/** + * \brief The result type returned by opendal_lister_next(). + * The list entry is the list result of the list operation, the error field is the error code and error message. + * If the operation succeeds, the error should be NULL. + * + * \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. + */ +typedef struct opendal_result_lister_next { + /** + * The next object name + */ + struct opendal_list_entry *entry; + /** + * The error, if ok, it is null + */ + struct opendal_error *error; +} opendal_result_lister_next; + +/** + * \brief The is the result type returned by opendal_reader_read(). + * The result type contains a size field, which is the size of the data read, + * which is zero on error. The error field is the error code and error message. + */ typedef struct opendal_result_reader_read { uintptr_t size; struct opendal_error *error; @@ -464,7 +487,7 @@ extern "C" { * option is set * @see opendal_operator_options * @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction - * succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator_ptr, + * succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator, * and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. * * # Example @@ -478,7 +501,7 @@ extern "C" { * * // Construct the operator based on the options and scheme * opendal_result_operator_new result = opendal_operator_new("memory", options); - * opendal_operator_ptr* op = result.operator_ptr; + * opendal_operator* op = result.operator_ptr; * * // you could free the options right away since the options is not used afterwards * opendal_operator_options_free(options); @@ -502,10 +525,10 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * \note It is important to notice that the `bytes` that is passes in will be consumed by this * function. Therefore, you should not use the `bytes` after this function returns. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to write your bytes in * @param bytes The opendal_byte typed bytes to be written - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_bytes * @see opendal_error * @return NULL if succeeds, otherwise it contains the error code and error message. @@ -514,7 +537,7 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * * Following is an example * ```C - * //...prepare your opendal_operator_ptr, named ptr for example + * //...prepare your opendal_operator, named ptr for example * * // prepare your data * char* data = "Hello, World!"; @@ -539,7 +562,7 @@ struct opendal_result_operator_new opendal_operator_new(const char *scheme, * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_write(const struct opendal_operator_ptr *ptr, +struct opendal_error *opendal_operator_write(const struct opendal_operator *op, const char *path, struct opendal_bytes bytes); @@ -548,9 +571,9 @@ struct opendal_error *opendal_operator_write(const struct opendal_operator_ptr * * * Read the data out from `path` blockingly by operator. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to read the data out - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_read * @see opendal_error * @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated @@ -583,7 +606,7 @@ struct opendal_error *opendal_operator_write(const struct opendal_operator_ptr * * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_read opendal_operator_read(const struct opendal_operator_ptr *ptr, +struct opendal_result_read opendal_operator_read(const struct opendal_operator *op, const char *path); /** @@ -592,11 +615,11 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator_p * 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 ptr The opendal_operator 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_operator * @see opendal_result_read * @see opendal_code * @return Returns opendal_code @@ -608,14 +631,12 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator_p * * 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_read_with_buffer(ptr, "testpath", buffer, length); - * assert(r == OPENDAL_OK); - * // assert buffer == "Hello, World!" + * // ... you have created an operator named op * + * opendal_result_reader result = opendal_operator_reader(op, "/testpath"); + * assert(result.error == NULL); + * // The reader is in result.reader + * opendal_reader *reader = result.reader; * ``` * * # Safety @@ -628,7 +649,7 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator_p * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_reader opendal_operator_reader(const struct opendal_operator_ptr *ptr, +struct opendal_result_reader opendal_operator_reader(const struct opendal_operator *op, const char *path); /** @@ -637,9 +658,9 @@ struct opendal_result_reader opendal_operator_reader(const struct opendal_operat * Delete the object in `path` blockingly by `op_ptr`. * Error is NULL if successful, otherwise it contains the error code and error message. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to delete - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_error * @return NULL if succeeds, otherwise it contains the error code and error message. * @@ -647,7 +668,7 @@ struct opendal_result_reader opendal_operator_reader(const struct opendal_operat * * Following is an example * ```C - * //...prepare your opendal_operator_ptr, named ptr for example + * //...prepare your opendal_operator, named ptr for example * * // prepare your data * char* data = "Hello, World!"; @@ -673,8 +694,7 @@ struct opendal_result_reader opendal_operator_reader(const struct opendal_operat * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_error *opendal_operator_delete(const struct opendal_operator_ptr *ptr, - const char *path); +struct opendal_error *opendal_operator_delete(const struct opendal_operator *op, const char *path); /** * \brief Check whether the path exists. @@ -683,9 +703,9 @@ struct opendal_error *opendal_operator_delete(const struct opendal_operator_ptr * the error should be a nullptr. Otherwise, the field `is_exist` * is filled with false, and the error is set * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to check existence - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_is_exist * @see opendal_error * @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. @@ -715,7 +735,7 @@ struct opendal_error *opendal_operator_delete(const struct opendal_operator_ptr * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_operator_ptr *ptr, +struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_operator *op, const char *path); /** @@ -723,9 +743,9 @@ struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_op * * Error is NULL if successful, otherwise it contains the error code and error message. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The path you want to stat - * @see opendal_operator_ptr + * @see opendal_operator * @see opendal_result_stat * @see opendal_metadata * @return Returns opendal_result_stat, containing a metadata and an opendal_error. @@ -756,7 +776,7 @@ struct opendal_result_is_exist opendal_operator_is_exist(const struct opendal_op * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_ptr *ptr, +struct opendal_result_stat opendal_operator_stat(const struct opendal_operator *op, const char *path); /** @@ -766,7 +786,7 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * opendal_lister. Users should call opendal_lister_next() on the * lister. * - * @param ptr The opendal_operator_ptr created previously + * @param ptr The opendal_operator created previously * @param path The designated path you want to delete * @see opendal_lister * @return Returns opendal_result_list, containing a lister and an opendal_error. @@ -779,7 +799,7 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * Following is an example * ```C * // You have written some data into some files path "root/dir1" - * // Your opendal_operator_ptr was called ptr + * // Your opendal_operator was called ptr * opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); * assert(l.error == ERROR); * @@ -809,7 +829,7 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator_p * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_list opendal_operator_list(const struct opendal_operator_ptr *ptr, +struct opendal_result_list opendal_operator_list(const struct opendal_operator *op, const char *path); /** @@ -818,23 +838,23 @@ struct opendal_result_list opendal_operator_list(const struct opendal_operator_p void opendal_error_free(struct opendal_error *ptr); /** - * \brief Free the heap-allocated operator pointed by opendal_operator_ptr. + * \brief Free the heap-allocated operator pointed by opendal_operator. * - * Please only use this for a pointer pointing at a valid opendal_operator_ptr. + * Please only use this for a pointer pointing at a valid opendal_operator. * Calling this function on NULL does nothing, but calling this function on pointers * of other type will lead to segfault. * * # Example * * ```C - * opendal_operator_ptr *ptr = opendal_operator_new("fs", NULL); + * opendal_operator *ptr = opendal_operator_new("fs", NULL); * // ... use this ptr, maybe some reads and writes * * // free this operator * opendal_operator_free(ptr); * ``` */ -void opendal_operator_free(const struct opendal_operator_ptr *op); +void opendal_operator_free(const struct opendal_operator *op); /** * \brief Frees the heap memory used by the opendal_bytes @@ -943,7 +963,7 @@ void opendal_operator_options_free(const struct opendal_operator_options *option * For examples, please see the comment section of opendal_operator_list() * @see opendal_operator_list() */ -struct opendal_list_entry *opendal_lister_next(const struct opendal_lister *self); +struct opendal_result_lister_next opendal_lister_next(const struct opendal_lister *self); /** * \brief Free the heap-allocated metadata used by opendal_lister diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 421b3587cfac..9d474fc0aa47 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -47,8 +47,8 @@ use crate::result::opendal_result_read; use crate::result::opendal_result_stat; use crate::types::opendal_bytes; use crate::types::opendal_metadata; +use crate::types::opendal_operator; use crate::types::opendal_operator_options; -use crate::types::opendal_operator_ptr; static RUNTIME: Lazy = Lazy::new(|| { tokio::runtime::Builder::new_multi_thread() @@ -82,7 +82,7 @@ fn build_operator(schema: od::Scheme, map: HashMap) -> od::Resul /// option is set /// @see opendal_operator_options /// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction -/// succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator_ptr, +/// succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator, /// and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. /// /// # Example @@ -96,7 +96,7 @@ fn build_operator(schema: od::Scheme, map: HashMap) -> od::Resul /// /// // Construct the operator based on the options and scheme /// opendal_result_operator_new result = opendal_operator_new("memory", options); -/// opendal_operator_ptr* op = result.operator_ptr; +/// opendal_operator* op = result.operator_ptr; /// /// // you could free the options right away since the options is not used afterwards /// opendal_operator_options_free(options); @@ -146,7 +146,7 @@ pub unsafe extern "C" fn opendal_operator_new( match build_operator(scheme, map) { Ok(op) => { - let op = opendal_operator_ptr::from(Box::into_raw(Box::new(op.blocking()))); + let op = opendal_operator::from(Box::into_raw(Box::new(op.blocking()))); opendal_result_operator_new { operator_ptr: Box::into_raw(Box::new(op)), error: std::ptr::null_mut(), @@ -170,10 +170,10 @@ pub unsafe extern "C" fn opendal_operator_new( /// \note It is important to notice that the `bytes` that is passes in will be consumed by this /// function. Therefore, you should not use the `bytes` after this function returns. /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The designated path you want to write your bytes in /// @param bytes The opendal_byte typed bytes to be written -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_bytes /// @see opendal_error /// @return NULL if succeeds, otherwise it contains the error code and error message. @@ -182,7 +182,7 @@ pub unsafe extern "C" fn opendal_operator_new( /// /// Following is an example /// ```C -/// //...prepare your opendal_operator_ptr, named ptr for example +/// //...prepare your opendal_operator, named ptr for example /// /// // prepare your data /// char* data = "Hello, World!"; @@ -208,7 +208,7 @@ pub unsafe extern "C" fn opendal_operator_new( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_write( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, bytes: opendal_bytes, ) -> *mut opendal_error { @@ -216,7 +216,7 @@ pub unsafe extern "C" fn opendal_operator_write( panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.write(path, bytes) { Ok(_) => std::ptr::null_mut(), @@ -231,9 +231,9 @@ pub unsafe extern "C" fn opendal_operator_write( /// /// Read the data out from `path` blockingly by operator. /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The path you want to read the data out -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_result_read /// @see opendal_error /// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated @@ -267,14 +267,14 @@ pub unsafe extern "C" fn opendal_operator_write( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_read( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, ) -> opendal_result_read { if path.is_null() { panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; let data = op.read(path); match data { @@ -300,11 +300,11 @@ pub unsafe extern "C" fn opendal_operator_read( /// 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 ptr The opendal_operator 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_operator /// @see opendal_result_read /// @see opendal_code /// @return Returns opendal_code @@ -316,14 +316,12 @@ pub unsafe extern "C" fn opendal_operator_read( /// /// 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_read_with_buffer(ptr, "testpath", buffer, length); -/// assert(r == OPENDAL_OK); -/// // assert buffer == "Hello, World!" +/// // ... you have created an operator named op /// +/// opendal_result_reader result = opendal_operator_reader(op, "/testpath"); +/// assert(result.error == NULL); +/// // The reader is in result.reader +/// opendal_reader *reader = result.reader; /// ``` /// /// # Safety @@ -337,13 +335,13 @@ pub unsafe extern "C" fn opendal_operator_read( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_reader( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, 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 op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.reader(path) { Ok(reader) => opendal_result_reader { @@ -365,9 +363,9 @@ pub unsafe extern "C" fn opendal_operator_reader( /// Delete the object in `path` blockingly by `op_ptr`. /// Error is NULL if successful, otherwise it contains the error code and error message. /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The designated path you want to delete -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_error /// @return NULL if succeeds, otherwise it contains the error code and error message. /// @@ -375,7 +373,7 @@ pub unsafe extern "C" fn opendal_operator_reader( /// /// Following is an example /// ```C -/// //...prepare your opendal_operator_ptr, named ptr for example +/// //...prepare your opendal_operator, named ptr for example /// /// // prepare your data /// char* data = "Hello, World!"; @@ -402,14 +400,14 @@ pub unsafe extern "C" fn opendal_operator_reader( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_delete( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, ) -> *mut opendal_error { if path.is_null() { panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.delete(path) { Ok(_) => std::ptr::null_mut(), @@ -426,9 +424,9 @@ pub unsafe extern "C" fn opendal_operator_delete( /// the error should be a nullptr. Otherwise, the field `is_exist` /// is filled with false, and the error is set /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The path you want to check existence -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_result_is_exist /// @see opendal_error /// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. @@ -459,14 +457,14 @@ pub unsafe extern "C" fn opendal_operator_delete( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_is_exist( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, ) -> opendal_result_is_exist { if path.is_null() { panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.is_exist(path) { Ok(e) => opendal_result_is_exist { @@ -487,9 +485,9 @@ pub unsafe extern "C" fn opendal_operator_is_exist( /// /// Error is NULL if successful, otherwise it contains the error code and error message. /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The path you want to stat -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_result_stat /// @see opendal_metadata /// @return Returns opendal_result_stat, containing a metadata and an opendal_error. @@ -521,14 +519,14 @@ pub unsafe extern "C" fn opendal_operator_is_exist( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_stat( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, ) -> opendal_result_stat { if path.is_null() { panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.stat(path) { Ok(m) => opendal_result_stat { @@ -551,7 +549,7 @@ pub unsafe extern "C" fn opendal_operator_stat( /// opendal_lister. Users should call opendal_lister_next() on the /// lister. /// -/// @param ptr The opendal_operator_ptr created previously +/// @param ptr The opendal_operator created previously /// @param path The designated path you want to delete /// @see opendal_lister /// @return Returns opendal_result_list, containing a lister and an opendal_error. @@ -564,7 +562,7 @@ pub unsafe extern "C" fn opendal_operator_stat( /// Following is an example /// ```C /// // You have written some data into some files path "root/dir1" -/// // Your opendal_operator_ptr was called ptr +/// // Your opendal_operator was called ptr /// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); /// assert(l.error == ERROR); /// @@ -595,14 +593,14 @@ pub unsafe extern "C" fn opendal_operator_stat( /// * If the `path` points to NULL, this function panics, i.e. exits with information #[no_mangle] pub unsafe extern "C" fn opendal_operator_list( - ptr: *const opendal_operator_ptr, + op: *const opendal_operator, path: *const c_char, ) -> opendal_result_list { if path.is_null() { panic!("The path given is pointing at NULL"); } - let op = (*ptr).as_ref(); + let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.lister(path) { Ok(lister) => opendal_result_list { diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index 37bf3cccb148..1a7c85da3996 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -17,14 +17,15 @@ //! This is for better naming in C header file. If we use generics for Result type, //! it will no doubt work find. However, the generics will lead to naming like -//! "opendal_result_opendal_operator_ptr", which is unacceptable. Therefore, +//! "opendal_result_opendal_operator", which is unacceptable. Therefore, //! we are defining all Result types here use crate::error::opendal_error; use crate::types::opendal_bytes; +use crate::types::opendal_list_entry; use crate::types::opendal_lister; use crate::types::opendal_metadata; -use crate::types::opendal_operator_ptr; +use crate::types::opendal_operator; use crate::types::opendal_reader; /// \brief The result type returned by opendal_operator_new() operation. @@ -35,11 +36,11 @@ use crate::types::opendal_reader; /// valid pointer with error code and error message. /// /// @see opendal_operator_new() -/// @see opendal_operator_ptr +/// @see opendal_operator /// @see opendal_error #[repr(C)] pub struct opendal_result_operator_new { - pub operator_ptr: *mut opendal_operator_ptr, + pub operator_ptr: *mut opendal_operator, pub error: *mut opendal_error, } @@ -99,6 +100,19 @@ pub struct opendal_result_list { pub error: *mut opendal_error, } +/// \brief The result type returned by opendal_lister_next(). +/// The list entry is the list result of the list operation, the error field is the error code and error message. +/// If the operation succeeds, the error should be NULL. +/// +/// \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. +#[repr(C)] +pub struct opendal_result_lister_next { + /// The next object name + pub entry: *mut opendal_list_entry, + /// 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 @@ -108,3 +122,12 @@ pub struct opendal_result_reader { pub reader: *mut opendal_reader, pub error: *mut opendal_error, } + +/// \brief The is the result type returned by opendal_reader_read(). +/// The result type contains a size field, which is the size of the data read, +/// which is zero on error. The error field is the error code and error message. +#[repr(C)] +pub struct opendal_result_reader_read { + pub size: usize, + pub error: *mut opendal_error, +} diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index 0510d92d83c6..f1c753e2dc51 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -24,6 +24,8 @@ use ::opendal as od; use crate::error::opendal_code; use crate::error::opendal_error; +use crate::result::opendal_result_lister_next; +use crate::result::opendal_result_reader_read; /// \brief Used to access almost all OpenDAL APIs. It represents a /// operator that provides the unified interfaces provided by OpenDAL. @@ -31,42 +33,42 @@ use crate::error::opendal_error; /// @see opendal_operator_new This function construct the operator /// @see opendal_operator_free This function frees the heap memory of the operator /// -/// \note The opendal_operator_ptr actually owns a pointer to +/// \note The opendal_operator actually owns a pointer to /// a opendal::BlockingOperator, which is inside the Rust core code. /// /// \remark You may use the field `ptr` to check whether this is a NULL /// operator. #[repr(C)] -pub struct opendal_operator_ptr { +pub struct opendal_operator { /// The pointer to the opendal::BlockingOperator in the Rust code. /// Only touch this on judging whether it is NULL. ptr: *const od::BlockingOperator, } -impl opendal_operator_ptr { - /// \brief Free the heap-allocated operator pointed by opendal_operator_ptr. +impl opendal_operator { + /// \brief Free the heap-allocated operator pointed by opendal_operator. /// - /// Please only use this for a pointer pointing at a valid opendal_operator_ptr. + /// Please only use this for a pointer pointing at a valid opendal_operator. /// Calling this function on NULL does nothing, but calling this function on pointers /// of other type will lead to segfault. /// /// # Example /// /// ```C - /// opendal_operator_ptr *ptr = opendal_operator_new("fs", NULL); + /// opendal_operator *ptr = opendal_operator_new("fs", NULL); /// // ... use this ptr, maybe some reads and writes /// /// // free this operator /// opendal_operator_free(ptr); /// ``` #[no_mangle] - pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator_ptr) { + pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator) { let _ = unsafe { Box::from_raw((*op).ptr as *mut od::BlockingOperator) }; - let _ = unsafe { Box::from_raw(op as *mut opendal_operator_ptr) }; + let _ = unsafe { Box::from_raw(op as *mut opendal_operator) }; } } -impl opendal_operator_ptr { +impl opendal_operator { /// Returns a reference to the underlying [`od::BlockingOperator`] pub(crate) fn as_ref(&self) -> &od::BlockingOperator { unsafe { &*(self.ptr) } @@ -74,14 +76,14 @@ impl opendal_operator_ptr { } #[allow(clippy::from_over_into)] -impl From<*const od::BlockingOperator> for opendal_operator_ptr { +impl From<*const od::BlockingOperator> for opendal_operator { fn from(value: *const od::BlockingOperator) -> Self { Self { ptr: value } } } #[allow(clippy::from_over_into)] -impl From<*mut od::BlockingOperator> for opendal_operator_ptr { +impl From<*mut od::BlockingOperator> for opendal_operator { fn from(value: *mut od::BlockingOperator) -> Self { Self { ptr: value } } @@ -228,7 +230,7 @@ impl opendal_metadata { } } -/// \brief The configuration for the initialization of opendal_operator_ptr. +/// \brief The configuration for the initialization of opendal_operator. /// /// \note This is also a heap-allocated struct, please free it after you use it /// @@ -336,15 +338,30 @@ impl opendal_lister { /// For examples, please see the comment section of opendal_operator_list() /// @see opendal_operator_list() #[no_mangle] - pub unsafe extern "C" fn opendal_lister_next(&self) -> *mut opendal_list_entry { + pub unsafe extern "C" fn opendal_lister_next(&self) -> opendal_result_lister_next { let e = (*self.inner).next(); if e.is_none() { - return std::ptr::null_mut(); + return opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: std::ptr::null_mut(), + }; } match e.unwrap() { - Ok(e) => Box::into_raw(Box::new(opendal_list_entry::new(e))), - Err(_) => std::ptr::null_mut(), + Ok(e) => { + let ent = Box::into_raw(Box::new(opendal_list_entry::new(e))); + opendal_result_lister_next { + entry: ent, + error: std::ptr::null_mut(), + } + } + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: Box::into_raw(e), + } + } } } @@ -418,12 +435,6 @@ 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 { diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp index 84288e5f0880..634fa4cc0296 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -25,7 +25,7 @@ extern "C" { class OpendalBddTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; std::string scheme; std::string path; diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp index 4ef2687c1f56..4a791042b347 100644 --- a/bindings/c/tests/error_msg.cpp +++ b/bindings/c/tests/error_msg.cpp @@ -24,7 +24,7 @@ extern "C" { class OpendalErrorTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; // set up a brand new operator void SetUp() override diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index 61876c744ecc..b4220d2f46e7 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -27,7 +27,7 @@ extern "C" { class OpendalListTest : public ::testing::Test { protected: - const opendal_operator_ptr* p; + const opendal_operator* p; // set up a brand new operator void SetUp() override @@ -77,7 +77,9 @@ TEST_F(OpendalListTest, ListDirTest) // start checking the lister's result bool found = false; - opendal_list_entry* entry = opendal_lister_next(lister); + opendal_result_lister_next result = opendal_lister_next(lister); + EXPECT_EQ(result.error, nullptr); + opendal_list_entry* entry = result.entry; while (entry) { char* de_path = opendal_list_entry_path(entry); @@ -97,7 +99,9 @@ TEST_F(OpendalListTest, ListDirTest) opendal_metadata_free(s.meta); opendal_list_entry_free(entry); - entry = opendal_lister_next(lister); + result = opendal_lister_next(lister); + EXPECT_EQ(result.error, nullptr); + entry = result.entry; } // we must have found the file we wrote From 624454b36e335969786b1a2464e0fcfca5fde9e9 Mon Sep 17 00:00:00 2001 From: Ji-Xinyou Date: Mon, 16 Oct 2023 01:33:59 -0400 Subject: [PATCH 04/16] rename opendal_result_reader --- bindings/c/include/opendal.h | 10 +++++----- bindings/c/src/lib.rs | 10 +++++----- bindings/c/src/result.rs | 2 +- bindings/c/tests/bdd.cpp | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index c0aa19f78b51..c87441ffbc20 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -336,10 +336,10 @@ typedef struct opendal_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 { +typedef struct opendal_result_operator_reader { struct opendal_reader *reader; struct opendal_error *error; -} opendal_result_reader; +} opendal_result_operator_reader; /** * \brief The result type returned by opendal_operator_is_exist(). @@ -633,7 +633,7 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator * * ```C * // ... you have created an operator named op * - * opendal_result_reader result = opendal_operator_reader(op, "/testpath"); + * opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); * assert(result.error == NULL); * // The reader is in result.reader * opendal_reader *reader = result.reader; @@ -649,8 +649,8 @@ struct opendal_result_read opendal_operator_read(const struct opendal_operator * * * * If the `path` points to NULL, this function panics, i.e. exits with information */ -struct opendal_result_reader opendal_operator_reader(const struct opendal_operator *op, - const char *path); +struct opendal_result_operator_reader opendal_operator_reader(const struct opendal_operator *op, + const char *path); /** * \brief Blockingly delete the object in `path`. diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 9d474fc0aa47..137d134e4f97 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -38,7 +38,7 @@ 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 result::opendal_result_operator_reader; use types::opendal_lister; use types::opendal_reader; @@ -318,7 +318,7 @@ pub unsafe extern "C" fn opendal_operator_read( /// ```C /// // ... you have created an operator named op /// -/// opendal_result_reader result = opendal_operator_reader(op, "/testpath"); +/// opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); /// assert(result.error == NULL); /// // The reader is in result.reader /// opendal_reader *reader = result.reader; @@ -337,20 +337,20 @@ pub unsafe extern "C" fn opendal_operator_read( pub unsafe extern "C" fn opendal_operator_reader( op: *const opendal_operator, path: *const c_char, -) -> opendal_result_reader { +) -> opendal_result_operator_reader { if path.is_null() { panic!("The path given is pointing at NULL"); } let op = (*op).as_ref(); let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.reader(path) { - Ok(reader) => opendal_result_reader { + Ok(reader) => opendal_result_operator_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 { + opendal_result_operator_reader { reader: std::ptr::null_mut(), error: Box::into_raw(e), } diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index 1a7c85da3996..54fc5446f97f 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -118,7 +118,7 @@ pub struct opendal_result_lister_next { /// 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 struct opendal_result_operator_reader { pub reader: *mut opendal_reader, pub error: *mut opendal_error, } diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp index 634fa4cc0296..e822e19e3b4f 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -90,7 +90,7 @@ TEST_F(OpendalBddTest, FeatureTest) // 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_reader(this->p, this->path.c_str()); + opendal_result_operator_reader reader = opendal_operator_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); From 4dcdb14f043bc18ce8dd11cd1995532330c7453a Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 17:56:25 +0800 Subject: [PATCH 05/16] refactor Signed-off-by: Xuanwo --- bindings/c/examples/basic.c | 4 +- bindings/c/examples/error_handle.c | 4 +- bindings/c/include/opendal.h | 406 +++++++++--------- bindings/c/src/entry.rs | 75 ++++ bindings/c/src/error.rs | 2 +- bindings/c/src/lib.rs | 608 ++------------------------ bindings/c/src/lister.rs | 84 ++++ bindings/c/src/metadata.rs | 114 +++++ bindings/c/src/operator.rs | 656 +++++++++++++++++++++++++++++ bindings/c/src/reader.rs | 79 ++++ bindings/c/src/result.rs | 22 +- bindings/c/src/types.rs | 349 +-------------- bindings/c/tests/bdd.cpp | 2 +- bindings/c/tests/error_msg.cpp | 2 +- bindings/c/tests/list.cpp | 8 +- 15 files changed, 1277 insertions(+), 1138 deletions(-) create mode 100644 bindings/c/src/entry.rs create mode 100644 bindings/c/src/lister.rs create mode 100644 bindings/c/src/metadata.rs create mode 100644 bindings/c/src/operator.rs create mode 100644 bindings/c/src/reader.rs diff --git a/bindings/c/examples/basic.c b/bindings/c/examples/basic.c index c6d018b42f2a..5e4e7925ba77 100644 --- a/bindings/c/examples/basic.c +++ b/bindings/c/examples/basic.c @@ -25,10 +25,10 @@ int main() { /* Initialize a operator for "memory" backend, with no options */ opendal_result_operator_new result = opendal_operator_new("memory", 0); - assert(result.operator_ptr != NULL); + assert(result.op != NULL); assert(result.error == NULL); - opendal_operator* op = result.operator_ptr; + opendal_operator* op = result.op; /* Prepare some data to be written */ opendal_bytes data = { diff --git a/bindings/c/examples/error_handle.c b/bindings/c/examples/error_handle.c index 28610d519616..193a788afa97 100644 --- a/bindings/c/examples/error_handle.c +++ b/bindings/c/examples/error_handle.c @@ -26,10 +26,10 @@ int main() { /* Initialize a operator for "memory" backend, with no options */ opendal_result_operator_new result = opendal_operator_new("memory", 0); - assert(result.operator_ptr != NULL); + assert(result.op != NULL); assert(result.error == NULL); - opendal_operator* op = result.operator_ptr; + opendal_operator* op = result.op; /* The read is supposed to fail */ opendal_result_read r = opendal_operator_read(op, "/testpath"); diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index c87441ffbc20..df55fffb35b1 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -207,27 +207,6 @@ typedef struct HashMap_String__String HashMap_String__String; */ typedef struct Metadata Metadata; -/** - * \brief Used to access almost all OpenDAL APIs. It represents a - * operator that provides the unified interfaces provided by OpenDAL. - * - * @see opendal_operator_new This function construct the operator - * @see opendal_operator_free This function frees the heap memory of the operator - * - * \note The opendal_operator actually owns a pointer to - * a opendal::BlockingOperator, which is inside the Rust core code. - * - * \remark You may use the field `ptr` to check whether this is a NULL - * operator. - */ -typedef struct opendal_operator { - /** - * The pointer to the opendal::BlockingOperator in the Rust code. - * Only touch this on judging whether it is NULL. - */ - const struct BlockingOperator *ptr; -} opendal_operator; - /** * \brief opendal_bytes carries raw-bytes with its length * @@ -272,12 +251,95 @@ typedef struct opendal_error { struct opendal_bytes message; } opendal_error; +/** + * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + * @see opendal_list_entry_path() + * @see opendal_list_entry_name() + */ +typedef struct opendal_entry { + struct Entry *inner; +} opendal_entry; + +/** + * \brief The result type returned by opendal_lister_next(). + * The list entry is the list result of the list operation, the error field is the error code and error message. + * If the operation succeeds, the error should be NULL. + * + * \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. + */ +typedef struct opendal_result_lister_next { + /** + * The next object name + */ + struct opendal_entry *entry; + /** + * The error, if ok, it is null + */ + struct opendal_error *error; +} opendal_result_lister_next; + +/** + * \brief BlockingLister is designed to list entries at given path in a blocking + * manner. + * + * Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + */ +typedef struct opendal_lister { + struct BlockingLister *inner; +} opendal_lister; + +/** + * \brief Carries all metadata associated with a path. + * + * The metadata of the "thing" under a path. Please **only** use the opendal_metadata + * with our provided API, e.g. opendal_metadata_content_length(). + * + * \note The metadata is also heap-allocated, please call opendal_metadata_free() on this + * to free the heap memory. + * + * @see opendal_metadata_free + */ +typedef struct opendal_metadata { + /** + * The pointer to the opendal::Metadata in the Rust code. + * Only touch this on judging whether it is NULL. + */ + struct Metadata *inner; +} opendal_metadata; + +/** + * \brief Used to access almost all OpenDAL APIs. It represents a + * operator that provides the unified interfaces provided by OpenDAL. + * + * @see opendal_operator_new This function construct the operator + * @see opendal_operator_free This function frees the heap memory of the operator + * + * \note The opendal_operator actually owns a pointer to + * a opendal::BlockingOperator, which is inside the Rust core code. + * + * \remark You may use the field `ptr` to check whether this is a NULL + * operator. + */ +typedef struct opendal_operator { + /** + * The pointer to the opendal::BlockingOperator in the Rust code. + * Only touch this on judging whether it is NULL. + */ + const struct BlockingOperator *ptr; +} opendal_operator; + /** * \brief The result type returned by opendal_operator_new() operation. * - * If the init logic is successful, the `operator_ptr` field will be set to a valid + * If the init logic is successful, the `op` field will be set to a valid * pointer, and the `error` field will be set to null. If the init logic fails, the - * `operator_ptr` field will be set to null, and the `error` field will be set to a + * `op` field will be set to null, and the `error` field will be set to a * valid pointer with error code and error message. * * @see opendal_operator_new() @@ -285,7 +347,13 @@ typedef struct opendal_error { * @see opendal_error */ typedef struct opendal_result_operator_new { - struct opendal_operator *operator_ptr; + /** + * The pointer for operator. + */ + struct opendal_operator *op; + /** + * The error pointer for error. + */ struct opendal_error *error; } opendal_result_operator_new; @@ -326,6 +394,12 @@ typedef struct opendal_result_read { struct opendal_error *error; } opendal_result_read; +/** + * \brief The result type returned by opendal's reader operation. + * + * \note The opendal_reader actually owns a pointer to + * a opendal::BlockingReader, which is inside the Rust core code. + */ typedef struct opendal_reader { struct BlockingReader *inner; } opendal_reader; @@ -337,7 +411,13 @@ typedef struct opendal_reader { * whether the stat operation is successful. */ typedef struct opendal_result_operator_reader { + /** + * The pointer for opendal_reader + */ struct opendal_reader *reader; + /** + * The error, if ok, it is null + */ struct opendal_error *error; } opendal_result_operator_reader; @@ -362,25 +442,6 @@ typedef struct opendal_result_is_exist { struct opendal_error *error; } opendal_result_is_exist; -/** - * \brief Carries all metadata associated with a path. - * - * The metadata of the "thing" under a path. Please **only** use the opendal_metadata - * with our provided API, e.g. opendal_metadata_content_length(). - * - * \note The metadata is also heap-allocated, please call opendal_metadata_free() on this - * to free the heap memory. - * - * @see opendal_metadata_free - */ -typedef struct opendal_metadata { - /** - * The pointer to the opendal::Metadata in the Rust code. - * Only touch this on judging whether it is NULL. - */ - struct Metadata *inner; -} opendal_metadata; - /** * \brief The result type returned by opendal_operator_stat(). * @@ -399,19 +460,6 @@ typedef struct opendal_result_stat { struct opendal_error *error; } opendal_result_stat; -/** - * \brief BlockingLister is designed to list entries at given path in a blocking - * manner. - * - * Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) - * - * For examples, please see the comment section of opendal_operator_list() - * @see opendal_operator_list() - */ -typedef struct opendal_lister { - struct BlockingLister *inner; -} opendal_lister; - /** * \brief The result type returned by opendal_operator_list(). * @@ -430,43 +478,19 @@ typedef struct opendal_result_list { struct opendal_error *error; } opendal_result_list; -/** - * \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister - * - * For examples, please see the comment section of opendal_operator_list() - * @see opendal_operator_list() - * @see opendal_list_entry_path() - * @see opendal_list_entry_name() - */ -typedef struct opendal_list_entry { - struct Entry *inner; -} opendal_list_entry; - -/** - * \brief The result type returned by opendal_lister_next(). - * The list entry is the list result of the list operation, the error field is the error code and error message. - * If the operation succeeds, the error should be NULL. - * - * \note Please notice if the lister reaches the end, both the list_entry and error will be NULL. - */ -typedef struct opendal_result_lister_next { - /** - * The next object name - */ - struct opendal_list_entry *entry; - /** - * The error, if ok, it is null - */ - struct opendal_error *error; -} opendal_result_lister_next; - /** * \brief The is the result type returned by opendal_reader_read(). * The result type contains a size field, which is the size of the data read, * which is zero on error. The error field is the error code and error message. */ typedef struct opendal_result_reader_read { + /** + * The read size if succeed. + */ uintptr_t size; + /** + * The error, if ok, it is null + */ struct opendal_error *error; } opendal_result_reader_read; @@ -474,6 +498,101 @@ typedef struct opendal_result_reader_read { extern "C" { #endif // __cplusplus +/** + * \brief Frees the opendal_error, ok to call on NULL + */ +void opendal_error_free(struct opendal_error *ptr); + +/** + * \brief Return the next object to be listed + * + * Lister is an iterator of the objects under its path, this method is the same as + * calling next() on the iterator + * + * For examples, please see the comment section of opendal_operator_list() + * @see opendal_operator_list() + */ +struct opendal_result_lister_next opendal_lister_next(const struct opendal_lister *self); + +/** + * \brief Free the heap-allocated metadata used by opendal_lister + */ +void opendal_lister_free(const struct opendal_lister *p); + +/** + * \brief Free the heap-allocated metadata used by opendal_metadata + */ +void opendal_metadata_free(struct opendal_metadata *ptr); + +/** + * \brief Return the content_length of the metadata + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * assert(opendal_metadata_content_length(meta) == 13); + * ``` + */ +uint64_t opendal_metadata_content_length(const struct opendal_metadata *self); + +/** + * \brief Return whether the path represents a file + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * assert(opendal_metadata_is_file(meta)); + * ``` + */ +bool opendal_metadata_is_file(const struct opendal_metadata *self); + +/** + * \brief Return whether the path represents a directory + * + * # Example + * ```C + * // ... previously you wrote "Hello, World!" to path "/testpath" + * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + * assert(s.error == NULL); + * + * opendal_metadata *meta = s.meta; + * + * // this is not a directory + * assert(!opendal_metadata_is_dir(meta)); + * ``` + * + * \todo This is not a very clear example. A clearer example will be added + * after we support opendal_operator_mkdir() + */ +bool opendal_metadata_is_dir(const struct opendal_metadata *self); + +/** + * \brief Free the heap-allocated operator pointed by opendal_operator. + * + * Please only use this for a pointer pointing at a valid opendal_operator. + * Calling this function on NULL does nothing, but calling this function on pointers + * of other type will lead to segfault. + * + * # Example + * + * ```C + * opendal_operator *ptr = opendal_operator_new("fs", NULL); + * // ... use this ptr, maybe some reads and writes + * + * // free this operator + * opendal_operator_free(ptr); + * ``` + */ +void opendal_operator_free(const struct opendal_operator *op); + /** * \brief Construct an operator based on `scheme` and `options` * @@ -487,8 +606,8 @@ extern "C" { * option is set * @see opendal_operator_options * @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction - * succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator, - * and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. + * succeeds. On success the operator field is a valid pointer to a newly allocated opendal_operator, + * and the error field is NULL. Otherwise, the operator field is a NULL pointer and the error field. * * # Example * @@ -501,7 +620,7 @@ extern "C" { * * // Construct the operator based on the options and scheme * opendal_result_operator_new result = opendal_operator_new("memory", options); - * opendal_operator* op = result.operator_ptr; + * opendal_operator* op = result.operator; * * // you could free the options right away since the options is not used afterwards * opendal_operator_options_free(options); @@ -832,89 +951,10 @@ struct opendal_result_stat opendal_operator_stat(const struct opendal_operator * struct opendal_result_list opendal_operator_list(const struct opendal_operator *op, const char *path); -/** - * \brief Frees the opendal_error, ok to call on NULL - */ -void opendal_error_free(struct opendal_error *ptr); - -/** - * \brief Free the heap-allocated operator pointed by opendal_operator. - * - * Please only use this for a pointer pointing at a valid opendal_operator. - * Calling this function on NULL does nothing, but calling this function on pointers - * of other type will lead to segfault. - * - * # Example - * - * ```C - * opendal_operator *ptr = opendal_operator_new("fs", NULL); - * // ... use this ptr, maybe some reads and writes - * - * // free this operator - * opendal_operator_free(ptr); - * ``` - */ -void opendal_operator_free(const struct opendal_operator *op); - /** * \brief Frees the heap memory used by the opendal_bytes */ -void opendal_bytes_free(struct opendal_bytes *ptr); - -/** - * \brief Free the heap-allocated metadata used by opendal_metadata - */ -void opendal_metadata_free(struct opendal_metadata *ptr); - -/** - * \brief Return the content_length of the metadata - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * assert(opendal_metadata_content_length(meta) == 13); - * ``` - */ -uint64_t opendal_metadata_content_length(const struct opendal_metadata *self); - -/** - * \brief Return whether the path represents a file - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * assert(opendal_metadata_is_file(meta)); - * ``` - */ -bool opendal_metadata_is_file(const struct opendal_metadata *self); - -/** - * \brief Return whether the path represents a directory - * - * # Example - * ```C - * // ... previously you wrote "Hello, World!" to path "/testpath" - * opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - * assert(s.error == NULL); - * - * opendal_metadata *meta = s.meta; - * - * // this is not a directory - * assert(!opendal_metadata_is_dir(meta)); - * ``` - * - * \todo This is not a very clear example. A clearer example will be added - * after we support opendal_operator_mkdir() - */ -bool opendal_metadata_is_dir(const struct opendal_metadata *self); +void opendal_bytes_free(struct opendal_bytes *bs); /** * \brief Construct a heap-allocated opendal_operator_options @@ -954,22 +994,6 @@ void opendal_operator_options_set(struct opendal_operator_options *self, */ void opendal_operator_options_free(const struct opendal_operator_options *options); -/** - * \brief Return the next object to be listed - * - * Lister is an iterator of the objects under its path, this method is the same as - * calling next() on the iterator - * - * For examples, please see the comment section of opendal_operator_list() - * @see opendal_operator_list() - */ -struct opendal_result_lister_next opendal_lister_next(const struct opendal_lister *self); - -/** - * \brief Free the heap-allocated metadata used by opendal_lister - */ -void opendal_lister_free(const struct opendal_lister *p); - /** * \brief Path of entry. * @@ -977,7 +1001,7 @@ void opendal_lister_free(const struct opendal_lister *p); * * \note To free the string, you can directly call free() */ -char *opendal_list_entry_path(const struct opendal_list_entry *self); +char *opendal_entry_path(const struct opendal_entry *self); /** * \brief Name of entry. @@ -988,17 +1012,23 @@ char *opendal_list_entry_path(const struct opendal_list_entry *self); * * \note To free the string, you can directly call free() */ -char *opendal_list_entry_name(const struct opendal_list_entry *self); +char *opendal_entry_name(const struct opendal_entry *self); /** * \brief Frees the heap memory used by the opendal_list_entry */ -void opendal_list_entry_free(struct opendal_list_entry *ptr); +void opendal_entry_free(struct opendal_entry *ptr); +/** + * \brief Read data from the reader. + */ struct opendal_result_reader_read opendal_reader_read(const struct opendal_reader *reader, uint8_t *buf, uintptr_t len); +/** + * \brief Frees the heap memory used by the opendal_reader. + */ void opendal_reader_free(struct opendal_reader *ptr); #ifdef __cplusplus diff --git a/bindings/c/src/entry.rs b/bindings/c/src/entry.rs new file mode 100644 index 000000000000..aca35cda4de2 --- /dev/null +++ b/bindings/c/src/entry.rs @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as od; +use std::ffi::CString; +use std::os::raw::c_char; + +/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister +/// +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() +/// @see opendal_list_entry_path() +/// @see opendal_list_entry_name() +#[repr(C)] +pub struct opendal_entry { + inner: *mut od::Entry, +} + +impl opendal_entry { + /// Used to convert the Rust type into C type + pub(crate) fn new(entry: od::Entry) -> Self { + Self { + inner: Box::into_raw(Box::new(entry)), + } + } + + /// \brief Path of entry. + /// + /// Path is relative to operator's root. Only valid in current operator. + /// + /// \note To free the string, you can directly call free() + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_path(&self) -> *mut c_char { + let s = (*self.inner).path(); + let c_str = CString::new(s).unwrap(); + c_str.into_raw() + } + + /// \brief Name of entry. + /// + /// Name is the last segment of path. + /// If this entry is a dir, `Name` MUST endswith `/` + /// Otherwise, `Name` MUST NOT endswith `/`. + /// + /// \note To free the string, you can directly call free() + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_name(&self) -> *mut c_char { + let s = (*self.inner).name(); + let c_str = CString::new(s).unwrap(); + c_str.into_raw() + } + + /// \brief Frees the heap memory used by the opendal_list_entry + #[no_mangle] + pub unsafe extern "C" fn opendal_entry_free(ptr: *mut opendal_entry) { + if !ptr.is_null() { + let _ = unsafe { Box::from_raw((*ptr).inner) }; + let _ = unsafe { Box::from_raw(ptr) }; + } + } +} diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs index 366d646ae0a4..63c4ec6ee363 100644 --- a/bindings/c/src/error.rs +++ b/bindings/c/src/error.rs @@ -29,7 +29,7 @@ struct raw_error(od::Error); /// \todo The error handling is not complete, the error with error message will be /// added in the future. #[repr(C)] -pub(crate) enum opendal_code { +pub enum opendal_code { /// returning it back. For example, s3 returns an internal service error. OPENDAL_UNEXPECTED, /// Underlying service doesn't support this operation. diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 137d134e4f97..7607c52f5e26 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -26,594 +26,34 @@ //! For examples, you may see the examples subdirectory mod error; -mod result; -mod types; - -use std::collections::HashMap; -use std::os::raw::c_char; -use std::str::FromStr; - -use ::opendal as od; -use error::opendal_error; -use once_cell::sync::Lazy; -use result::opendal_result_list; -use result::opendal_result_operator_new; -use result::opendal_result_operator_reader; -use types::opendal_lister; -use types::opendal_reader; - -use crate::result::opendal_result_is_exist; -use crate::result::opendal_result_read; -use crate::result::opendal_result_stat; -use crate::types::opendal_bytes; -use crate::types::opendal_metadata; -use crate::types::opendal_operator; -use crate::types::opendal_operator_options; - -static RUNTIME: Lazy = Lazy::new(|| { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() -}); - -fn build_operator(schema: od::Scheme, map: HashMap) -> od::Result { - let mut op = match od::Operator::via_map(schema, map) { - Ok(o) => o, - Err(e) => return Err(e), - }; - if !op.info().full_capability().blocking { - let runtime = - tokio::runtime::Handle::try_current().unwrap_or_else(|_| RUNTIME.handle().clone()); - let _guard = runtime.enter(); - op = op.layer(od::layers::BlockingLayer::create().expect("blocking layer must be created")); - } - Ok(op) -} -/// \brief Construct an operator based on `scheme` and `options` -/// -/// Uses an array of key-value pairs to initialize the operator based on provided `scheme` -/// and `options`. For each scheme, i.e. Backend, different options could be set, you may -/// reference the [documentation](https://opendal.apache.org/docs/category/services/) for -/// each service, especially for the **Configuration Part**. -/// -/// @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" -/// @param options the pointer to the options for this operators, it could be NULL, which means no -/// option is set -/// @see opendal_operator_options -/// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction -/// succeeds. On success the operator_ptr field is a valid pointer to a newly allocated opendal_operator, -/// and the error field is NULL. Otherwise, the operator_ptr field is a NULL pointer and the error field. -/// -/// # Example -/// -/// Following is an example. -/// ```C -/// // Allocate a new options -/// opendal_operator_options *options = opendal_operator_options_new(); -/// // Set the options you need -/// opendal_operator_options_set(options, "root", "/myroot"); -/// -/// // Construct the operator based on the options and scheme -/// opendal_result_operator_new result = opendal_operator_new("memory", options); -/// opendal_operator* op = result.operator_ptr; -/// -/// // you could free the options right away since the options is not used afterwards -/// opendal_operator_options_free(options); -/// -/// // ... your operations -/// ``` -/// -/// # Safety -/// -/// The only unsafe case is passing a invalid c string pointer to the `scheme` argument. -#[no_mangle] -pub unsafe extern "C" fn opendal_operator_new( - scheme: *const c_char, - options: *const opendal_operator_options, -) -> opendal_result_operator_new { - if scheme.is_null() { - let error = opendal_error::manual_error( - error::opendal_code::OPENDAL_CONFIG_INVALID, - "The scheme given is pointing at NULL".into(), - ); - let result = opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(error)), - }; - return result; - } - - let scheme_str = unsafe { std::ffi::CStr::from_ptr(scheme).to_str().unwrap() }; - let scheme = match od::Scheme::from_str(scheme_str) { - Ok(s) => s, - Err(e) => { - let e = opendal_error::from_opendal_error(e); - let result = opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), - }; - return result; - } - }; - - let mut map = HashMap::default(); - if !options.is_null() { - for (k, v) in (*options).as_ref() { - map.insert(k.to_string(), v.to_string()); - } - } - - match build_operator(scheme, map) { - Ok(op) => { - let op = opendal_operator::from(Box::into_raw(Box::new(op.blocking()))); - opendal_result_operator_new { - operator_ptr: Box::into_raw(Box::new(op)), - error: std::ptr::null_mut(), - } - } - Err(e) => { - let e = opendal_error::from_opendal_error(e); - opendal_result_operator_new { - operator_ptr: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), - } - } - } -} +pub use error::opendal_code; +pub use error::opendal_error; -/// \brief Blockingly write raw bytes to `path`. -/// -/// Write the `bytes` into the `path` blockingly by `op_ptr`. -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// \note It is important to notice that the `bytes` that is passes in will be consumed by this -/// function. Therefore, you should not use the `bytes` after this function returns. -/// -/// @param ptr The opendal_operator created previously -/// @param path The designated path you want to write your bytes in -/// @param bytes The opendal_byte typed bytes to be written -/// @see opendal_operator -/// @see opendal_bytes -/// @see opendal_error -/// @return NULL if succeeds, otherwise it contains the error code and error message. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// //...prepare your opendal_operator, named ptr for example -/// -/// // prepare your data -/// char* data = "Hello, World!"; -/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; -/// -/// // now you can write! -/// opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); -/// -/// // Assert that this succeeds -/// assert(err == NULL); -/// ``` -/// -/// # 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. -/// * The `bytes` provided has valid byte in the `data` field and the `len` field is set -/// correctly. -/// -/// # Panic -/// -/// * If the `path` points to NULL, this function panics, i.e. exits with information -#[no_mangle] -pub unsafe extern "C" fn opendal_operator_write( - op: *const opendal_operator, - path: *const c_char, - bytes: opendal_bytes, -) -> *mut opendal_error { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod lister; +pub use lister::opendal_lister; - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.write(path, bytes) { - Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } - } -} +mod metadata; +pub use metadata::opendal_metadata; -/// \brief Blockingly read the data from `path`. -/// -/// Read the data out from `path` blockingly by operator. -/// -/// @param ptr The opendal_operator created previously -/// @param path The path you want to read the data out -/// @see opendal_operator -/// @see opendal_result_read -/// @see opendal_error -/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated -/// opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then -/// the operation failed and the `data` field is a nullptr. -/// -/// \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" -/// -/// opendal_result_read r = opendal_operator_read(ptr, "testpath"); -/// assert(r.error == NULL); -/// -/// opendal_bytes *bytes = r.data; -/// assert(bytes->len == 13); -/// ``` -/// -/// # 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_read( - op: *const opendal_operator, - path: *const c_char, -) -> opendal_result_read { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod operator; +pub use operator::opendal_operator; - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - let data = op.read(path); - match data { - Ok(d) => { - let v = Box::new(opendal_bytes::new(d)); - opendal_result_read { - data: Box::into_raw(v), - error: std::ptr::null_mut(), - } - } - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_read { - data: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} - -/// \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 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 -/// @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 created an operator named op -/// -/// opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); -/// assert(result.error == NULL); -/// // The reader is in result.reader -/// opendal_reader *reader = result.reader; -/// ``` -/// -/// # 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_reader( - op: *const opendal_operator, - path: *const c_char, -) -> opendal_result_operator_reader { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.reader(path) { - Ok(reader) => opendal_result_operator_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_operator_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`. -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// @param ptr The opendal_operator created previously -/// @param path The designated path you want to delete -/// @see opendal_operator -/// @see opendal_error -/// @return NULL if succeeds, otherwise it contains the error code and error message. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// //...prepare your opendal_operator, named ptr for example -/// -/// // prepare your data -/// char* data = "Hello, World!"; -/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; -/// opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); -/// -/// assert(error == NULL); -/// -/// // now you can delete! -/// opendal_error *error = opendal_operator_delete(ptr, "/testpath"); -/// -/// // Assert that this succeeds -/// assert(error == NULL); -/// ``` -/// -/// # 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_delete( - op: *const opendal_operator, - path: *const c_char, -) -> *mut opendal_error { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.delete(path) { - Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } - } -} - -/// \brief Check whether the path exists. -/// -/// If the operation succeeds, no matter the path exists or not, -/// the error should be a nullptr. Otherwise, the field `is_exist` -/// is filled with false, and the error is set -/// -/// @param ptr The opendal_operator created previously -/// @param path The path you want to check existence -/// @see opendal_operator -/// @see opendal_result_is_exist -/// @see opendal_error -/// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. -/// However, it the operation fails, the `is_exist` will contains false and the error will be set. -/// -/// # Example -/// -/// ```C -/// // .. you previously wrote some data to path "/mytest/obj" -/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj"); -/// assert(e.error == NULL); -/// assert(e.is_exist); -/// -/// // but you previously did **not** write any data to path "/yourtest/obj" -/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj"); -/// assert(e.error == NULL); -/// assert(!e.is_exist); -/// ``` -/// -/// # 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_is_exist( - op: *const opendal_operator, - path: *const c_char, -) -> opendal_result_is_exist { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - - let op = (*op).as_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, - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_is_exist { - is_exist: false, - error: Box::into_raw(e), - } - } - } -} - -/// \brief Stat the path, return its metadata. -/// -/// Error is NULL if successful, otherwise it contains the error code and error message. -/// -/// @param ptr The opendal_operator created previously -/// @param path The path you want to stat -/// @see opendal_operator -/// @see opendal_result_stat -/// @see opendal_metadata -/// @return Returns opendal_result_stat, containing a metadata and an opendal_error. -/// If the operation succeeds, the `meta` field would holds a valid metadata and -/// the `error` field should hold nullptr. Otherwise the metadata will contain a -/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. -/// -/// # Example -/// -/// ```C -/// // ... previously you wrote "Hello, World!" to path "/testpath" -/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); -/// assert(s.error == NULL); -/// -/// const opendal_metadata *meta = s.meta; -/// -/// // ... you could now use your metadata, notice that please only access metadata -/// // using the APIs provided by OpenDAL -/// ``` -/// -/// # 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_stat( - op: *const opendal_operator, - path: *const c_char, -) -> opendal_result_stat { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } - - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.stat(path) { - Ok(m) => opendal_result_stat { - meta: Box::into_raw(Box::new(opendal_metadata::new(m))), - error: std::ptr::null_mut(), - }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_stat { - meta: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} +mod result; +pub use result::opendal_result_is_exist; +pub use result::opendal_result_list; +pub use result::opendal_result_lister_next; +pub use result::opendal_result_operator_new; +pub use result::opendal_result_operator_reader; +pub use result::opendal_result_read; +pub use result::opendal_result_reader_read; +pub use result::opendal_result_stat; -/// \brief Blockingly list the objects in `path`. -/// -/// List the object in `path` blockingly by `op_ptr`, return a result with a -/// opendal_lister. Users should call opendal_lister_next() on the -/// lister. -/// -/// @param ptr The opendal_operator created previously -/// @param path The designated path you want to delete -/// @see opendal_lister -/// @return Returns opendal_result_list, containing a lister and an opendal_error. -/// If the operation succeeds, the `lister` field would holds a valid lister and -/// the `error` field should hold nullptr. Otherwise the `lister`` will contain a -/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. -/// -/// # Example -/// -/// Following is an example -/// ```C -/// // You have written some data into some files path "root/dir1" -/// // Your opendal_operator was called ptr -/// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); -/// assert(l.error == ERROR); -/// -/// opendal_lister *lister = l.lister; -/// opendal_list_entry *entry; -/// -/// while ((entry = opendal_lister_next(lister)) != NULL) { -/// const char* de_path = opendal_list_entry_path(entry); -/// const char* de_name = opendal_list_entry_name(entry); -/// // ...... your operations -/// -/// // remember to free the entry after you are done using it -/// opendal_list_entry_free(entry); -/// } -/// -/// // and remember to free the lister -/// opendal_lister_free(lister); -/// ``` -/// -/// # 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_list( - op: *const opendal_operator, - path: *const c_char, -) -> opendal_result_list { - if path.is_null() { - panic!("The path given is pointing at NULL"); - } +mod types; +pub use types::opendal_bytes; +pub use types::opendal_operator_options; - let op = (*op).as_ref(); - let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; - match op.lister(path) { - Ok(lister) => opendal_result_list { - lister: Box::into_raw(Box::new(opendal_lister::new(lister))), - error: std::ptr::null_mut(), - }, +mod entry; +pub use entry::opendal_entry; - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_list { - lister: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } -} +mod reader; +pub use reader::opendal_reader; diff --git a/bindings/c/src/lister.rs b/bindings/c/src/lister.rs new file mode 100644 index 000000000000..072bba79cc12 --- /dev/null +++ b/bindings/c/src/lister.rs @@ -0,0 +1,84 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as od; + +use super::*; + +/// \brief BlockingLister is designed to list entries at given path in a blocking +/// manner. +/// +/// Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) +/// +/// For examples, please see the comment section of opendal_operator_list() +/// @see opendal_operator_list() +#[repr(C)] +pub struct opendal_lister { + inner: *mut od::BlockingLister, +} + +impl opendal_lister { + pub(crate) fn new(lister: od::BlockingLister) -> Self { + Self { + inner: Box::into_raw(Box::new(lister)), + } + } + + /// \brief Return the next object to be listed + /// + /// Lister is an iterator of the objects under its path, this method is the same as + /// calling next() on the iterator + /// + /// For examples, please see the comment section of opendal_operator_list() + /// @see opendal_operator_list() + #[no_mangle] + pub unsafe extern "C" fn opendal_lister_next(&self) -> opendal_result_lister_next { + let e = (*self.inner).next(); + if e.is_none() { + return opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: std::ptr::null_mut(), + }; + } + + match e.unwrap() { + Ok(e) => { + let ent = Box::into_raw(Box::new(opendal_entry::new(e))); + opendal_result_lister_next { + entry: ent, + error: std::ptr::null_mut(), + } + } + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: Box::into_raw(e), + } + } + } + } + + /// \brief Free the heap-allocated metadata used by opendal_lister + #[no_mangle] + pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_lister) { + unsafe { + let _ = Box::from_raw((*p).inner); + let _ = Box::from_raw(p as *mut opendal_lister); + } + } +} diff --git a/bindings/c/src/metadata.rs b/bindings/c/src/metadata.rs new file mode 100644 index 000000000000..0987d3682909 --- /dev/null +++ b/bindings/c/src/metadata.rs @@ -0,0 +1,114 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use ::opendal as od; + +/// \brief Carries all metadata associated with a path. +/// +/// The metadata of the "thing" under a path. Please **only** use the opendal_metadata +/// with our provided API, e.g. opendal_metadata_content_length(). +/// +/// \note The metadata is also heap-allocated, please call opendal_metadata_free() on this +/// to free the heap memory. +/// +/// @see opendal_metadata_free +#[repr(C)] +pub struct opendal_metadata { + /// The pointer to the opendal::Metadata in the Rust code. + /// Only touch this on judging whether it is NULL. + pub inner: *mut od::Metadata, +} + +impl opendal_metadata { + /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible + /// [`opendal_metadata`] + pub(crate) fn new(m: od::Metadata) -> Self { + Self { + inner: Box::into_raw(Box::new(m)), + } + } + + /// \brief Free the heap-allocated metadata used by opendal_metadata + #[no_mangle] + pub extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { + unsafe { + let _ = Box::from_raw((*ptr).inner); + let _ = Box::from_raw(ptr); + } + } + + /// \brief Return the content_length of the metadata + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// assert(opendal_metadata_content_length(meta) == 13); + /// ``` + #[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() } + } + + /// \brief Return whether the path represents a file + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// assert(opendal_metadata_is_file(meta)); + /// ``` + #[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 + let m = unsafe { &*self.inner }; + + m.is_file() + } + + /// \brief Return whether the path represents a directory + /// + /// # Example + /// ```C + /// // ... previously you wrote "Hello, World!" to path "/testpath" + /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); + /// assert(s.error == NULL); + /// + /// opendal_metadata *meta = s.meta; + /// + /// // this is not a directory + /// assert(!opendal_metadata_is_dir(meta)); + /// ``` + /// + /// \todo This is not a very clear example. A clearer example will be added + /// after we support opendal_operator_mkdir() + #[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() } + } +} diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs new file mode 100644 index 000000000000..87414379bc19 --- /dev/null +++ b/bindings/c/src/operator.rs @@ -0,0 +1,656 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::collections::HashMap; +use std::os::raw::c_char; +use std::str::FromStr; + +use ::opendal as od; +use once_cell::sync::Lazy; + +use super::*; + +static RUNTIME: Lazy = Lazy::new(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() +}); + +/// \brief Used to access almost all OpenDAL APIs. It represents a +/// operator that provides the unified interfaces provided by OpenDAL. +/// +/// @see opendal_operator_new This function construct the operator +/// @see opendal_operator_free This function frees the heap memory of the operator +/// +/// \note The opendal_operator actually owns a pointer to +/// a opendal::BlockingOperator, which is inside the Rust core code. +/// +/// \remark You may use the field `ptr` to check whether this is a NULL +/// operator. +#[repr(C)] +pub struct opendal_operator { + /// The pointer to the opendal::BlockingOperator in the Rust code. + /// Only touch this on judging whether it is NULL. + ptr: *const od::BlockingOperator, +} + +impl opendal_operator { + /// \brief Free the heap-allocated operator pointed by opendal_operator. + /// + /// Please only use this for a pointer pointing at a valid opendal_operator. + /// Calling this function on NULL does nothing, but calling this function on pointers + /// of other type will lead to segfault. + /// + /// # Example + /// + /// ```C + /// opendal_operator *ptr = opendal_operator_new("fs", NULL); + /// // ... use this ptr, maybe some reads and writes + /// + /// // free this operator + /// opendal_operator_free(ptr); + /// ``` + #[no_mangle] + pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator) { + let _ = unsafe { Box::from_raw((*op).ptr as *mut od::BlockingOperator) }; + let _ = unsafe { Box::from_raw(op as *mut opendal_operator) }; + } +} + +impl opendal_operator { + /// Returns a reference to the underlying [`od::BlockingOperator`] + pub(crate) fn as_ref(&self) -> &od::BlockingOperator { + unsafe { &*(self.ptr) } + } +} + +#[allow(clippy::from_over_into)] +impl From<*const od::BlockingOperator> for opendal_operator { + fn from(value: *const od::BlockingOperator) -> Self { + Self { ptr: value } + } +} + +#[allow(clippy::from_over_into)] +impl From<*mut od::BlockingOperator> for opendal_operator { + fn from(value: *mut od::BlockingOperator) -> Self { + Self { ptr: value } + } +} + +fn build_operator(schema: od::Scheme, map: HashMap) -> od::Result { + let mut op = match od::Operator::via_map(schema, map) { + Ok(o) => o, + Err(e) => return Err(e), + }; + if !op.info().full_capability().blocking { + let runtime = + tokio::runtime::Handle::try_current().unwrap_or_else(|_| RUNTIME.handle().clone()); + let _guard = runtime.enter(); + op = op.layer(od::layers::BlockingLayer::create().expect("blocking layer must be created")); + } + Ok(op) +} + +/// \brief Construct an operator based on `scheme` and `options` +/// +/// Uses an array of key-value pairs to initialize the operator based on provided `scheme` +/// and `options`. For each scheme, i.e. Backend, different options could be set, you may +/// reference the [documentation](https://opendal.apache.org/docs/category/services/) for +/// each service, especially for the **Configuration Part**. +/// +/// @param scheme the service scheme you want to specify, e.g. "fs", "s3", "supabase" +/// @param options the pointer to the options for this operators, it could be NULL, which means no +/// option is set +/// @see opendal_operator_options +/// @return A valid opendal_result_operator_new setup with the `scheme` and `options` is the construction +/// succeeds. On success the operator field is a valid pointer to a newly allocated opendal_operator, +/// and the error field is NULL. Otherwise, the operator field is a NULL pointer and the error field. +/// +/// # Example +/// +/// Following is an example. +/// ```C +/// // Allocate a new options +/// opendal_operator_options *options = opendal_operator_options_new(); +/// // Set the options you need +/// opendal_operator_options_set(options, "root", "/myroot"); +/// +/// // Construct the operator based on the options and scheme +/// opendal_result_operator_new result = opendal_operator_new("memory", options); +/// opendal_operator* op = result.operator; +/// +/// // you could free the options right away since the options is not used afterwards +/// opendal_operator_options_free(options); +/// +/// // ... your operations +/// ``` +/// +/// # Safety +/// +/// The only unsafe case is passing a invalid c string pointer to the `scheme` argument. +#[no_mangle] +pub unsafe extern "C" fn opendal_operator_new( + scheme: *const c_char, + options: *const opendal_operator_options, +) -> opendal_result_operator_new { + if scheme.is_null() { + let error = opendal_error::manual_error( + opendal_code::OPENDAL_CONFIG_INVALID, + "The scheme given is pointing at NULL".into(), + ); + let result = opendal_result_operator_new { + op: std::ptr::null_mut(), + error: Box::into_raw(Box::new(error)), + }; + return result; + } + + let scheme_str = unsafe { std::ffi::CStr::from_ptr(scheme).to_str().unwrap() }; + let scheme = match od::Scheme::from_str(scheme_str) { + Ok(s) => s, + Err(e) => { + let e = opendal_error::from_opendal_error(e); + let result = opendal_result_operator_new { + op: std::ptr::null_mut(), + error: Box::into_raw(Box::new(e)), + }; + return result; + } + }; + + let mut map = HashMap::default(); + if !options.is_null() { + for (k, v) in (*options).as_ref() { + map.insert(k.to_string(), v.to_string()); + } + } + + match build_operator(scheme, map) { + Ok(op) => { + let op = opendal_operator::from(Box::into_raw(Box::new(op.blocking()))); + opendal_result_operator_new { + op: Box::into_raw(Box::new(op)), + error: std::ptr::null_mut(), + } + } + Err(e) => { + let e = opendal_error::from_opendal_error(e); + opendal_result_operator_new { + op: std::ptr::null_mut(), + error: Box::into_raw(Box::new(e)), + } + } + } +} + +/// \brief Blockingly write raw bytes to `path`. +/// +/// Write the `bytes` into the `path` blockingly by `op_ptr`. +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// \note It is important to notice that the `bytes` that is passes in will be consumed by this +/// function. Therefore, you should not use the `bytes` after this function returns. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to write your bytes in +/// @param bytes The opendal_byte typed bytes to be written +/// @see opendal_operator +/// @see opendal_bytes +/// @see opendal_error +/// @return NULL if succeeds, otherwise it contains the error code and error message. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// //...prepare your opendal_operator, named ptr for example +/// +/// // prepare your data +/// char* data = "Hello, World!"; +/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; +/// +/// // now you can write! +/// opendal_error *err = opendal_operator_write(ptr, "/testpath", bytes); +/// +/// // Assert that this succeeds +/// assert(err == NULL); +/// ``` +/// +/// # 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. +/// * The `bytes` provided has valid byte in the `data` field and the `len` field is set +/// correctly. +/// +/// # Panic +/// +/// * If the `path` points to NULL, this function panics, i.e. exits with information +#[no_mangle] +pub unsafe extern "C" fn opendal_operator_write( + op: *const opendal_operator, + path: *const c_char, + bytes: opendal_bytes, +) -> *mut opendal_error { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + match op.write(path, bytes) { + Ok(_) => std::ptr::null_mut(), + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + Box::into_raw(e) + } + } +} + +/// \brief Blockingly read the data from `path`. +/// +/// Read the data out from `path` blockingly by operator. +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to read the data out +/// @see opendal_operator +/// @see opendal_result_read +/// @see opendal_error +/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated +/// opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then +/// the operation failed and the `data` field is a nullptr. +/// +/// \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" +/// +/// opendal_result_read r = opendal_operator_read(ptr, "testpath"); +/// assert(r.error == NULL); +/// +/// opendal_bytes *bytes = r.data; +/// assert(bytes->len == 13); +/// ``` +/// +/// # 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_read( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_read { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + let data = op.read(path); + match data { + Ok(d) => { + let v = Box::new(opendal_bytes::new(d)); + opendal_result_read { + data: Box::into_raw(v), + error: std::ptr::null_mut(), + } + } + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_read { + data: std::ptr::null_mut(), + error: Box::into_raw(e), + } + } + } +} + +/// \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 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 +/// @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 created an operator named op +/// +/// opendal_result_operator_reader result = opendal_operator_reader(op, "/testpath"); +/// assert(result.error == NULL); +/// // The reader is in result.reader +/// opendal_reader *reader = result.reader; +/// ``` +/// +/// # 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_reader( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_operator_reader { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + match op.reader(path) { + Ok(reader) => opendal_result_operator_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_operator_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`. +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to delete +/// @see opendal_operator +/// @see opendal_error +/// @return NULL if succeeds, otherwise it contains the error code and error message. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// //...prepare your opendal_operator, named ptr for example +/// +/// // prepare your data +/// char* data = "Hello, World!"; +/// opendal_bytes bytes = opendal_bytes { .data = (uint8_t*)data, .len = 13 }; +/// opendal_error *error = opendal_operator_write(ptr, "/testpath", bytes); +/// +/// assert(error == NULL); +/// +/// // now you can delete! +/// opendal_error *error = opendal_operator_delete(ptr, "/testpath"); +/// +/// // Assert that this succeeds +/// assert(error == NULL); +/// ``` +/// +/// # 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_delete( + op: *const opendal_operator, + path: *const c_char, +) -> *mut opendal_error { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + match op.delete(path) { + Ok(_) => std::ptr::null_mut(), + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + Box::into_raw(e) + } + } +} + +/// \brief Check whether the path exists. +/// +/// If the operation succeeds, no matter the path exists or not, +/// the error should be a nullptr. Otherwise, the field `is_exist` +/// is filled with false, and the error is set +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to check existence +/// @see opendal_operator +/// @see opendal_result_is_exist +/// @see opendal_error +/// @return Returns opendal_result_is_exist, the `is_exist` field contains whether the path exists. +/// However, it the operation fails, the `is_exist` will contains false and the error will be set. +/// +/// # Example +/// +/// ```C +/// // .. you previously wrote some data to path "/mytest/obj" +/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/mytest/obj"); +/// assert(e.error == NULL); +/// assert(e.is_exist); +/// +/// // but you previously did **not** write any data to path "/yourtest/obj" +/// opendal_result_is_exist e = opendal_operator_is_exist(ptr, "/yourtest/obj"); +/// assert(e.error == NULL); +/// assert(!e.is_exist); +/// ``` +/// +/// # 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_is_exist( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_is_exist { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_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, + error: std::ptr::null_mut(), + }, + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_is_exist { + is_exist: false, + error: Box::into_raw(e), + } + } + } +} + +/// \brief Stat the path, return its metadata. +/// +/// Error is NULL if successful, otherwise it contains the error code and error message. +/// +/// @param ptr The opendal_operator created previously +/// @param path The path you want to stat +/// @see opendal_operator +/// @see opendal_result_stat +/// @see opendal_metadata +/// @return Returns opendal_result_stat, containing a metadata and an opendal_error. +/// If the operation succeeds, the `meta` field would holds a valid metadata and +/// the `error` field should hold nullptr. Otherwise the metadata will contain a +/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. +/// +/// # Example +/// +/// ```C +/// // ... previously you wrote "Hello, World!" to path "/testpath" +/// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); +/// assert(s.error == NULL); +/// +/// const opendal_metadata *meta = s.meta; +/// +/// // ... you could now use your metadata, notice that please only access metadata +/// // using the APIs provided by OpenDAL +/// ``` +/// +/// # 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_stat( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_stat { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + match op.stat(path) { + Ok(m) => opendal_result_stat { + meta: Box::into_raw(Box::new(opendal_metadata::new(m))), + error: std::ptr::null_mut(), + }, + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_stat { + meta: std::ptr::null_mut(), + error: Box::into_raw(e), + } + } + } +} + +/// \brief Blockingly list the objects in `path`. +/// +/// List the object in `path` blockingly by `op_ptr`, return a result with a +/// opendal_lister. Users should call opendal_lister_next() on the +/// lister. +/// +/// @param ptr The opendal_operator created previously +/// @param path The designated path you want to delete +/// @see opendal_lister +/// @return Returns opendal_result_list, containing a lister and an opendal_error. +/// If the operation succeeds, the `lister` field would holds a valid lister and +/// the `error` field should hold nullptr. Otherwise the `lister`` will contain a +/// NULL pointer, i.e. invalid, and the `error` will be set correspondingly. +/// +/// # Example +/// +/// Following is an example +/// ```C +/// // You have written some data into some files path "root/dir1" +/// // Your opendal_operator was called ptr +/// opendal_result_list l = opendal_operator_list(ptr, "root/dir1"); +/// assert(l.error == ERROR); +/// +/// opendal_lister *lister = l.lister; +/// opendal_list_entry *entry; +/// +/// while ((entry = opendal_lister_next(lister)) != NULL) { +/// const char* de_path = opendal_list_entry_path(entry); +/// const char* de_name = opendal_list_entry_name(entry); +/// // ...... your operations +/// +/// // remember to free the entry after you are done using it +/// opendal_list_entry_free(entry); +/// } +/// +/// // and remember to free the lister +/// opendal_lister_free(lister); +/// ``` +/// +/// # 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_list( + op: *const opendal_operator, + path: *const c_char, +) -> opendal_result_list { + if path.is_null() { + panic!("The path given is pointing at NULL"); + } + + let op = (*op).as_ref(); + let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; + match op.lister(path) { + Ok(lister) => opendal_result_list { + lister: Box::into_raw(Box::new(opendal_lister::new(lister))), + error: std::ptr::null_mut(), + }, + + Err(e) => { + let e = Box::new(opendal_error::from_opendal_error(e)); + opendal_result_list { + lister: std::ptr::null_mut(), + error: Box::into_raw(e), + } + } + } +} diff --git a/bindings/c/src/reader.rs b/bindings/c/src/reader.rs new file mode 100644 index 000000000000..2738b6737dd7 --- /dev/null +++ b/bindings/c/src/reader.rs @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use super::*; +use ::opendal as od; +use std::io::Read; + +/// \brief The result type returned by opendal's reader operation. +/// +/// \note The opendal_reader actually owns a pointer to +/// a opendal::BlockingReader, which is inside the Rust core code. +#[repr(C)] +pub struct opendal_reader { + inner: *mut od::BlockingReader, +} + +impl opendal_reader { + pub(crate) fn new(reader: od::BlockingReader) -> Self { + Self { + inner: Box::into_raw(Box::new(reader)), + } + } + + /// \brief Read data from the reader. + #[no_mangle] + pub unsafe extern "C" fn opendal_reader_read( + reader: *const 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 inner = unsafe { &mut *(*reader).inner }; + let r = 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), + } + } + } + } + + /// \brief Frees the heap memory used by the opendal_reader. + #[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/src/result.rs b/bindings/c/src/result.rs index 54fc5446f97f..64b27892288f 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -20,19 +20,13 @@ //! "opendal_result_opendal_operator", which is unacceptable. Therefore, //! we are defining all Result types here -use crate::error::opendal_error; -use crate::types::opendal_bytes; -use crate::types::opendal_list_entry; -use crate::types::opendal_lister; -use crate::types::opendal_metadata; -use crate::types::opendal_operator; -use crate::types::opendal_reader; +use super::*; /// \brief The result type returned by opendal_operator_new() operation. /// -/// If the init logic is successful, the `operator_ptr` field will be set to a valid +/// If the init logic is successful, the `op` field will be set to a valid /// pointer, and the `error` field will be set to null. If the init logic fails, the -/// `operator_ptr` field will be set to null, and the `error` field will be set to a +/// `op` field will be set to null, and the `error` field will be set to a /// valid pointer with error code and error message. /// /// @see opendal_operator_new() @@ -40,7 +34,9 @@ use crate::types::opendal_reader; /// @see opendal_error #[repr(C)] pub struct opendal_result_operator_new { - pub operator_ptr: *mut opendal_operator, + /// The pointer for operator. + pub op: *mut opendal_operator, + /// The error pointer for error. pub error: *mut opendal_error, } @@ -108,7 +104,7 @@ pub struct opendal_result_list { #[repr(C)] pub struct opendal_result_lister_next { /// The next object name - pub entry: *mut opendal_list_entry, + pub entry: *mut opendal_entry, /// The error, if ok, it is null pub error: *mut opendal_error, } @@ -119,7 +115,9 @@ pub struct opendal_result_lister_next { /// whether the stat operation is successful. #[repr(C)] pub struct opendal_result_operator_reader { + /// The pointer for opendal_reader pub reader: *mut opendal_reader, + /// The error, if ok, it is null pub error: *mut opendal_error, } @@ -128,6 +126,8 @@ pub struct opendal_result_operator_reader { /// which is zero on error. The error field is the error code and error message. #[repr(C)] pub struct opendal_result_reader_read { + /// The read size if succeed. pub size: usize, + /// The error, if ok, it is null pub error: *mut opendal_error, } diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index f1c753e2dc51..e03b930b1224 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -16,79 +16,8 @@ // under the License. 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; -use crate::result::opendal_result_lister_next; -use crate::result::opendal_result_reader_read; - -/// \brief Used to access almost all OpenDAL APIs. It represents a -/// operator that provides the unified interfaces provided by OpenDAL. -/// -/// @see opendal_operator_new This function construct the operator -/// @see opendal_operator_free This function frees the heap memory of the operator -/// -/// \note The opendal_operator actually owns a pointer to -/// a opendal::BlockingOperator, which is inside the Rust core code. -/// -/// \remark You may use the field `ptr` to check whether this is a NULL -/// operator. -#[repr(C)] -pub struct opendal_operator { - /// The pointer to the opendal::BlockingOperator in the Rust code. - /// Only touch this on judging whether it is NULL. - ptr: *const od::BlockingOperator, -} - -impl opendal_operator { - /// \brief Free the heap-allocated operator pointed by opendal_operator. - /// - /// Please only use this for a pointer pointing at a valid opendal_operator. - /// Calling this function on NULL does nothing, but calling this function on pointers - /// of other type will lead to segfault. - /// - /// # Example - /// - /// ```C - /// opendal_operator *ptr = opendal_operator_new("fs", NULL); - /// // ... use this ptr, maybe some reads and writes - /// - /// // free this operator - /// opendal_operator_free(ptr); - /// ``` - #[no_mangle] - pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator) { - let _ = unsafe { Box::from_raw((*op).ptr as *mut od::BlockingOperator) }; - let _ = unsafe { Box::from_raw(op as *mut opendal_operator) }; - } -} - -impl opendal_operator { - /// Returns a reference to the underlying [`od::BlockingOperator`] - pub(crate) fn as_ref(&self) -> &od::BlockingOperator { - unsafe { &*(self.ptr) } - } -} - -#[allow(clippy::from_over_into)] -impl From<*const od::BlockingOperator> for opendal_operator { - fn from(value: *const od::BlockingOperator) -> Self { - Self { ptr: value } - } -} - -#[allow(clippy::from_over_into)] -impl From<*mut od::BlockingOperator> for opendal_operator { - fn from(value: *mut od::BlockingOperator) -> Self { - Self { ptr: value } - } -} - /// \brief opendal_bytes carries raw-bytes with its length /// /// The opendal_bytes type is a C-compatible substitute for Vec type @@ -115,13 +44,13 @@ impl opendal_bytes { /// \brief Frees the heap memory used by the opendal_bytes #[no_mangle] - pub extern "C" fn opendal_bytes_free(ptr: *mut opendal_bytes) { - if !ptr.is_null() { - let data_mut = unsafe { (*ptr).data as *mut u8 }; + pub extern "C" fn opendal_bytes_free(bs: *mut opendal_bytes) { + if !bs.is_null() { + let data_mut = unsafe { (*bs).data as *mut u8 }; // free the vector - let _ = unsafe { Vec::from_raw_parts(data_mut, (*ptr).len, (*ptr).len) }; + let _ = unsafe { Vec::from_raw_parts(data_mut, (*bs).len, (*bs).len) }; // free the pointer - let _ = unsafe { Box::from_raw(ptr) }; + let _ = unsafe { Box::from_raw(bs) }; } } } @@ -134,102 +63,6 @@ impl Into for opendal_bytes { } } -/// \brief Carries all metadata associated with a path. -/// -/// The metadata of the "thing" under a path. Please **only** use the opendal_metadata -/// with our provided API, e.g. opendal_metadata_content_length(). -/// -/// \note The metadata is also heap-allocated, please call opendal_metadata_free() on this -/// to free the heap memory. -/// -/// @see opendal_metadata_free -#[repr(C)] -pub struct opendal_metadata { - /// The pointer to the opendal::Metadata in the Rust code. - /// Only touch this on judging whether it is NULL. - pub inner: *mut od::Metadata, -} - -impl opendal_metadata { - /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible - /// [`opendal_metadata`] - pub(crate) fn new(m: od::Metadata) -> Self { - Self { - inner: Box::into_raw(Box::new(m)), - } - } - - /// \brief Free the heap-allocated metadata used by opendal_metadata - #[no_mangle] - pub extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { - unsafe { - let _ = Box::from_raw((*ptr).inner); - let _ = Box::from_raw(ptr); - } - } - - /// \brief Return the content_length of the metadata - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// assert(opendal_metadata_content_length(meta) == 13); - /// ``` - #[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() } - } - - /// \brief Return whether the path represents a file - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// assert(opendal_metadata_is_file(meta)); - /// ``` - #[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 - let m = unsafe { &*self.inner }; - - m.is_file() - } - - /// \brief Return whether the path represents a directory - /// - /// # Example - /// ```C - /// // ... previously you wrote "Hello, World!" to path "/testpath" - /// opendal_result_stat s = opendal_operator_stat(ptr, "/testpath"); - /// assert(s.error == NULL); - /// - /// opendal_metadata *meta = s.meta; - /// - /// // this is not a directory - /// assert(!opendal_metadata_is_dir(meta)); - /// ``` - /// - /// \todo This is not a very clear example. A clearer example will be added - /// after we support opendal_operator_mkdir() - #[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() } - } -} - /// \brief The configuration for the initialization of opendal_operator. /// /// \note This is also a heap-allocated struct, please free it after you use it @@ -310,175 +143,3 @@ impl opendal_operator_options { let _ = unsafe { Box::from_raw(options as *mut opendal_operator_options) }; } } - -/// \brief BlockingLister is designed to list entries at given path in a blocking -/// manner. -/// -/// Users can construct Lister by `blocking_list` or `blocking_scan`(currently not supported in C binding) -/// -/// For examples, please see the comment section of opendal_operator_list() -/// @see opendal_operator_list() -#[repr(C)] -pub struct opendal_lister { - inner: *mut od::BlockingLister, -} - -impl opendal_lister { - pub(crate) fn new(lister: od::BlockingLister) -> Self { - Self { - inner: Box::into_raw(Box::new(lister)), - } - } - - /// \brief Return the next object to be listed - /// - /// Lister is an iterator of the objects under its path, this method is the same as - /// calling next() on the iterator - /// - /// For examples, please see the comment section of opendal_operator_list() - /// @see opendal_operator_list() - #[no_mangle] - pub unsafe extern "C" fn opendal_lister_next(&self) -> opendal_result_lister_next { - let e = (*self.inner).next(); - if e.is_none() { - return opendal_result_lister_next { - entry: std::ptr::null_mut(), - error: std::ptr::null_mut(), - }; - } - - match e.unwrap() { - Ok(e) => { - let ent = Box::into_raw(Box::new(opendal_list_entry::new(e))); - opendal_result_lister_next { - entry: ent, - error: std::ptr::null_mut(), - } - } - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_lister_next { - entry: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } - } - } - - /// \brief Free the heap-allocated metadata used by opendal_lister - #[no_mangle] - pub unsafe extern "C" fn opendal_lister_free(p: *const opendal_lister) { - unsafe { - let _ = Box::from_raw((*p).inner); - let _ = Box::from_raw(p as *mut opendal_lister); - } - } -} - -/// \brief opendal_list_entry is the entry under a path, which is listed from the opendal_lister -/// -/// For examples, please see the comment section of opendal_operator_list() -/// @see opendal_operator_list() -/// @see opendal_list_entry_path() -/// @see opendal_list_entry_name() -#[repr(C)] -pub struct opendal_list_entry { - inner: *mut od::Entry, -} - -impl opendal_list_entry { - /// Used to convert the Rust type into C type - pub(crate) fn new(entry: od::Entry) -> Self { - Self { - inner: Box::into_raw(Box::new(entry)), - } - } - - /// \brief Path of entry. - /// - /// Path is relative to operator's root. Only valid in current operator. - /// - /// \note To free the string, you can directly call free() - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_path(&self) -> *mut c_char { - let s = (*self.inner).path(); - let c_str = CString::new(s).unwrap(); - c_str.into_raw() - } - - /// \brief Name of entry. - /// - /// Name is the last segment of path. - /// If this entry is a dir, `Name` MUST endswith `/` - /// Otherwise, `Name` MUST NOT endswith `/`. - /// - /// \note To free the string, you can directly call free() - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_name(&self) -> *mut c_char { - let s = (*self.inner).name(); - let c_str = CString::new(s).unwrap(); - c_str.into_raw() - } - - /// \brief Frees the heap memory used by the opendal_list_entry - #[no_mangle] - pub unsafe extern "C" fn opendal_list_entry_free(ptr: *mut opendal_list_entry) { - if !ptr.is_null() { - let _ = unsafe { Box::from_raw((*ptr).inner) }; - let _ = unsafe { Box::from_raw(ptr) }; - } - } -} - -#[repr(C)] -pub struct opendal_reader { - inner: *mut od::BlockingReader, -} - -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( - reader: *const 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 inner = unsafe { &mut *(*reader).inner }; - let r = 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 e822e19e3b4f..fd33a0125527 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -44,7 +44,7 @@ class OpendalBddTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new(scheme.c_str(), options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); diff --git a/bindings/c/tests/error_msg.cpp b/bindings/c/tests/error_msg.cpp index 4a791042b347..f2087ea3b0bf 100644 --- a/bindings/c/tests/error_msg.cpp +++ b/bindings/c/tests/error_msg.cpp @@ -35,7 +35,7 @@ class OpendalErrorTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new("memory", options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); diff --git a/bindings/c/tests/list.cpp b/bindings/c/tests/list.cpp index b4220d2f46e7..410823973751 100644 --- a/bindings/c/tests/list.cpp +++ b/bindings/c/tests/list.cpp @@ -38,7 +38,7 @@ class OpendalListTest : public ::testing::Test { opendal_result_operator_new result = opendal_operator_new("memory", options); EXPECT_TRUE(result.error == nullptr); - this->p = result.operator_ptr; + this->p = result.op; EXPECT_TRUE(this->p); opendal_operator_options_free(options); @@ -79,9 +79,9 @@ TEST_F(OpendalListTest, ListDirTest) opendal_result_lister_next result = opendal_lister_next(lister); EXPECT_EQ(result.error, nullptr); - opendal_list_entry* entry = result.entry; + opendal_entry* entry = result.entry; while (entry) { - char* de_path = opendal_list_entry_path(entry); + char* de_path = opendal_entry_path(entry); // stat must succeed opendal_result_stat s = opendal_operator_stat(this->p, de_path); @@ -97,7 +97,7 @@ TEST_F(OpendalListTest, ListDirTest) free(de_path); opendal_metadata_free(s.meta); - opendal_list_entry_free(entry); + opendal_entry_free(entry); result = opendal_lister_next(lister); EXPECT_EQ(result.error, nullptr); From 9b2d7c50296226a99e2eae1a28f3c15cd1a5f4f9 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:16:58 +0800 Subject: [PATCH 06/16] polish code Signed-off-by: Xuanwo --- bindings/c/src/entry.rs | 6 +- bindings/c/src/error.rs | 56 +++++++--------- bindings/c/src/lister.rs | 17 ++--- bindings/c/src/metadata.rs | 6 +- bindings/c/src/operator.rs | 130 +++++++++++++++---------------------- bindings/c/src/reader.rs | 23 +++---- 6 files changed, 97 insertions(+), 141 deletions(-) diff --git a/bindings/c/src/entry.rs b/bindings/c/src/entry.rs index aca35cda4de2..44e9c2f19ad6 100644 --- a/bindings/c/src/entry.rs +++ b/bindings/c/src/entry.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use ::opendal as od; +use ::opendal as core; use std::ffi::CString; use std::os::raw::c_char; @@ -27,12 +27,12 @@ use std::os::raw::c_char; /// @see opendal_list_entry_name() #[repr(C)] pub struct opendal_entry { - inner: *mut od::Entry, + inner: *mut core::Entry, } impl opendal_entry { /// Used to convert the Rust type into C type - pub(crate) fn new(entry: od::Entry) -> Self { + pub(crate) fn new(entry: core::Entry) -> Self { Self { inner: Box::into_raw(Box::new(entry)), } diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs index 63c4ec6ee363..359984620161 100644 --- a/bindings/c/src/error.rs +++ b/bindings/c/src/error.rs @@ -15,16 +15,10 @@ // specific language governing permissions and limitations // under the License. -use ::opendal as od; +use ::opendal as core; use crate::types::opendal_bytes; -/// \brief The wrapper type for opendal's Rust core error, wrapped because of the -/// orphan rule. -/// -/// \note User should never use this type directly, use [`opendal_error`] instead. -struct raw_error(od::Error); - /// \brief The error code for all opendal APIs in C binding. /// \todo The error handling is not complete, the error with error message will be /// added in the future. @@ -52,21 +46,19 @@ pub enum opendal_code { OPENDAL_IS_SAME_FILE, } -impl raw_error { - /// Convert the [`od::ErrorKind`] of [`od::Error`] to [`opendal_code`] - pub(crate) fn error_code(&self) -> opendal_code { - let e = &self.0; - match e.kind() { - od::ErrorKind::Unexpected => opendal_code::OPENDAL_UNEXPECTED, - od::ErrorKind::Unsupported => opendal_code::OPENDAL_UNSUPPORTED, - od::ErrorKind::ConfigInvalid => opendal_code::OPENDAL_CONFIG_INVALID, - od::ErrorKind::NotFound => opendal_code::OPENDAL_NOT_FOUND, - od::ErrorKind::PermissionDenied => opendal_code::OPENDAL_PERMISSION_DENIED, - od::ErrorKind::IsADirectory => opendal_code::OPENDAL_IS_A_DIRECTORY, - od::ErrorKind::NotADirectory => opendal_code::OPENDAL_NOT_A_DIRECTORY, - od::ErrorKind::AlreadyExists => opendal_code::OPENDAL_ALREADY_EXISTS, - od::ErrorKind::RateLimited => opendal_code::OPENDAL_RATE_LIMITED, - od::ErrorKind::IsSameFile => opendal_code::OPENDAL_IS_SAME_FILE, +impl From for opendal_code { + fn from(value: core::ErrorKind) -> Self { + match value { + core::ErrorKind::Unexpected => opendal_code::OPENDAL_UNEXPECTED, + core::ErrorKind::Unsupported => opendal_code::OPENDAL_UNSUPPORTED, + core::ErrorKind::ConfigInvalid => opendal_code::OPENDAL_CONFIG_INVALID, + core::ErrorKind::NotFound => opendal_code::OPENDAL_NOT_FOUND, + core::ErrorKind::PermissionDenied => opendal_code::OPENDAL_PERMISSION_DENIED, + core::ErrorKind::IsADirectory => opendal_code::OPENDAL_IS_A_DIRECTORY, + core::ErrorKind::NotADirectory => opendal_code::OPENDAL_NOT_A_DIRECTORY, + core::ErrorKind::AlreadyExists => opendal_code::OPENDAL_ALREADY_EXISTS, + core::ErrorKind::RateLimited => opendal_code::OPENDAL_RATE_LIMITED, + core::ErrorKind::IsSameFile => opendal_code::OPENDAL_IS_SAME_FILE, // if this is triggered, check the [`core`] crate and add a // new error code accordingly _ => panic!("The newly added ErrorKind in core crate is not handled in C bindings"), @@ -98,19 +90,15 @@ pub struct opendal_error { } impl opendal_error { - // The caller should sink the error to heap memory and return the pointer - // that will not be freed by rustc - pub(crate) fn from_opendal_error(error: od::Error) -> Self { - let error = raw_error(error); - let code = error.error_code(); - let c_str = format!("{}", error.0); - let message = opendal_bytes::new(c_str.into_bytes()); - opendal_error { code, message } - } + /// Create a new opendal error via `core::Error`. + /// + /// We will call `Box::leak()` to leak this error, so the caller should be responsible for + /// free this error. + pub fn new(err: core::Error) -> *mut opendal_error { + let code = opendal_code::from(err.kind()); + let message = opendal_bytes::new(err.to_string().into_bytes()); - pub(crate) fn manual_error(code: opendal_code, message: String) -> Self { - let message = opendal_bytes::new(message.into_bytes()); - opendal_error { code, message } + Box::into_raw(Box::new(opendal_error { code, message })) } /// \brief Frees the opendal_error, ok to call on NULL diff --git a/bindings/c/src/lister.rs b/bindings/c/src/lister.rs index 072bba79cc12..08f787480e3f 100644 --- a/bindings/c/src/lister.rs +++ b/bindings/c/src/lister.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use ::opendal as od; +use ::opendal as core; use super::*; @@ -28,11 +28,11 @@ use super::*; /// @see opendal_operator_list() #[repr(C)] pub struct opendal_lister { - inner: *mut od::BlockingLister, + inner: *mut core::BlockingLister, } impl opendal_lister { - pub(crate) fn new(lister: od::BlockingLister) -> Self { + pub(crate) fn new(lister: core::BlockingLister) -> Self { Self { inner: Box::into_raw(Box::new(lister)), } @@ -63,13 +63,10 @@ impl opendal_lister { error: std::ptr::null_mut(), } } - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_lister_next { - entry: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_lister_next { + entry: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } diff --git a/bindings/c/src/metadata.rs b/bindings/c/src/metadata.rs index 0987d3682909..226c443277aa 100644 --- a/bindings/c/src/metadata.rs +++ b/bindings/c/src/metadata.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use ::opendal as od; +use ::opendal as core; /// \brief Carries all metadata associated with a path. /// @@ -30,13 +30,13 @@ use ::opendal as od; pub struct opendal_metadata { /// The pointer to the opendal::Metadata in the Rust code. /// Only touch this on judging whether it is NULL. - pub inner: *mut od::Metadata, + pub inner: *mut core::Metadata, } impl opendal_metadata { /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible /// [`opendal_metadata`] - pub(crate) fn new(m: od::Metadata) -> Self { + pub(crate) fn new(m: core::Metadata) -> Self { Self { inner: Box::into_raw(Box::new(m)), } diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs index 87414379bc19..7bcc3a78a8be 100644 --- a/bindings/c/src/operator.rs +++ b/bindings/c/src/operator.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::os::raw::c_char; use std::str::FromStr; -use ::opendal as od; +use ::opendal as core; use once_cell::sync::Lazy; use super::*; @@ -46,7 +46,7 @@ static RUNTIME: Lazy = Lazy::new(|| { pub struct opendal_operator { /// The pointer to the opendal::BlockingOperator in the Rust code. /// Only touch this on judging whether it is NULL. - ptr: *const od::BlockingOperator, + ptr: *const core::BlockingOperator, } impl opendal_operator { @@ -67,34 +67,42 @@ impl opendal_operator { /// ``` #[no_mangle] pub unsafe extern "C" fn opendal_operator_free(op: *const opendal_operator) { - let _ = unsafe { Box::from_raw((*op).ptr as *mut od::BlockingOperator) }; + let _ = unsafe { Box::from_raw((*op).ptr as *mut core::BlockingOperator) }; let _ = unsafe { Box::from_raw(op as *mut opendal_operator) }; } } -impl opendal_operator { - /// Returns a reference to the underlying [`od::BlockingOperator`] - pub(crate) fn as_ref(&self) -> &od::BlockingOperator { +/// Returns a reference to the underlying [`od::BlockingOperator`] +/// +/// # Safety +/// +/// The ptr is a raw pointer to the underlying [`od::BlockingOperator`]. +/// It will only free when opendal_operator_free has been called. +impl AsRef for opendal_operator { + fn as_ref(&self) -> &core::BlockingOperator { unsafe { &*(self.ptr) } } } #[allow(clippy::from_over_into)] -impl From<*const od::BlockingOperator> for opendal_operator { - fn from(value: *const od::BlockingOperator) -> Self { +impl From<*const core::BlockingOperator> for opendal_operator { + fn from(value: *const core::BlockingOperator) -> Self { Self { ptr: value } } } #[allow(clippy::from_over_into)] -impl From<*mut od::BlockingOperator> for opendal_operator { - fn from(value: *mut od::BlockingOperator) -> Self { +impl From<*mut core::BlockingOperator> for opendal_operator { + fn from(value: *mut core::BlockingOperator) -> Self { Self { ptr: value } } } -fn build_operator(schema: od::Scheme, map: HashMap) -> od::Result { - let mut op = match od::Operator::via_map(schema, map) { +fn build_operator( + schema: core::Scheme, + map: HashMap, +) -> core::Result { + let mut op = match core::Operator::via_map(schema, map) { Ok(o) => o, Err(e) => return Err(e), }; @@ -102,7 +110,8 @@ fn build_operator(schema: od::Scheme, map: HashMap) -> od::Resul let runtime = tokio::runtime::Handle::try_current().unwrap_or_else(|_| RUNTIME.handle().clone()); let _guard = runtime.enter(); - op = op.layer(od::layers::BlockingLayer::create().expect("blocking layer must be created")); + op = op + .layer(core::layers::BlockingLayer::create().expect("blocking layer must be created")); } Ok(op) } @@ -150,27 +159,17 @@ pub unsafe extern "C" fn opendal_operator_new( options: *const opendal_operator_options, ) -> opendal_result_operator_new { if scheme.is_null() { - let error = opendal_error::manual_error( - opendal_code::OPENDAL_CONFIG_INVALID, - "The scheme given is pointing at NULL".into(), - ); - let result = opendal_result_operator_new { - op: std::ptr::null_mut(), - error: Box::into_raw(Box::new(error)), - }; - return result; + panic!("The scheme given is pointing at NULL"); } let scheme_str = unsafe { std::ffi::CStr::from_ptr(scheme).to_str().unwrap() }; - let scheme = match od::Scheme::from_str(scheme_str) { + let scheme = match core::Scheme::from_str(scheme_str) { Ok(s) => s, Err(e) => { - let e = opendal_error::from_opendal_error(e); - let result = opendal_result_operator_new { + return opendal_result_operator_new { op: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), + error: opendal_error::new(e), }; - return result; } }; @@ -189,13 +188,10 @@ pub unsafe extern "C" fn opendal_operator_new( error: std::ptr::null_mut(), } } - Err(e) => { - let e = opendal_error::from_opendal_error(e); - opendal_result_operator_new { - op: std::ptr::null_mut(), - error: Box::into_raw(Box::new(e)), - } - } + Err(e) => opendal_result_operator_new { + op: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } @@ -257,10 +253,7 @@ pub unsafe extern "C" fn opendal_operator_write( let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.write(path, bytes) { Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } + Err(e) => opendal_error::new(e), } } @@ -322,13 +315,10 @@ pub unsafe extern "C" fn opendal_operator_read( error: std::ptr::null_mut(), } } - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_read { - data: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_read { + data: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } @@ -385,13 +375,10 @@ pub unsafe extern "C" fn opendal_operator_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_operator_reader { - reader: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_operator_reader { + reader: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } @@ -448,10 +435,7 @@ pub unsafe extern "C" fn opendal_operator_delete( let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.delete(path) { Ok(_) => std::ptr::null_mut(), - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - Box::into_raw(e) - } + Err(e) => opendal_error::new(e), } } @@ -508,13 +492,10 @@ pub unsafe extern "C" fn opendal_operator_is_exist( is_exist: e, error: std::ptr::null_mut(), }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_is_exist { - is_exist: false, - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_is_exist { + is_exist: false, + error: opendal_error::new(e), + }, } } @@ -570,13 +551,10 @@ pub unsafe extern "C" fn opendal_operator_stat( meta: Box::into_raw(Box::new(opendal_metadata::new(m))), error: std::ptr::null_mut(), }, - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_stat { - meta: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_stat { + meta: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } @@ -644,13 +622,9 @@ pub unsafe extern "C" fn opendal_operator_list( lister: Box::into_raw(Box::new(opendal_lister::new(lister))), error: std::ptr::null_mut(), }, - - Err(e) => { - let e = Box::new(opendal_error::from_opendal_error(e)); - opendal_result_list { - lister: std::ptr::null_mut(), - error: Box::into_raw(e), - } - } + Err(e) => opendal_result_list { + lister: std::ptr::null_mut(), + error: opendal_error::new(e), + }, } } diff --git a/bindings/c/src/reader.rs b/bindings/c/src/reader.rs index 2738b6737dd7..f9c52fc17f73 100644 --- a/bindings/c/src/reader.rs +++ b/bindings/c/src/reader.rs @@ -16,7 +16,7 @@ // under the License. use super::*; -use ::opendal as od; +use ::opendal as core; use std::io::Read; /// \brief The result type returned by opendal's reader operation. @@ -25,11 +25,11 @@ use std::io::Read; /// a opendal::BlockingReader, which is inside the Rust core code. #[repr(C)] pub struct opendal_reader { - inner: *mut od::BlockingReader, + inner: *mut core::BlockingReader, } impl opendal_reader { - pub(crate) fn new(reader: od::BlockingReader) -> Self { + pub(crate) fn new(reader: core::BlockingReader) -> Self { Self { inner: Box::into_raw(Box::new(reader)), } @@ -55,16 +55,13 @@ impl opendal_reader { 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), - } - } + Err(e) => opendal_result_reader_read { + size: 0, + error: opendal_error::new( + core::Error::new(core::ErrorKind::Unexpected, "read failed from reader") + .set_source(e), + ), + }, } } From 39fe4a995ef3198674e181b2596f30ad423c5bf5 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:19:49 +0800 Subject: [PATCH 07/16] Fix doc Signed-off-by: Xuanwo --- bindings/c/src/operator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs index 7bcc3a78a8be..4f6406236a38 100644 --- a/bindings/c/src/operator.rs +++ b/bindings/c/src/operator.rs @@ -142,7 +142,7 @@ fn build_operator( /// /// // Construct the operator based on the options and scheme /// opendal_result_operator_new result = opendal_operator_new("memory", options); -/// opendal_operator* op = result.operator; +/// opendal_operator* op = result.op; /// /// // you could free the options right away since the options is not used afterwards /// opendal_operator_options_free(options); From 5460a34865519e9fae18d2588d8d750e0c362fef Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:26:53 +0800 Subject: [PATCH 08/16] format Signed-off-by: Xuanwo --- bindings/c/include/opendal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index df55fffb35b1..cc43391513fc 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -620,7 +620,7 @@ void opendal_operator_free(const struct opendal_operator *op); * * // Construct the operator based on the options and scheme * opendal_result_operator_new result = opendal_operator_new("memory", options); - * opendal_operator* op = result.operator; + * opendal_operator* op = result.op; * * // you could free the options right away since the options is not used afterwards * opendal_operator_options_free(options); From bd4599a70cd6ec64c6fb21197131b967eab6fb4a Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:31:43 +0800 Subject: [PATCH 09/16] Fix go Signed-off-by: Xuanwo --- bindings/go/opendal.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/go/opendal.go b/bindings/go/opendal.go index 4677a99dc675..027d87ad24e7 100644 --- a/bindings/go/opendal.go +++ b/bindings/go/opendal.go @@ -37,7 +37,7 @@ var ( type Options map[string]string type Operator struct { - inner *C.opendal_operator_ptr + inner *C.opendal_operator } func NewOperator(scheme string, opt Options) (*Operator, error) { @@ -56,7 +56,7 @@ func NewOperator(scheme string, opt Options) (*Operator, error) { return nil, errors.New(fmt.Sprintf("create operator failed, error code: %d, error message: %s", code, message)) } return &Operator{ - inner: ret.operator_ptr, + inner: ret.op, }, nil } @@ -65,7 +65,7 @@ func (o *Operator) Write(key string, value []byte) error { return errValueEmpty } bytes := C.opendal_bytes{data: (*C.uchar)(unsafe.Pointer(&value[0])), len: C.ulong(len(value))} - ret := C.opendal_operator_blocking_write(o.inner, C.CString(key), bytes) + ret := C.opendal_operator_write(o.inner, C.CString(key), bytes) if ret != nil { defer C.opendal_error_free(ret) code, message := parseError(ret) @@ -75,7 +75,7 @@ func (o *Operator) Write(key string, value []byte) error { } func (o *Operator) Read(key string) ([]byte, error) { - ret := C.opendal_operator_blocking_read(o.inner, C.CString(key)) + ret := C.opendal_operator_read(o.inner, C.CString(key)) if ret.error != nil { defer C.opendal_error_free(ret.error) code, message := parseError(ret.error) From 290eda2ffcaaa86767c7bd995c0911ce625514b7 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:32:34 +0800 Subject: [PATCH 10/16] Fix zig Signed-off-by: Xuanwo --- bindings/zig/test/bdd.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/zig/test/bdd.zig b/bindings/zig/test/bdd.zig index c835e45b3400..68548d1f9fd9 100644 --- a/bindings/zig/test/bdd.zig +++ b/bindings/zig/test/bdd.zig @@ -24,7 +24,7 @@ test "Opendal BDD test" { const c_str = [*:0]const u8; // define a type for 'const char*' in C const OpendalBDDTest = struct { - p: [*c]const opendal.c.opendal_operator_ptr, + p: [*c]const opendal.c.opendal_operator, scheme: c_str, path: c_str, content: c_str, @@ -42,7 +42,7 @@ test "Opendal BDD test" { // Given A new OpenDAL Blocking Operator var result = opendal.c.opendal_operator_new(self.scheme, options); testing.expectEqual(result.@"error", null) catch unreachable; - self.p = result.operator_ptr; + self.p = result.op; return self; } @@ -63,7 +63,7 @@ test "Opendal BDD test" { // c_str does not have len field (.* is ptr) .len = std.mem.len(testkit.content), }; - const result = opendal.c.opendal_operator_blocking_write(testkit.p, testkit.path, data); + const result = opendal.c.opendal_operator_write(testkit.p, testkit.path, data); try testing.expectEqual(result, null); // The blocking file "test" should exist @@ -82,7 +82,7 @@ test "Opendal BDD test" { defer opendal.c.opendal_metadata_free(meta); // The blocking file "test" must have content "Hello, World!" - var r: opendal.c.opendal_result_read = opendal.c.opendal_operator_blocking_read(testkit.p, testkit.path); + var r: opendal.c.opendal_result_read = opendal.c.opendal_operator_read(testkit.p, testkit.path); defer opendal.c.opendal_bytes_free(r.data); try testing.expect(r.@"error" == null); try testing.expectEqual(std.mem.len(testkit.content), r.data.*.len); From ddb125391aa2f14ad7e0b8207a7d41f68cce7729 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:33:21 +0800 Subject: [PATCH 11/16] fix swift Signed-off-by: Xuanwo --- bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift index 6104c17c15d5..2ce27551e39e 100644 --- a/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift +++ b/bindings/swift/OpenDAL/Sources/OpenDAL/Operator.swift @@ -24,7 +24,7 @@ public struct OperatorError: Error { } public class Operator { - var nativeOp: UnsafePointer + var nativeOp: UnsafePointer deinit { opendal_operator_free(nativeOp) @@ -54,14 +54,14 @@ public class Operator { ) } - self.nativeOp = UnsafePointer(ret.operator_ptr)! + self.nativeOp = UnsafePointer(ret.op)! } public func blockingWrite(_ data: Data, to path: String) throws { let ret = data.withUnsafeBytes { dataPointer in let address = dataPointer.baseAddress!.assumingMemoryBound(to: UInt8.self) let bytes = opendal_bytes(data: address, len: UInt(dataPointer.count)) - return opendal_operator_blocking_write(nativeOp, path, bytes) + return opendal_operator_write(nativeOp, path, bytes) } if let err = ret { @@ -79,7 +79,7 @@ public class Operator { } public func blockingRead(_ path: String) throws -> Data { - let ret = opendal_operator_blocking_read(nativeOp, path) + let ret = opendal_operator_read(nativeOp, path) if let err = ret.error { defer { opendal_error_free(err) From 5be79ba96453e45aecf785feacf9abe449b947b0 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:37:28 +0800 Subject: [PATCH 12/16] Add upgrade docs Signed-off-by: Xuanwo --- bindings/c/upgrade.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 bindings/c/upgrade.md diff --git a/bindings/c/upgrade.md b/bindings/c/upgrade.md new file mode 100644 index 000000000000..3e9563ad3e97 --- /dev/null +++ b/bindings/c/upgrade.md @@ -0,0 +1,18 @@ +# Upgrade to v0.41 + +The naming convention for C binding has been altered since version 0.41. + +## Struct Naming + +Renaming certain struct names for consistency. + +- `opendal_operator_ptr` => `opendal_operator` +- `opendal_blocking_lister` => `opendal_lister` +- `opendal_list_entry` => `opendal_entry` + +## API Naming + +We've eliminated the `blocking_` prefix from our API because C binding doesn't currently support async. In the future, we plan to introduce APIs such as `opendal_operator_async_write`. + +- `opendal_operator_blocking_write` => `opendal_operator_write` +- `opendal_operator_blocking_read` => `opendal_operator_read` From 8396c4398af9281cbcbc4258e8773b05e85e75cf Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:38:01 +0800 Subject: [PATCH 13/16] Fix Signed-off-by: Xuanwo --- bindings/c/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/upgrade.md b/bindings/c/upgrade.md index 3e9563ad3e97..ae83fe2ec8bc 100644 --- a/bindings/c/upgrade.md +++ b/bindings/c/upgrade.md @@ -1,4 +1,4 @@ -# Upgrade to v0.41 +# Upgrade to v0.42 The naming convention for C binding has been altered since version 0.41. From 97f70516fdec0c26b85b296d6839a46e4440590e Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:39:25 +0800 Subject: [PATCH 14/16] polish Signed-off-by: Xuanwo --- bindings/c/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/upgrade.md b/bindings/c/upgrade.md index ae83fe2ec8bc..db84e0d5ea95 100644 --- a/bindings/c/upgrade.md +++ b/bindings/c/upgrade.md @@ -1,6 +1,6 @@ # Upgrade to v0.42 -The naming convention for C binding has been altered since version 0.41. +The naming convention for C binding has been altered. ## Struct Naming From a5bfa86982329fd0a86df68b16f367ee7759667b Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 20 Oct 2023 18:52:29 +0800 Subject: [PATCH 15/16] Make clippy happy Signed-off-by: Xuanwo --- bindings/c/src/lib.rs | 5 +++++ bindings/c/src/metadata.rs | 2 +- bindings/c/src/types.rs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 7607c52f5e26..76f26381ac75 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -16,7 +16,12 @@ // under the License. #![warn(missing_docs)] +// This crate is the C binding for the OpenDAL project. +// So it's type node can't meet camel case. #![allow(non_camel_case_types)] +// This crate is the C binding for the OpenDAL project. +// Nearly all the functions exposed to C FFI are unsafe. +#![allow(clippy::missing_safety_doc)] //! The OpenDAL C binding. //! diff --git a/bindings/c/src/metadata.rs b/bindings/c/src/metadata.rs index 226c443277aa..15c38bbd2cf7 100644 --- a/bindings/c/src/metadata.rs +++ b/bindings/c/src/metadata.rs @@ -44,7 +44,7 @@ impl opendal_metadata { /// \brief Free the heap-allocated metadata used by opendal_metadata #[no_mangle] - pub extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { + pub unsafe extern "C" fn opendal_metadata_free(ptr: *mut opendal_metadata) { unsafe { let _ = Box::from_raw((*ptr).inner); let _ = Box::from_raw(ptr); diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index e03b930b1224..3e8b88c48caf 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -44,7 +44,7 @@ impl opendal_bytes { /// \brief Frees the heap memory used by the opendal_bytes #[no_mangle] - pub extern "C" fn opendal_bytes_free(bs: *mut opendal_bytes) { + pub unsafe extern "C" fn opendal_bytes_free(bs: *mut opendal_bytes) { if !bs.is_null() { let data_mut = unsafe { (*bs).data as *mut u8 }; // free the vector From b933f7055f06e5db3ab31f7e53e30d28c7db34ff Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Sat, 21 Oct 2023 11:51:09 +0800 Subject: [PATCH 16/16] Format Signed-off-by: Xuanwo --- bindings/c/src/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index fc834ffe7a49..3e8b88c48caf 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -143,4 +143,3 @@ impl opendal_operator_options { let _ = unsafe { Box::from_raw(options as *mut opendal_operator_options) }; } } -