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;
-}

Reply via email to