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