Skip to content
Merged
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
6 changes: 3 additions & 3 deletions tree/ntuple/inc/ROOT/RMiniFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ private:
/// Used when the file turns out to be a TFile container. The ntuplePath variable is either the ntuple name
/// or an ntuple name preceded by a directory (`myNtuple` or `foo/bar/myNtuple` or `/foo/bar/myNtuple`)
RResult<RNTuple> GetNTupleProper(std::string_view ntuplePath);
/// Loads an RNTuple anchor from a TFile at the given file offset (unzipping it if necessary).
RResult<RNTuple>
GetNTupleProperAtOffset(std::uint64_t payloadOffset, std::uint64_t compSize, std::uint64_t uncompLen);

/// Searches for a key with the given name and type in the key index of the directory starting at offsetDir.
/// The offset points to the start of the TDirectory DATA section, without the key and without the name and title
Expand All @@ -83,6 +80,9 @@ public:
explicit RMiniFileReader(ROOT::Internal::RRawFile *rawFile);
/// Extracts header and footer location for the RNTuple identified by ntupleName
RResult<RNTuple> GetNTuple(std::string_view ntupleName);
/// Loads an RNTuple anchor from a TFile at the given file offset (unzipping it if necessary).
RResult<RNTuple>
GetNTupleProperAtOffset(std::uint64_t payloadOffset, std::uint64_t compSize, std::uint64_t uncompLen);
/// Reads a given byte range from the file into the provided memory buffer.
/// If `nbytes > fMaxKeySize` it will perform chunked read from multiple blobs,
/// whose addresses are listed at the end of the first chunk.
Expand Down
5 changes: 5 additions & 0 deletions tree/ntuple/inc/ROOT/RPageStorageFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ public:
RPageSourceFile &operator=(RPageSourceFile &&) = delete;
~RPageSourceFile() override;

/// Creates a new PageSourceFile using the same underlying file as this but referring to a different RNTuple,
/// represented by `anchor`.
std::unique_ptr<RPageSourceFile>
OpenWithDifferentAnchor(const RNTuple &anchor, const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());

void
LoadSealedPage(ROOT::DescriptorId_t physicalColumnId, RNTupleLocalIndex localIndex, RSealedPage &sealedPage) final;

Expand Down
9 changes: 9 additions & 0 deletions tree/ntuple/src/RPageStorageFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,15 @@ ROOT::Internal::RPageSourceFile::CreateFromAnchor(const RNTuple &anchor, const R

ROOT::Internal::RPageSourceFile::~RPageSourceFile() = default;

std::unique_ptr<ROOT::Internal::RPageSourceFile>
ROOT::Internal::RPageSourceFile::OpenWithDifferentAnchor(const RNTuple &anchor, const ROOT::RNTupleReadOptions &options)
{
auto pageSource = std::make_unique<RPageSourceFile>("", fFile->Clone(), options);
pageSource->fAnchor = anchor;
// NOTE: fNTupleName gets set only upon Attach().
return pageSource;
}

void ROOT::Internal::RPageSourceFile::LoadStructureImpl()
{
// If we constructed the page source with (ntuple name, path), we need to find the anchor first.
Expand Down
59 changes: 59 additions & 0 deletions tree/ntuple/test/ntuple_storage.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1145,3 +1145,62 @@ TEST(RPageSourceFile, NameFromAnchor)
source->Attach();
EXPECT_EQ(source->GetNTupleName(), "ntpl");
}

TEST(RPageSourceFile, OpenDifferentAnchor)
{
FileRaii fileGuard("test_ntuple_open_diff_anchor.root");

auto model = RNTupleModel::Create();
auto pF = model->MakeField<float>("f");
auto file = std::unique_ptr<TFile>(TFile::Open(fileGuard.GetPath().c_str(), "RECREATE"));
{
auto writer = RNTupleWriter::Append(std::move(model), "ntpl1", *file);
for (auto i = 0; i < 100; ++i) {
*pF = i;
writer->Fill();
}
}
{
model = RNTupleModel::Create();
auto pI = model->MakeField<int>("i");
auto pC = model->MakeField<char>("c");

auto writer = RNTupleWriter::Append(std::move(model), "ntpl2", *file);
for (auto i = 0; i < 20; ++i) {
*pI = i;
*pC = i;
writer->Fill();
}
}

auto source = std::make_unique<RPageSourceFile>("ntpl1", fileGuard.GetPath(), RNTupleReadOptions());
source->Attach();
EXPECT_EQ(source->GetNEntries(), 100);
{
auto desc = source->GetSharedDescriptorGuard();
EXPECT_NE(desc->FindFieldId("f"), ROOT::kInvalidDescriptorId);
}

auto anchor2 = file->Get<ROOT::RNTuple>("ntpl2");
ASSERT_NE(anchor2, nullptr);
auto source2 = source->OpenWithDifferentAnchor(*anchor2);
source2->Attach();
EXPECT_EQ(source2->GetNTupleName(), "ntpl2");
EXPECT_EQ(source2->GetNEntries(), 20);
{
auto desc2 = source2->GetSharedDescriptorGuard();
EXPECT_EQ(desc2->FindFieldId("f"), ROOT::kInvalidDescriptorId);
EXPECT_NE(desc2->FindFieldId("i"), ROOT::kInvalidDescriptorId);
EXPECT_NE(desc2->FindFieldId("c"), ROOT::kInvalidDescriptorId);
}

source.reset();
// source2 should still be valid after dropping the first source.
EXPECT_EQ(source2->GetNEntries(), 20);
{
auto desc2 = source2->GetSharedDescriptorGuard();
EXPECT_EQ(desc2->FindFieldId("f"), ROOT::kInvalidDescriptorId);
EXPECT_NE(desc2->FindFieldId("i"), ROOT::kInvalidDescriptorId);
EXPECT_NE(desc2->FindFieldId("c"), ROOT::kInvalidDescriptorId);
}
}
Loading