From 0b96b866d54723ae1329524f3aed75e3b72455d3 Mon Sep 17 00:00:00 2001
From: Jakob Blomer <jblomer@cern.ch>
Date: Sat, 12 Oct 2019 17:53:18 +0200
Subject: [PATCH] [rawfile, davix] add support multi-range HTTP queries

---
 io/io/inc/ROOT/RRawFile.hxx          |  7 +++++--
 io/io/src/RRawFile.cxx               | 14 +++++++++++---
 net/davix/inc/ROOT/RRawFileDavix.hxx |  1 +
 net/davix/src/RRawFileDavix.cxx      | 22 ++++++++++++++++++++++
 net/davix/test/RRawFileDavix.cxx     | 24 ++++++++++++++++++++++++
 5 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/io/io/inc/ROOT/RRawFile.hxx b/io/io/inc/ROOT/RRawFile.hxx
index f7f35b893fc..1cf7849c9c8 100644
--- a/io/io/inc/ROOT/RRawFile.hxx
+++ b/io/io/inc/ROOT/RRawFile.hxx
@@ -133,6 +133,9 @@ protected:
    /// Derived classes with mmap support must be able to unmap the memory area handed out by Map()
    virtual void DoUnmap(void *region, size_t nbytes);
 
+   /// By default implemented as a loop of ReadAt calls but can be overwritten, e.g. XRootD or DAVIX implementations
+   virtual void DoReadV(RIOVec *ioVec, unsigned int nReq);
+
 public:
    RRawFile(std::string_view url, ROptions options);
    RRawFile(const RRawFile &) = delete;
@@ -161,8 +164,8 @@ public:
    /// Returns the size of the file
    std::uint64_t GetSize();
 
-   /// By default implemented as a loop of ReadAt calls but can be overwritten, e.g. XRootD or DAVIX implementations
-   virtual void ReadV(RIOVec *ioVec, unsigned int nReq);
+   /// Opens the file if necessary and calls DoReadV
+   void ReadV(RIOVec *ioVec, unsigned int nReq);
 
    /// Memory mapping according to POSIX standard; in particular, new mappings of the same range replace older ones.
    /// Mappings need to be aligned at page boundaries, therefore the real offset can be smaller than the desired value.
diff --git a/io/io/src/RRawFile.cxx b/io/io/src/RRawFile.cxx
index 375aecf8886..66f335f00e7 100644
--- a/io/io/src/RRawFile.cxx
+++ b/io/io/src/RRawFile.cxx
@@ -98,6 +98,13 @@ void *ROOT::Experimental::Detail::RRawFile::DoMap(size_t /* nbytes */, std::uint
    throw std::runtime_error("Memory mapping unsupported");
 }
 
+void ROOT::Experimental::Detail::RRawFile::DoReadV(RIOVec *ioVec, unsigned int nReq)
+{
+   for (unsigned i = 0; i < nReq; ++i) {
+      ioVec[i].fOutBytes = ReadAt(ioVec[i].fBuffer, ioVec[i].fSize, ioVec[i].fOffset);
+   }
+}
+
 void ROOT::Experimental::Detail::RRawFile::DoUnmap(void * /* region */, size_t /* nbytes */)
 {
    throw std::runtime_error("Memory mapping unsupported");
@@ -195,9 +202,10 @@ size_t ROOT::Experimental::Detail::RRawFile::ReadAt(void *buffer, size_t nbytes,
 
 void ROOT::Experimental::Detail::RRawFile::ReadV(RIOVec *ioVec, unsigned int nReq)
 {
-   for (unsigned i = 0; i < nReq; ++i) {
-      ioVec[i].fOutBytes = ReadAt(ioVec[i].fBuffer, ioVec[i].fSize, ioVec[i].fOffset);
-   }
+   if (!fIsOpen)
+      DoOpen();
+   fIsOpen = true;
+   DoReadV(ioVec, nReq);
 }
 
 bool ROOT::Experimental::Detail::RRawFile::Readln(std::string &line)
diff --git a/net/davix/inc/ROOT/RRawFileDavix.hxx b/net/davix/inc/ROOT/RRawFileDavix.hxx
index c1d42adcd7c..d413c851d19 100644
--- a/net/davix/inc/ROOT/RRawFileDavix.hxx
+++ b/net/davix/inc/ROOT/RRawFileDavix.hxx
@@ -41,6 +41,7 @@ private:
 protected:
    void DoOpen() final;
    size_t DoReadAt(void *buffer, size_t nbytes, std::uint64_t offset) final;
+   void DoReadV(RIOVec *ioVec, unsigned int nReq) final;
    std::uint64_t DoGetSize() final;
 
 public:
diff --git a/net/davix/src/RRawFileDavix.cxx b/net/davix/src/RRawFileDavix.cxx
index 677a0b531ce..81e20e79850 100644
--- a/net/davix/src/RRawFileDavix.cxx
+++ b/net/davix/src/RRawFileDavix.cxx
@@ -88,3 +88,25 @@ size_t ROOT::Experimental::Detail::RRawFileDavix::DoReadAt(void *buffer, size_t
    }
    return static_cast<size_t>(retval);
 }
+
+void ROOT::Experimental::Detail::RRawFileDavix::DoReadV(RIOVec *ioVec, unsigned int nReq)
+{
+   Davix::DavixError *davixErr = NULL;
+   Davix::DavIOVecInput in[nReq];
+   Davix::DavIOVecOuput out[nReq];
+
+   for (unsigned int i = 0; i < nReq; ++i) {
+      in[i].diov_buffer = ioVec[i].fBuffer;
+      in[i].diov_offset = ioVec[i].fOffset;
+      in[i].diov_size = ioVec[i].fSize;
+   }
+
+   auto ret = fFileDes->pos.preadVec(fFileDes->fd, in, out, nReq, &davixErr);
+   if (ret < 0) {
+      throw std::runtime_error("Cannot do vector read from '" + fUrl + "', error: " + davixErr->getErrMsg());
+   }
+
+   for (unsigned int i = 0; i < nReq; ++i) {
+      ioVec[i].fOutBytes = out[i].diov_size;
+   }
+}
diff --git a/net/davix/test/RRawFileDavix.cxx b/net/davix/test/RRawFileDavix.cxx
index 2b84cd2c722..ec376e7a185 100644
--- a/net/davix/test/RRawFileDavix.cxx
+++ b/net/davix/test/RRawFileDavix.cxx
@@ -38,3 +38,27 @@ TEST(RRawFileDavix, Eof)
    EXPECT_EQ(3u, nbytes);
    EXPECT_STREQ("ld\n", tail);
 }
+
+
+TEST(RRawFileDavix, ReadV)
+{
+   RRawFile::ROptions options;
+   options.fBlockSize = 0;
+   std::unique_ptr<RRawFileDavix> f(new RRawFileDavix("http://root.cern.ch/files/davix.test", options));
+
+   char buffer[2];
+   buffer[0] = buffer[1] = 0;
+   RRawFile::RIOVec iovec[2];
+   iovec[0].fBuffer = &buffer[0];
+   iovec[0].fOffset = 0;
+   iovec[0].fSize = 1;
+   iovec[1].fBuffer = &buffer[1];
+   iovec[1].fOffset = 11;
+   iovec[1].fSize = 1;
+   f->ReadV(iovec, 2);
+
+   EXPECT_EQ(1U, iovec[0].fOutBytes);
+   EXPECT_EQ(1U, iovec[1].fOutBytes);
+   EXPECT_EQ('H', buffer[0]);
+   EXPECT_EQ('d', buffer[1]);
+}
-- 
GitLab