DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
Serialization.hpp
Go to the documentation of this file.
1
10#ifndef SERIALIZATION_INCLUDE_SERIALIZATION_SERIALIZATION_HPP_
11#define SERIALIZATION_INCLUDE_SERIALIZATION_SERIALIZATION_HPP_
12
13#include "ers/Issue.hpp"
14#include "logging/Logging.hpp" // NOTE: if ISSUES ARE DECLARED BEFORE include logging/Logging.hpp, TLOG_DEBUG<<issue wont work.
15
16#include "boost/preprocessor.hpp"
17#include "msgpack.hpp"
18
19#include <algorithm>
20#include <string>
21#include <vector>
22
23#define DUNE_DAQ_TYPESTRING(Type, typestring) \
24 template<> \
25 inline std::string dunedaq::datatype_to_string<Type>() \
26 { \
27 return typestring; \
28 }
29
30#define DUNE_DAQ_SERIALIZABLE(Type, typestring) \
31 DUNE_DAQ_TYPESTRING(Type, typestring) \
32 template<> \
33 struct dunedaq::serialization::is_serializable<Type> \
34 { \
35 static constexpr bool value = true; \
36 }
37
55// NOLINTNEXTLINE(build/define_used)
56#define DUNE_DAQ_SERIALIZE(Type, ...) \
57 MSGPACK_DEFINE(__VA_ARGS__) \
58 static_assert(true, "")
59
60// Helper macros for DUNE_DAQ_SERIALIZE_NON_INTRUSIVE()
61// NOLINTNEXTLINE(build/define_used)
62#define OPACK(r, data, elem) o.pack(m.elem);
63// NOLINTNEXTLINE
64#define OUNPACK(r, data, elem) m.elem = o.via.array.ptr[i++].as<decltype(m.elem)>();
65
86// NOLINTNEXTLINE
87#define DUNE_DAQ_SERIALIZE_NON_INTRUSIVE(NS, Type, ...) \
88 DUNE_DAQ_SERIALIZABLE(NS::Type, #Type); \
89 namespace msgpack { \
90 MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) \
91 { \
92 namespace adaptor { \
93 template<> \
94 struct pack<NS::Type> \
95 { \
96 template<typename Stream> \
97 packer<Stream>& operator()(msgpack::packer<Stream>& o, NS::Type const& m) const \
98 { \
99 o.pack_array(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)); \
100 BOOST_PP_SEQ_FOR_EACH(OPACK, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
101 return o; \
102 } \
103 }; \
104 template<> \
105 struct convert<NS::Type> \
106 { \
107 msgpack::object const& operator()(msgpack::object const& o, NS::Type& m) const \
108 { \
109 if (o.type != msgpack::type::ARRAY) \
110 throw msgpack::type_error(); \
111 if (o.via.array.size != BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)) \
112 throw msgpack::type_error(); \
113 int i = 0; \
114 BOOST_PP_SEQ_FOR_EACH(OUNPACK, , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
115 return o; \
116 } \
117 }; \
118 } \
119 } \
120 }
121
122namespace dunedaq {
123
124// clang-format off
125// Disable coverage collection LCOV_EXCL_START
126ERS_DECLARE_ISSUE(serialization, // namespace
127 UnknownSerializationTypeString, // issue name
128 "Unknown serialization type " << t, // message
129 ((std::string)t)) // attributes
130
131ERS_DECLARE_ISSUE(serialization, // namespace
133 "Unknown serialization type",) // message
134
135ERS_DECLARE_ISSUE(serialization, // namespace
136 UnknownSerializationTypeByte, // issue name
137 "Unknown serialization type " << t, // message
138 ((char)t)) // attributes // NOLINT
139
140ERS_DECLARE_ISSUE(serialization, // namespace
142 "Cannot deserialize message",) // message
143
144// clang-format on
145// Re-enable coverage collection LCOV_EXCL_STOP
146
147template<typename T>
148inline std::string
150{
151 return "Unknown";
152}
153
154namespace serialization {
155
156template<typename T>
157struct is_serializable : std::false_type
158{};
159
167
172from_string(const std::string s)
173{
174 if (s == "msgpack")
175 return kMsgPack;
176 throw UnknownSerializationTypeString(ERS_HERE, s);
177}
178
179constexpr uint8_t // NOLINT(build/unsigned)
181{
182 switch (stype) {
183 case kMsgPack:
184 return 'M';
185 default:
187 }
188}
189
193template<class T>
194std::vector<uint8_t> // NOLINT(build/unsigned)
195serialize(const T& obj, SerializationType stype)
196{
197 switch (stype) {
198 case kMsgPack: {
199 // Serialize into the sbuffer and then copy to a
200 // std::vector. Seems like it would be more efficient to
201 // write directly to the vector (by creating a class that
202 // implements `void write(char* buf, size_t len)`), but my
203 // tests aren't any faster than this
204 msgpack::sbuffer buf;
205 msgpack::pack(buf, obj);
206 std::vector<uint8_t> ret(buf.size() + 1); // NOLINT(build/unsigned)
207 ret[0] = serialization_type_byte(stype);
208 std::copy(buf.data(), buf.data() + buf.size(), ret.begin() + 1); // NOLINT
209 return ret;
210 }
211 default:
213 }
214}
215
219template<class T, typename CharType = unsigned char>
220T
221deserialize(const std::vector<CharType>& v)
222{
223 // The first byte in the array indicates the serialization format;
224 // the rest is the actual message
225 switch (v[0]) {
227 try {
228 // The lambda function here is of type `unpack_reference_func`
229 // as described at
230 // https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_unpacker#memory-management
231 // . It is called for every STR, BIN and EXT field in the
232 // MsgPack data. If the function returns false, the object is
233 // copied into MsgPack's "zone", otherwise a pointer to the
234 // original buffer is stored. Our input buffer is going to exist
235 // at least until the end of this function, so it's safe to
236 // return true (ie, store a pointer in the MsgPack object; no
237 // copy) everywhere. Doing so results in a factor ~2 speedup in
238 // deserializing Fragment, which is just a large BIN field
239 msgpack::object_handle oh = msgpack::unpack(
240 const_cast<char*>(reinterpret_cast<const char*>(v.data() + 1)),
241 v.size() - 1,
242 [](msgpack::type::object_type /*typ*/, std::size_t /*length*/, void* /*user_data*/) -> bool { return true; });
243 msgpack::object obj = oh.get();
244 return obj.as<T>();
245 } catch (msgpack::type_error& e) {
247 } catch (msgpack::unpack_error& e) {
249 }
250 }
251 default:
252 throw UnknownSerializationTypeByte(ERS_HERE, (char)v[0]); // NOLINT
253 }
254}
255
256} // namespace serialization
257} // namespace dunedaq
258
259#endif // SERIALIZATION_INCLUDE_SERIALIZATION_SERIALIZATION_HPP_
#define ERS_DECLARE_ISSUE(namespace_name, class_name, message, attributes)
#define ERS_HERE
SerializationType
Serialization methods that are available.
T deserialize(const std::vector< CharType > &v)
Deserialize vector of bytes v into an instance of class T.
std::vector< uint8_t > serialize(const T &obj, SerializationType stype)
Serialize object obj using serialization method stype.
constexpr uint8_t serialization_type_byte(SerializationType stype)
SerializationType from_string(const std::string s)
Convert string to SerializationType.
Including Qt Headers.
Unknown serialization Cannot deserialize std::string datatype_to_string()
UnknownSerializationTypeEnum
Unknown serialization CannotDeserializeMessage