You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by yu...@apache.org on 2025/04/15 05:10:45 UTC

(opendal) 01/01: feat(bindings/c): add support for read_range

This is an automated email from the ASF dual-hosted git repository.

yuchanns pushed a commit to branch binding-go-reader-at
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit ec3a616d67fa2eadb7babe1b9ab0843d49c1e078
Author: Hanchin Hsieh <me...@yuchanns.xyz>
AuthorDate: Tue Apr 15 13:10:31 2025 +0800

    feat(bindings/c): add support for read_range
---
 bindings/c/README.md         | 17 +++++++++++
 bindings/c/include/opendal.h | 50 +++++++++++++++++++++++++++++++
 bindings/c/src/operator.rs   | 71 ++++++++++++++++++++++++++++++++++++++++++++
 bindings/c/tests/bdd.cpp     |  8 +++++
 4 files changed, 146 insertions(+)

diff --git a/bindings/c/README.md b/bindings/c/README.md
index abd7d97f3..e382f3ec7 100644
--- a/bindings/c/README.md
+++ b/bindings/c/README.md
@@ -84,6 +84,23 @@ sudo apt-get install cmake
 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
 ```
 
+For Fedora:
+
+```shell
+# install C/C++ toolchain
+sudo dnf groupinstall "Development Tools" "Development Libraries"
+sudo dnf install gcc-c++
+
+# install clang-format
+sudo dnf install clang-tools-extra
+
+# install and build GTest library
+sudo dnf install gtest-devel
+
+# install CMake 
+sudo dnf install cmake
+```
+
 ## Makefile
 
 - To **build the library and header file**.
diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 73953d04e..e5b7f02d6 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -863,6 +863,56 @@ struct opendal_error *opendal_operator_write(const struct opendal_operator *op,
 struct opendal_result_read opendal_operator_read(const struct opendal_operator *op,
                                                  const char *path);
 
+/**
+ * \brief Blocking read a range of data from `path`.
+ *
+ * Read a range of data from `path` blocking by operator. The range starts from `offset`
+ * with size of `size`.
+ *
+ * @param op The opendal_operator created previously
+ * @param path The path you want to read the data out
+ * @param offset The start position of the range to read
+ * @param size The size of the range to read
+ * @see opendal_operator
+ * @see opendal_result_read
+ * @see opendal_error
+ * @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated
+ * opendal_bytes containing the range of data, the `error` field contains the error. If the
+ * `error` is not NULL, then the operation failed and the `data` field contains empty bytes.
+ *
+ * \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 write "Hello, World!" to path "/testpath"
+ *
+ * // Read 5 bytes starting from offset 0
+ * opendal_result_read r = opendal_operator_read_range(op, "/testpath", 0, 5);
+ * assert(r.error == NULL);
+ *
+ * opendal_bytes bytes = r.data;
+ * assert(bytes.len == 5);
+ * 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_range(const struct opendal_operator *op,
+                                                       const char *path,
+                                                       uint64_t offset,
+                                                       uint64_t size);
+
 /**
  * \brief Blocking read the data from `path`.
  *
diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs
index 84751fc49..64e86ced2 100644
--- a/bindings/c/src/operator.rs
+++ b/bindings/c/src/operator.rs
@@ -295,6 +295,77 @@ pub unsafe extern "C" fn opendal_operator_read(
     }
 }
 
+/// \brief Blocking read a range of data from `path`.
+///
+/// Read a range of data from `path` blocking by operator. The range starts from `offset` 
+/// with size of `size`.
+///
+/// @param op The opendal_operator created previously
+/// @param path The path you want to read the data out
+/// @param offset The start position of the range to read
+/// @param size The size of the range to read
+/// @see opendal_operator
+/// @see opendal_result_read
+/// @see opendal_error
+/// @return Returns opendal_result_read, the `data` field is a pointer to a newly allocated
+/// opendal_bytes containing the range of data, the `error` field contains the error. If the
+/// `error` is not NULL, then the operation failed and the `data` field contains empty bytes.
+///
+/// \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 write "Hello, World!" to path "/testpath"
+///
+/// // Read 5 bytes starting from offset 0
+/// opendal_result_read r = opendal_operator_read_range(op, "/testpath", 0, 5);
+/// assert(r.error == NULL);
+///
+/// opendal_bytes bytes = r.data;
+/// assert(bytes.len == 5);
+/// 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_range(
+    op: &opendal_operator,
+    path: *const c_char,
+    offset: u64,
+    size: u64,
+) -> opendal_result_read {
+    assert!(!path.is_null());
+    let path = std::ffi::CStr::from_ptr(path)
+        .to_str()
+        .expect("malformed path");
+    match op
+        .deref()
+        .read_with(path)
+        .range(offset..offset + size)
+        .call()
+    {
+        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 read the data from `path`.
 ///
 /// Read the data out from `path` blocking by operator, returns
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index 93b514845..1957ed721 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -93,6 +93,14 @@ TEST_F(OpendalBddTest, FeatureTest)
         EXPECT_EQ(this->content[i], (char)(r.data.data[i]));
     }
 
+    // The blocking file "test" must have content "Hello"
+    struct opendal_result_read r2 = opendal_operator_read_range(this->p, this->path.c_str(), 0, 5);
+    EXPECT_EQ(r2.error, nullptr);
+    EXPECT_EQ(r2.data.len, 5);
+    for (int i = 0; i < r2.data.len; i++) {
+        EXPECT_EQ(this->content[i], (char)(r2.data.data[i]));
+    }
+
     // The blocking file should be deleted
     error = opendal_operator_delete(this->p, this->path.c_str());
     EXPECT_EQ(error, nullptr);