30#include <Compression.h>
39static int s_compressionLevel = ROOT::CompressionSettings( ROOT::RCompressionSetting::EAlgorithm::kLZMA, 4 );
41#define ROOT_HAS_630_FWD_COMPAT ROOT_VERSION_CODE > ROOT_VERSION( 6, 30, 4 )
54static const string s_empty;
55static const string s_local =
"<localDB>";
60 std::array<char, 256> init_table() {
61 std::array<char, 256> table;
62 std::iota( std::begin( table ), std::end( table ), 0 );
67 const char* name()
const override {
return "RootDataConnection"; }
74 return "ROOT_READ_ERROR";
76 return "ROOT_OPEN_ERROR";
83 static bool match_wild(
const char* str,
const char* pat ) {
87 static const auto table = init_table();
91 for ( s = str, p = pat; *s; ++s, ++p ) {
94 if ( *s ==
'.' )
goto starCheck;
99 do { ++pat; }
while ( *pat ==
'*' );
100 if ( !*pat )
return true;
103 if ( table[*s] != table[*p] )
goto starCheck;
107 while ( *p ==
'*' ) ++p;
111 if ( !star )
return false;
121 int res = 0, level = ROOT::CompressionSettings( ROOT::RCompressionSetting::EAlgorithm::kLZMA, 6 );
123 if ( idx != string::npos ) {
125 ROOT::RCompressionSetting::EAlgorithm::EValues alg_code = ROOT::RCompressionSetting::EAlgorithm::kUseGlobal;
126 if ( alg.size() == 4 && strncasecmp( alg.data(),
"ZLIB", 4 ) == 0 )
127 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZLIB;
128 else if ( alg.size() == 4 && strncasecmp( alg.data(),
"LZMA", 4 ) == 0 )
129 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZMA;
130 else if ( alg.size() == 3 && strncasecmp( alg.data(),
"LZ4", 3 ) == 0 )
131 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZ4;
132 else if ( alg.size() == 4 && strncasecmp( alg.data(),
"ZSTD", 4 ) == 0 )
133 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZSTD;
135 throw runtime_error(
"ERROR: request to set unknown ROOT compression algorithm:" + std::string{ alg } );
136 res = ::sscanf( std::string{
compression.substr( idx + 1 ) }.c_str(),
"%d",
139 s_compressionLevel = ROOT::CompressionSettings( alg_code, level );
142 throw runtime_error(
"ERROR: request to set unknown ROOT compression level:" +
144 }
else if ( 1 == ::sscanf( std::string{
compression }.c_str(),
"%d", &level ) ) {
146 s_compressionLevel = level;
149 throw runtime_error(
"ERROR: request to set unknown ROOT compression mechanism:" + std::string{
compression } );
163 std::shared_ptr<RootConnectionSetup> setup )
167 if ( fname.size() == 36 && fname[8] ==
'-' && fname[13] ==
'-' && fname[18] ==
'-' && fname[23] ==
'-' ) {
169 m_name.append( fname.data(), fname.size() );
201 if ( !statisticsFile.empty() )
m_statistics->SaveAs( std::string{ statisticsFile }.c_str() );
211 m_statistics.reset(
new TTreePerfStats( ( std::string{ section } +
"_ioperf" ).c_str(), t ) );
254 if ( elem.first ==
"FID" ) {
256 if ( elem.second !=
m_fid ) {
266 !std::ranges::equal(
fid,
m_fid, [](
char a,
char b ) {
return std::tolower( a ) == std::tolower( b ); } ) ) {
280 std::string spec =
m_pfn;
281 if (
m_setup->produceReproducibleFiles ) spec +=
"?reproducible";
285 m_file.reset( TFile::Open( spec.c_str(),
"CREATE",
"Root event data", compress ) );
286#if ROOT_HAS_630_FWD_COMPAT
287 if (
m_file &&
m_setup->root630ForwardCompatibility )
m_file->SetBit( TFile::k630forwardCompatibility );
289 m_refs =
new TTree(
"Refs",
"Root reference data" );
297 m_file.reset( TFile::Open( spec.c_str(),
"RECREATE",
"Root event data", compress ) );
298#if ROOT_HAS_630_FWD_COMPAT
299 if (
m_file &&
m_setup->root630ForwardCompatibility )
m_file->SetBit( TFile::k630forwardCompatibility );
302 m_refs =
new TTree(
"Refs",
"Root reference data" );
309 m_file.reset( TFile::Open( spec.c_str(),
"UPDATE",
"Root event data", compress ) );
321 TDirectory::TContext ctxt(
m_file.get() );
322 m_refs =
new TTree(
"Refs",
"Root reference data" );
338 if ( !
m_file->IsZombie() ) {
339 if (
m_file->IsWritable() ) {
341 TDirectory::TContext ctxt(
m_file.get() );
348 if ( i.second->Write() < 0 )
badWriteError(
"Write section:" + i.first );
349 msgSvc() <<
"Disconnect section " << i.first <<
" " << i.second->GetName() <<
endmsg;
370 TTree* t = ( it !=
m_sections.end() ? it->second :
nullptr );
372 t = (TTree*)
m_file->Get( std::string{ section }.c_str() );
373 if ( !t && create ) {
374 TDirectory::TContext ctxt(
m_file.get() );
375 t =
new TTree( std::string{ section }.c_str(),
"Root data for Gaudi" );
378 int cacheSize =
m_setup->cacheSize;
382 if ( section ==
m_setup->loadSection && cacheSize > -2 ) {
384 int learnEntries =
m_setup->learnEntries;
385 t->SetCacheSize( cacheSize );
386 t->SetCacheLearnEntries( learnEntries );
389 msg <<
"Tree:" << section <<
"Setting up tree cache:" << cacheSize <<
endmsg;
393 msg <<
"Tree:" << section <<
" Setting up tree cache:" << cacheSize <<
" Add all branches." <<
endmsg;
394 msg <<
"Tree:" << section <<
" Learn for " << learnEntries <<
" entries." <<
endmsg;
396 if ( cB.empty() && vB.empty() ) {
397 msg <<
"Adding (default) all branches to tree cache." <<
endmsg;
398 t->AddBranchToCache(
"*", kTRUE );
400 if ( cB.size() == 1 && cB[0] ==
"*" ) {
401 msg <<
"Adding all branches to tree cache according to option \"CacheBranches\"." <<
endmsg;
402 t->AddBranchToCache(
"*", kTRUE );
404 for ( TIter it( t->GetListOfBranches() ); it.Next(); ) {
405 const char* n = ( (TNamed*)( *it ) )->GetName();
406 bool add =
false, veto =
false;
407 for (
const auto& i : cB ) {
408 if ( !match_wild( n, ( i ).c_str() ) )
continue;
412 for (
auto i = vB.cbegin(); !add && i != vB.cend(); ++i ) {
413 if ( !match_wild( n, ( *i ).c_str() ) )
continue;
417 if ( add && !veto ) {
418 msg <<
"Add " << n <<
" to branch cache." <<
endmsg;
419 t->AddBranchToCache( n, kTRUE );
421 msg <<
"Do not cache branch " << n <<
endmsg;
431 auto key =
m_file->GetKey( std::string{ section }.c_str() );
443 int buff_siz,
int split_lvl ) {
444 string n = std::string{ branch_name };
446 begin( n ), end( n ), [](
const char c ) {
return !isalnum( c ); },
'_' );
449 TBranch* b = t->GetBranch( n.c_str() );
450 if ( !b && cl &&
m_file->IsWritable() ) {
451 b = t->Branch( n.c_str(), cl->GetName(), (
void*)( ptr ? &ptr :
nullptr ), buff_siz, split_lvl );
453 if ( !b ) b = t->GetBranch( std::string{ branch_name }.c_str() );
454 if ( b ) b->SetAutoDelete( kFALSE );
460 auto ip = std::find( std::begin(
m_links ), std::end(
m_links ), p );
461 if ( ip != std::end(
m_links ) )
return std::distance( std::begin(
m_links ), ip );
462 m_links.push_back( std::string{ p } );
468 if ( ( which >= 0 ) && (
size_t( which ) <
m_dbs.size() ) ) {
469 if ( *(
m_dbs.begin() + which ) == s_local )
return m_fid;
470 return *(
m_dbs.begin() + which );
480 DataObject* pObj,
int minBufferSize,
int maxBufferSize,
481 int approxEventsPerBasket,
int split_lvl,
bool fill ) {
482 DataObjectPush push( pObj );
483 return save( section, cnt, cl, pObj, minBufferSize, maxBufferSize, approxEventsPerBasket, split_lvl, fill );
488 void* pObj,
int minBufferSize,
int maxBufferSize,
489 int approxEventsPerBasket,
int split_lvl,
bool fill_missing ) {
491 TBranch* b =
getBranch( section, cnt, cl, pObj ? &pObj :
nullptr, minBufferSize, split_lvl );
493 Long64_t evt = b->GetEntries();
496 bool set_buffer_size = ( evt == 0 );
497 if ( fill_missing ) {
498 Long64_t num, nevt = b->GetTree()->GetEntries();
500 set_buffer_size =
true;
501 b->SetAddress(
nullptr );
507 msgSvc() <<
MSG::DEBUG <<
"Added " << long( nevt - evt ) <<
" / Tree: " << nevt
508 <<
" / Branch: " << b->GetEntries() + 1 <<
" NULL entries to:" << cnt <<
endmsg;
509 evt = b->GetEntries();
512 if ( set_buffer_size ) {
513 auto dummy_file = make_unique<TMemFile>(
"dummy.root",
"CREATE" );
514 auto dummy_tree = make_unique<TTree>(
"DummyTree",
"DummyTree", split_lvl, dummy_file->GetDirectory(
"/" ) );
515 TBranch* dummy_branch = dummy_tree->Branch(
"DummyBranch", cl->GetName(), &pObj, minBufferSize, split_lvl );
516 Int_t nWritten = dummy_branch->Fill();
517 if ( nWritten < 0 )
return { nWritten, evt };
518 Int_t newBasketSize = nWritten * approxEventsPerBasket;
520 if ( std::numeric_limits<Int_t>::max() / approxEventsPerBasket < nWritten ) {
521 newBasketSize = std::numeric_limits<Int_t>::max();
523 b->SetBasketSize( std::min( maxBufferSize, std::max( minBufferSize, newBasketSize ) ) );
526 b->SetAddress( &pObj );
527 return { b->Fill(), evt };
538 TClass* cl = gROOT->GetClass( b->GetClassName(), kTRUE );
543 DataObjectPush push( pObj );
544 b->SetAddress( &pObj );
545 if ( section ==
m_setup->loadSection ) {
546 TTree* t = b->GetTree();
547 if ( Long64_t( entry ) != t->GetReadEntry() ) { t->LoadTree( Long64_t( entry ) ); }
549 nb = b->GetEntry( entry );
551 if (
msgSvc().isActive() ) {
552 msgSvc() <<
"Load [" << entry <<
"] --> " << section <<
":" << cnt <<
" " << nb <<
" bytes." <<
endmsg;
557 }
else if ( nb == 0 && pObj->
clID() == CLID_DataObject ) {
558 TFile* f = b->GetFile();
559 int vsn = f->GetVersion();
564 }
else if ( vsn > 1000000 && ( vsn % 1000000 ) < 52400 ) {
584 int nbytes =
m_tool->loadRefs( section, cnt, entry, refs );
596pair<const RootRef*, const RootDataConnection::ContainerSection*>
604 for (
auto j = s.cbegin(); j != s.cend(); ++j, ++cnt ) {
606 if ( entry >= c.start && entry < ( c.start + c.length ) ) {
608 if (
msgSvc().isActive() ) {
617 msgSvc() <<
MSG::DEBUG <<
"Return INVALID MergeSection for:" << container <<
" [" << entry <<
"]" <<
endmsg
619 return {
nullptr,
nullptr };
630 std::string_view cnt,
int entry,
RootRef& ref ) {
631 auto db = ( dbase ==
m_fid ? std::string_view{ s_local } : dbase );
636 auto idb = std::find_if(
m_dbs.begin(),
m_dbs.end(), [&](
const std::string& i ) { return i == db; } );
637 cdb = std::distance(
m_dbs.begin(), idb );
638 if ( idb ==
m_dbs.end() )
m_dbs.push_back( std::string{ db } );
642 if ( !cnt.empty() ) {
643 auto icnt = std::find_if(
m_conts.begin(),
m_conts.end(), [&](
const std::string& i ) { return i == cnt; } );
644 ccnt = std::distance(
m_conts.begin(), icnt );
645 if ( icnt ==
m_conts.end() )
m_conts.push_back( std::string{ cnt } );
649 if ( !
name.empty() ) {
650 auto ilnk = std::find_if(
m_links.begin(),
m_links.end(), [&](
const std::string& i ) { return i == name; } );
651 clnk = std::distance(
m_links.begin(), ilnk );
656 ref.container = ccnt;
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
#define STATUSCODE_ENUM_IMPL(...)
Assign a category to the StatusCode enum declared with STATUSCODE_ENUM_DECL( ENUM )
A DataObject is the base class of any identifiable object on any data store.
virtual const CLID & clID() const
Retrieve reference to class definition structure.
std::string m_fid
File ID of the connection.
const IInterface * owner() const
Owner instance.
void resetAge()
Reset age.
std::string m_pfn
Physical file name of the connection.
const std::string & fid() const
Access file id.
std::string m_name
Connection name/identifier.
const std::string & name() const
Connection name.
IDataConnection(const IInterface *own, std::string nam)
Standard constructor.
const std::string & pfn() const
Access physical file name.
IoType
I/O Connection types.
RootConnectionSetup()=default
Standard constructor.
SmartIF< IIncidentSvc > m_incidentSvc
Reference to incident service.
void setMessageSvc(MsgStream *m)
Set message service reference.
static int compression()
Access to global compression level.
void setIncidentSvc(IIncidentSvc *m)
Set incident service reference.
static StatusCode setCompression(std::string_view compression)
Set the global compression level.
std::unique_ptr< MsgStream > m_msgSvc
Reference to message service.
Sections m_sections
Tree sections in TFile.
LinkSections m_linkSects
Database link sections.
void addClient(const IInterface *client)
Add new client to this data source.
Tool * makeTool()
Create file access tool.
StringVec m_links
Map containing internal links names.
const std::string & empty() const
Empty string reference.
MsgStream & msgSvc() const
Allow access to printer service.
std::vector< std::string > StringVec
Type definition for string maps.
std::vector< ContainerSection > ContainerSections
Definition of container sections to handle merged files.
RootDataConnection(const IInterface *own, std::string_view nam, std::shared_ptr< RootConnectionSetup > setup)
Standard constructor.
std::unique_ptr< TTreePerfStats > m_statistics
I/O read statistics from TTree.
std::unique_ptr< Tool > m_tool
Clients m_clients
Client list.
ParamMap m_params
Parameter map for file parameters.
TTree * getSection(std::string_view sect, bool create=false)
Access TTree section from section name. The section is created if required.
std::unique_ptr< TFile > m_file
Reference to ROOT file.
std::pair< int, unsigned long > save(std::string_view section, std::string_view cnt, TClass *cl, void *pObj, int minBufferSize, int maxBufferSize, int approxEventsPerBasket, int split_lvl, bool fill_missing=false)
Save object of a given class to section and container.
MergeSections m_mergeSects
Database section map for merged files.
StatusCode disconnect() override
Release data stream and release implementation dependent resources.
StatusCode connectRead() override
Open data stream in read mode.
int makeLink(std::string_view p)
Convert path string to path index.
StringVec m_conts
Map containing external container names.
bool lookupClient(const IInterface *client) const
Lookup client for this data source.
StatusCode connectWrite(IoType typ) override
Open data stream in write mode.
void saveStatistics(std::string_view statisticsFile)
Save TTree access statistics if required.
TBranch * getBranch(std::string_view section, std::string_view branch_name)
Access data branch by name: Get existing branch in read only mode.
StringVec m_mergeFIDs
Map containing merge FIDs.
StringVec m_dbs
Map containing external database file names (fids)
std::shared_ptr< RootConnectionSetup > m_setup
Reference to the setup structure.
void makeRef(const IRegistry &pA, RootRef &ref)
Create reference object from registry entry.
const std::string & getDb(int which) const
Access database/file name from saved index.
void enableStatistics(std::string_view section)
Enable TTreePerStats.
std::pair< int, unsigned long > saveObj(std::string_view section, std::string_view cnt, TClass *cl, DataObject *pObj, int minBufferSize, int maxBufferSize, int approxEventsPerBasket, int split_lvl, bool fill_missing=false)
Save object of a given class to section and container.
int loadRefs(std::string_view section, std::string_view cnt, unsigned long entry, RootObjectRefs &refs)
Load references object.
std::pair< const RootRef *, const ContainerSection * > getMergeSection(std::string_view container, int entry) const
Access link section for single container and entry.
void badWriteError(std::string_view msg) const
Error handler when bad write statements occur.
int loadObj(std::string_view section, std::string_view cnt, unsigned long entry, DataObject *&pObj)
Load object.
IIncidentSvc * incidentSvc() const
TTree * m_refs
Pointer to the reference tree.
size_t removeClient(const IInterface *client)
Remove client from this data source.
The interface implemented by the IncidentSvc service.
virtual void fireIncident(const Incident &incident)=0
Fire an Incident.
Definition of the basic interface.
Opaque address interface definition.
virtual long svcType() const =0
Retrieve service type.
virtual const CLID & clID() const =0
Retrieve class information from link.
virtual const std::string * par() const =0
Retrieve String parameters.
The IRegistry represents the entry door to the environment any data object residing in a transient da...
virtual const name_type & name() const =0
Name of the directory (or key)
virtual IOpaqueAddress * address() const =0
Retrieve opaque storage address.
Base class for all Incidents (computing events).
Definition of the MsgStream class used to transmit messages.
This class is used for returning status codes from appropriate routines.
static const Category & default_category() noexcept
Default Gaudi StatusCode category.
const StatusCode & ignore() const
Allow discarding a StatusCode without warning.
unsigned long code_t
type of StatusCode value
constexpr static const auto SUCCESS
constexpr static const auto FAILURE
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Internal helper class, which described a TBranch section in a ROOT file.
Persistent reference object containing all leafs and links corresponding to a Gaudi DataObject.
Persistent reference object.
The category assigned to a StatusCode.
virtual std::string message(code_t code) const
Description for code within this category.