Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions bindings/c/include/opendal.h
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,42 @@ typedef struct opendal_copy_options {
uintptr_t chunk;
} opendal_copy_options;

/**
* \brief BlockingCopier is designed to drive a long-running copy operation in a
* blocking manner.
*
* Users can construct a copier by `opendal_operator_copier` or
* `opendal_operator_copier_with`. Each `opendal_copier_next` call drives the copy
* forward by one step and reports the number of bytes copied; the copy is complete
* once `has_next` is false.
*
* @see opendal_operator_copier()
*/
typedef struct opendal_copier {
/**
* The pointer to the opendal::blocking::Copier in the Rust code.
* Only touch this on judging whether it is NULL.
*/
void *inner;
} opendal_copier;

/**
* \brief The result type returned by opendal_operator_copier().
* The result type for opendal_operator_copier(), the field `copier` contains the copier
* used to drive a long-running copy operation. The field `error` represents whether the
* operation is successful. If successful, the `error` field is null.
*/
typedef struct opendal_result_operator_copier {
/**
* The pointer for opendal_copier
*/
struct opendal_copier *copier;
/**
* The error, if ok, it is null
*/
struct opendal_error *error;
} opendal_result_operator_copier;

/**
* \brief Metadata for **operator**, users can use this metadata to get information
* of operator.
Expand Down Expand Up @@ -1131,6 +1167,28 @@ typedef struct opendal_result_writer_write {
struct opendal_error *error;
} opendal_result_writer_write;

/**
* \brief The result type returned by opendal_copier_next().
* The result type contains a `size` field, which is the number of bytes copied in this
* step (zero on error or completion), and a `has_next` field, which is true when the copy
* made progress and should be driven again. When `has_next` is false and `error` is null,
* the copy has completed. The error field is the error code and error message.
*/
typedef struct opendal_result_copier_next {
/**
* The number of bytes copied in this step.
*/
uintptr_t size;
/**
* Whether the copy operation made progress and should be driven again.
*/
bool has_next;
/**
* The error, if ok, it is null
*/
struct opendal_error *error;
} opendal_result_copier_next;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
Expand Down Expand Up @@ -2102,6 +2160,57 @@ struct opendal_error *opendal_operator_copy_with(const struct opendal_operator *

struct opendal_error *opendal_operator_check(const struct opendal_operator *op);

/**
* \brief Blocking create a copier to copy a file from `src` to `dest`.
*
* The returned copier drives a long-running copy operation. Call
* `opendal_copier_next` repeatedly to make progress, and `opendal_copier_free`
* to release it once finished.
*
* @param op The opendal_operator created previously
* @param src The designated source path you want to copy
* @param dest The designated destination path you want to copy
* @see opendal_operator
* @see opendal_copier
* @see opendal_result_operator_copier
* @return opendal_result_operator_copier, containing a copier and an opendal_error.
* If the operation succeeds, the `copier` field holds a valid copier and the `error`
* field is null. Otherwise, the `copier` will be null and the `error` will be set
* correspondingly.
*
* # Safety
*
* It is **safe** under the cases below
* * The memory pointed to by `src` and `dest` must contain a valid nul terminator at the
* end of the string.
*
* # Panic
*
* * If the `src` or `dest` points to NULL, this function panics
*/
struct opendal_result_operator_copier opendal_operator_copier(const struct opendal_operator *op,
const char *src,
const char *dest);

/**
* \brief Blocking create a copier to copy a file from `src` to `dest` with options.
*
* This is the same as `opendal_operator_copier` but accepts an `opendal_copy_options`
* to control the behavior, e.g. `concurrent` or `chunk`. Pass NULL to use defaults.
*
* @param op The opendal_operator created previously
* @param src The designated source path you want to copy
* @param dest The designated destination path you want to copy
* @param opts The options for the copy operation; pass NULL to use defaults
* @see opendal_operator_copier
* @see opendal_copy_options
* @return opendal_result_operator_copier, containing a copier and an opendal_error.
*/
struct opendal_result_operator_copier opendal_operator_copier_with(const struct opendal_operator *op,
const char *src,
const char *dest,
const struct opendal_copy_options *opts);

/**
* \brief Get information of underlying accessor.
*
Expand Down Expand Up @@ -2685,6 +2794,30 @@ struct opendal_error *opendal_writer_close(struct opendal_writer *ptr);
*/
void opendal_writer_free(struct opendal_writer *ptr);

/**
* \brief Drive the copy operation forward by one step.
*
* Returns the number of bytes copied in this step. When `has_next` is true the
* caller should keep calling this function to drive the copy. When `has_next` is
* false and `error` is null the copy has completed.
*
* @see opendal_operator_copier()
*/
struct opendal_result_copier_next opendal_copier_next(struct opendal_copier *self);

/**
* \brief Abort the pending copy operation.
*
* Returns NULL if the abort succeeds, otherwise it contains the error code and
* error message.
*/
struct opendal_error *opendal_copier_abort(struct opendal_copier *self);

/**
* \brief Free the heap memory used by the opendal_copier.
*/
void opendal_copier_free(struct opendal_copier *ptr);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
105 changes: 105 additions & 0 deletions bindings/c/src/copier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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 core;
use std::ffi::c_void;

use super::*;

/// \brief BlockingCopier is designed to drive a long-running copy operation in a
/// blocking manner.
///
/// Users can construct a copier by `opendal_operator_copier` or
/// `opendal_operator_copier_with`. Each `opendal_copier_next` call drives the copy
/// forward by one step and reports the number of bytes copied; the copy is complete
/// once `has_next` is false.
///
/// @see opendal_operator_copier()
#[repr(C)]
pub struct opendal_copier {
/// The pointer to the opendal::blocking::Copier in the Rust code.
/// Only touch this on judging whether it is NULL.
inner: *mut c_void,
}

impl opendal_copier {
fn deref_mut(&mut self) -> &mut core::blocking::Copier {
// Safety: the inner should never be null once constructed
// The use-after-free is undefined behavior
unsafe { &mut *(self.inner as *mut core::blocking::Copier) }
}
}

impl opendal_copier {
pub(crate) fn new(copier: core::blocking::Copier) -> Self {
Self {
inner: Box::into_raw(Box::new(copier)) as _,
}
}

/// \brief Drive the copy operation forward by one step.
///
/// Returns the number of bytes copied in this step. When `has_next` is true the
/// caller should keep calling this function to drive the copy. When `has_next` is
/// false and `error` is null the copy has completed.
///
/// @see opendal_operator_copier()
#[no_mangle]
pub unsafe extern "C" fn opendal_copier_next(&mut self) -> opendal_result_copier_next {
match self.deref_mut().next() {
Some(Ok(n)) => opendal_result_copier_next {
size: n,
has_next: true,
error: std::ptr::null_mut(),
},
None => opendal_result_copier_next {
size: 0,
has_next: false,
error: std::ptr::null_mut(),
},
Some(Err(e)) => opendal_result_copier_next {
size: 0,
has_next: false,
error: opendal_error::new(e),
},
}
}

/// \brief Abort the pending copy operation.
///
/// Returns NULL if the abort succeeds, otherwise it contains the error code and
/// error message.
#[no_mangle]
pub unsafe extern "C" fn opendal_copier_abort(&mut self) -> *mut opendal_error {
if let Err(e) = self.deref_mut().abort() {
opendal_error::new(e)
} else {
std::ptr::null_mut()
}
}

/// \brief Free the heap memory used by the opendal_copier.
#[no_mangle]
pub unsafe extern "C" fn opendal_copier_free(ptr: *mut opendal_copier) {
unsafe {
if !ptr.is_null() {
drop(Box::from_raw((*ptr).inner as *mut core::blocking::Copier));
drop(Box::from_raw(ptr));
}
}
}
}
5 changes: 5 additions & 0 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ pub use presign::opendal_presigned_request;
pub use presign::opendal_result_presign;

mod result;
pub use result::opendal_result_copier_next;
pub use result::opendal_result_exists;
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_copier;
pub use result::opendal_result_operator_new;
pub use result::opendal_result_operator_reader;
pub use result::opendal_result_operator_writer;
Expand Down Expand Up @@ -89,3 +91,6 @@ pub use reader::opendal_reader;

mod writer;
pub use writer::opendal_writer;

mod copier;
pub use copier::opendal_copier;
96 changes: 96 additions & 0 deletions bindings/c/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,3 +1307,99 @@ pub unsafe extern "C" fn opendal_operator_check(op: &opendal_operator) -> *mut o
std::ptr::null_mut()
}
}

/// \brief Blocking create a copier to copy a file from `src` to `dest`.
///
/// The returned copier drives a long-running copy operation. Call
/// `opendal_copier_next` repeatedly to make progress, and `opendal_copier_free`
/// to release it once finished.
///
/// @param op The opendal_operator created previously
/// @param src The designated source path you want to copy
/// @param dest The designated destination path you want to copy
/// @see opendal_operator
/// @see opendal_copier
/// @see opendal_result_operator_copier
/// @return opendal_result_operator_copier, containing a copier and an opendal_error.
/// If the operation succeeds, the `copier` field holds a valid copier and the `error`
/// field is null. Otherwise, the `copier` will be null and the `error` will be set
/// correspondingly.
///
/// # Safety
///
/// It is **safe** under the cases below
/// * The memory pointed to by `src` and `dest` must contain a valid nul terminator at the
/// end of the string.
///
/// # Panic
///
/// * If the `src` or `dest` points to NULL, this function panics
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_copier(
op: &opendal_operator,
src: *const c_char,
dest: *const c_char,
) -> opendal_result_operator_copier {
assert!(!src.is_null());
assert!(!dest.is_null());
let src = std::ffi::CStr::from_ptr(src)
.to_str()
.expect("malformed src");
let dest = std::ffi::CStr::from_ptr(dest)
.to_str()
.expect("malformed dest");
match op.deref().copier(src, dest) {
Ok(copier) => opendal_result_operator_copier {
copier: Box::into_raw(Box::new(opendal_copier::new(copier))),
error: std::ptr::null_mut(),
},
Err(err) => opendal_result_operator_copier {
copier: std::ptr::null_mut(),
error: opendal_error::new(err),
},
}
}

/// \brief Blocking create a copier to copy a file from `src` to `dest` with options.
///
/// This is the same as `opendal_operator_copier` but accepts an `opendal_copy_options`
/// to control the behavior, e.g. `concurrent` or `chunk`. Pass NULL to use defaults.
///
/// @param op The opendal_operator created previously
/// @param src The designated source path you want to copy
/// @param dest The designated destination path you want to copy
/// @param opts The options for the copy operation; pass NULL to use defaults
/// @see opendal_operator_copier
/// @see opendal_copy_options
/// @return opendal_result_operator_copier, containing a copier and an opendal_error.
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_copier_with(
op: &opendal_operator,
src: *const c_char,
dest: *const c_char,
opts: *const opendal_copy_options,
) -> opendal_result_operator_copier {
assert!(!src.is_null());
assert!(!dest.is_null());
let src = std::ffi::CStr::from_ptr(src)
.to_str()
.expect("malformed src");
let dest = std::ffi::CStr::from_ptr(dest)
.to_str()
.expect("malformed dest");
let copy_opts = if opts.is_null() {
core::options::CopyOptions::default()
} else {
core::options::CopyOptions::from(&*opts)
};
match op.deref().copier_options(src, dest, copy_opts) {
Ok(copier) => opendal_result_operator_copier {
copier: Box::into_raw(Box::new(opendal_copier::new(copier))),
error: std::ptr::null_mut(),
},
Err(err) => opendal_result_operator_copier {
copier: std::ptr::null_mut(),
error: opendal_error::new(err),
},
}
}
Loading