This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 920c3d68 feat(bindings/c): framework of add basic io and init logics
(#1861)
920c3d68 is described below
commit 920c3d68e79f670dccc61725edcd9341818a8899
Author: xyJi <[email protected]>
AuthorDate: Fri Apr 7 20:51:04 2023 +0800
feat(bindings/c): framework of add basic io and init logics (#1861)
* feat(bindings/c): framework of add basic io and init logics
* Add the init logics for operator
* Add the basic io operations (r/w)
* Add the test for basicio
Fixes: #1201
Signed-off-by: Ji-Xinyou <[email protected]>
* feat(bindings/c): remove place holder code for c binding
* The hello_opendal() works as a place holder for c binding,
remove it since it will not be used anymore
Signed-off-by: Ji-Xinyou <[email protected]>
* feat(bindings/c): use transparent layout for more native ptr operation
* Previously the type opendal_operator_ptr uses a C layout, i.e.,
it STORES a pointer inside it. This commit changes it to transparent
layout, which guarantees it has the SAME layout of the inner pointer.
This allows the opendal_operator_ptr uses native boolean operation to
check validity. e.g. (!ptr) means invalid.
Signed-off-by: Ji-Xinyou <[email protected]>
* feat(bindings/c): add error and result types for error handling
* Add error code opendal_error for error handling, which decouples
the od::ErrorKind
Signed-off-by: Ji-Xinyou <[email protected]>
---------
Signed-off-by: Ji-Xinyou <[email protected]>
---
Cargo.lock | 1 +
bindings/c/Cargo.toml | 1 +
bindings/c/Makefile | 6 +-
bindings/c/include/opendal.h | 137 ++++++++++++++++++++++++++++++++++-
bindings/c/src/error.rs | 81 +++++++++++++++++++++
bindings/c/src/lib.rs | 126 ++++++++++++++++++++++++++++++--
bindings/c/src/{lib.rs => macros.rs} | 21 ++++--
bindings/c/src/{lib.rs => result.rs} | 21 ++++--
bindings/c/src/types.rs | 110 ++++++++++++++++++++++++++++
bindings/c/tests/basicio.c | 59 +++++++++++++++
bindings/c/tests/hello.c | 24 ------
11 files changed, 537 insertions(+), 50 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index eb62479b..c57103ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2275,6 +2275,7 @@ dependencies = [
name = "opendal-c"
version = "0.1.0"
dependencies = [
+ "bytes",
"cbindgen",
"opendal",
]
diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml
index 9d39b43e..0992763d 100644
--- a/bindings/c/Cargo.toml
+++ b/bindings/c/Cargo.toml
@@ -35,4 +35,5 @@ doc = false
cbindgen = "0.24.0"
[dependencies]
+bytes = "1.4.0"
opendal = { version = "0.30", path = "../../core" }
diff --git a/bindings/c/Makefile b/bindings/c/Makefile
index f949cc09..28e391c3 100644
--- a/bindings/c/Makefile
+++ b/bindings/c/Makefile
@@ -19,7 +19,7 @@ RPATH=$(PWD)/../../target/debug
CFLAGS = -I./include
LDFLAGS = -L$(RPATH) -Wl,-rpath,$(RPATH)
LIBS = -lopendal_c
-OBJ_DIR=build
+OBJ_DIR=./build
.PHONY: all
all: build test
@@ -31,9 +31,9 @@ build:
.PHONY: test
test:
- $(CC) tests/hello.c -o $(OBJ_DIR)/hello $(CFLAGS) $(LDFLAGS) $(LIBS)
+ $(CC) tests/basicio.c -o $(OBJ_DIR)/basicio $(CFLAGS) $(LDFLAGS) $(LIBS)
.PHONY: clean
clean:
cargo clean
- rm -rf "./$(OBJ_DIR)"
+ rm -rf $(OBJ_DIR)
diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 3cd506b4..eaec9f68 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -25,14 +25,147 @@
#include <stddef.h>
#include <stdbool.h>
+/*
+ The error code for opendal APIs in C binding
+ */
+typedef enum opendal_code {
+ /*
+ All is well
+ */
+ OPENDAL_OK,
+ /*
+ General error
+ */
+ OPENDAL_ERROR,
+ /*
+ returning it back. For example, s3 returns an internal service error.
+ */
+ OPENDAL_UNEXPECTED,
+ /*
+ Underlying service doesn't support this operation.
+ */
+ OPENDAL_UNSUPPORTED,
+ /*
+ The config for backend is invalid.
+ */
+ OPENDAL_CONFIG_INVALID,
+ /*
+ The given path is not found.
+ */
+ OPENDAL_NOT_FOUND,
+ /*
+ The given path doesn't have enough permission for this operation
+ */
+ OPENDAL_PERMISSION_DENIED,
+ /*
+ The given path is a directory.
+ */
+ OPENDAL_IS_A_DIRECTORY,
+ /*
+ The given path is not a directory.
+ */
+ OPENDAL_NOT_A_DIRECTORY,
+ /*
+ The given path already exists thus we failed to the specified operation on
it.
+ */
+ OPENDAL_ALREADY_EXISTS,
+ /*
+ Requests that sent to this path is over the limit, please slow down.
+ */
+ OPENDAL_RATE_LIMITED,
+ /*
+ The given file paths are same.
+ */
+ OPENDAL_IS_SAME_FILE,
+} opendal_code;
+
+/*
+ The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`].
+ It is also the key struct that OpenDAL's APIs access the real
+ operator's memory. The use of OperatorPtr is zero cost, it
+ only returns a reference of the underlying Operator.
+
+ The [`OperatorPtr`] also has a transparent layout, allowing you
+ to check its validity by native boolean operator.
+ e.g. you could check by (!ptr) on a opendal_operator_ptr type
+ */
+typedef const void *opendal_operator_ptr;
+
+/*
+ The [`Bytes`] type is a C-compatible substitute for [`Bytes`]
+ in Rust, it will not be deallocated automatically like what
+ has been done in Rust. Instead, you have to call [`opendal_free_bytes`]
+ to free the heap memory to avoid memory leak.
+ The field `data` should not be modified since it might causes
+ the reallocation of the Vector.
+ */
+typedef struct opendal_bytes {
+ const uint8_t *data;
+ uintptr_t len;
+} opendal_bytes;
+
+/*
+ The Rust-like Result type of opendal C binding, it contains
+ the data that the read operation returns and a error code
+ If the read operation failed, the `data` fields should be a nullptr
+ and the error code is NOT OPENDAL_OK.
+ */
+typedef struct opendal_result_read {
+ struct opendal_bytes *data;
+ enum opendal_code code;
+} opendal_result_read;
+
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/*
- Hello, OpenDAL!
+ Returns a result type [`opendal_result_op`], with operator_ptr. If the
construction succeeds
+ the error is nullptr, otherwise it contains the error information.
+
+ # Safety
+
+ It is [safe] under two cases below
+ * The memory pointed to by `scheme` must contain a valid nul terminator at
the end of
+ the string.
+ * The `scheme` points to NULL, this function simply returns you a null
opendal_operator_ptr
+ */
+opendal_operator_ptr opendal_operator_new(const char *scheme);
+
+/*
+ Write the data into the path blockingly by operator, returns the error code
OPENDAL_OK
+ if succeeds, others otherwise
+
+ # Safety
+
+ It is [safe] under two cases below
+ * The memory pointed to by `path` must contain a valid nul terminator at the
end of
+ the string.
+ * The `path` points to NULL, this function simply returns you false
+ */
+enum opendal_code opendal_operator_blocking_write(opendal_operator_ptr op_ptr,
+ const char *path,
+ struct opendal_bytes bytes);
+
+/*
+ Read the data out from path into a [`Bytes`] blockingly by operator, returns
+ a result with error code. If the error code is not OPENDAL_OK, the `data`
field
+ of the result points to NULL.
+
+ # Safety
+
+ It is [safe] under two cases below
+ * The memory pointed to by `path` must contain a valid nul terminator at the
end of
+ the string.
+ * The `path` points to NULL, this function simply returns you a nullptr
+ */
+struct opendal_result_read opendal_operator_blocking_read(opendal_operator_ptr
op_ptr,
+ const char *path);
+
+/*
+ Frees the heap memory used by the [`Bytes`]
*/
-void hello_opendal(void);
+void opendal_bytes_free(const struct opendal_bytes *vec);
#ifdef __cplusplus
} // extern "C"
diff --git a/bindings/c/src/error.rs b/bindings/c/src/error.rs
new file mode 100644
index 00000000..61696a06
--- /dev/null
+++ b/bindings/c/src/error.rs
@@ -0,0 +1,81 @@
+// 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;
+
+/// The wrapper type for opendal's error, wrapped because of the
+/// orphan rule
+pub struct opendal_error(od::Error);
+
+/// The error code for opendal APIs in C binding
+#[repr(C)]
+pub enum opendal_code {
+ /// All is well
+ OPENDAL_OK,
+ /// General error
+ // todo: make details in the `opendal_error *`
+ OPENDAL_ERROR,
+ /// returning it back. For example, s3 returns an internal service error.
+ OPENDAL_UNEXPECTED,
+ /// Underlying service doesn't support this operation.
+ OPENDAL_UNSUPPORTED,
+ /// The config for backend is invalid.
+ OPENDAL_CONFIG_INVALID,
+ /// The given path is not found.
+ OPENDAL_NOT_FOUND,
+ /// The given path doesn't have enough permission for this operation
+ OPENDAL_PERMISSION_DENIED,
+ /// The given path is a directory.
+ OPENDAL_IS_A_DIRECTORY,
+ /// The given path is not a directory.
+ OPENDAL_NOT_A_DIRECTORY,
+ /// The given path already exists thus we failed to the specified
operation on it.
+ OPENDAL_ALREADY_EXISTS,
+ /// Requests that sent to this path is over the limit, please slow down.
+ OPENDAL_RATE_LIMITED,
+ /// The given file paths are same.
+ OPENDAL_IS_SAME_FILE,
+}
+
+impl opendal_code {
+ pub(crate) fn from_opendal_error(e: od::Error) -> Self {
+ let error = opendal_error(e);
+ error.error_code()
+ }
+}
+
+impl opendal_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,
+ // 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"),
+ }
+ }
+}
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs
index 814db98c..8259f476 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/lib.rs
@@ -15,12 +15,126 @@
// specific language governing permissions and limitations
// under the License.
-use opendal::services::Memory;
-use opendal::Operator;
+#![allow(non_camel_case_types)]
-/// Hello, OpenDAL!
+mod error;
+mod macros;
+mod result;
+mod types;
+
+use std::collections::HashMap;
+use std::os::raw::c_char;
+use std::str::FromStr;
+
+use crate::types::{opendal_bytes, opendal_operator_ptr};
+
+use ::opendal as od;
+use error::opendal_code;
+use result::opendal_result_read;
+
+/// Returns a result type [`opendal_result_op`], with operator_ptr. If the
construction succeeds
+/// the error is nullptr, otherwise it contains the error information.
+///
+/// # Safety
+///
+/// It is [safe] under two cases below
+/// * The memory pointed to by `scheme` must contain a valid nul terminator at
the end of
+/// the string.
+/// * The `scheme` points to NULL, this function simply returns you a null
opendal_operator_ptr
#[no_mangle]
-pub extern "C" fn hello_opendal() {
- let op = Operator::new(Memory::default()).unwrap().finish();
- println!("{op:?}")
+pub unsafe extern "C" fn opendal_operator_new(scheme: *const c_char) ->
opendal_operator_ptr {
+ if scheme.is_null() {
+ return opendal_operator_ptr::null();
+ }
+
+ 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(_) => {
+ return opendal_operator_ptr::null();
+ }
+ };
+
+ // todo: api for map construction
+ let map = HashMap::default();
+
+ let op = match scheme {
+ od::Scheme::Memory => generate_operator!(od::services::Memory, map),
+ _ => {
+ return opendal_operator_ptr::null();
+ }
+ }
+ .blocking();
+
+ // this prevents the operator memory from being dropped by the Box
+ let op = Box::leak(Box::new(op));
+
+ opendal_operator_ptr::from(op)
+}
+
+/// Write the data into the path blockingly by operator, returns the error
code OPENDAL_OK
+/// if succeeds, others otherwise
+///
+/// # Safety
+///
+/// It is [safe] under two cases below
+/// * The memory pointed to by `path` must contain a valid nul terminator at
the end of
+/// the string.
+/// * The `path` points to NULL, this function simply returns you false
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_blocking_write(
+ op_ptr: opendal_operator_ptr,
+ path: *const c_char,
+ bytes: opendal_bytes,
+) -> opendal_code {
+ if path.is_null() {
+ return opendal_code::OPENDAL_ERROR;
+ }
+
+ let op = op_ptr.get_ref();
+ let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
+ match op.write(path, bytes) {
+ Ok(_) => opendal_code::OPENDAL_OK,
+ Err(e) => opendal_code::from_opendal_error(e),
+ }
+}
+
+/// Read the data out from path into a [`Bytes`] blockingly by operator,
returns
+/// a result with error code. If the error code is not OPENDAL_OK, the `data`
field
+/// of the result points to NULL.
+///
+/// # Safety
+///
+/// It is [safe] under two cases below
+/// * The memory pointed to by `path` must contain a valid nul terminator at
the end of
+/// the string.
+/// * The `path` points to NULL, this function simply returns you a nullptr
+#[no_mangle]
+pub unsafe extern "C" fn opendal_operator_blocking_read(
+ op_ptr: opendal_operator_ptr,
+ path: *const c_char,
+) -> opendal_result_read {
+ if path.is_null() {
+ return opendal_result_read {
+ data: std::ptr::null_mut(),
+ code: opendal_code::OPENDAL_ERROR,
+ };
+ }
+
+ let op = op_ptr.get_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::from_vec(d));
+ opendal_result_read {
+ data: Box::into_raw(v),
+ code: opendal_code::OPENDAL_OK,
+ }
+ }
+ Err(e) => opendal_result_read {
+ data: std::ptr::null_mut(),
+ code: opendal_code::from_opendal_error(e),
+ },
+ }
}
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/macros.rs
similarity index 67%
copy from bindings/c/src/lib.rs
copy to bindings/c/src/macros.rs
index 814db98c..f77811d1 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/macros.rs
@@ -15,12 +15,17 @@
// specific language governing permissions and limitations
// under the License.
-use opendal::services::Memory;
-use opendal::Operator;
-
-/// Hello, OpenDAL!
-#[no_mangle]
-pub extern "C" fn hello_opendal() {
- let op = Operator::new(Memory::default()).unwrap().finish();
- println!("{op:?}")
+/// Macro used to generate operator upon construction and return C-compatible
+/// error if failed
+#[macro_export]
+macro_rules! generate_operator {
+ ($type:ty, $map:expr) => {{
+ let b = od::Operator::from_map::<$type>($map);
+ match b {
+ Ok(b) => b.finish(),
+ Err(_) => {
+ return opendal_operator_ptr::null();
+ }
+ }
+ }};
}
diff --git a/bindings/c/src/lib.rs b/bindings/c/src/result.rs
similarity index 54%
copy from bindings/c/src/lib.rs
copy to bindings/c/src/result.rs
index 814db98c..fe69dd35 100644
--- a/bindings/c/src/lib.rs
+++ b/bindings/c/src/result.rs
@@ -15,12 +15,19 @@
// specific language governing permissions and limitations
// under the License.
-use opendal::services::Memory;
-use opendal::Operator;
+//! 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,
+//! we are defining all Result types here
-/// Hello, OpenDAL!
-#[no_mangle]
-pub extern "C" fn hello_opendal() {
- let op = Operator::new(Memory::default()).unwrap().finish();
- println!("{op:?}")
+use crate::{error::opendal_code, types::opendal_bytes};
+
+/// The Rust-like Result type of opendal C binding, it contains
+/// the data that the read operation returns and a error code
+/// If the read operation failed, the `data` fields should be a nullptr
+/// and the error code is NOT OPENDAL_OK.
+#[repr(C)]
+pub struct opendal_result_read {
+ pub data: *mut opendal_bytes,
+ pub code: opendal_code,
}
diff --git a/bindings/c/src/types.rs b/bindings/c/src/types.rs
new file mode 100644
index 00000000..dd03546f
--- /dev/null
+++ b/bindings/c/src/types.rs
@@ -0,0 +1,110 @@
+// 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::os::raw::c_void;
+
+use ::opendal as od;
+
+/// The [`OperatorPtr`] owns a pointer to a [`od::BlockingOperator`].
+/// It is also the key struct that OpenDAL's APIs access the real
+/// operator's memory. The use of OperatorPtr is zero cost, it
+/// only returns a reference of the underlying Operator.
+///
+/// The [`OperatorPtr`] also has a transparent layout, allowing you
+/// to check its validity by native boolean operator.
+/// e.g. you could check by (!ptr) on a opendal_operator_ptr type
+#[repr(transparent)]
+pub struct opendal_operator_ptr {
+ // this is typed with [`c_void`] because cbindgen does not
+ // support our own custom type.
+ ptr: *const c_void,
+}
+
+impl opendal_operator_ptr {
+ /// Creates an OperatorPtr will nullptr, indicating this [`OperatorPtr`]
+ /// is invalid. The `transparent` layout also guarantees that if the
+ /// underlying field `ptr` is a nullptr, the [`OperatorPtr`] has the
+ /// same layout as the nullptr.
+ pub(crate) fn null() -> Self {
+ Self {
+ ptr: std::ptr::null(),
+ }
+ }
+
+ /// Returns a reference to the underlying [`BlockingOperator`]
+ pub(crate) fn get_ref(&self) -> &od::BlockingOperator {
+ unsafe { &*(self.ptr as *const od::BlockingOperator) }
+ }
+}
+
+#[allow(clippy::from_over_into)]
+impl From<&od::BlockingOperator> for opendal_operator_ptr {
+ fn from(value: &od::BlockingOperator) -> Self {
+ Self {
+ ptr: value as *const _ as *const c_void,
+ }
+ }
+}
+
+#[allow(clippy::from_over_into)]
+impl From<&mut od::BlockingOperator> for opendal_operator_ptr {
+ fn from(value: &mut od::BlockingOperator) -> Self {
+ Self {
+ ptr: value as *const _ as *const c_void,
+ }
+ }
+}
+
+/// The [`Bytes`] type is a C-compatible substitute for [`Bytes`]
+/// in Rust, it will not be deallocated automatically like what
+/// has been done in Rust. Instead, you have to call [`opendal_free_bytes`]
+/// to free the heap memory to avoid memory leak.
+/// The field `data` should not be modified since it might causes
+/// the reallocation of the Vector.
+#[repr(C)]
+pub struct opendal_bytes {
+ pub data: *const u8,
+ pub len: usize,
+}
+
+impl opendal_bytes {
+ /// Construct a [`Vector`] from the Rust [`Vec`] of bytes
+ pub(crate) fn from_vec(vec: Vec<u8>) -> Self {
+ let data = vec.as_ptr() as *const u8;
+ let len = vec.len();
+ std::mem::forget(vec); // To avoid deallocation of the vec.
+ Self { data, len }
+ }
+}
+
+#[allow(clippy::from_over_into)]
+impl Into<bytes::Bytes> for opendal_bytes {
+ fn into(self) -> bytes::Bytes {
+ let slice = unsafe { std::slice::from_raw_parts(self.data, self.len) };
+ bytes::Bytes::from_static(slice)
+ }
+}
+
+/// Frees the heap memory used by the [`Bytes`]
+#[no_mangle]
+pub extern "C" fn opendal_bytes_free(vec: *const opendal_bytes) {
+ unsafe {
+ // this deallocates the vector by reconstructing the vector and letting
+ // it be dropped when its out of scope
+ Vec::from_raw_parts((*vec).data as *mut u8, (*vec).len, (*vec).len);
+ }
+}
diff --git a/bindings/c/tests/basicio.c b/bindings/c/tests/basicio.c
new file mode 100644
index 00000000..a2e44ef3
--- /dev/null
+++ b/bindings/c/tests/basicio.c
@@ -0,0 +1,59 @@
+/**
+ * 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.
+ */
+
+#include "assert.h"
+#include "stdio.h"
+#include "opendal.h"
+
+void test_operator_rw(opendal_operator_ptr ptr) {
+ // have to be valid ptr
+ assert(ptr);
+
+ // write some contents by the operator, must be successful
+ char path[] = "test";
+ char content[] = "Hello World";
+ const opendal_bytes data = {
+ .len = sizeof(content) - 1,
+ .data = (uint8_t*)content,
+ };
+ opendal_code code = opendal_operator_blocking_write(ptr, path, data);
+ assert(code == OPENDAL_OK);
+
+ // reads the data out from the bytes, must be successful
+ struct opendal_result_read r = opendal_operator_blocking_read(ptr, path);
+ assert(r.code == OPENDAL_OK);
+ assert(r.data->len == (sizeof(content) - 1));
+
+ for (int i = 0; i < r.data->len; i++) {
+ printf("%c", (char)(r.data->data[i]));
+ }
+
+ // free the bytes's heap memory
+ opendal_bytes_free(r.data);
+}
+
+int main(int argc, char *argv[]) {
+ // test memory operator
+ char scheme1[] = "memory";
+ opendal_operator_ptr p1 = opendal_operator_new(scheme1);
+ assert(p1);
+ test_operator_rw(p1);
+
+ return 0;
+}
diff --git a/bindings/c/tests/hello.c b/bindings/c/tests/hello.c
deleted file mode 100644
index 4a6d921d..00000000
--- a/bindings/c/tests/hello.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * 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.
- */
-#include "opendal.h"
-
-int main(int argc, char *argv[]) {
- hello_opendal();
- return 0;
-}