diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h index ca028278b9c..73e1cdf7ba6 100644 --- a/bindings/c/include/opendal.h +++ b/bindings/c/include/opendal.h @@ -241,7 +241,7 @@ typedef struct opendal_metadata { * The pointer to the opendal::Metadata in the Rust code. * Only touch this on judging whether it is NULL. */ - const struct Metadata *inner; + struct Metadata *inner; } opendal_metadata; /** @@ -254,7 +254,7 @@ typedef struct opendal_result_stat { /** * The metadata output of the stat */ - const struct opendal_metadata *meta; + struct opendal_metadata *meta; /** * The error code, should be OPENDAL_OK if succeeds */ @@ -558,7 +558,7 @@ void opendal_bytes_free(const struct opendal_bytes *self); /** * \brief Free the heap-allocated metadata used by opendal_metadata */ -void opendal_metadata_free(const struct opendal_metadata *self); +void opendal_metadata_free(struct opendal_metadata *self); /** * \brief Return the content_length of the metadata diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index 047b0b38858..8569a7a0190 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -403,11 +403,11 @@ pub unsafe extern "C" fn opendal_operator_stat( let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }; match op.stat(path) { Ok(m) => opendal_result_stat { - meta: opendal_metadata::from_metadata(m), + meta: Box::into_raw(Box::new(opendal_metadata::new(m))), code: opendal_code::OPENDAL_OK, }, Err(err) => opendal_result_stat { - meta: std::ptr::null(), + meta: std::ptr::null_mut(), code: opendal_code::from_opendal_error(err), }, } diff --git a/bindings/c/src/result.rs b/bindings/c/src/result.rs index fef1c5d971c..bb338645ff2 100644 --- a/bindings/c/src/result.rs +++ b/bindings/c/src/result.rs @@ -61,7 +61,7 @@ pub struct opendal_result_is_exist { #[repr(C)] pub struct opendal_result_stat { /// The metadata output of the stat - pub meta: *const opendal_metadata, + pub meta: *mut opendal_metadata, /// The error code, should be OPENDAL_OK if succeeds pub code: opendal_code, } diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs index 454248a4d0e..f84f1819099 100644 --- a/bindings/c/src/types.rs +++ b/bindings/c/src/types.rs @@ -16,6 +16,7 @@ // under the License. use std::collections::HashMap; +use std::mem; use std::os::raw::c_char; use ::opendal as od; @@ -143,17 +144,28 @@ impl Into for opendal_bytes { 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: *const od::Metadata, + 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(&self) { - if self.inner.is_null() { - return; + pub extern "C" fn opendal_metadata_free(&mut self) { + if !self.inner.is_null() { + unsafe { + mem::drop(Box::from_raw(self.inner)); + } } - let _ = unsafe { Box::from_raw(self.inner as *mut od::Metadata) }; + + unsafe { mem::drop(Box::from_raw(self as *mut Self)) } } /// \brief Return the content_length of the metadata @@ -189,7 +201,9 @@ impl opendal_metadata { pub extern "C" fn opendal_metadata_is_file(&self) -> bool { // Safety: the inner should never be null once constructed // The use-after-free is undefined behavior - unsafe { (*self.inner).is_file() } + let m = unsafe { &*self.inner }; + + m.is_file() } /// \brief Return whether the path represents a directory @@ -216,16 +230,6 @@ impl opendal_metadata { } } -impl opendal_metadata { - /// Convert a Rust core [`od::Metadata`] into a heap allocated C-compatible - /// [`opendal_metadata`] - pub(crate) fn from_metadata(m: od::Metadata) -> *const Self { - &Self { - inner: Box::leak(Box::new(m)), - } - } -} - /// \brief The configuration for the initialization of opendal_operator_ptr. /// /// \note This is also a heap-allocated struct, please free it after you use it diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp index 17414b83bec..cac9a11a077 100644 --- a/bindings/c/tests/bdd.cpp +++ b/bindings/c/tests/bdd.cpp @@ -69,7 +69,7 @@ TEST_F(OpendalBddTest, FeatureTest) // The blocking file "test" entry mode must be file opendal_result_stat s = opendal_operator_stat(this->p, this->path.c_str()); EXPECT_EQ(s.code, OPENDAL_OK); - const opendal_metadata *meta = s.meta; + opendal_metadata *meta = s.meta; EXPECT_TRUE(opendal_metadata_is_file(meta)); // The blocking file "test" content length must be 13 diff --git a/bindings/zig/test/bdd.zig b/bindings/zig/test/bdd.zig index 04451f78497..c998454c9b8 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: opendal.c.opendal_operator_ptr, + p: [*c]const opendal.c.opendal_operator_ptr, scheme: c_str, path: c_str, content: c_str, @@ -35,19 +35,18 @@ test "Opendal BDD test" { self.path = "test"; self.content = "Hello, World!"; - var options: opendal.c.opendal_operator_options = opendal.c.opendal_operator_options_new(); - defer opendal.c.opendal_operator_options_free(&options); - opendal.c.opendal_operator_options_set(&options, "root", "/myroot"); + var options: [*c]opendal.c.opendal_operator_options = opendal.c.opendal_operator_options_new(); + defer opendal.c.opendal_operator_options_free(options); + opendal.c.opendal_operator_options_set(options, "root", "/myroot"); // Given A new OpenDAL Blocking Operator - self.p = opendal.c.opendal_operator_new(self.scheme, &options); - std.debug.assert(self.p.ptr != null); + self.p = opendal.c.opendal_operator_new(self.scheme, options); return self; } pub fn deinit(self: *Self) void { - opendal.c.opendal_operator_free(&self.p); + opendal.c.opendal_operator_free(self.p); } const Self = @This(); @@ -73,12 +72,12 @@ test "Opendal BDD test" { // The blocking file "test" entry mode must be file var s: opendal.c.opendal_result_stat = opendal.c.opendal_operator_stat(testkit.p, testkit.path); try testing.expectEqual(s.code, @enumToInt(Code.OK)); - var meta: opendal.c.opendal_metadata = s.meta; - try testing.expect(opendal.c.opendal_metadata_is_file(&meta)); + var meta: [*c]opendal.c.opendal_metadata = s.meta; + try testing.expect(opendal.c.opendal_metadata_is_file(meta)); // The blocking file "test" content length must be 13 - try testing.expectEqual(opendal.c.opendal_metadata_content_length(&meta), 13); - defer opendal.c.opendal_metadata_free(&meta); + try testing.expectEqual(opendal.c.opendal_metadata_content_length(meta), 13); + 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);