Project
Loading...
Searching...
No Matches
DataRefUtils.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11#ifndef O2_FRAMEWORK_DATAREFUTILS_H_
12#define O2_FRAMEWORK_DATAREFUTILS_H_
13
15#include "Framework/DataRef.h"
20#include "Headers/DataHeader.h"
23
24#include <gsl/gsl>
25
26#include <type_traits>
27#include <typeinfo>
28
29namespace o2::conf
30{
31class ConfigurableParam;
32}
33
34namespace o2::framework
35{
36
37template <typename H>
38concept DataHeaderLike = requires(H& dh) {dh.dataOrigin; dh.dataDescription; dh.subSpecification; };
39
40// FIXME: Should enforce the fact that DataRefs are read only...
42
43 template <typename T>
44 static auto as(DataRef const& ref)
45 {
46 auto payloadSize = DataRefUtils::getPayloadSize(ref);
47 // SFINAE makes this available only for the case we are using
48 // trivially copyable type, this is to distinguish it from the
49 // alternative below, which works for TObject (which are serialised).
50 if constexpr (is_messageable<T>::value == true) {
52 auto header = o2::header::get<const DataHeader*>(ref.header);
53 if (header->payloadSerializationMethod != o2::header::gSerializationMethodNone) {
54 throw runtime_error("Attempt to extract a POD from a wrong message kind");
55 }
56 if ((payloadSize % sizeof(T)) != 0) {
57 throw runtime_error("Cannot extract POD from message as size do not match");
58 }
59 // FIXME: provide a const collection
60 return gsl::span<T>(reinterpret_cast<T*>(const_cast<char*>(ref.payload)), payloadSize / sizeof(T));
61 } else if constexpr (has_root_dictionary<T>::value == true &&
62 is_messageable<T>::value == false) {
63 std::unique_ptr<T> result;
64 static_assert(is_type_complete_v<struct RootSerializationSupport>, "Framework/RootSerializationSupport.h not included");
65 call_if_defined<struct RootSerializationSupport>([&](auto* p) {
66 // Classes using the ROOT ClassDef/ClassImp macros can be detected automatically
67 // and the serialization method can be deduced. If the class is also
68 // messageable, gSerializationMethodNone is used by default. This substitution
69 // does not apply for such types, the serialization method needs to be specified
70 // explicitely using type wrapper @a ROOTSerialized.
71 using RSS = std::decay_t<decltype(*p)>;
73 auto header = o2::header::get<const DataHeader*>(ref.header);
74 if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) {
75 throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message");
76 }
77
78 typename RSS::FairInputTBuffer ftm(const_cast<char*>(ref.payload), payloadSize);
79 auto* requestedClass = RSS::TClass::GetClass(typeid(T));
80 ftm.InitMap();
81 auto* storedClass = ftm.ReadClass();
82 // should always have the class description if has_root_dictionary is true
83 assert(requestedClass != nullptr);
84
85 ftm.SetBufferOffset(0);
86 ftm.ResetMap();
87 auto* object = ftm.ReadObjectAny(storedClass);
88 if (object == nullptr) {
89 throw runtime_error_f("Failed to read object with name %s from message using ROOT serialization.",
90 (storedClass != nullptr ? storedClass->GetName() : "<unknown>"));
91 }
92
93 if constexpr (std::is_base_of<typename RSS::TObject, T>::value) { // compile time switch
94 // if the type to be extracted inherits from TObject, the class descriptions
95 // do not need to match exactly, only the dynamic_cast has to work
96 // FIXME: could probably try to extend this to arbitrary types T being a base
97 // to the stored type, but this would require to cast the extracted object to
98 // the actual type. This requires this information to be available as a type
99 // in the ROOT dictionary, check if this is the case or if TClass::DynamicCast
100 // can be used for the test. Right now, this case was and is not covered and
101 // not yet checked in the unit test either.
102 auto* r = dynamic_cast<T*>(static_cast<typename RSS::TObject*>(object));
103 if (r) {
104 result.reset(r);
105 }
106 // This check includes the case: storedClass == requestedClass
107 } else if (storedClass->InheritsFrom(requestedClass)) {
108 result.reset(static_cast<T*>(object));
109 }
110 if (result == nullptr) {
111 // did not manage to cast the pointer to result
112 // delete object via the class info if available, not the case for all types,
113 // e.g. for standard containers of ROOT objects apparently this is not always
114 // the case
115 auto* delfunc = storedClass->GetDelete();
116 if (delfunc) {
117 (*delfunc)(object);
118 }
119
120 throw runtime_error_f("Attempting to extract a %s but a %s is actually stored which cannot be casted to the requested one.",
121 (requestedClass != nullptr ? requestedClass->GetName() : "<unknown>"),
122 (storedClass != nullptr ? storedClass->GetName() : "<unknown>"));
123 }
124 // collections in ROOT can be non-owning or owning and the proper cleanup depends on
125 // this flag. Be it a bug or a feature in ROOT, but the owning flag of the extracted
126 // object only depends on the state at serialization of the original object. However,
127 // all objects created during deserialization are new and must be owned by the collection
128 // to avoid memory leak. So we call SetOwner if it is available for the type.
129 if constexpr (requires(T t) { t.SetOwner(true); }) {
130 result->SetOwner(true);
131 }
132 });
133
134 return std::move(result);
135 } else if constexpr (is_specialization_v<T, ROOTSerialized> == true) {
136 // See above. SFINAE allows us to use this to extract a ROOT-serialized object
137 // with a somewhat uniform API. ROOT serialization method is enforced by using
138 // type wrapper @a ROOTSerialized
139 static_assert(is_type_complete_v<struct RootSerializationSupport>, "Framework/RootSerializationSupport.h not included");
140 using wrapped = typename T::wrapped_type;
142 std::unique_ptr<wrapped> result;
143
144 call_if_defined<struct RootSerializationSupport>([&](auto* p) {
145 using RSS = std::decay_t<decltype(*p)>;
146
147 auto header = o2::header::get<const DataHeader*>(ref.header);
148 if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) {
149 throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message");
150 }
151 auto* cl = RSS::TClass::GetClass(typeid(wrapped));
152 if (has_root_dictionary<wrapped>::value == false && cl == nullptr) {
153 throw runtime_error("ROOT serialization not supported, dictionary not found for data type");
154 }
155
156 typename RSS::FairInputTBuffer ftm(const_cast<char*>(ref.payload), payloadSize);
157 ftm.InitMap();
158 auto* classInfo = ftm.ReadClass();
159 ftm.SetBufferOffset(0);
160 ftm.ResetMap();
161 result.reset(static_cast<wrapped*>(ftm.ReadObjectAny(cl)));
162 if (result.get() == nullptr) {
163 throw runtime_error_f("Unable to extract class %s", cl == nullptr ? "<name not available>" : cl->GetName());
164 }
165 // workaround for ROOT feature, see above
166 if constexpr (requires(T t) { t.SetOwner(true); }) {
167 result->SetOwner(true);
168 }
169 });
170 return std::move(result);
171 } else if constexpr (is_specialization_v<T, CCDBSerialized> == true) {
172 using wrapped = typename T::wrapped_type;
174 auto* ptr = DataRefUtils::decodeCCDB(ref, typeid(wrapped));
175 if constexpr (std::is_base_of<o2::conf::ConfigurableParam, wrapped>::value) {
176 auto& param = const_cast<typename std::remove_const<wrapped&>::type>(wrapped::Instance());
177 param.syncCCDBandRegistry(ptr);
178 ptr = &param;
179 }
180 std::unique_ptr<wrapped> result(static_cast<wrapped*>(ptr));
181 return std::move(result);
182 }
183 }
184 // Decode a CCDB object using the CcdbApi.
185 static void* decodeCCDB(DataRef const& ref, std::type_info const& info);
186 static std::map<std::string, std::string> extractCCDBHeaders(DataRef const& ref);
187
189 {
191 auto* header = o2::header::get<const DataHeader*>(ref.header);
192 if (!header) {
193 return 0;
194 }
195 // in case of an O2 message with multiple payloads, the size of the message stored
196 // in DataRef is returned,
197 // as a prototype solution we are using splitPayloadIndex == splitPayloadParts to
198 // indicate that there are splitPayloadParts payloads following the header
199 if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) {
200 return ref.payloadSize;
201 }
202 return header->payloadSize < ref.payloadSize || ref.payloadSize == 0 ? header->payloadSize : ref.payloadSize;
203 }
204
205 template <typename T>
206 static auto getHeader(const DataRef& ref)
207 {
208 using HeaderT = typename std::remove_pointer<T>::type;
209 static_assert(std::is_pointer<T>::value && std::is_base_of<o2::header::BaseHeader, HeaderT>::value,
210 "pointer to BaseHeader-derived type required");
211 return o2::header::get<T>(ref.header);
212 }
213
214 static bool isValid(DataRef const& ref)
215 {
216 return ref.header != nullptr;
217 }
218
222 static bool match(DataRef const& ref, const char* binding)
223 {
224 return ref.spec != nullptr && ref.spec->binding == binding;
225 }
226
227 template <DataHeaderLike H>
228 static bool matchHeader(DataRef const& ref, InputSpec const& spec)
229 {
230 auto const* dh = o2::header::get<H*>(ref.header);
231 if (dh == nullptr) {
232 return false;
233 }
234 return DataSpecUtils::match(spec, dh->dataOrigin, dh->dataDescription, dh->subSpecification);
235 }
236
240 template <DataHeaderLike... H>
241 static bool match(DataRef const& ref, InputSpec const& spec)
242 {
243 return (DataRefUtils::matchHeader<H>(ref, spec) || ... || matchHeader<o2::header::DataHeader>(ref, spec));
244 }
245};
246
247} // namespace o2::framework
248
249#endif // O2_FRAMEWORK_DATAREFUTILS_H_
Type wrappers for enfording a specific serialization method.
TBranch * ptr
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint object
Definition glcorearb.h:4041
GLboolean r
Definition glcorearb.h:1233
GLenum GLfloat param
Definition glcorearb.h:271
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
RuntimeErrorRef runtime_error(const char *)
RuntimeErrorRef runtime_error_f(const char *,...)
constexpr o2::header::SerializationMethod gSerializationMethodROOT
Definition DataHeader.h:328
constexpr o2::header::SerializationMethod gSerializationMethodNone
Definition DataHeader.h:327
static o2::header::DataHeader::PayloadSizeType getPayloadSize(const DataRef &ref)
static std::map< std::string, std::string > extractCCDBHeaders(DataRef const &ref)
static void * decodeCCDB(DataRef const &ref, std::type_info const &info)
static bool match(DataRef const &ref, const char *binding)
static bool matchHeader(DataRef const &ref, InputSpec const &spec)
static auto getHeader(const DataRef &ref)
static bool match(DataRef const &ref, InputSpec const &spec)
static auto as(DataRef const &ref)
static bool isValid(DataRef const &ref)
static bool match(InputSpec const &spec, ConcreteDataMatcher const &target)
the main header struct
Definition DataHeader.h:618