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 <span>
27#include <type_traits>
28#include <typeinfo>
29
30namespace o2::conf
31{
32class ConfigurableParam;
33}
34
35namespace o2::framework
36{
37
38template <typename H>
39concept DataHeaderLike = requires(H& dh) {dh.dataOrigin; dh.dataDescription; dh.subSpecification; };
40
41// FIXME: Should enforce the fact that DataRefs are read only...
43
44 template <typename T>
45 static auto as(DataRef const& ref)
46 {
47 auto payloadSize = DataRefUtils::getPayloadSize(ref);
48 // SFINAE makes this available only for the case we are using
49 // trivially copyable type, this is to distinguish it from the
50 // alternative below, which works for TObject (which are serialised).
51 if constexpr (is_messageable<T>::value == true) {
53 auto header = o2::header::get<const DataHeader*>(ref.header);
54 if (header->payloadSerializationMethod != o2::header::gSerializationMethodNone) {
55 throw runtime_error("Attempt to extract a POD from a wrong message kind");
56 }
57 if ((payloadSize % sizeof(T)) != 0) {
58 throw runtime_error("Cannot extract POD from message as size do not match");
59 }
60 // FIXME: provide a const collection
61 return gsl::span<T>(reinterpret_cast<T*>(const_cast<char*>(ref.payload)), payloadSize / sizeof(T));
62 } else if constexpr (has_root_dictionary<T>::value == true &&
63 is_messageable<T>::value == false) {
64 std::unique_ptr<T> result;
65 static_assert(is_type_complete_v<struct RootSerializationSupport>, "Framework/RootSerializationSupport.h not included");
66 call_if_defined<struct RootSerializationSupport>([&](auto* p) {
67 // Classes using the ROOT ClassDef/ClassImp macros can be detected automatically
68 // and the serialization method can be deduced. If the class is also
69 // messageable, gSerializationMethodNone is used by default. This substitution
70 // does not apply for such types, the serialization method needs to be specified
71 // explicitely using type wrapper @a ROOTSerialized.
72 using RSS = std::decay_t<decltype(*p)>;
74 auto header = o2::header::get<const DataHeader*>(ref.header);
75 if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) {
76 throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message");
77 }
78
79 typename RSS::FairInputTBuffer ftm(const_cast<char*>(ref.payload), payloadSize);
80 auto* requestedClass = RSS::TClass::GetClass(typeid(T));
81 ftm.InitMap();
82 auto* storedClass = ftm.ReadClass();
83 // should always have the class description if has_root_dictionary is true
84 assert(requestedClass != nullptr);
85
86 ftm.SetBufferOffset(0);
87 ftm.ResetMap();
88 auto* object = ftm.ReadObjectAny(storedClass);
89 if (object == nullptr) {
90 throw runtime_error_f("Failed to read object with name %s from message using ROOT serialization.",
91 (storedClass != nullptr ? storedClass->GetName() : "<unknown>"));
92 }
93
94 if constexpr (std::is_base_of<typename RSS::TObject, T>::value) { // compile time switch
95 // if the type to be extracted inherits from TObject, the class descriptions
96 // do not need to match exactly, only the dynamic_cast has to work
97 // FIXME: could probably try to extend this to arbitrary types T being a base
98 // to the stored type, but this would require to cast the extracted object to
99 // the actual type. This requires this information to be available as a type
100 // in the ROOT dictionary, check if this is the case or if TClass::DynamicCast
101 // can be used for the test. Right now, this case was and is not covered and
102 // not yet checked in the unit test either.
103 auto* r = dynamic_cast<T*>(static_cast<typename RSS::TObject*>(object));
104 if (r) {
105 result.reset(r);
106 }
107 // This check includes the case: storedClass == requestedClass
108 } else if (storedClass->InheritsFrom(requestedClass)) {
109 result.reset(static_cast<T*>(object));
110 }
111 if (result == nullptr) {
112 // did not manage to cast the pointer to result
113 // delete object via the class info if available, not the case for all types,
114 // e.g. for standard containers of ROOT objects apparently this is not always
115 // the case
116 auto* delfunc = storedClass->GetDelete();
117 if (delfunc) {
118 (*delfunc)(object);
119 }
120
121 throw runtime_error_f("Attempting to extract a %s but a %s is actually stored which cannot be casted to the requested one.",
122 (requestedClass != nullptr ? requestedClass->GetName() : "<unknown>"),
123 (storedClass != nullptr ? storedClass->GetName() : "<unknown>"));
124 }
125 // collections in ROOT can be non-owning or owning and the proper cleanup depends on
126 // this flag. Be it a bug or a feature in ROOT, but the owning flag of the extracted
127 // object only depends on the state at serialization of the original object. However,
128 // all objects created during deserialization are new and must be owned by the collection
129 // to avoid memory leak. So we call SetOwner if it is available for the type.
130 if constexpr (requires(T t) { t.SetOwner(true); }) {
131 result->SetOwner(true);
132 }
133 });
134
135 return std::move(result);
136 } else if constexpr (is_specialization_v<T, ROOTSerialized> == true) {
137 // See above. SFINAE allows us to use this to extract a ROOT-serialized object
138 // with a somewhat uniform API. ROOT serialization method is enforced by using
139 // type wrapper @a ROOTSerialized
140 static_assert(is_type_complete_v<struct RootSerializationSupport>, "Framework/RootSerializationSupport.h not included");
141 using wrapped = typename T::wrapped_type;
143 std::unique_ptr<wrapped> result;
144
145 call_if_defined<struct RootSerializationSupport>([&](auto* p) {
146 using RSS = std::decay_t<decltype(*p)>;
147
148 auto header = o2::header::get<const DataHeader*>(ref.header);
149 if (header->payloadSerializationMethod != o2::header::gSerializationMethodROOT) {
150 throw runtime_error("Attempt to extract a TMessage from non-ROOT serialised message");
151 }
152 auto* cl = RSS::TClass::GetClass(typeid(wrapped));
153 if (has_root_dictionary<wrapped>::value == false && cl == nullptr) {
154 throw runtime_error("ROOT serialization not supported, dictionary not found for data type");
155 }
156
157 typename RSS::FairInputTBuffer ftm(const_cast<char*>(ref.payload), payloadSize);
158 ftm.InitMap();
159 auto* classInfo = ftm.ReadClass();
160 ftm.SetBufferOffset(0);
161 ftm.ResetMap();
162 result.reset(static_cast<wrapped*>(ftm.ReadObjectAny(cl)));
163 if (result.get() == nullptr) {
164 throw runtime_error_f("Unable to extract class %s", cl == nullptr ? "<name not available>" : cl->GetName());
165 }
166 // workaround for ROOT feature, see above
167 if constexpr (requires(T t) { t.SetOwner(true); }) {
168 result->SetOwner(true);
169 }
170 });
171 return std::move(result);
172 } else if constexpr (is_specialization_v<T, CCDBSerialized> == true) {
173 using wrapped = typename T::wrapped_type;
175 auto* ptr = DataRefUtils::decodeCCDB(ref, typeid(wrapped));
176 if constexpr (std::is_base_of<o2::conf::ConfigurableParam, wrapped>::value) {
177 auto& param = const_cast<typename std::remove_const<wrapped&>::type>(wrapped::Instance());
178 param.syncCCDBandRegistry(ptr);
179 ptr = &param;
180 }
181 std::unique_ptr<wrapped> result(static_cast<wrapped*>(ptr));
182 return std::move(result);
183 }
184 }
185 // Decode a CCDB object using the CcdbApi.
186 static void* decodeCCDB(DataRef const& ref, std::type_info const& info);
187 static std::map<std::string, std::string> extractCCDBHeaders(DataRef const& ref);
191 static std::span<const char> getCCDBPayloadBlob(DataRef const& ref);
192
194 {
196 auto* header = o2::header::get<const DataHeader*>(ref.header);
197 if (!header) {
198 return 0;
199 }
200 // in case of an O2 message with multiple payloads, the size of the message stored
201 // in DataRef is returned,
202 // as a prototype solution we are using splitPayloadIndex == splitPayloadParts to
203 // indicate that there are splitPayloadParts payloads following the header
204 if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) {
205 return ref.payloadSize;
206 }
207 return header->payloadSize < ref.payloadSize || ref.payloadSize == 0 ? header->payloadSize : ref.payloadSize;
208 }
209
210 template <typename T>
211 static auto getHeader(const DataRef& ref)
212 {
213 using HeaderT = typename std::remove_pointer<T>::type;
214 static_assert(std::is_pointer<T>::value && std::is_base_of<o2::header::BaseHeader, HeaderT>::value,
215 "pointer to BaseHeader-derived type required");
216 return o2::header::get<T>(ref.header);
217 }
218
219 static bool isValid(DataRef const& ref)
220 {
221 return ref.header != nullptr;
222 }
223
227 static bool match(DataRef const& ref, const char* binding)
228 {
229 return ref.spec != nullptr && ref.spec->binding == binding;
230 }
231
232 template <DataHeaderLike H>
233 static bool matchHeader(DataRef const& ref, InputSpec const& spec)
234 {
235 auto const* dh = o2::header::get<H*>(ref.header);
236 if (dh == nullptr) {
237 return false;
238 }
239 return DataSpecUtils::match(spec, dh->dataOrigin, dh->dataDescription, dh->subSpecification);
240 }
241
245 template <DataHeaderLike... H>
246 static bool match(DataRef const& ref, InputSpec const& spec)
247 {
248 return (DataRefUtils::matchHeader<H>(ref, spec) || ... || matchHeader<o2::header::DataHeader>(ref, spec));
249 }
250};
251
252} // namespace o2::framework
253
254#endif // O2_FRAMEWORK_DATAREFUTILS_H_
std::string binding
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 ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
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 std::span< const char > getCCDBPayloadBlob(DataRef const &ref)
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:620