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>";
57#ifdef __POOL_COMPATIBILITY
63 std::array<char, 256> init_table() {
64 std::array<char, 256> table;
65 std::iota( std::begin( table ), std::end( table ), 0 );
70 const char* name()
const override {
return "RootDataConnection"; }
77 return "ROOT_READ_ERROR";
79 return "ROOT_OPEN_ERROR";
86 static bool match_wild(
const char* str,
const char* pat ) {
90 static const auto table = init_table();
94 for ( s = str, p = pat; *s; ++s, ++p ) {
97 if ( *s ==
'.' )
goto starCheck;
102 do { ++pat; }
while ( *pat ==
'*' );
103 if ( !*pat )
return true;
106 if ( table[*s] != table[*p] )
goto starCheck;
110 while ( *p ==
'*' ) ++p;
114 if ( !star )
return false;
124 int res = 0, level = ROOT::CompressionSettings( ROOT::RCompressionSetting::EAlgorithm::kLZMA, 6 );
126 if ( idx != string::npos ) {
128 ROOT::RCompressionSetting::EAlgorithm::EValues alg_code = ROOT::RCompressionSetting::EAlgorithm::kUseGlobal;
129 if ( alg.size() == 4 && strncasecmp( alg.data(),
"ZLIB", 4 ) == 0 )
130 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZLIB;
131 else if ( alg.size() == 4 && strncasecmp( alg.data(),
"LZMA", 4 ) == 0 )
132 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZMA;
133 else if ( alg.size() == 3 && strncasecmp( alg.data(),
"LZ4", 3 ) == 0 )
134 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZ4;
135 else if ( alg.size() == 4 && strncasecmp( alg.data(),
"ZSTD", 4 ) == 0 )
136 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZSTD;
138 throw runtime_error(
"ERROR: request to set unknown ROOT compression algorithm:" + std::string{ alg } );
139 res = ::sscanf( std::string{
compression.substr( idx + 1 ) }.c_str(),
"%d",
142 s_compressionLevel = ROOT::CompressionSettings( alg_code, level );
145 throw runtime_error(
"ERROR: request to set unknown ROOT compression level:" +
147 }
else if ( 1 == ::sscanf( std::string{
compression }.c_str(),
"%d", &level ) ) {
149 s_compressionLevel = level;
152 throw runtime_error(
"ERROR: request to set unknown ROOT compression mechanism:" + std::string{
compression } );
166 std::shared_ptr<RootConnectionSetup> setup )
170 if ( fname.size() == 36 && fname[8] ==
'-' && fname[13] ==
'-' && fname[18] ==
'-' && fname[23] ==
'-' ) {
172 m_name.append( fname.data(), fname.size() );
204 if ( !statisticsFile.empty() )
m_statistics->SaveAs( std::string{ statisticsFile }.c_str() );
214 m_statistics.reset(
new TTreePerfStats( ( std::string{ section } +
"_ioperf" ).c_str(), t ) );
227#ifdef __POOL_COMPATIBILITY
228 else if (
m_file->Get(
"##Links" ) !=
nullptr )
261 if ( elem.first ==
"FID" ) {
263 if ( elem.second !=
m_fid ) {
273 !std::ranges::equal(
fid,
m_fid, [](
char a,
char b ) {
return std::tolower( a ) == std::tolower( b ); } ) ) {
287 std::string spec =
m_pfn;
288 if (
m_setup->produceReproducibleFiles ) spec +=
"?reproducible";
292 m_file.reset( TFile::Open( spec.c_str(),
"CREATE",
"Root event data", compress ) );
293#if ROOT_HAS_630_FWD_COMPAT
294 if (
m_file &&
m_setup->root630ForwardCompatibility )
m_file->SetBit( TFile::k630forwardCompatibility );
296 m_refs =
new TTree(
"Refs",
"Root reference data" );
304 m_file.reset( TFile::Open( spec.c_str(),
"RECREATE",
"Root event data", compress ) );
305#if ROOT_HAS_630_FWD_COMPAT
306 if (
m_file &&
m_setup->root630ForwardCompatibility )
m_file->SetBit( TFile::k630forwardCompatibility );
309 m_refs =
new TTree(
"Refs",
"Root reference data" );
316 m_file.reset( TFile::Open( spec.c_str(),
"UPDATE",
"Root event data", compress ) );
328 TDirectory::TContext ctxt(
m_file.get() );
329 m_refs =
new TTree(
"Refs",
"Root reference data" );
345 if ( !
m_file->IsZombie() ) {
346 if (
m_file->IsWritable() ) {
348 TDirectory::TContext ctxt(
m_file.get() );
355 if ( i.second->Write() < 0 )
badWriteError(
"Write section:" + i.first );
356 msgSvc() <<
"Disconnect section " << i.first <<
" " << i.second->GetName() <<
endmsg;
377 TTree* t = ( it !=
m_sections.end() ? it->second :
nullptr );
379 t = (TTree*)
m_file->Get( std::string{ section }.c_str() );
380 if ( !t && create ) {
381 TDirectory::TContext ctxt(
m_file.get() );
382 t =
new TTree( std::string{ section }.c_str(),
"Root data for Gaudi" );
385 int cacheSize =
m_setup->cacheSize;
389 if ( section ==
m_setup->loadSection && cacheSize > -2 ) {
391 int learnEntries =
m_setup->learnEntries;
392 t->SetCacheSize( cacheSize );
393 t->SetCacheLearnEntries( learnEntries );
396 msg <<
"Tree:" << section <<
"Setting up tree cache:" << cacheSize <<
endmsg;
400 msg <<
"Tree:" << section <<
" Setting up tree cache:" << cacheSize <<
" Add all branches." <<
endmsg;
401 msg <<
"Tree:" << section <<
" Learn for " << learnEntries <<
" entries." <<
endmsg;
403 if ( cB.empty() && vB.empty() ) {
404 msg <<
"Adding (default) all branches to tree cache." <<
endmsg;
405 t->AddBranchToCache(
"*", kTRUE );
407 if ( cB.size() == 1 && cB[0] ==
"*" ) {
408 msg <<
"Adding all branches to tree cache according to option \"CacheBranches\"." <<
endmsg;
409 t->AddBranchToCache(
"*", kTRUE );
411 for ( TIter it( t->GetListOfBranches() ); it.Next(); ) {
412 const char* n = ( (TNamed*)( *it ) )->GetName();
413 bool add =
false, veto =
false;
414 for (
const auto& i : cB ) {
415 if ( !match_wild( n, ( i ).c_str() ) )
continue;
419 for (
auto i = vB.cbegin(); !add && i != vB.cend(); ++i ) {
420 if ( !match_wild( n, ( *i ).c_str() ) )
continue;
424 if ( add && !veto ) {
425 msg <<
"Add " << n <<
" to branch cache." <<
endmsg;
426 t->AddBranchToCache( n, kTRUE );
428 msg <<
"Do not cache branch " << n <<
endmsg;
438 auto key =
m_file->GetKey( std::string{ section }.c_str() );
450 int buff_siz,
int split_lvl ) {
451 string n = std::string{ branch_name };
453 begin( n ), end( n ), [](
const char c ) {
return !isalnum( c ); },
'_' );
456 TBranch* b = t->GetBranch( n.c_str() );
457 if ( !b && cl &&
m_file->IsWritable() ) {
458 b = t->Branch( n.c_str(), cl->GetName(), (
void*)( ptr ? &ptr :
nullptr ), buff_siz, split_lvl );
460 if ( !b ) b = t->GetBranch( std::string{ branch_name }.c_str() );
461 if ( b ) b->SetAutoDelete( kFALSE );
467 auto ip = std::find( std::begin(
m_links ), std::end(
m_links ), p );
468 if ( ip != std::end(
m_links ) )
return std::distance( std::begin(
m_links ), ip );
469 m_links.push_back( std::string{ p } );
475 if ( ( which >= 0 ) && (
size_t( which ) <
m_dbs.size() ) ) {
476 if ( *(
m_dbs.begin() + which ) == s_local )
return m_fid;
477 return *(
m_dbs.begin() + which );
487 DataObject* pObj,
int minBufferSize,
int maxBufferSize,
488 int approxEventsPerBasket,
int split_lvl,
bool fill ) {
489 DataObjectPush push( pObj );
490 return save( section, cnt, cl, pObj, minBufferSize, maxBufferSize, approxEventsPerBasket, split_lvl, fill );
495 void* pObj,
int minBufferSize,
int maxBufferSize,
496 int approxEventsPerBasket,
int split_lvl,
bool fill_missing ) {
498 TBranch* b =
getBranch( section, cnt, cl, pObj ? &pObj :
nullptr, minBufferSize, split_lvl );
500 Long64_t evt = b->GetEntries();
503 bool set_buffer_size = ( evt == 0 );
504 if ( fill_missing ) {
505 Long64_t num, nevt = b->GetTree()->GetEntries();
507 set_buffer_size =
true;
508 b->SetAddress(
nullptr );
514 msgSvc() <<
MSG::DEBUG <<
"Added " << long( nevt - evt ) <<
" / Tree: " << nevt
515 <<
" / Branch: " << b->GetEntries() + 1 <<
" NULL entries to:" << cnt <<
endmsg;
516 evt = b->GetEntries();
519 if ( set_buffer_size ) {
520 auto dummy_file = make_unique<TMemFile>(
"dummy.root",
"CREATE" );
521 auto dummy_tree = make_unique<TTree>(
"DummyTree",
"DummyTree", split_lvl, dummy_file->GetDirectory(
"/" ) );
522 TBranch* dummy_branch = dummy_tree->Branch(
"DummyBranch", cl->GetName(), &pObj, minBufferSize, split_lvl );
523 Int_t nWritten = dummy_branch->Fill();
524 if ( nWritten < 0 )
return { nWritten, evt };
525 Int_t newBasketSize = nWritten * approxEventsPerBasket;
527 if ( std::numeric_limits<Int_t>::max() / approxEventsPerBasket < nWritten ) {
528 newBasketSize = std::numeric_limits<Int_t>::max();
530 b->SetBasketSize( std::min( maxBufferSize, std::max( minBufferSize, newBasketSize ) ) );
533 b->SetAddress( &pObj );
534 return { b->Fill(), evt };
545 TClass* cl = gROOT->GetClass( b->GetClassName(), kTRUE );
550 DataObjectPush push( pObj );
551 b->SetAddress( &pObj );
552 if ( section ==
m_setup->loadSection ) {
553 TTree* t = b->GetTree();
554 if ( Long64_t( entry ) != t->GetReadEntry() ) { t->LoadTree( Long64_t( entry ) ); }
556 nb = b->GetEntry( entry );
558 if (
msgSvc().isActive() ) {
559 msgSvc() <<
"Load [" << entry <<
"] --> " << section <<
":" << cnt <<
" " << nb <<
" bytes." <<
endmsg;
564 }
else if ( nb == 0 && pObj->
clID() == CLID_DataObject ) {
565 TFile* f = b->GetFile();
566 int vsn = f->GetVersion();
571 }
else if ( vsn > 1000000 && ( vsn % 1000000 ) < 52400 ) {
591 int nbytes =
m_tool->loadRefs( section, cnt, entry, refs );
603pair<const RootRef*, const RootDataConnection::ContainerSection*>
611 for (
auto j = s.cbegin(); j != s.cend(); ++j, ++cnt ) {
613 if ( entry >= c.start && entry < ( c.start + c.length ) ) {
615 if (
msgSvc().isActive() ) {
624 msgSvc() <<
MSG::DEBUG <<
"Return INVALID MergeSection for:" << container <<
" [" << entry <<
"]" <<
endmsg
626 return {
nullptr,
nullptr };
637 std::string_view cnt,
int entry,
RootRef& ref ) {
638 auto db = ( dbase ==
m_fid ? std::string_view{ s_local } : dbase );
643 auto idb = std::find_if(
m_dbs.begin(),
m_dbs.end(), [&](
const std::string& i ) { return i == db; } );
644 cdb = std::distance(
m_dbs.begin(), idb );
645 if ( idb ==
m_dbs.end() )
m_dbs.push_back( std::string{ db } );
649 if ( !cnt.empty() ) {
650 auto icnt = std::find_if(
m_conts.begin(),
m_conts.end(), [&](
const std::string& i ) { return i == cnt; } );
651 ccnt = std::distance(
m_conts.begin(), icnt );
652 if ( icnt ==
m_conts.end() )
m_conts.push_back( std::string{ cnt } );
656 if ( !
name.empty() ) {
657 auto ilnk = std::find_if(
m_links.begin(),
m_links.end(), [&](
const std::string& i ) { return i == name; } );
658 clnk = std::distance(
m_links.begin(), ilnk );
663 ref.container = ccnt;
const long POOL_ROOTKEY_StorageType
const long POOL_ROOT_StorageType
const long POOL_ROOTTREE_StorageType
const long ROOT_StorageType
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 to encapsulate POOL compatibiliy.
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.