The Gaudi Framework  master (34daa81a)
Loading...
Searching...
No Matches
BranchWrapper.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 2024-2026 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#include <Gaudi/Algorithm.h>
13#include <TMemFile.h>
14#include <TTree.h>
15#include <format>
16#include <map>
17#include <memory>
18#include <string>
19#include <utility>
20
21namespace {
22 // Mapping from C++ type names to ROOT branch types for branch creation
23 const std::map<std::string, std::string, std::less<>> typeMap = {
24 { "char", "B" }, { "unsigned char", "b" }, { "short", "S" }, { "unsigned short", "s" },
25 { "int", "I" }, { "unsigned int", "i" }, { "float", "F" }, { "double", "D" },
26 { "long long", "L" }, { "unsigned long long", "l" }, { "long", "G" }, { "unsigned long", "g" },
27 { "bool", "O" } };
28
29 // Helper function to find the ROOT branch type corresponding to a C++ type
30 std::optional<std::string> getLeafListForType( const std::string_view& typeName ) {
31 auto it = typeMap.find( typeName );
32 return ( it != typeMap.end() ) ? std::optional<std::string>{ it->second } : std::nullopt;
33 }
34} // namespace
35
36namespace Gaudi::details {
37
38 BranchWrapper::BranchWrapper( const gsl::not_null<TTree*> tree, const std::string& className,
39 const std::string& branchName, const std::string& location, const std::string& algName,
40 unsigned int bufferSize, unsigned int splitLevel )
41 : m_className( className ), m_location( location ) {
42 auto leafListTag = getLeafListForType( m_className );
43 if ( leafListTag ) {
44 // Create a branch for fundamental types using the leaflist
45 m_branch = tree->Branch( branchName.c_str(), &m_dataBuffer,
46 ( std::format( "{}/{}", m_className, leafListTag.value() ) ).c_str(), bufferSize );
47 setBranchAddress = []( gsl::not_null<TBranch*> br, const void** wrappedDataPtr ) {
48 br->SetAddress( const_cast<void*>( *wrappedDataPtr ) );
49 };
50
51 } else if ( TClass::GetClass( m_className.c_str() ) ) {
52 // Create a branch for object types using the classname string
53 m_branch = tree->Branch( branchName.c_str(), m_className.c_str(), &m_dataBuffer, bufferSize, splitLevel );
54 // ROOT auto-allocates an object
55 m_ownsBuffer = m_dataBuffer != nullptr;
56 setBranchAddress = []( gsl::not_null<TBranch*> br, const void** wrappedDataPtr ) {
57 br->SetAddress( wrappedDataPtr );
58 };
59
60 } else {
61 throw GaudiException( std::format( "Cannot create branch {} for unknown class: {}. Provide a dictionary please.",
62 branchName, m_className ),
63 algName, StatusCode::FAILURE );
64 }
65
66 if ( !m_branch ) {
67 throw GaudiException( std::format( "Failed to create branch {} for type {}.", branchName, m_className ), algName,
69 }
70 }
71
73 if ( m_ownsBuffer && m_dataBuffer ) {
74 auto* cls = TClass::GetClass( m_className.c_str() );
75 if ( cls ) cls->Destructor( const_cast<void*>( m_dataBuffer ) );
76 m_dataBuffer = nullptr;
77 m_ownsBuffer = false;
78 }
79 }
80
82 : m_dataBuffer( other.m_dataBuffer )
83 , m_branch( std::exchange( other.m_branch, nullptr ) )
84 , m_ownsBuffer( std::exchange( other.m_ownsBuffer, false ) )
85 , m_className( std::move( other.m_className ) )
86 , m_location( std::move( other.m_location ) )
87 , setBranchAddress( other.setBranchAddress ) {}
88
90 if ( this != &other ) {
91 // Clean up current state
92 if ( m_ownsBuffer && m_dataBuffer ) {
93 auto* cls = TClass::GetClass( m_className.c_str() );
94 if ( cls ) cls->Destructor( const_cast<void*>( m_dataBuffer ) );
95 }
96 m_dataBuffer = other.m_dataBuffer;
97 m_branch = std::exchange( other.m_branch, nullptr );
98 m_ownsBuffer = std::exchange( other.m_ownsBuffer, false );
99 m_className = std::move( other.m_className );
100 m_location = std::move( other.m_location );
101 setBranchAddress = other.setBranchAddress;
102 }
103 return *this;
104 }
105
106 // Set the data pointer for the branch from a given address
107 // Used by Gaudi::NTuple::Writer
108 void BranchWrapper::setDataPtr( void const* dataPtr ) {
109 m_ownsBuffer = false;
110 m_dataBuffer = dataPtr;
112 }
113
114 // Set the data for the branch from a given DataObject
115 // Used by Gaudi::NTuple::GenericWriter
116 void BranchWrapper::setBranchData( const gsl::not_null<DataObject*> pObj ) {
117 auto baseWrapper = dynamic_cast<AnyDataWrapperBase*>( pObj.get() );
118 m_ownsBuffer = false;
119 m_dataBuffer = baseWrapper ? baseWrapper->payload() : pObj.get();
121 }
122
123 unsigned int BranchWrapper::computeOptimalBufferSize( unsigned int minBufferSize, unsigned int maxBufferSize,
124 unsigned int approxEventsPerBasket, unsigned int splitLevel ) {
125 // simply try to write one item in a dummey branch of a dummy tree im memory and measure size
126 auto dummy_file = std::make_unique<TMemFile>( "dummy.root", "CREATE" );
127 auto dummy_tree = std::make_unique<TTree>( "DummyTree", "DummyTree", splitLevel, dummy_file->GetDirectory( "/" ) );
128 auto leafListTag = getLeafListForType( m_className );
129 TBranch* dummy_branch{ nullptr };
130 if ( leafListTag ) {
131 // Create a branch for fundamental types using the leaflist
132 dummy_branch =
133 dummy_tree->Branch( "DummyBranch", &m_dataBuffer,
134 ( fmt::format( "{}/{}", m_className, leafListTag.value() ) ).c_str(), minBufferSize );
135 } else if ( TClass::GetClass( m_className.c_str() ) ) {
136 // Create a branch for object types using the classname string
137 dummy_branch = dummy_tree->Branch( "DummyBranch", m_className.c_str(), &m_dataBuffer, minBufferSize, splitLevel );
138 } // no else as unknown className would have raised an exception at constructor level
139 int nWritten = dummy_branch->Fill();
140 if ( nWritten >= 0 ) {
141 unsigned int newBasketSize = nWritten * approxEventsPerBasket;
142 // Ensure that newBasketSize doesn't wrap around
143 if ( std::numeric_limits<Int_t>::max() / approxEventsPerBasket < (unsigned int)nWritten ) {
144 newBasketSize = std::numeric_limits<Int_t>::max();
145 }
146 return std::min( maxBufferSize, std::max( minBufferSize, newBasketSize ) );
147 }
148 return minBufferSize;
149 }
150
152 if ( !m_branch ) return;
153 auto nEvents = m_branch->GetTree()->GetEntries();
154 auto nEntries = m_branch->GetEntries();
155 if ( nEntries < nEvents ) {
156 m_branch->SetAddress( nullptr );
157 for ( auto i = nEntries; i < nEvents; i++ ) { m_branch->Fill(); }
158 }
159 }
160
161} // namespace Gaudi::details
Define general base for Gaudi exception.
constexpr static const auto FAILURE
Definition StatusCode.h:100
unsigned int computeOptimalBufferSize(unsigned int minBufferSize, unsigned int maxBufferSize, unsigned int approxEventsPerBasket, unsigned int splitLevel)
compute optimal buffer size to fit given number of element per basket, respecting given min and max
void padEntries()
pad the number of item in the branch to the one of the Tree in which it leaves by adding empty entrie...
void(* setBranchAddress)(gsl::not_null< TBranch * >, const void **)
void setDataPtr(void const *dataPtr)
BranchWrapper(const gsl::not_null< TTree * > tree, const std::string &className, const std::string &branchName, const std::string &location, const std::string &algName, unsigned int bufferSize=32000, unsigned int splitLevel=99)
BranchWrapper & operator=(BranchWrapper &&other) noexcept
void setBranchData(const gsl::not_null< DataObject * > pObj)