14#include <XrdCl/XrdClFile.hh>
15#include <XrdCl/XrdClXRootDResponses.hh>
16#include <condition_variable>
28constexpr size_t kMaxVectorChunk = 2097136;
29constexpr size_t kMaxVectorChunks = 1024;
30constexpr size_t kMaxPlainRead = 64 * 1024 * 1024;
37 explicit Completion(
size_t pending) : mPending(pending) {}
41 std::lock_guard lock{mMutex};
42 if (!
error.empty() && mError.empty()) {
43 mError = std::move(
error);
45 if (--mPending == 0) {
54 std::unique_lock lock{mMutex};
55 mDone.wait(lock, [
this] {
return mPending == 0; });
61 std::condition_variable mDone;
67class Reply :
public XrdCl::ResponseHandler
70 Reply(Completion& completion,
size_t expected) : mCompletion(completion), mExpected(
expected) {}
72 void HandleResponse(XrdCl::XRootDStatus* status, XrdCl::AnyObject* response)
override
75 if (!status->IsOK()) {
76 error = status->ToString();
77 }
else if (
auto got =
received(response); got != mExpected) {
82 mCompletion.done(std::move(
error));
87 static size_t received(XrdCl::AnyObject* response)
92 XrdCl::ChunkInfo* chunk =
nullptr;
97 XrdCl::VectorReadInfo*
vector =
nullptr;
98 response->Get(vector);
102 Completion& mCompletion;
119 : mName(
url.substr(0,
url.find(
'?'))), mFile(
std::make_unique<
XrdCl::File>())
121 auto status = mFile->Open(
url, XrdCl::OpenFlags::Read);
122 if (!status.IsOK()) {
123 throw IoError(mName, status.ToString());
125 XrdCl::StatInfo* info =
nullptr;
126 status = mFile->Stat(
false, info);
127 if (!status.IsOK() || !info) {
129 throw IoError(mName,
"stat failed: " + status.ToString());
131 mSize = info->GetSize();
137 if (mFile->IsOpen()) {
138 [[maybe_unused]]
auto status = mFile->Close();
142void XRootDByteSource::checkRange(uint64_t off,
size_t len)
const
144 if (off > mSize ||
len > mSize - off) {
157 std::vector<PlainRead> plain;
158 std::vector<VectorRead> vectors;
160 for (
auto const&
r : requests) {
161 checkRange(
r.offset,
r.length);
163 if (
r.length > kMaxVectorChunk) {
164 for (
size_t done = 0;
done <
r.length;
done += kMaxPlainRead) {
165 auto n = std::min(kMaxPlainRead,
r.length -
done);
166 plain.push_back({
r.offset +
done, (uint32_t)
n, (uint8_t*)
r.dst +
done});
173 if (vectors.empty() || vectors.back().chunks.size() == kMaxVectorChunks) {
174 vectors.emplace_back();
176 vectors.back().chunks.emplace_back(
r.offset, (uint32_t)
r.length,
r.dst);
177 vectors.back().bytes +=
r.length;
180 for (
auto&
v : vectors) {
181 if (
v.chunks.size() == 1) {
182 auto&
c =
v.chunks.front();
183 plain.push_back({
c.offset,
c.length,
c.buffer});
187 std::erase_if(vectors, [](VectorRead
const&
v) {
return v.chunks.empty(); });
188 if (plain.empty() && vectors.empty()) {
192 Completion completion{plain.size() + vectors.size()};
193 for (
auto const& p : plain) {
194 auto* reply =
new Reply(completion, p.length);
195 auto status = mFile->Read(p.offset, p.length, p.dst, reply);
196 if (!status.IsOK()) {
198 completion.done(status.ToString());
201 for (
auto const&
v : vectors) {
202 auto* reply =
new Reply(completion,
v.bytes);
203 auto status = mFile->VectorRead(
v.chunks,
nullptr, reply);
204 if (!status.IsOK()) {
206 completion.done(status.ToString());
209 auto error = completion.wait();
210 if (!
error.empty()) {
213 mReadCalls.fetch_add(plain.size() + vectors.size(), std::memory_order_relaxed);
214 mBytesRead.fetch_add(total, std::memory_order_relaxed);
char error[CURL_ERROR_SIZE]
~XRootDByteSource() override
std::string url() const override
Without the query, which may hold an access token.
void readvInto(std::span< ReadRequest > requests) override
XRootDByteSource(std::string url, Options const &options)
void readInto(uint64_t off, size_t len, void *dst) override
Throws IoError, or CorruptData when the range is past the end of the file.
GLuint GLsizei GLsizei * length
GLenum GLenum GLsizei len
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
std::string to_string(gsl::span< T, Size > span)
Failure of the underlying transport.
std::map< std::string, ID > expected