Skip to content

Commit

Permalink
feat(bindings/c): framework of add basic io and init logics
Browse files Browse the repository at this point in the history
* Add the init logics for operator
* Add the basic io operations (r/w)
* Add the test for basicio

Fixes: apache#1201
Signed-off-by: Ji-Xinyou <jerryji0414@outlook.com>
  • Loading branch information
xyjixyjixyji committed Apr 5, 2023
1 parent 889c84e commit 2fe646f
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bindings/c/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ doc = false
cbindgen = "0.24.0"

[dependencies]
bytes = "1.4.0"
opendal = { version = "0.30", path = "../../core" }
3 changes: 2 additions & 1 deletion bindings/c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,6 +32,7 @@ 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:
Expand Down
8 changes: 8 additions & 0 deletions bindings/c/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,11 @@ include_guard = "_OPENDAL_H"
language = "C"
no_includes = true
sys_includes = ["stdint.h", "stddef.h", "stdbool.h"]

[export]
prefix = "opendal_"

[export.rename]
"OperatorPtr" = "operator_ptr"
"Vector" = "vector"

54 changes: 54 additions & 0 deletions bindings/c/include/opendal.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,69 @@
#include <stddef.h>
#include <stdbool.h>

/*
The [`OperatorPtr`] owns a pointer to a [`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.
*/
typedef struct opendal_operator_ptr {
const void *ptr;
} opendal_operator_ptr;

/*
The [`Vector`] type is a C-compatable substitute for [`Vec`]
in Rust, it will not be deallocated automatically like what
has been done in Rust. Instead, you have to call [`free_vec`]
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_vector {
const uint8_t *data;
uintptr_t len;
} opendal_vector;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

/*
Constructs a new [`OperatorPtr`] which contains a underlying [`BlockingOperator`]
If the scheme is invalid, or the operator contructions failed, a nullptr is returned
*/
struct opendal_operator_ptr od_new_operator(const char *scheme);

/*
Write the data into the path blockingly by operator, returns whether the write succeeds
*/
bool od_operator_blocking_write(struct opendal_operator_ptr op_ptr,
const char *path,
struct opendal_vector bytes);

/*
Read the data out from path into a [`Vector`] blockingly by operator, returns
a pointer to the vector if succeeds, nullptr otherwise
*/
struct opendal_vector *od_operator_blocking_read(struct opendal_operator_ptr op_ptr,
const char *path);

/*
Hello, OpenDAL!
*/
void hello_opendal(void);

/*
Returns whether the [`OperatorPtr`] is valid, i.e. whether
there exists a underlying [`BlockingOperator`]
*/
bool od_is_ptr_valid(const struct opendal_operator_ptr *self);

/*
Frees the heap memory used by the [`Vector`]
*/
void od_free_vec(const struct opendal_vector *vec);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
95 changes: 92 additions & 3 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,101 @@
// specific language governing permissions and limitations
// under the License.

use opendal::services::Memory;
use opendal::Operator;
mod types;

use std::collections::HashMap;
use std::os::raw::c_char;
use std::str::FromStr;

use crate::types::{OperatorPtr, Vector};
use ::opendal as od;

/// Constructs a new [`OperatorPtr`] which contains a underlying [`BlockingOperator`]
/// If the scheme is invalid, or the operator contructions failed, a nullptr is returned
#[no_mangle]
pub extern "C" fn od_new_operator(scheme: *const c_char) -> OperatorPtr {
use od::services::*;

if scheme.is_null() {
return OperatorPtr::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 OperatorPtr::null(),
};

// todo: api for map construction
let map = HashMap::default();

let op = match scheme {
od::Scheme::Memory => {
let b = od::Operator::from_map::<Memory>(map);
match b {
Ok(b) => b.finish(),
Err(_) => return OperatorPtr::null(),
}
}
od::Scheme::Fs => {
let b = od::Operator::from_map::<Fs>(map);
match b {
Ok(b) => b.finish(),
Err(_) => return OperatorPtr::null(),
}
}
_ => return OperatorPtr::null(),
}
.blocking();

// this prevents the operator memory from being dropped by the Box
let op = Box::leak(Box::new(op));

OperatorPtr::from(op)
}

/// Write the data into the path blockingly by operator, returns whether the write succeeds
#[no_mangle]
pub extern "C" fn od_operator_blocking_write(
op_ptr: OperatorPtr,
path: *const c_char,
bytes: Vector,
) -> bool {
let op = op_ptr.get_ref();
if path.is_null() {
return false;
}
let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
op.write(path, bytes).is_ok()
}

/// Read the data out from path into a [`Vector`] blockingly by operator, returns
/// a pointer to the vector if succeeds, nullptr otherwise
#[no_mangle]
pub extern "C" fn od_operator_blocking_read(
op_ptr: OperatorPtr,
path: *const c_char,
) -> *mut Vector {
let op = op_ptr.get_ref();
if path.is_null() {
return std::ptr::null_mut();
}
let path = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() };
let data = op.read(path);
match data {
Ok(d) => {
let mut v = std::mem::ManuallyDrop::new(Vector::from_vec(d));
&mut *v
}
Err(_) => std::ptr::null_mut(),
}
}

/// Hello, OpenDAL!
#[no_mangle]
pub extern "C" fn hello_opendal() {
let op = Operator::new(Memory::default()).unwrap().finish();
let op = od::Operator::new(od::services::Memory::default())
.unwrap()
.finish();
println!("{op:?}")
}
110 changes: 110 additions & 0 deletions bindings/c/src/types.rs
Original file line number Diff line number Diff line change
@@ -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 bytes::Bytes;

use ::opendal as od;

/// The [`OperatorPtr`] owns a pointer to a [`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.
#[repr(C)]
pub struct OperatorPtr {
// this is typed with [`c_void`] because cbindgen does not
// support our own custom type.
ptr: *const c_void,
}

impl OperatorPtr {
/// Creates an OperatorPtr will nullptr, indicating this [`OperatorPtr`]
/// is invalid
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) }
}

/// Returns whether the [`OperatorPtr`] is valid, i.e. whether
/// there exists a underlying [`BlockingOperator`]
#[no_mangle]
pub extern "C" fn od_is_ptr_valid(&self) -> bool {
!self.ptr.is_null()
}
}

impl From<&od::BlockingOperator> for OperatorPtr {
fn from(value: &od::BlockingOperator) -> Self {
Self {
ptr: value as *const _ as *const c_void,
}
}
}

impl From<&mut od::BlockingOperator> for OperatorPtr {
fn from(value: &mut od::BlockingOperator) -> Self {
Self {
ptr: value as *const _ as *const c_void,
}
}
}

/// The [`Vector`] type is a C-compatable substitute for [`Vec`]
/// in Rust, it will not be deallocated automatically like what
/// has been done in Rust. Instead, you have to call [`free_vec`]
/// 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 Vector {
pub data: *const u8,
pub len: usize,
}

impl Vector {
/// Construct a [`Vector`] from the Rust [`Vec`] of bytes
pub(crate) fn from_vec(vec: Vec<u8>) -> Vector {
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 }
}
}

impl Into<Bytes> for Vector {
fn into(self) -> Bytes {
let slice = unsafe { std::slice::from_raw_parts(self.data, self.len) };
Bytes::from_static(slice)
}
}

/// Frees the heap memory used by the [`Vector`]
#[no_mangle]
pub extern "C" fn od_free_vec(vec: *const Vector) {
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);
}
}
51 changes: 51 additions & 0 deletions bindings/c/tests/basicio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 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 "stdio.h"
#include "opendal.h"

int main(int argc, char *argv[]) {
// creates a memory operator
char scheme[] = "memory";
opendal_operator_ptr ptr = od_new_operator(scheme);
if (!od_is_ptr_valid(&ptr)) {
return -1;
}

// write some contents by the operator
char path[] = "test";
char content[] = "Hello World";
const opendal_vector data = {
.len = sizeof(content) - 1,
.data = (uint8_t*)content,
};
if (!od_operator_blocking_write(ptr, path, data)) {
return -2;
}

// reads the data out from the vector
opendal_vector* v = od_operator_blocking_read(ptr, path);
for (int i = 0; i < v->len; i++) {
printf("%c", (char)(v->data[i]));
}

// free the vector's heap memory
od_free_vec(v);

return 0;
}

0 comments on commit 2fe646f

Please sign in to comment.