You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by de...@apache.org on 2026/07/30 07:16:23 UTC
(opendal) branch main updated: feat(binding/cpp): expose positional read API (#7986)
This is an automated email from the ASF dual-hosted git repository.
dentiny pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 2fed9d89e feat(binding/cpp): expose positional read API (#7986)
2fed9d89e is described below
commit 2fed9d89ee70cd98775e444119a161587b992125
Author: dentiny <de...@gmail.com>
AuthorDate: Thu Jul 30 00:16:17 2026 -0700
feat(binding/cpp): expose positional read API (#7986)
---
bindings/cpp/include/opendal.hpp | 3 +++
bindings/cpp/src/lib.rs | 10 ++++----
bindings/cpp/src/reader.cpp | 5 ++++
bindings/cpp/src/reader.rs | 54 +++++++++++++++++++++++++++++++--------
bindings/cpp/tests/basic_test.cpp | 8 ++++++
5 files changed, 64 insertions(+), 16 deletions(-)
diff --git a/bindings/cpp/include/opendal.hpp b/bindings/cpp/include/opendal.hpp
index 64447973f..7685956ba 100644
--- a/bindings/cpp/include/opendal.hpp
+++ b/bindings/cpp/include/opendal.hpp
@@ -19,6 +19,7 @@
#pragma once
+#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
@@ -196,6 +197,8 @@ class Reader {
std::streamsize Read(void *s, std::streamsize n);
+ std::streamsize ReadAt(void *s, std::streamsize n, uint64_t offset);
+
std::streampos Seek(std::streamoff off, std::ios_base::seekdir way);
private:
diff --git a/bindings/cpp/src/lib.rs b/bindings/cpp/src/lib.rs
index 737276ce7..6a8d219c3 100644
--- a/bindings/cpp/src/lib.rs
+++ b/bindings/cpp/src/lib.rs
@@ -176,7 +176,8 @@ mod ffi {
unsafe fn delete_reader(reader: *mut Reader);
fn read(self: &mut Reader, buf: &mut [u8]) -> Result<usize>;
- fn seek(self: &mut Reader, offset: u64, dir: SeekFrom) -> Result<u64>;
+ fn read_at(self: &Reader, buf: &mut [u8], offset: u64) -> Result<usize>;
+ fn seek(self: &mut Reader, offset: i64, dir: SeekFrom) -> Result<u64>;
unsafe fn delete_writer(writer: *mut Writer);
fn write(self: &mut Writer, bs: Vec<u8>) -> Result<()>;
@@ -317,10 +318,9 @@ impl Operator {
fn reader(&self, path: &str) -> Result<*mut Reader> {
let meta = self.0.stat(path)?;
- let reader = Box::into_raw(Box::new(Reader(
- self.0
- .reader(path)?
- .into_std_read(0..meta.content_length())?,
+ let reader = Box::into_raw(Box::new(Reader::new(
+ self.0.reader(path)?,
+ meta.content_length(),
)));
Ok(reader)
}
diff --git a/bindings/cpp/src/reader.cpp b/bindings/cpp/src/reader.cpp
index fa980c902..9ddfca1f1 100644
--- a/bindings/cpp/src/reader.cpp
+++ b/bindings/cpp/src/reader.cpp
@@ -41,6 +41,11 @@ std::streamsize Reader::Read(void *s, std::streamsize n) {
return reader_->read(rust::Slice<uint8_t>(static_cast<uint8_t *>(s), n));
}
+std::streamsize Reader::ReadAt(void *s, std::streamsize n, uint64_t offset) {
+ return reader_->read_at(rust::Slice<uint8_t>(static_cast<uint8_t *>(s), n),
+ offset);
+}
+
ffi::SeekDir rust_seek_dir(std::ios_base::seekdir dir) {
switch (dir) {
case std::ios_base::beg:
diff --git a/bindings/cpp/src/reader.rs b/bindings/cpp/src/reader.rs
index ed7d48fff..e042a864f 100644
--- a/bindings/cpp/src/reader.rs
+++ b/bindings/cpp/src/reader.rs
@@ -15,30 +15,62 @@
// specific language governing permissions and limitations
// under the License.
-use std::io::Read;
-use std::io::Seek;
-
use anyhow::Result;
use opendal as od;
use super::ffi;
-pub struct Reader(pub od::blocking::StdReader);
+pub struct Reader {
+ reader: od::blocking::Reader,
+ position: u64,
+ content_length: u64,
+}
impl Reader {
+ pub fn new(reader: od::blocking::Reader, content_length: u64) -> Self {
+ Self {
+ reader,
+ position: 0,
+ content_length,
+ }
+ }
+
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
- let n = self.0.read(buf)?;
+ if self.position >= self.content_length || buf.is_empty() {
+ return Ok(0);
+ }
+
+ let remaining = self.content_length - self.position;
+ let len = u64::try_from(buf.len())?.min(remaining);
+ let n = self.read_at(&mut buf[..usize::try_from(len)?], self.position)?;
+ self.position = self
+ .position
+ .checked_add(u64::try_from(n)?)
+ .ok_or_else(|| anyhow::anyhow!("reader position overflow"))?;
Ok(n)
}
- pub fn seek(&mut self, offset: u64, dir: ffi::SeekFrom) -> Result<u64> {
- let pos = match dir {
- ffi::SeekFrom::Start => std::io::SeekFrom::Start(offset),
- ffi::SeekFrom::Current => std::io::SeekFrom::Current(offset as i64),
- ffi::SeekFrom::End => std::io::SeekFrom::End(offset as i64),
+ pub fn read_at(&self, mut buf: &mut [u8], offset: u64) -> Result<usize> {
+ let len = u64::try_from(buf.len())?;
+ let end = offset
+ .checked_add(len)
+ .ok_or_else(|| anyhow::anyhow!("read range end overflow"))?;
+ Ok(self.reader.read_into(&mut buf, offset..end)?)
+ }
+
+ pub fn seek(&mut self, offset: i64, dir: ffi::SeekFrom) -> Result<u64> {
+ let base = match dir {
+ ffi::SeekFrom::Start => 0,
+ ffi::SeekFrom::Current => i128::from(self.position),
+ ffi::SeekFrom::End => i128::from(self.content_length),
_ => return Err(anyhow::anyhow!("invalid seek dir")),
};
+ let pos = base + i128::from(offset);
+ if pos < 0 {
+ return Err(anyhow::anyhow!("invalid seek to negative position"));
+ }
- Ok(self.0.seek(pos)?)
+ self.position = u64::try_from(pos)?;
+ Ok(self.position)
}
}
diff --git a/bindings/cpp/tests/basic_test.cpp b/bindings/cpp/tests/basic_test.cpp
index 84c9bcd37..e316d85ed 100644
--- a/bindings/cpp/tests/basic_test.cpp
+++ b/bindings/cpp/tests/basic_test.cpp
@@ -114,6 +114,14 @@ OPENDAL_TEST_F(OpenDALTest, ReaderTest) {
for (int i = 0; i < 100; ++i) {
EXPECT_EQ(part_data[i], data[200 + i]);
}
+
+ std::string positional_data(100, 0);
+ auto positional_read_size = reader.ReadAt(positional_data.data(), /*size=*/ 100, /*offset=*/500);
+ EXPECT_EQ(positional_read_size, 100);
+ EXPECT_EQ(reader.Seek(0, std::ios::cur), 300);
+ for (int i = 0; i < 100; ++i) {
+ EXPECT_EQ(positional_data[i], data[500 + i]);
+ }
reader.Seek(0, std::ios::beg);
// reader stream