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
112 changes: 112 additions & 0 deletions bindings/c/include/opendal.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,68 @@ typedef struct opendal_result_operator_writer {
struct opendal_error *error;
} opendal_result_operator_writer;

/**
* \brief Options for read operations used by C side.
*
* \note For detail description of each field, please refer to [`core::ReadOptions`]
*/
typedef struct opendal_operator_options_read {
/**
* Set `range` for this operation.
*/
const uint64_t *range;
/**
* Set `version` for this operation.
*/
const char *version;
/**
* Set `if_match` for this operation.
*/
const char *if_match;
/**
* Set `if_none_match` for this operation.
*/
const char *if_none_match;
/**
* Set `if_modified_since` for this operation.
*
* \note The value should be in RFC 3339 format.
*/
const char *if_modified_since;
/**
* Set `if_unmodified_since` for this operation.
*
* \note The value should be in RFC 3339 format.
*/
const char *if_unmodified_since;
/**
* Set `concurrent` for the operation.
*
* \note for we do not provide default value in C, so it must be Option in C side.
*/
const uintptr_t *concurrent;
/**
* Set `chunk` for the operation.
*/
const uintptr_t *chunk;
/**
* Controls the optimization strategy for range reads in [`Reader::fetch`].
*/
const uintptr_t *gap;
/**
* Specify the content-type header that should be sent back by the operation.
*/
const char *override_content_type;
/**
* Specify the `cache-control` header that should be sent back by the operation.
*/
const char *override_cache_control;
/**
* Specify the `content-disposition` header that should be sent back by the operation.
*/
const char *override_content_disposition;
} opendal_operator_options_read;

/**
* \brief The result type returned by opendal_operator_is_exist().
*
Expand Down Expand Up @@ -963,6 +1025,56 @@ struct opendal_result_operator_reader opendal_operator_reader(const struct opend
struct opendal_result_operator_writer opendal_operator_writer(const struct opendal_operator *op,
const char *path);

/**
* \brief Blocking read the data from `path` with additional options.
*
* Read the data out from `path` blocking by operator with additional options.
*
* @param op The opendal_operator created previously
* @param path The path you want to read the data out
* @param opts The options for read operations
* @see opendal_operator
* @see opendal_result_read
* @see opendal_operator_options_read
* @see opendal_error
* @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated
* opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then
* the operation failed and the `data` field is a nullptr.
*
* \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap.
* After your usage of that, please call opendal_bytes_free() to free the space.
*
* # Example
*
* Following is an example
* ```C
* // ... you have either write "Hello, World!" to path "/testpath" and created an operator named op
*
* opendal_operator_options_read opts = {};
* uint64_t range[2] = {0, 14};
* opts.range = range;
* opendal_result_read r = opendal_operator_read_options(op, "testpath", opts);
* assert(r.error == NULL);
*
* opendal_bytes bytes = r.data;
* assert(bytes.len == 13);
* opendal_bytes_free(&bytes);
* ```
*
* # Safety
*
* It is **safe** under the cases below
* * The memory pointed to by `path` must contain a valid nul terminator at the end of
* the string.
*
* # Panic
*
* * If the `path` points to NULL, this function panics, i.e. exits with information
*/
struct opendal_result_read opendal_operator_read_options(const struct opendal_operator *op,
const char *path,
const struct opendal_operator_options_read *opts);

/**
* \brief Blocking delete the object in `path`.
*
Expand Down
3 changes: 3 additions & 0 deletions bindings/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ pub use reader::opendal_reader;

mod writer;
pub use writer::opendal_writer;

mod options;
pub use options::opendal_operator_options_read;
76 changes: 76 additions & 0 deletions bindings/c/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,82 @@ pub unsafe extern "C" fn opendal_operator_writer(
}
}

/// \brief Blocking read the data from `path` with additional options.
///
/// Read the data out from `path` blocking by operator with additional options.
///
/// @param op The opendal_operator created previously
/// @param path The path you want to read the data out
/// @param opts The options for read operations
/// @see opendal_operator
/// @see opendal_result_read
/// @see opendal_operator_options_read
/// @see opendal_error
/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated
/// opendal_bytes, the `error` field contains the error. If the `error` is not NULL, then
/// the operation failed and the `data` field is a nullptr.
///
/// \note If the read operation succeeds, the returned opendal_bytes is newly allocated on heap.
/// After your usage of that, please call opendal_bytes_free() to free the space.
///
/// # Example
///
/// Following is an example
/// ```C
/// // ... you have either write "Hello, World!" to path "/testpath" and created an operator named op
///
/// opendal_operator_options_read opts = {};
/// uint64_t range[2] = {0, 14};
/// opts.range = range;
/// opendal_result_read r = opendal_operator_read_options(op, "testpath", opts);
/// assert(r.error == NULL);
///
/// opendal_bytes bytes = r.data;
/// assert(bytes.len == 13);
/// opendal_bytes_free(&bytes);
/// ```
///
/// # Safety
///
/// It is **safe** under the cases below
/// * The memory pointed to by `path` must contain a valid nul terminator at the end of
/// the string.
///
/// # Panic
///
/// * If the `path` points to NULL, this function panics, i.e. exits with information
#[no_mangle]
pub unsafe extern "C" fn opendal_operator_read_options(
op: &opendal_operator,
path: *const c_char,
opts: *const opendal_operator_options_read,
) -> opendal_result_read {
assert!(!path.is_null());
let path = std::ffi::CStr::from_ptr(path)
.to_str()
.expect("malformed path");
let opts = match options::parse_read_options(opts) {
Ok(opts) => opts,
Err(e) => {
return opendal_result_read {
data: opendal_bytes::empty(),
error: opendal_error::new(e),
};
}
};

match op.deref().read_options(path, opts) {
Ok(b) => opendal_result_read {
data: opendal_bytes::new(b),
error: std::ptr::null_mut(),
},
Err(e) => opendal_result_read {
data: opendal_bytes::empty(),
error: opendal_error::new(e),
},
}
}

/// \brief Blocking delete the object in `path`.
///
/// Delete the object in `path` blocking by `op_ptr`.
Expand Down
158 changes: 158 additions & 0 deletions bindings/c/src/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// 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 opendal::{
options::ReadOptions,
raw::{BytesRange, Timestamp},
Error,
};
use std::{os::raw::c_char, str::FromStr};

/// \brief Options for read operations used by C side.
///
/// \note For detail description of each field, please refer to [`core::ReadOptions`]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct opendal_operator_options_read {
/// Set `range` for this operation.
pub range: *const u64,
/// Set `version` for this operation.
pub version: *const c_char,
/// Set `if_match` for this operation.
pub if_match: *const c_char,
/// Set `if_none_match` for this operation.
pub if_none_match: *const c_char,
/// Set `if_modified_since` for this operation.
///
/// \note The value should be in RFC 3339 format.
pub if_modified_since: *const c_char,
/// Set `if_unmodified_since` for this operation.
///
/// \note The value should be in RFC 3339 format.
pub if_unmodified_since: *const c_char,
/// Set `concurrent` for the operation.
///
/// \note for we do not provide default value in C, so it must be Option in C side.
pub concurrent: *const usize,
/// Set `chunk` for the operation.
pub chunk: *const usize,
/// Controls the optimization strategy for range reads in [`Reader::fetch`].
pub gap: *const usize,
/// Specify the content-type header that should be sent back by the operation.
pub override_content_type: *const c_char,
/// Specify the `cache-control` header that should be sent back by the operation.
pub override_cache_control: *const c_char,
/// Specify the `content-disposition` header that should be sent back by the operation.
pub override_content_disposition: *const c_char,
}

impl opendal_operator_options_read {}

pub fn parse_read_options(
options: *const opendal_operator_options_read,
) -> Result<core::options::ReadOptions, Error> {
// if original opts is blank, we will use the default options
let mut opts = ReadOptions::default();

unsafe {
let options = *options;
if !options.range.is_null() {
// TODO:
// Do we need to make sure it has no more than 2 usize?
let range = std::slice::from_raw_parts(options.range, 2);
opts.range = BytesRange::new(range[0], Some(range[1]));
}
if !options.version.is_null() {
opts.version = Some(
std::ffi::CStr::from_ptr(options.version)
.to_str()
.expect("malformed version")
.to_string(),
);
}
if !options.if_match.is_null() {
opts.if_match = Some(
std::ffi::CStr::from_ptr(options.if_match)
.to_str()
.expect("malformed if_match")
.to_string(),
);
}
if !options.if_none_match.is_null() {
opts.if_none_match = Some(
std::ffi::CStr::from_ptr(options.if_none_match)
.to_str()
.expect("malformed if_none_match")
.to_string(),
);
}
if !options.if_modified_since.is_null() {
let ts_str = std::ffi::CStr::from_ptr(options.if_modified_since)
.to_str()
.expect("malformed if_modified_since")
.to_string();

let ts = Timestamp::from_str(&ts_str)?;
opts.if_modified_since = Some(ts);
}
if !options.if_unmodified_since.is_null() {
let ts_str = std::ffi::CStr::from_ptr(options.if_unmodified_since)
.to_str()
.expect("malformed if_unmodified_since")
.to_string();

let ts = Timestamp::from_str(&ts_str)?;
opts.if_unmodified_since = Some(ts);
}
if !options.concurrent.is_null() {
opts.concurrent = *options.concurrent;
}
if !options.chunk.is_null() {
opts.chunk = Some(*options.chunk);
}
if !options.gap.is_null() {
opts.gap = Some(*options.gap);
}
if !options.override_content_type.is_null() {
opts.override_content_type = Some(
std::ffi::CStr::from_ptr(options.override_content_type)
.to_str()
.expect("malformed override_content_type")
.to_string(),
);
}
if !options.override_cache_control.is_null() {
opts.override_cache_control = Some(
std::ffi::CStr::from_ptr(options.override_cache_control)
.to_str()
.expect("malformed override_cache_control")
.to_string(),
);
}
if !options.override_content_disposition.is_null() {
opts.override_content_disposition = Some(
std::ffi::CStr::from_ptr(options.override_content_disposition)
.to_str()
.expect("malformed override_content_disposition")
.to_string(),
);
}
}

Ok(opts)
}
2 changes: 1 addition & 1 deletion bindings/c/tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ endif

# Source files
FRAMEWORK_SOURCES = test_framework.cpp
SUITE_SOURCES = test_suites_basic.cpp test_suites_list.cpp test_suites_reader_writer.cpp
SUITE_SOURCES = test_suites_basic.cpp test_suites_list.cpp test_suites_reader_writer.cpp test_suites_options.cpp
RUNNER_SOURCES = test_runner.cpp
ALL_SOURCES = $(FRAMEWORK_SOURCES) $(SUITE_SOURCES) $(RUNNER_SOURCES)

Expand Down
Loading
Loading