The Gaudi Framework  master (181af51f)
Loading...
Searching...
No Matches
RootDataConnection.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3* *
4* This software is distributed under the terms of the Apache version 2 licence, *
5* copied verbatim in the file "LICENSE". *
6* *
7* In applying this licence, CERN does not waive the privileges and immunities *
8* granted to it by virtue of its status as an Intergovernmental Organization *
9* or submit itself to any jurisdiction. *
10\***********************************************************************************/
11//====================================================================
12// RootDataConnection.cpp
13//--------------------------------------------------------------------
14//
15// Author : M.Frank
16//====================================================================
17
18// Framework include files
19#include "RootUtils.h"
29// ROOT include files
30#include <TBranch.h>
31#include <TClass.h>
32#include <TFile.h>
33#include <TLeaf.h>
34#include <TMemFile.h>
35#include <TROOT.h>
36#include <TTree.h>
37#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
38# include <Compression.h>
39static int s_compressionLevel = ROOT::CompressionSettings( ROOT::RCompressionSetting::EAlgorithm::kLZMA, 4 );
40#else
41static int s_compressionLevel = 1;
42#endif
43
44#define ROOT_HAS_630_FWD_COMPAT ROOT_VERSION_CODE > ROOT_VERSION( 6, 30, 4 )
45
46// C/C++ include files
47#include <fmt/format.h>
48#include <limits>
49#include <numeric>
50#include <stdexcept>
51#include <strings.h>
52
53using namespace Gaudi;
54using namespace std;
55typedef const string& CSTR;
56
57static const string s_empty;
58static const string s_local = "<localDB>";
59
60#ifdef __POOL_COMPATIBILITY
61# include "PoolTool.h"
62#endif
63#include "RootTool.h"
64
65namespace {
66 std::array<char, 256> init_table() {
67 std::array<char, 256> table;
68 std::iota( std::begin( table ), std::end( table ), 0 );
69 return table; // cppcheck-suppress uninitvar; false positive
70 }
71
72 struct RootDataConnectionCategory : StatusCode::Category {
73 const char* name() const override { return "RootDataConnection"; }
74
75 bool isRecoverable( StatusCode::code_t ) const override { return false; }
76
77 std::string message( StatusCode::code_t code ) const override {
78 switch ( static_cast<RootDataConnection::Status>( code ) ) {
80 return "ROOT_READ_ERROR";
82 return "ROOT_OPEN_ERROR";
83 default:
85 }
86 }
87 };
88
89 static bool match_wild( const char* str, const char* pat ) {
90 //
91 // Credits: Code from Alessandro Felice Cantatore.
92 //
93 static const auto table = init_table();
94 const char * s, *p;
95 bool star = false;
96 loopStart:
97 for ( s = str, p = pat; *s; ++s, ++p ) {
98 switch ( *p ) {
99 case '?':
100 if ( *s == '.' ) goto starCheck;
101 break;
102 case '*':
103 star = true;
104 str = s, pat = p;
105 do { ++pat; } while ( *pat == '*' );
106 if ( !*pat ) return true;
107 goto loopStart;
108 default:
109 if ( table[*s] != table[*p] ) goto starCheck;
110 break;
111 } /* endswitch */
112 } /* endfor */
113 while ( *p == '*' ) ++p;
114 return ( !*p );
115
116 starCheck:
117 if ( !star ) return false;
118 str++;
119 goto loopStart;
120 }
121} // namespace
122
123STATUSCODE_ENUM_IMPL( Gaudi::RootDataConnection::Status, RootDataConnectionCategory )
124
125
127#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
128 int res = 0, level = ROOT::CompressionSettings( ROOT::RCompressionSetting::EAlgorithm::kLZMA, 6 );
129 auto idx = compression.find( ':' );
130 if ( idx != string::npos ) {
131 auto alg = compression.substr( 0, idx );
132 ROOT::RCompressionSetting::EAlgorithm::EValues alg_code = ROOT::RCompressionSetting::EAlgorithm::kUseGlobal;
133 if ( alg.size() == 4 && strncasecmp( alg.data(), "ZLIB", 4 ) == 0 )
134 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZLIB;
135 else if ( alg.size() == 4 && strncasecmp( alg.data(), "LZMA", 4 ) == 0 )
136 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZMA;
137 else if ( alg.size() == 3 && strncasecmp( alg.data(), "LZ4", 3 ) == 0 )
138 alg_code = ROOT::RCompressionSetting::EAlgorithm::kLZ4;
139# if ROOT_VERSION_CODE >= ROOT_VERSION( 6, 20, 0 )
140 else if ( alg.size() == 4 && strncasecmp( alg.data(), "ZSTD", 4 ) == 0 )
141 alg_code = ROOT::RCompressionSetting::EAlgorithm::kZSTD;
142# endif
143 else
144 throw runtime_error( "ERROR: request to set unknown ROOT compression algorithm:" + std::string{ alg } );
145 res = ::sscanf( std::string{ compression.substr( idx + 1 ) }.c_str(), "%d",
146 &level ); // TODO: use C++17 std::from_chars instead...
147 if ( res == 1 ) {
148 s_compressionLevel = ROOT::CompressionSettings( alg_code, level );
149 return StatusCode::SUCCESS;
150 }
151 throw runtime_error( "ERROR: request to set unknown ROOT compression level:" +
152 std::string{ compression.substr( idx + 1 ) } );
153 } else if ( 1 == ::sscanf( std::string{ compression }.c_str(), "%d", &level ) ) { // TODO: use C++17 std::from_chars
154 // instead
155 s_compressionLevel = level;
156 return StatusCode::SUCCESS;
157 }
158 throw runtime_error( "ERROR: request to set unknown ROOT compression mechanism:" + std::string{ compression } );
159#else
160 if ( !compression.empty() ) {}
161 return StatusCode::SUCCESS;
162#endif
163}
164
166int RootConnectionSetup::compression() { return s_compressionLevel; }
167
170
173
176 std::shared_ptr<RootConnectionSetup> setup )
177 : IDataConnection( owner, std::string{ fname } ), m_setup( std::move( setup ) ) {
178 // 01234567890123456789012345678901234567890
179 // Check if FID: A82A3BD8-7ECB-DC11-8DC0-000423D950B0
180 if ( fname.size() == 36 && fname[8] == '-' && fname[13] == '-' && fname[18] == '-' && fname[23] == '-' ) {
181 m_name = "FID:";
182 m_name.append( fname.data(), fname.size() );
183 }
184 m_age = 0;
185 m_file.reset();
186 addClient( owner );
187}
188
190void RootDataConnection::addClient( const IInterface* client ) { m_clients.insert( client ); }
191
194 auto i = m_clients.find( client );
195 if ( i != m_clients.end() ) m_clients.erase( i );
196 return m_clients.size();
197}
198
200bool RootDataConnection::lookupClient( const IInterface* client ) const {
201 auto i = m_clients.find( client );
202 return i != m_clients.end();
203}
204
206void RootDataConnection::badWriteError( std::string_view msg ) const {
207 msgSvc() << MSG::ERROR << "File:" << fid() << "Failed action:" << msg << endmsg;
208}
209
211void RootDataConnection::saveStatistics( std::string_view statisticsFile ) {
212 if ( m_statistics ) {
213 m_statistics->Print();
214 if ( !statisticsFile.empty() ) m_statistics->SaveAs( std::string{ statisticsFile }.c_str() );
215 m_statistics.reset();
216 }
217}
218
220void RootDataConnection::enableStatistics( std::string_view section ) {
221 if ( m_statistics ) {
222 TTree* t = getSection( section, false );
223 if ( t ) {
224 m_statistics.reset( new TTreePerfStats( ( std::string{ section } + "_ioperf" ).c_str(), t ) );
225 return;
226 }
227 msgSvc() << MSG::WARNING << "Failed to enable perfstats for tree:" << section << endmsg;
228 return;
229 }
230 msgSvc() << MSG::INFO << "Perfstats are ALREADY ENABLED." << endmsg;
231}
232
235 if ( !m_refs ) m_refs = (TTree*)m_file->Get( "Refs" );
236 if ( m_refs ) m_tool.reset( new RootTool( this ) );
237#ifdef __POOL_COMPATIBILITY
238 else if ( m_file->Get( "##Links" ) != nullptr )
239 m_tool.reset( new PoolTool( this ) );
240#endif
241 else
242 m_tool.reset();
243 return m_tool.get();
244}
245
248 m_file.reset( TFile::Open( m_pfn.c_str() ) );
249 if ( !m_file || m_file->IsZombie() ) {
250 m_file.reset();
251 return StatusCode::FAILURE;
252 }
254 msgSvc() << MSG::DEBUG << "Opened file " << m_pfn << " in mode READ. [" << m_fid << "]" << endmsg << MSG::DEBUG;
255 if ( msgSvc().isActive() ) m_file->ls();
256 msgSvc() << MSG::VERBOSE;
257 if ( msgSvc().isActive() ) m_file->Print();
258 if ( makeTool() ) {
259 sc = m_tool->readRefs();
260 sc.ignore();
261#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
262 if ( sc == Status::ROOT_READ_ERROR ) {
263 IIncidentSvc* inc = m_setup->incidentSvc();
264 if ( inc ) { inc->fireIncident( Incident( pfn(), IncidentType::CorruptedInputFile ) ); }
265 }
266#endif
267 }
268 if ( !sc.isSuccess() ) return sc;
269 bool need_fid = m_fid == m_pfn;
270 string fid = m_fid;
271 m_mergeFIDs.clear();
272 for ( auto& elem : m_params ) {
273 if ( elem.first == "FID" ) {
274 m_mergeFIDs.push_back( elem.second );
275 if ( elem.second != m_fid ) {
276 msgSvc() << MSG::DEBUG << "Check FID param:" << elem.second << endmsg;
277 // if ( m_fid == m_pfn ) {
278 m_fid = elem.second;
279 //}
280 }
281 }
282 }
283 if ( !need_fid && fid != m_fid ) {
284 msgSvc() << MSG::ERROR << "FID mismatch:" << fid << "(Catalog) != " << m_fid << "(file)" << endmsg
285 << "for PFN:" << m_pfn << endmsg;
286 return StatusCode::FAILURE;
287 }
288 msgSvc() << MSG::DEBUG << "Using FID " << m_fid << " from params table...." << endmsg << "for PFN:" << m_pfn
289 << endmsg;
290 return sc;
291}
292
295 int compress = RootConnectionSetup::compression();
296 msgSvc() << MSG::DEBUG;
297 std::string spec = m_pfn;
298 if ( m_setup->produceReproducibleFiles ) spec += "?reproducible"; // https://root.cern.ch/doc/master/classTFile.html
299 switch ( typ ) {
300 case CREATE:
301 resetAge();
302 m_file.reset( TFile::Open( spec.c_str(), "CREATE", "Root event data", compress ) );
303#if ROOT_HAS_630_FWD_COMPAT
304 if ( m_file && m_setup->root630ForwardCompatibility ) m_file->SetBit( TFile::k630forwardCompatibility );
305#endif
306 m_refs = new TTree( "Refs", "Root reference data" );
307 msgSvc() << "Opened file " << m_pfn << " in mode CREATE. [" << m_fid << "]" << endmsg;
308 m_params.emplace_back( "PFN", m_pfn );
309 if ( m_fid != m_pfn ) { m_params.emplace_back( "FID", m_fid ); }
310 makeTool();
311 break;
312 case RECREATE:
313 resetAge();
314 m_file.reset( TFile::Open( spec.c_str(), "RECREATE", "Root event data", compress ) );
315#if ROOT_HAS_630_FWD_COMPAT
316 if ( m_file && m_setup->root630ForwardCompatibility ) m_file->SetBit( TFile::k630forwardCompatibility );
317#endif
318 msgSvc() << "Opened file " << m_pfn << " in mode RECREATE. [" << m_fid << "]" << endmsg;
319 m_refs = new TTree( "Refs", "Root reference data" );
320 m_params.emplace_back( "PFN", m_pfn );
321 if ( m_fid != m_pfn ) { m_params.emplace_back( "FID", m_fid ); }
322 makeTool();
323 break;
324 case UPDATE:
325 resetAge();
326 m_file.reset( TFile::Open( spec.c_str(), "UPDATE", "Root event data", compress ) );
327 msgSvc() << "Opened file " << m_pfn << " in mode UPDATE. [" << m_fid << "]" << endmsg;
328 if ( m_file && !m_file->IsZombie() ) {
329 if ( makeTool() ) {
330 StatusCode sc = m_tool->readRefs();
331 sc.ignore();
332 if ( sc == Status::ROOT_READ_ERROR ) {
333#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
334 IIncidentSvc* inc = m_setup->incidentSvc();
335 if ( inc ) { inc->fireIncident( Incident( pfn(), IncidentType::CorruptedInputFile ) ); }
336#endif
337 }
338 return sc;
339 }
340 TDirectory::TContext ctxt( m_file.get() );
341 m_refs = new TTree( "Refs", "Root reference data" );
342 makeTool();
343 return StatusCode::SUCCESS;
344 }
345 break;
346 default:
347 m_refs = nullptr;
348 m_file.reset();
349 return StatusCode::FAILURE;
350 }
352}
353
356 if ( m_file ) {
357 if ( !m_file->IsZombie() ) {
358 if ( m_file->IsWritable() ) {
359 msgSvc() << MSG::DEBUG;
360 TDirectory::TContext ctxt( m_file.get() );
361 if ( m_refs ) {
362 if ( !m_tool->saveRefs().isSuccess() ) badWriteError( "Saving References" );
363 if ( m_refs->Write() < 0 ) badWriteError( "Write Reference branch" );
364 }
365 for ( auto& i : m_sections ) {
366 if ( i.second ) {
367 if ( i.second->Write() < 0 ) badWriteError( "Write section:" + i.first );
368 msgSvc() << "Disconnect section " << i.first << " " << i.second->GetName() << endmsg;
369 }
370 }
371 m_sections.clear();
372 }
373 msgSvc() << MSG::DEBUG;
374 if ( msgSvc().isActive() ) m_file->ls();
375 msgSvc() << MSG::VERBOSE;
376 if ( msgSvc().isActive() ) m_file->Print();
377 m_file->Close();
378 }
379 msgSvc() << MSG::DEBUG << "Disconnected file " << m_pfn << " " << m_file->GetName() << endmsg;
380 m_file.reset();
381 m_tool.reset();
382 }
383 return StatusCode::SUCCESS;
384}
385
387TTree* RootDataConnection::getSection( std::string_view section, bool create ) {
388 auto it = m_sections.find( section );
389 TTree* t = ( it != m_sections.end() ? it->second : nullptr );
390 if ( !t ) {
391 t = (TTree*)m_file->Get( std::string{ section }.c_str() );
392 if ( !t && create ) {
393 TDirectory::TContext ctxt( m_file.get() );
394 t = new TTree( std::string{ section }.c_str(), "Root data for Gaudi" );
395 }
396 if ( t ) {
397 int cacheSize = m_setup->cacheSize;
398 if ( create ) {
399 // t->SetAutoFlush(100);
400 }
401 if ( section == m_setup->loadSection && cacheSize > -2 ) {
402 MsgStream& msg = msgSvc();
403 int learnEntries = m_setup->learnEntries;
404 t->SetCacheSize( cacheSize );
405 t->SetCacheLearnEntries( learnEntries );
406 msg << MSG::DEBUG;
407 if ( create ) {
408 msg << "Tree:" << section << "Setting up tree cache:" << cacheSize << endmsg;
409 } else {
410 const StringVec& vB = m_setup->vetoBranches;
411 const StringVec& cB = m_setup->cacheBranches;
412 msg << "Tree:" << section << " Setting up tree cache:" << cacheSize << " Add all branches." << endmsg;
413 msg << "Tree:" << section << " Learn for " << learnEntries << " entries." << endmsg;
414
415 if ( cB.empty() && vB.empty() ) {
416 msg << "Adding (default) all branches to tree cache." << endmsg;
417 t->AddBranchToCache( "*", kTRUE );
418 }
419 if ( cB.size() == 1 && cB[0] == "*" ) {
420 msg << "Adding all branches to tree cache according to option \"CacheBranches\"." << endmsg;
421 t->AddBranchToCache( "*", kTRUE );
422 } else {
423 for ( TIter it( t->GetListOfBranches() ); it.Next(); ) {
424 const char* n = ( (TNamed*)( *it ) )->GetName();
425 bool add = false, veto = false;
426 for ( const auto& i : cB ) {
427 if ( !match_wild( n, ( i ).c_str() ) ) continue;
428 add = true;
429 break;
430 }
431 for ( auto i = vB.cbegin(); !add && i != vB.cend(); ++i ) {
432 if ( !match_wild( n, ( *i ).c_str() ) ) continue;
433 veto = true;
434 break;
435 }
436 if ( add && !veto ) {
437 msg << "Add " << n << " to branch cache." << endmsg;
438 t->AddBranchToCache( n, kTRUE );
439 } else {
440 msg << "Do not cache branch " << n << endmsg;
441 }
442 }
443 }
444 }
445 }
446 m_sections[std::string{ section }] = t;
447 } else {
448 // in some rare cases we do have the entry we expect, but we cannot read it
449 // https://gitlab.cern.ch/gaudi/Gaudi/-/issues/301
450 auto key = m_file->GetKey( std::string{ section }.c_str() );
451 if ( key ) {
452 incidentSvc()->fireIncident( Incident( pfn(), IncidentType::CorruptedInputFile ) );
453 msgSvc() << MSG::ERROR << fmt::format( "failed to get TTree '{}' in {}", section, pfn() ) << endmsg;
454 }
455 }
456 }
457 return t;
458}
459
461TBranch* RootDataConnection::getBranch( std::string_view section, std::string_view branch_name, TClass* cl, void* ptr,
462 int buff_siz, int split_lvl ) {
463 string n = std::string{ branch_name };
464 std::replace_if(
465 begin( n ), end( n ), []( const char c ) { return !isalnum( c ); }, '_' );
466 n += ".";
467 TTree* t = getSection( section, true );
468 TBranch* b = t->GetBranch( n.c_str() );
469 if ( !b && cl && m_file->IsWritable() ) {
470 b = t->Branch( n.c_str(), cl->GetName(), (void*)( ptr ? &ptr : nullptr ), buff_siz, split_lvl );
471 }
472 if ( !b ) b = t->GetBranch( std::string{ branch_name }.c_str() );
473 if ( b ) b->SetAutoDelete( kFALSE );
474 return b;
475}
476
478int RootDataConnection::makeLink( std::string_view p ) {
479 auto ip = std::find( std::begin( m_links ), std::end( m_links ), p );
480 if ( ip != std::end( m_links ) ) return std::distance( std::begin( m_links ), ip );
481 m_links.push_back( std::string{ p } );
482 return m_links.size() - 1;
483}
484
486CSTR RootDataConnection::getDb( int which ) const {
487 if ( ( which >= 0 ) && ( size_t( which ) < m_dbs.size() ) ) {
488 if ( *( m_dbs.begin() + which ) == s_local ) return m_fid;
489 return *( m_dbs.begin() + which );
490 }
491 return s_empty;
492}
493
495CSTR RootDataConnection::empty() const { return s_empty; }
496
498pair<int, unsigned long> RootDataConnection::saveObj( std::string_view section, std::string_view cnt, TClass* cl,
499 DataObject* pObj, int minBufferSize, int maxBufferSize,
500 int approxEventsPerBasket, int split_lvl, bool fill ) {
501 DataObjectPush push( pObj );
502 return save( section, cnt, cl, pObj, minBufferSize, maxBufferSize, approxEventsPerBasket, split_lvl, fill );
503}
504
506pair<int, unsigned long> RootDataConnection::save( std::string_view section, std::string_view cnt, TClass* cl,
507 void* pObj, int minBufferSize, int maxBufferSize,
508 int approxEventsPerBasket, int split_lvl, bool fill_missing ) {
509 split_lvl = 0;
510 TBranch* b = getBranch( section, cnt, cl, pObj ? &pObj : nullptr, minBufferSize, split_lvl );
511 if ( b ) {
512 Long64_t evt = b->GetEntries();
513 // msgSvc() << MSG::DEBUG << cnt.c_str() << " Obj:" << (void*)pObj
514 // << " Split:" << split_lvl << " Buffer size:" << minBufferSize << endl;
515 bool set_buffer_size = ( evt == 0 );
516 if ( fill_missing ) {
517 Long64_t num, nevt = b->GetTree()->GetEntries();
518 if ( nevt > evt ) {
519 set_buffer_size = true;
520 b->SetAddress( nullptr );
521 num = nevt - evt;
522 while ( num > 0 ) {
523 b->Fill();
524 --num;
525 }
526 msgSvc() << MSG::DEBUG << "Added " << long( nevt - evt ) << " / Tree: " << nevt
527 << " / Branch: " << b->GetEntries() + 1 << " NULL entries to:" << cnt << endmsg;
528 evt = b->GetEntries();
529 }
530 }
531 if ( set_buffer_size ) {
532 auto dummy_file = make_unique<TMemFile>( "dummy.root", "CREATE" );
533 auto dummy_tree = make_unique<TTree>( "DummyTree", "DummyTree", split_lvl, dummy_file->GetDirectory( "/" ) );
534 TBranch* dummy_branch = dummy_tree->Branch( "DummyBranch", cl->GetName(), &pObj, minBufferSize, split_lvl );
535 Int_t nWritten = dummy_branch->Fill();
536 if ( nWritten < 0 ) return { nWritten, evt };
537 Int_t newBasketSize = nWritten * approxEventsPerBasket;
538 // Ensure that newBasketSize doesn't wrap around
539 if ( std::numeric_limits<Int_t>::max() / approxEventsPerBasket < nWritten ) {
540 newBasketSize = std::numeric_limits<Int_t>::max();
541 }
542 b->SetBasketSize( std::min( maxBufferSize, std::max( minBufferSize, newBasketSize ) ) );
543 msgSvc() << MSG::DEBUG << "Setting basket size to " << newBasketSize << " for " << cnt << endmsg;
544 }
545 b->SetAddress( &pObj );
546 return { b->Fill(), evt };
547 }
548 if ( pObj ) { msgSvc() << MSG::ERROR << "Failed to access branch " << m_name << "/" << cnt << endmsg; }
549 return { -1, ~0 };
550}
551
553int RootDataConnection::loadObj( std::string_view section, std::string_view cnt, unsigned long entry,
554 DataObject*& pObj ) {
555 TBranch* b = getBranch( section, cnt );
556 if ( b ) {
557 TClass* cl = gROOT->GetClass( b->GetClassName(), kTRUE );
558 if ( cl ) {
559 int nb = -1;
560 pObj = (DataObject*)cl->New();
561 {
562 DataObjectPush push( pObj );
563 b->SetAddress( &pObj );
564 if ( section == m_setup->loadSection ) {
565 TTree* t = b->GetTree();
566 if ( Long64_t( entry ) != t->GetReadEntry() ) { t->LoadTree( Long64_t( entry ) ); }
567 }
568 nb = b->GetEntry( entry );
569 msgSvc() << MSG::VERBOSE;
570 if ( msgSvc().isActive() ) {
571 msgSvc() << "Load [" << entry << "] --> " << section << ":" << cnt << " " << nb << " bytes." << endmsg;
572 }
573 if ( nb < 0 ) { // This is definitely an error...ROOT says if reads fail, -1 is issued.
574#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
575 IIncidentSvc* inc = m_setup->incidentSvc();
576 if ( inc ) { inc->fireIncident( Incident( pfn(), IncidentType::CorruptedInputFile ) ); }
577#endif
578 } else if ( nb == 0 && pObj->clID() == CLID_DataObject ) {
579 TFile* f = b->GetFile();
580 int vsn = f->GetVersion();
581 if ( vsn < 52400 ) {
582 // For Gaudi v21r5 (ROOT 5.24.00b) DataObject::m_version was not written!
583 // Still this call be well be successful.
584 nb = 1;
585 } else if ( vsn > 1000000 && ( vsn % 1000000 ) < 52400 ) {
586 // dto. Some POOL files have for unknown reasons a version
587 // not according to ROOT standards. Hack this explicitly.
588 nb = 1;
589 }
590 }
591 if ( nb < 0 ) {
592 delete pObj;
593 pObj = nullptr;
594 }
595 }
596 return nb;
597 }
598 }
599 return -1;
600}
601
603int RootDataConnection::loadRefs( std::string_view section, std::string_view cnt, unsigned long entry,
604 RootObjectRefs& refs ) {
605 int nbytes = m_tool->loadRefs( section, cnt, entry, refs );
606#if ROOT_VERSION_CODE >= ROOT_VERSION( 5, 33, 0 )
607 if ( nbytes < 0 ) {
608 // This is definitely an error:
609 // -- Either branch not present at all or
610 // -- ROOT I/O error, which issues -1
611 IIncidentSvc* inc = m_setup->incidentSvc();
612 if ( inc ) { inc->fireIncident( Incident( pfn(), IncidentType::CorruptedInputFile ) ); }
613 }
614#endif
615 return nbytes;
616}
617
619pair<const RootRef*, const RootDataConnection::ContainerSection*>
620RootDataConnection::getMergeSection( std::string_view container, int entry ) const {
621 // size_t idx = cont.find('/',1);
622 // string container = cont[0]=='/' ? cont.substr(1,idx==string::npos?idx:idx-1) : cont;
623 auto i = m_mergeSects.find( container );
624 if ( i != m_mergeSects.end() ) {
625 size_t cnt = 0;
626 const ContainerSections& s = ( *i ).second;
627 for ( auto j = s.cbegin(); j != s.cend(); ++j, ++cnt ) {
628 const ContainerSection& c = *j;
629 if ( entry >= c.start && entry < ( c.start + c.length ) ) {
630 if ( m_linkSects.size() > cnt ) {
631 if ( msgSvc().isActive() ) {
632 msgSvc() << MSG::VERBOSE << "MergeSection for:" << container << " [" << entry << "]" << endmsg
633 << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
634 }
635 return { &( m_linkSects[cnt] ), &c };
636 }
637 }
638 }
639 }
640 msgSvc() << MSG::DEBUG << "Return INVALID MergeSection for:" << container << " [" << entry << "]" << endmsg
641 << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
642 return { nullptr, nullptr };
643}
644
647 IOpaqueAddress* pA = pR.address();
648 makeRef( pR.name(), pA->clID(), pA->svcType(), pA->par()[0], pA->par()[1], -1, ref );
649}
650
652void RootDataConnection::makeRef( std::string_view name, long clid, int tech, std::string_view dbase,
653 std::string_view cnt, int entry, RootRef& ref ) {
654 auto db = ( dbase == m_fid ? std::string_view{ s_local } : dbase );
655 ref.entry = entry;
656
657 int cdb = -1;
658 if ( !db.empty() ) {
659 auto idb = std::find_if( m_dbs.begin(), m_dbs.end(), [&]( const std::string& i ) { return i == db; } );
660 cdb = std::distance( m_dbs.begin(), idb );
661 if ( idb == m_dbs.end() ) m_dbs.push_back( std::string{ db } );
662 }
663
664 int ccnt = -1;
665 if ( !cnt.empty() ) {
666 auto icnt = std::find_if( m_conts.begin(), m_conts.end(), [&]( const std::string& i ) { return i == cnt; } );
667 ccnt = std::distance( m_conts.begin(), icnt );
668 if ( icnt == m_conts.end() ) m_conts.push_back( std::string{ cnt } );
669 }
670
671 int clnk = -1;
672 if ( !name.empty() ) {
673 auto ilnk = std::find_if( m_links.begin(), m_links.end(), [&]( const std::string& i ) { return i == name; } );
674 clnk = std::distance( m_links.begin(), ilnk );
675 if ( ilnk == m_links.end() ) m_links.push_back( std::string{ name } );
676 }
677
678 ref.dbase = cdb;
679 ref.container = ccnt;
680 ref.link = clnk;
681 ref.clid = clid;
682 ref.svc = tech;
683 if ( ref.svc == POOL_ROOT_StorageType || ref.svc == POOL_ROOTKEY_StorageType ||
684 ref.svc == POOL_ROOTTREE_StorageType ) {
685 ref.svc = ROOT_StorageType;
686 }
687}
const long POOL_ROOTKEY_StorageType
Definition ClassID.h:77
const long POOL_ROOT_StorageType
Definition ClassID.h:76
const long POOL_ROOTTREE_StorageType
Definition ClassID.h:78
const long ROOT_StorageType
Definition ClassID.h:60
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
const std::string CSTR
#define STATUSCODE_ENUM_IMPL(...)
Assign a category to the StatusCode enum declared with STATUSCODE_ENUM_DECL( ENUM )
Definition StatusCode.h:295
A DataObject is the base class of any identifiable object on any data store.
Definition DataObject.h:37
virtual const CLID & clID() const
Retrieve reference to class definition structure.
int m_age
Age counter.
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.
Helper class to facilitate an abstraction layer for reading POOL style files with this package.
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.
Description:
Definition RootTool.h:28
The interface implemented by the IncidentSvc service.
virtual void fireIncident(const Incident &incident)=0
Fire an Incident.
Definition of the basic interface.
Definition IInterface.h:225
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...
Definition IRegistry.h:29
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 Incident.h:24
Definition of the MsgStream class used to transmit messages.
Definition MsgStream.h:29
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
static const Category & default_category() noexcept
Default Gaudi StatusCode category.
Definition StatusCode.h:310
const StatusCode & ignore() const
Allow discarding a StatusCode without warning.
Definition StatusCode.h:139
unsigned long code_t
type of StatusCode value
Definition StatusCode.h:66
bool isSuccess() const
Definition StatusCode.h:314
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition __init__.py:1
@ WARNING
Definition IMessageSvc.h:22
@ DEBUG
Definition IMessageSvc.h:22
@ ERROR
Definition IMessageSvc.h:22
@ INFO
Definition IMessageSvc.h:22
@ VERBOSE
Definition IMessageSvc.h:22
STL namespace.
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.
Definition extractEvt.C:81
Persistent reference object.
Definition extractEvt.C:44
The category assigned to a StatusCode.
virtual std::string message(code_t code) const
Description for code within this category.
Definition StatusCode.h:85