diff --git a/include/miniocpp/c_api.h b/include/miniocpp/c_api.h index b2c02853..64dd67d3 100644 --- a/include/miniocpp/c_api.h +++ b/include/miniocpp/c_api.h @@ -22,6 +22,7 @@ #ifdef MINIO_CPP_RDMA #include +#include #include // ssize_t #ifdef __cplusplus @@ -82,6 +83,25 @@ MINIOCPP_API ssize_t miniocpp_get_object(miniocpp_client* client, miniocpp_write_cb write_cb, void* userdata); +// Ranged GET: read `size` bytes starting at `offset` in the object into `buf`. +// Transport behaviour matches miniocpp_get_object (RDMA into the caller's +// buffer, HTTP-into-buf on decline); AIStor answers a ranged RDMA transfer with +// x-amz-rdma-reply: 206. +// +// GetObjectArgs already carries an offset and Client::GetObject already turns +// it into a ranged RDMA GET, but the C ABI had no way to set it, so bindings +// could only ever fetch whole objects. Two things that needs: +// +// - reading part of a large object without transferring all of it; +// - letting several threads cooperate on one buffer, each filling a disjoint +// window of it, instead of every thread needing a buffer of its own. +// +// Returns bytes transferred, or MINIOCPP_ERR_*. `buf` is required. +MINIOCPP_API ssize_t miniocpp_get_object_range(miniocpp_client* client, + const char* bucket, + const char* object, void* buf, + size_t size, uint64_t offset); + // Page-aligned host allocator suitable for RDMA registration. Caller must // release with miniocpp_free_aligned. Returns NULL on allocation failure. MINIOCPP_API void* miniocpp_alloc_aligned(size_t size); diff --git a/src/c_api.cc b/src/c_api.cc index 7cc99059..b00f5b48 100644 --- a/src/c_api.cc +++ b/src/c_api.cc @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -145,9 +146,14 @@ ssize_t miniocpp_put_object(miniocpp_client* c, const char* bucket, return static_cast(size); } -ssize_t miniocpp_get_object(miniocpp_client* c, const char* bucket, - const char* object, void* buf, size_t size, - miniocpp_write_cb write_cb, void* userdata) { +namespace { + +// Body shared by miniocpp_get_object and miniocpp_get_object_range. +// `offset` < 0 reads the whole object; >= 0 selects a byte range. +ssize_t GetObjectImpl(miniocpp_client* c, const char* bucket, + const char* object, void* buf, size_t size, + miniocpp_write_cb write_cb, void* userdata, + int64_t offset) { if (c == nullptr || bucket == nullptr || object == nullptr) { SetLastError("client, bucket, object are required"); return MINIOCPP_ERR_INVALID_ARG; @@ -162,6 +168,7 @@ ssize_t miniocpp_get_object(miniocpp_client* c, const char* bucket, args.bucket = bucket; args.object = object; args.region = holder->base_url.region; + if (offset >= 0) args.offset = static_cast(offset); ssize_t bytes_seen = 0; @@ -186,6 +193,31 @@ ssize_t miniocpp_get_object(miniocpp_client* c, const char* bucket, return buf != nullptr ? static_cast(size) : bytes_seen; } +} // namespace + +ssize_t miniocpp_get_object(miniocpp_client* c, const char* bucket, + const char* object, void* buf, size_t size, + miniocpp_write_cb write_cb, void* userdata) { + return GetObjectImpl(c, bucket, object, buf, size, write_cb, userdata, -1); +} + +ssize_t miniocpp_get_object_range(miniocpp_client* c, const char* bucket, + const char* object, void* buf, size_t size, + uint64_t offset) { + if (buf == nullptr) { + SetLastError("buf is required for a ranged get"); + return MINIOCPP_ERR_INVALID_ARG; + } + // GetObjectArgs::offset is a size_t and the range header is built from a + // signed value; refuse anything that would not survive the round trip. + if (offset > static_cast(INT64_MAX)) { + SetLastError("offset out of range"); + return MINIOCPP_ERR_INVALID_ARG; + } + return GetObjectImpl(c, bucket, object, buf, size, nullptr, nullptr, + static_cast(offset)); +} + void* miniocpp_alloc_aligned(size_t size) { void* p = nullptr; if (posix_memalign(&p, static_cast(getpagesize()), size) != 0) {