1313#include < Byteswap.h>
1414#include < TError.h>
1515#include < TFile.h>
16+ #include < TIterator.h>
1617#include < TKey.h>
18+ #include < TList.h>
1719#include < TROOT.h>
1820
1921#include < algorithm>
@@ -169,18 +171,14 @@ static std::string ValidateAndNormalizePath(std::string &path)
169171
170172// ///////////////////////////////////////////////////////////////////////////////////////////////
171173
172- RFile::RFile (std::unique_ptr<TFile> file) : fFile(std::move(file)) {}
173-
174- RFile::~RFile () = default ;
175-
176174std::unique_ptr<RFile> RFile::Open (std::string_view path)
177175{
178176 TDirectory::TContext ctx (nullptr ); // XXX: probably not thread safe?
179177 auto tfile = std::unique_ptr<TFile>(TFile::Open (std::string (path).c_str (), " READ_WITHOUT_GLOBALREGISTRATION" ));
180178 if (!tfile || tfile->IsZombie ())
181179 throw ROOT::RException (R__FAIL (" failed to open file " + std::string (path) + " for reading" ));
182180
183- if (tfile->IsRaw ())
181+ if (tfile->IsRaw () || !tfile-> IsBinary () )
184182 throw ROOT::RException (R__FAIL (" Opened file " + std::string (path) + " is not a ROOT file" ));
185183
186184 auto rfile = std::unique_ptr<RFile>(new RFile (std::move (tfile)));
@@ -194,7 +192,7 @@ std::unique_ptr<RFile> RFile::Update(std::string_view path)
194192 if (!tfile || tfile->IsZombie ())
195193 throw ROOT::RException (R__FAIL (" failed to open file " + std::string (path) + " for updating" ));
196194
197- if (tfile->IsRaw ())
195+ if (tfile->IsRaw () || !tfile-> IsBinary () )
198196 throw ROOT::RException (R__FAIL (" Opened file " + std::string (path) + " is not a ROOT file" ));
199197
200198 auto rfile = std::unique_ptr<RFile>(new RFile (std::move (tfile)));
@@ -208,13 +206,17 @@ std::unique_ptr<RFile> RFile::Recreate(std::string_view path)
208206 if (!tfile || tfile->IsZombie ())
209207 throw ROOT::RException (R__FAIL (" failed to open file " + std::string (path) + " for writing" ));
210208
211- if (tfile->IsRaw ())
209+ if (tfile->IsRaw () || !tfile-> IsBinary () )
212210 throw ROOT::RException (R__FAIL (" Opened file " + std::string (path) + " is not a ROOT file" ));
213211
214212 auto rfile = std::unique_ptr<RFile>(new RFile (std::move (tfile)));
215213 return rfile;
216214}
217215
216+ RFile::RFile (std::unique_ptr<TFile> file) : fFile(std::move(file)) {}
217+
218+ RFile::~RFile () = default ;
219+
218220TKey *RFile::GetTKey (std::string_view path) const
219221{
220222 // In RFile, differently from TFile, when dealing with a path like "a/b/c", we always consider it to mean
@@ -361,6 +363,138 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
361363 }
362364}
363365
366+ ROOT::Experimental::RFileKeyIterable::RIterator::RIterStackElem::RIterStackElem (TIterator *it, const std::string &path)
367+ : fIter(it), fDirPath(path)
368+ {
369+ }
370+
371+ ROOT::Experimental::RFileKeyIterable::RIterator::RIterStackElem::~RIterStackElem () = default ;
372+
373+ ROOT::Experimental::RFileKeyIterable::RIterator::RIterator (TIterator *iter, Pattern_t pattern, std::uint32_t flags)
374+ : fPattern(pattern), fFlags(flags)
375+ {
376+ if (iter) {
377+ fIterStack .emplace_back (iter);
378+
379+ if (!pattern.empty ()) {
380+ fRootDirNesting = std::count (pattern.begin (), pattern.end (), ' /' );
381+ // `pattern` may or may not end with '/', but we consider it a directory regardless.
382+ // In other words, like in virtually all filesystem operations, "dir" and "dir/" are equivalent.
383+ fRootDirNesting += pattern.back () != ' /' ;
384+ }
385+
386+ // Advance the iterator to skip the first key, which is always the TFile key.
387+ // This will also skip keys until we reach the first correct key we want to return.
388+ Advance ();
389+ }
390+ }
391+
392+ ROOT::Experimental::RFileKeyIterable::RIterator ROOT::Experimental::RFileKeyIterable::begin () const
393+ {
394+ return {fFile ->GetListOfKeys ()->MakeIterator (), fPattern , fFlags };
395+ }
396+
397+ ROOT::Experimental::RFileKeyIterable::RIterator ROOT::Experimental::RFileKeyIterable::end () const
398+ {
399+ return {nullptr , fPattern , fFlags };
400+ }
401+
402+ void ROOT::Experimental::RFileKeyIterable::RIterator::Advance ()
403+ {
404+ fCurKey = nullptr ;
405+
406+ const bool recursive = fFlags & RFile::kListRecursive ;
407+ const bool includeObj = fFlags & RFile::kListObjects ;
408+ const bool includeDirs = fFlags & RFile::kListDirs ;
409+
410+ // We only want to return keys that refer to user objects, not internal ones, therefore we skip
411+ // all keys that have internal class names.
412+ while (!fIterStack .empty ()) {
413+ auto &[iter, dirPath] = fIterStack .back ();
414+ assert (iter);
415+ TObject *keyObj = iter->Next ();
416+ if (!keyObj) {
417+ // reached end of the iteration
418+ fIterStack .pop_back ();
419+ continue ;
420+ }
421+
422+ assert (keyObj->IsA () == TClass::GetClass<TKey>());
423+ auto key = static_cast <TKey *>(keyObj);
424+
425+ const auto dirSep = (dirPath.empty () ? " " : " /" );
426+ const auto isDir =
427+ strcmp (key->GetClassName (), " TDirectory" ) == 0 || strcmp (key->GetClassName (), " TDirectoryFile" ) == 0 ;
428+
429+ if (isDir) {
430+ TDirectory *dir = key->ReadObject <TDirectory>();
431+ TIterator *innerIter = dir->GetListOfKeys ()->MakeIterator ();
432+ assert (innerIter);
433+ fIterStack .emplace_back (innerIter, dirPath + dirSep + dir->GetName ());
434+ if (!includeDirs)
435+ continue ;
436+ } else if (!includeObj) {
437+ continue ;
438+ }
439+
440+ // Reconstruct the full path of the key
441+ const auto &fullPath = dirPath + dirSep + key->GetName ();
442+ const auto nesting = fIterStack .size () - 1 ;
443+
444+ // Skip key if it's not a child of root dir
445+ if (!ROOT::StartsWith (fullPath, fPattern ))
446+ continue ;
447+
448+ // Check that we are in the same directory as "rootDir".
449+ // Note that for directories we list both the root dir and the immediate children (in non-recursive mode).
450+ if (!recursive && nesting != fRootDirNesting && (!isDir || nesting != fRootDirNesting + 1 ))
451+ continue ;
452+
453+ // All checks passed: return this key.
454+ assert (!fullPath.empty ());
455+ fCurKey = key;
456+ break ;
457+ }
458+ }
459+
460+ ROOT::Experimental::RKeyInfo ROOT::Experimental::RFileKeyIterable::RIterator::operator *()
461+ {
462+ if (fIterStack .empty ())
463+ throw ROOT::RException (R__FAIL (" tried to dereference an invalid iterator" ));
464+
465+ const TKey *key = fCurKey ;
466+ if (!key)
467+ throw ROOT::RException (R__FAIL (" tried to dereference an invalid iterator" ));
468+
469+ const bool isDir =
470+ strcmp (key->GetClassName (), " TDirectory" ) == 0 || strcmp (key->GetClassName (), " TDirectoryFile" ) == 0 ;
471+ const auto &dirPath = fIterStack .back ().fDirPath ;
472+
473+ RKeyInfo keyInfo;
474+ keyInfo.fCategory = isDir ? RKeyInfo::ECategory::kDirectory : RKeyInfo::ECategory::kObject ;
475+ keyInfo.fName = dirPath;
476+ if (!isDir)
477+ keyInfo.fName += std::string (dirPath.empty () ? " " : " /" ) + key->GetName ();
478+ keyInfo.fClassName = key->GetClassName ();
479+ keyInfo.fCycle = key->GetCycle ();
480+ keyInfo.fTitle = key->GetTitle ();
481+ return keyInfo;
482+ }
483+
484+ void RFile::Print (std::ostream &out) const
485+ {
486+ std::vector<RKeyInfo> keys;
487+ auto keysIter = ListKeys ();
488+ for (const auto &key : keysIter) {
489+ keys.emplace_back (key);
490+ }
491+
492+ std::sort (keys.begin (), keys.end (), [](const auto &a, const auto &b) { return a.fName < b.fName ; });
493+ for (const auto &key : keys) {
494+ out << key.fClassName << " " << key.fName << " ;" << key.fCycle << " : \" " << key.fTitle << " \"\n " ;
495+ }
496+ }
497+
364498size_t RFile::Flush ()
365499{
366500 return fFile ->Write ();
0 commit comments