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 ) {
 
 
  285  std::string spec = 
m_pfn;
 
  286  if ( 
m_setup->produceReproducibleFiles ) spec += 
"?reproducible"; 
 
  290    m_file.reset( TFile::Open( spec.c_str(), 
"CREATE", 
"Root event data", compress ) );
 
  291#if ROOT_HAS_630_FWD_COMPAT 
  292    if ( 
m_file && 
m_setup->root630ForwardCompatibility ) 
m_file->SetBit( TFile::k630forwardCompatibility );
 
  294    m_refs = 
new TTree( 
"Refs", 
"Root reference data" );
 
  302    m_file.reset( TFile::Open( spec.c_str(), 
"RECREATE", 
"Root event data", compress ) );
 
  303#if ROOT_HAS_630_FWD_COMPAT 
  304    if ( 
m_file && 
m_setup->root630ForwardCompatibility ) 
m_file->SetBit( TFile::k630forwardCompatibility );
 
  307    m_refs = 
new TTree( 
"Refs", 
"Root reference data" );
 
  314    m_file.reset( TFile::Open( spec.c_str(), 
"UPDATE", 
"Root event data", compress ) );
 
  326      TDirectory::TContext ctxt( 
m_file.get() );
 
  327      m_refs = 
new TTree( 
"Refs", 
"Root reference data" );
 
 
  343    if ( !
m_file->IsZombie() ) {
 
  344      if ( 
m_file->IsWritable() ) {
 
  346        TDirectory::TContext ctxt( 
m_file.get() );
 
  353            if ( i.second->Write() < 0 ) 
badWriteError( 
"Write section:" + i.first );
 
  354            msgSvc() << 
"Disconnect section " << i.first << 
" " << i.second->GetName() << 
endmsg;
 
 
  375  TTree* t  = ( it != 
m_sections.end() ? it->second : 
nullptr );
 
  377    t = (TTree*)
m_file->Get( std::string{ section }.c_str() );
 
  378    if ( !t && create ) {
 
  379      TDirectory::TContext ctxt( 
m_file.get() );
 
  380      t = 
new TTree( std::string{ section }.c_str(), 
"Root data for Gaudi" );
 
  383      int cacheSize = 
m_setup->cacheSize;
 
  387      if ( section == 
m_setup->loadSection && cacheSize > -2 ) {
 
  389        int        learnEntries = 
m_setup->learnEntries;
 
  390        t->SetCacheSize( cacheSize );
 
  391        t->SetCacheLearnEntries( learnEntries );
 
  394          msg << 
"Tree:" << section << 
"Setting up tree cache:" << cacheSize << 
endmsg;
 
  398          msg << 
"Tree:" << section << 
" Setting up tree cache:" << cacheSize << 
" Add all branches." << 
endmsg;
 
  399          msg << 
"Tree:" << section << 
" Learn for " << learnEntries << 
" entries." << 
endmsg;
 
  401          if ( cB.empty() && vB.empty() ) {
 
  402            msg << 
"Adding (default) all branches to tree cache." << 
endmsg;
 
  403            t->AddBranchToCache( 
"*", kTRUE );
 
  405          if ( cB.size() == 1 && cB[0] == 
"*" ) {
 
  406            msg << 
"Adding all branches to tree cache according to option \"CacheBranches\"." << 
endmsg;
 
  407            t->AddBranchToCache( 
"*", kTRUE );
 
  409            for ( TIter it( t->GetListOfBranches() ); it.Next(); ) {
 
  410              const char* n   = ( (TNamed*)( *it ) )->GetName();
 
  411              bool        add = 
false, veto = 
false;
 
  412              for ( 
const auto& i : cB ) {
 
  413                if ( !match_wild( n, ( i ).c_str() ) ) 
continue;
 
  417              for ( 
auto i = vB.cbegin(); !add && i != vB.cend(); ++i ) {
 
  418                if ( !match_wild( n, ( *i ).c_str() ) ) 
continue;
 
  422              if ( add && !veto ) {
 
  423                msg << 
"Add " << n << 
" to branch cache." << 
endmsg;
 
  424                t->AddBranchToCache( n, kTRUE );
 
  426                msg << 
"Do not cache branch " << n << 
endmsg;
 
  436      auto key = 
m_file->GetKey( std::string{ section }.c_str() );
 
 
  448                                        int buff_siz, 
int split_lvl ) {
 
  449  string n = std::string{ branch_name };
 
  451      begin( n ), end( n ), []( 
const char c ) { 
return !isalnum( c ); }, 
'_' );
 
  454  TBranch* b = t->GetBranch( n.c_str() );
 
  455  if ( !b && cl && 
m_file->IsWritable() ) {
 
  456    b = t->Branch( n.c_str(), cl->GetName(), (
void*)( ptr ? &ptr : 
nullptr ), buff_siz, split_lvl );
 
  458  if ( !b ) b = t->GetBranch( std::string{ branch_name }.c_str() );
 
  459  if ( b ) b->SetAutoDelete( kFALSE );
 
 
  465  auto ip = std::find( std::begin( 
m_links ), std::end( 
m_links ), p );
 
  466  if ( ip != std::end( 
m_links ) ) 
return std::distance( std::begin( 
m_links ), ip );
 
  467  m_links.push_back( std::string{ p } );
 
 
  473  if ( ( which >= 0 ) && ( 
size_t( which ) < 
m_dbs.size() ) ) {
 
  474    if ( *( 
m_dbs.begin() + which ) == s_local ) 
return m_fid;
 
  475    return *( 
m_dbs.begin() + which );
 
 
  485                                                      DataObject* pObj, 
int minBufferSize, 
int maxBufferSize,
 
  486                                                      int approxEventsPerBasket, 
int split_lvl, 
bool fill ) {
 
  487  DataObjectPush push( pObj );
 
  488  return save( section, cnt, cl, pObj, minBufferSize, maxBufferSize, approxEventsPerBasket, split_lvl, fill );
 
 
  493                                                   void* pObj, 
int minBufferSize, 
int maxBufferSize,
 
  494                                                   int approxEventsPerBasket, 
int split_lvl, 
bool fill_missing ) {
 
  496  TBranch* b = 
getBranch( section, cnt, cl, pObj ? &pObj : 
nullptr, minBufferSize, split_lvl );
 
  498    Long64_t evt = b->GetEntries();
 
  501    bool set_buffer_size = ( evt == 0 );
 
  502    if ( fill_missing ) {
 
  503      Long64_t num, nevt = b->GetTree()->GetEntries();
 
  505        set_buffer_size = 
true;
 
  506        b->SetAddress( 
nullptr );
 
  512        msgSvc() << 
MSG::DEBUG << 
"Added " << long( nevt - evt ) << 
" / Tree: " << nevt
 
  513                 << 
" / Branch: " << b->GetEntries() + 1 << 
" NULL entries to:" << cnt << 
endmsg;
 
  514        evt = b->GetEntries();
 
  517    if ( set_buffer_size ) {
 
  518      auto     dummy_file = make_unique<TMemFile>( 
"dummy.root", 
"CREATE" );
 
  519      auto     dummy_tree = make_unique<TTree>( 
"DummyTree", 
"DummyTree", split_lvl, dummy_file->GetDirectory( 
"/" ) );
 
  520      TBranch* dummy_branch = dummy_tree->Branch( 
"DummyBranch", cl->GetName(), &pObj, minBufferSize, split_lvl );
 
  521      Int_t    nWritten     = dummy_branch->Fill();
 
  522      if ( nWritten < 0 ) 
return { nWritten, evt };
 
  523      Int_t newBasketSize = nWritten * approxEventsPerBasket;
 
  525      if ( std::numeric_limits<Int_t>::max() / approxEventsPerBasket < nWritten ) {
 
  526        newBasketSize = std::numeric_limits<Int_t>::max();
 
  528      b->SetBasketSize( std::min( maxBufferSize, std::max( minBufferSize, newBasketSize ) ) );
 
  531    b->SetAddress( &pObj );
 
  532    return { b->Fill(), evt };
 
 
  543    TClass* cl = gROOT->GetClass( b->GetClassName(), kTRUE );
 
  548        DataObjectPush push( pObj );
 
  549        b->SetAddress( &pObj );
 
  550        if ( section == 
m_setup->loadSection ) {
 
  551          TTree* t = b->GetTree();
 
  552          if ( Long64_t( entry ) != t->GetReadEntry() ) { t->LoadTree( Long64_t( entry ) ); }
 
  554        nb = b->GetEntry( entry );
 
  556        if ( 
msgSvc().isActive() ) {
 
  557          msgSvc() << 
"Load [" << entry << 
"] --> " << section << 
":" << cnt << 
"  " << nb << 
" bytes." << 
endmsg;
 
  562        } 
else if ( nb == 0 && pObj->
clID() == CLID_DataObject ) {
 
  563          TFile* f   = b->GetFile();
 
  564          int    vsn = f->GetVersion();
 
  569          } 
else if ( vsn > 1000000 && ( vsn % 1000000 ) < 52400 ) {
 
 
  589  int nbytes = 
m_tool->loadRefs( section, cnt, entry, refs );
 
 
  601pair<const RootRef*, const RootDataConnection::ContainerSection*>
 
  609    for ( 
auto j = s.cbegin(); j != s.cend(); ++j, ++cnt ) {
 
  611      if ( entry >= c.start && entry < ( c.start + c.length ) ) {
 
  613          if ( 
msgSvc().isActive() ) {
 
  622  msgSvc() << 
MSG::DEBUG << 
"Return INVALID MergeSection for:" << container << 
"  [" << entry << 
"]" << 
endmsg 
  624  return { 
nullptr, 
nullptr };
 
 
  635                                  std::string_view cnt, 
int entry, 
RootRef& ref ) {
 
  636  auto db   = ( dbase == 
m_fid ? std::string_view{ s_local } : dbase );
 
  641    auto idb = std::find_if( 
m_dbs.begin(), 
m_dbs.end(), [&]( 
const std::string& i ) { return i == db; } );
 
  642    cdb      = std::distance( 
m_dbs.begin(), idb );
 
  643    if ( idb == 
m_dbs.end() ) 
m_dbs.push_back( std::string{ db } );
 
  647  if ( !cnt.empty() ) {
 
  648    auto icnt = std::find_if( 
m_conts.begin(), 
m_conts.end(), [&]( 
const std::string& i ) { return i == cnt; } );
 
  649    ccnt      = std::distance( 
m_conts.begin(), icnt );
 
  650    if ( icnt == 
m_conts.end() ) 
m_conts.push_back( std::string{ cnt } );
 
  654  if ( !
name.empty() ) {
 
  655    auto ilnk = std::find_if( 
m_links.begin(), 
m_links.end(), [&]( 
const std::string& i ) { return i == name; } );
 
  656    clnk      = std::distance( 
m_links.begin(), ilnk );
 
  661  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.