Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@
model = model.Clone()
import ROOT

if len(args) == 2 and type(args[1]) == ROOT.Experimental.RFile:

Check failure on line 158 in bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E721)

bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rntuple.py:158:27: E721 Use `is` and `is not` for type comparisons, or `isinstance()` for isinstance checks
return ROOT.Experimental.RNTupleWriter_Append(model, *args)

return ROOT.RNTupleWriter._Append(model, *args)


Expand Down
3 changes: 3 additions & 0 deletions io/io/inc/ROOT/RFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ ROOT::RLogChannel &RFileLog();
/// This method is meant to only be used by the pythonization.
[[nodiscard]] void *RFile_GetObjectFromKey(RFile &file, const RKeyInfo &key);

TFile *GetRFileTFile(RFile &rfile);

} // namespace Internal

namespace Detail {
Expand Down Expand Up @@ -224,6 +226,7 @@ auto myObj = file->Get<TH1D>("h");
*/
class RFile final {
friend void *Internal::RFile_GetObjectFromKey(RFile &file, const RKeyInfo &key);
friend TFile *Internal::GetRFileTFile(RFile &rfile);

/// Flags used in PutInternal()
enum PutFlags {
Expand Down
5 changes: 5 additions & 0 deletions io/io/src/RFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -551,3 +551,8 @@ void *ROOT::Experimental::Internal::RFile_GetObjectFromKey(RFile &file, const RK
void *obj = file.GetUntyped(key.GetPath(), key.GetClassName().c_str());
return obj;
}

TFile *ROOT::Experimental::Internal::GetRFileTFile(RFile &file)
{
return file.fFile.get();
}
2 changes: 1 addition & 1 deletion io/io/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ if(uring AND NOT DEFINED ENV{ROOTTEST_IGNORE_URING})
ROOT_ADD_GTEST(RIoUring RIoUring.cxx LIBRARIES RIO)
endif()

ROOT_ADD_GTEST(rfile rfile.cxx LIBRARIES RIO Hist)
ROOT_ADD_GTEST(rfile rfile.cxx LIBRARIES RIO Hist ROOTNTuple)
if(pyroot)
ROOT_ADD_PYUNITTEST(rfile_py rfile.py)
endif()
Expand Down
29 changes: 28 additions & 1 deletion io/io/test/rfile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include <ROOT/RFile.hxx>
#include <ROOT/TestSupport.hxx>
#include <ROOT/RLogger.hxx>
#include <ROOT/RNTuple.hxx>
#include <ROOT/RNTupleReader.hxx>
#include <ROOT/RNTupleWriter.hxx>

using ROOT::Experimental::RFile;
using ROOT::TestSupport::FileRaii;
Expand Down Expand Up @@ -685,7 +688,7 @@ TEST(RFile, GetKeyInfo)

EXPECT_EQ(file->GetKeyInfo("foo"), std::nullopt);

for (const std::string_view path : { "/s", "a/b/c", "b", "/a/d" }) {
for (const std::string_view path : {"/s", "a/b/c", "b", "/a/d"}) {
auto key = file->GetKeyInfo(path);
ASSERT_NE(key, std::nullopt);
EXPECT_EQ(key->GetPath(), path[0] == '/' ? path.substr(1) : path);
Expand All @@ -694,3 +697,27 @@ TEST(RFile, GetKeyInfo)
EXPECT_EQ(key->GetCycle(), 1);
}
}

TEST(RFile, RNTuple)
{
FileRaii fileGuard("test_rfile_rntuple.root");

// Writing
{
auto file = RFile::Recreate(fileGuard.GetPath());

auto model = ROOT::RNTupleModel::Create();
*model->MakeField<float>("x") = 42;

auto writer = ROOT::Experimental::RNTupleWriter_Append(std::move(model), "data", *file);
writer->Fill();
}

// Reading back
auto file = RFile::Open(fileGuard.GetPath());
auto ntuple = file->Get<ROOT::RNTuple>("data");
ASSERT_NE(ntuple, nullptr);
auto reader = ROOT::RNTupleReader::Open(*ntuple);
EXPECT_EQ(reader->GetNEntries(), 1);
EXPECT_FLOAT_EQ(reader->GetView<float>("x")(0), 42);
}
22 changes: 22 additions & 0 deletions io/io/test/rfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,27 @@ def test_putUnsupportedType(self):
finally:
os.remove(fileName)

def test_RNTuple(self):
fileName = "test_rfile_rntuple_py.root"
try:
with RFile.Recreate(fileName) as rfile:
model = ROOT.RNTupleModel.Create()
model.MakeField[float]("x")
with ROOT.RNTupleWriter.Append(model, "ntpl", rfile) as writer:
entry = writer.CreateEntry()
entry["x"] = 42
writer.Fill(entry)

with RFile.Open(fileName) as rfile:
ntpl = rfile.Get("ntpl")
self.assertIsNot(ntpl, None)
reader = ROOT.RNTupleReader.Open(ntpl)
self.assertEqual(reader.GetNEntries(), 1)
entry = reader.CreateEntry()
reader.LoadEntry(0, entry)
self.assertEqual(entry["x"], 42)
finally:
os.remove(fileName)

if __name__ == "__main__":
unittest.main()
30 changes: 29 additions & 1 deletion tree/ntuple/inc/ROOT/RMiniFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ class RRawFile;

class RNTupleWriteOptions;

namespace Experimental {

class RFile;

}

namespace Internal {
/// Holds status information of an open ROOT file during writing
struct RTFileControlBlock;
Expand Down Expand Up @@ -126,6 +132,18 @@ private:
operator bool() const { return fDirectory; }
};

struct RFileRFile {
ROOT::Experimental::RFile *fFile = nullptr;
std::string fDir;
/// Low-level writing using a TFile
void Write(const void *buffer, size_t nbytes, std::int64_t offset);
/// Reserves an RBlob opaque key as data record and returns the offset of the record. If keyBuffer is specified,
/// it must be written *before* the returned offset. (Note that the array type is purely documentation, the
/// argument is actually just a pointer.)
std::uint64_t ReserveBlobKey(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen] = nullptr);
operator bool() const { return fFile; }
};

struct RFileSimple {
/// Direct I/O requires that all buffers and write lengths are aligned. It seems 512 byte alignment is the minimum
/// for Direct I/O to work, but further testing showed that it results in worse performance than 4kB.
Expand Down Expand Up @@ -177,9 +195,16 @@ private:
operator bool() const { return fFile; }
};

template <typename T>
static std::uint64_t
ReserveBlobKey(T &caller, TFile &file, std::size_t nbytes, std::size_t len, unsigned char keyBuffer[kBlobKeyLen]);

/// RFileSimple: for simple use cases, survives without libRIO dependency
/// RFileProper: for updating existing files and for storing more than just an RNTuple in the file
std::variant<RFileSimple, RFileProper> fFile;
/// RFileRFile: like RFileProper but using RFile instead of TFile.
using FileType_t = std::variant<RFileSimple, RFileProper, RFileRFile>;
FileType_t fFile;

/// A simple file can either be written as TFile container or as NTuple bare file
bool fIsBare = false;
/// The identifier of the RNTuple; A single writer object can only write a single RNTuple but multiple
Expand Down Expand Up @@ -225,6 +250,9 @@ public:
static std::unique_ptr<RNTupleFileWriter>
Append(std::string_view ntupleName, TDirectory &fileOrDirectory, std::uint64_t maxKeySize);

static std::unique_ptr<RNTupleFileWriter> Append(std::string_view ntupleName, ROOT::Experimental::RFile &file,
std::string_view dirPath, std::uint64_t maxKeySize);

RNTupleFileWriter(const RNTupleFileWriter &other) = delete;
RNTupleFileWriter(RNTupleFileWriter &&other) = delete;
RNTupleFileWriter &operator=(const RNTupleFileWriter &other) = delete;
Expand Down
20 changes: 20 additions & 0 deletions tree/ntuple/inc/ROOT/RNTupleWriter.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ namespace ROOT {

class RNTupleWriteOptions;

namespace Experimental {
class RFile;

/// Creates an RNTupleWriter that writes into the given `file`, appending to it. The RNTuple is written under the
/// path `ntuplePath`.
/// `ntuplePath` may have the form `"path/to/ntuple"`, in which case the ntuple's name will be `"ntuple"` and it will
/// be stored under the given `ntuplePath` in the RFile.
/// Throws an exception if the model is null.
/// NOTE: this is a temporary, experimental API that will be replaced by an overload of RNTupleWriter::Append in the
/// future.
std::unique_ptr<RNTupleWriter>
RNTupleWriter_Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntuplePath,
ROOT::Experimental::RFile &file,
const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
} // namespace Experimental

namespace Internal {
// Non-public factory method for an RNTuple writer that uses an already constructed page sink
std::unique_ptr<RNTupleWriter>
Expand Down Expand Up @@ -102,6 +118,9 @@ class RNTupleWriter {
friend ROOT::RNTupleModel::RUpdater;
friend std::unique_ptr<RNTupleWriter>
Internal::CreateRNTupleWriter(std::unique_ptr<ROOT::RNTupleModel>, std::unique_ptr<Internal::RPageSink>);
friend std::unique_ptr<RNTupleWriter>
Experimental::RNTupleWriter_Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntuplePath,
ROOT::Experimental::RFile &file, const ROOT::RNTupleWriteOptions &options);

private:
RNTupleFillContext fFillContext;
Expand Down Expand Up @@ -152,6 +171,7 @@ public:
static std::unique_ptr<RNTupleWriter> Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName,
TDirectory &fileOrDirectory,
const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());

RNTupleWriter(const RNTupleWriter &) = delete;
RNTupleWriter &operator=(const RNTupleWriter &) = delete;
~RNTupleWriter();
Expand Down
6 changes: 6 additions & 0 deletions tree/ntuple/inc/ROOT/RPageStorageFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ namespace ROOT {
class RNTuple; // for making RPageSourceFile a friend of RNTuple
class RNTupleLocator;

namespace Experimental {
class RFile;
}

namespace Internal {
class RClusterPool;
class RRawFile;
Expand Down Expand Up @@ -97,6 +101,8 @@ protected:
public:
RPageSinkFile(std::string_view ntupleName, std::string_view path, const ROOT::RNTupleWriteOptions &options);
RPageSinkFile(std::string_view ntupleName, TDirectory &fileOrDirectory, const ROOT::RNTupleWriteOptions &options);
RPageSinkFile(std::string_view ntupleName, ROOT::Experimental::RFile &file, std::string_view ntupleDir,
const ROOT::RNTupleWriteOptions &options);
RPageSinkFile(const RPageSinkFile &) = delete;
RPageSinkFile &operator=(const RPageSinkFile &) = delete;
RPageSinkFile(RPageSinkFile &&) = default;
Expand Down
Loading
Loading