Coverage Report

Created: 2024-04-30 09:35

src/zserio/SerializeUtil.h
Line
Count
Source
1
/**
2
 * \file
3
 * It provides help methods for serialization and deserialization of generated objects.
4
 *
5
 * These utilities are not used by generated code and they are provided only for user convenience.
6
 *
7
 * \note Please note that file operations allocate memory as needed and are not designed to use allocators.
8
 */
9
10
#ifndef ZSERIO_SERIALIZE_UTIL_H_INC
11
#define ZSERIO_SERIALIZE_UTIL_H_INC
12
13
#include "zserio/BitStreamReader.h"
14
#include "zserio/BitStreamWriter.h"
15
#include "zserio/FileUtil.h"
16
#include "zserio/Traits.h"
17
#include "zserio/Vector.h"
18
19
namespace zserio
20
{
21
22
namespace detail
23
{
24
25
template <typename T>
26
void initializeChildrenImpl(std::true_type, T& object)
27
9
{
28
9
    object.initializeChildren();
29
9
}
30
31
template <typename T>
32
void initializeChildrenImpl(std::false_type, T&)
33
{}
34
35
template <typename T>
36
void initializeChildren(T& object)
37
9
{
38
9
    initializeChildrenImpl(has_initialize_children<T>(), object);
39
9
}
40
41
template <typename T, typename... ARGS>
42
void initializeImpl(std::true_type, T& object, ARGS&&... arguments)
43
6
{
44
6
    object.initialize(std::forward<ARGS>(arguments)...);
45
6
}
46
47
template <typename T>
48
void initializeImpl(std::false_type, T& object)
49
9
{
50
9
    initializeChildren(object);
51
9
}
52
53
template <typename T, typename... ARGS>
54
void initialize(T& object, ARGS&&... arguments)
55
15
{
56
15
    initializeImpl(has_initialize<T>(), object, std::forward<ARGS>(arguments)...);
57
15
}
58
59
template <typename T, typename = void>
60
struct allocator_chooser
61
{
62
    using type = std::allocator<uint8_t>;
63
};
64
65
template <typename T>
66
struct allocator_chooser<T, detail::void_t<typename T::allocator_type>>
67
{
68
    using type = typename T::allocator_type;
69
};
70
71
// This implementation needs to be in detail because old MSVC compiler 2015 has problems with calling overload.
72
template <typename T, typename ALLOC, typename... ARGS>
73
BasicBitBuffer<ALLOC> serialize(T& object, const ALLOC& allocator, ARGS&&... arguments)
74
15
{
75
15
    detail::initialize(object, std::forward<ARGS>(arguments)...);
76
15
    BasicBitBuffer<ALLOC> bitBuffer(object.initializeOffsets(), allocator);
77
15
    BitStreamWriter writer(bitBuffer);
78
15
    object.write(writer);
79
15
    return bitBuffer;
80
15
}
81
82
} // namespace detail
83
84
/**
85
 * Serializes given generated object to bit buffer using given allocator.
86
 *
87
 * Before serialization, the method properly calls on the given zserio object methods `initialize()`
88
 * (if exits), `initializeChildren()` (if exists) and `initializeOffsets()`.
89
 *
90
 * Example:
91
 * \code{.cpp}
92
 *     #include <zserio/SerializeUtil.h>
93
 *     #include <zserio/pmr/PolymorphicAllocator.h>
94
 *
95
 *     const zserio::pmr::PolymorphicAllocator<> allocator;
96
 *     SomeZserioObject object(allocator);
97
 *     const zserio::BasicBitBuffer<zserio::pmr::PolymorphicAllocator<>> bitBuffer =
98
 *             zserio::serialize(object, allocator);
99
 * \endcode
100
 *
101
 * \param object Generated object to serialize.
102
 * \param allocator Allocator to use to allocate bit buffer.
103
 * \param arguments Object's actual parameters for initialize() method (optional).
104
 *
105
 * \return Bit buffer containing the serialized object.
106
 *
107
 * \throw CppRuntimeException When serialization fails.
108
 */
109
template <typename T, typename ALLOC, typename... ARGS,
110
        typename std::enable_if<!std::is_enum<T>::value && is_allocator<ALLOC>::value, int>::type = 0>
111
BasicBitBuffer<ALLOC> serialize(T& object, const ALLOC& allocator, ARGS&&... arguments)
112
4
{
113
4
    return detail::serialize(object, allocator, std::forward<ARGS>(arguments)...);
114
4
}
115
116
/**
117
 * Serializes given generated object to bit buffer using default allocator 'std::allocator<uint8_t>'.
118
 *
119
 * Before serialization, the method properly calls on the given zserio object methods `initialize()`
120
 * (if exits), `initializeChildren()` (if exists) and `initializeOffsets()`.
121
 *
122
 * Example:
123
 * \code{.cpp}
124
 *     #include <zserio/SerializeUtil.h>
125
 *
126
 *     SomeZserioObject object;
127
 *     const zserio::BitBuffer bitBuffer = zserio::serialize(object);
128
 * \endcode
129
 *
130
 * \param object Generated object to serialize.
131
 * \param arguments Object's actual parameters for initialize() method (optional).
132
 *
133
 * \return Bit buffer containing the serialized object.
134
 *
135
 * \throw CppRuntimeException When serialization fails.
136
 */
137
template <typename T, typename ALLOC = typename detail::allocator_chooser<T>::type, typename... ARGS,
138
        typename std::enable_if<!std::is_enum<T>::value &&
139
                        !is_first_allocator<typename std::decay<ARGS>::type...>::value,
140
                int>::type = 0>
141
BasicBitBuffer<ALLOC> serialize(T& object, ARGS&&... arguments)
142
5
{
143
5
    return detail::serialize(object, ALLOC(), std::forward<ARGS>(arguments)...);
144
5
}
145
146
/**
147
 * Serializes given generated enum to bit buffer.
148
 *
149
 * Example:
150
 * \code{.cpp}
151
 *     #include <zserio/SerializeUtil.h>
152
 *
153
 *     const SomeZserioEnum enumValue = SomeZserioEnum::SomeEnumValue;
154
 *     const zserio::BitBuffer bitBuffer = zserio::serialize(enumValue);
155
 * \endcode
156
 *
157
 * \param enumValue Generated enum to serialize.
158
 * \param allocator Allocator to use to allocate bit buffer.
159
 *
160
 * \return Bit buffer containing the serialized enum.
161
 *
162
 * \throw CppRuntimeException When serialization fails.
163
 */
164
template <typename T, typename ALLOC = std::allocator<uint8_t>,
165
        typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
166
BasicBitBuffer<ALLOC> serialize(T enumValue, const ALLOC& allocator = ALLOC())
167
6
{
168
6
    BasicBitBuffer<ALLOC> bitBuffer(zserio::bitSizeOf(enumValue), allocator);
169
6
    BitStreamWriter writer(bitBuffer);
170
6
    zserio::write(writer, enumValue);
171
6
    return bitBuffer;
172
6
}
173
174
/**
175
 * Deserializes given bit buffer to instance of generated object.
176
 *
177
 * Example:
178
 * \code{.cpp}
179
 *     #include <zserio/SerializeUtil.h>
180
 *
181
 *     SomeZserioObject object;
182
 *     const zserio::BitBuffer bitBuffer = zserio::serialize(object);
183
 *     SomeZserioObject readObject = zserio::deserialize<SomeZserioObject>(bitBuffer);
184
 * \endcode
185
 *
186
 * \param bitBuffer Bit buffer to use.
187
 * \param arguments Object's actual parameters together with allocator for object's read constructor (optional).
188
 *
189
 * \return Generated object created from the given bit buffer.
190
 *
191
 * \throw CppRuntimeException When deserialization fails.
192
 */
193
template <typename T, typename ALLOC, typename... ARGS>
194
typename std::enable_if<!std::is_enum<T>::value, T>::type deserialize(
195
        const BasicBitBuffer<ALLOC>& bitBuffer, ARGS&&... arguments)
196
12
{
197
12
    BitStreamReader reader(bitBuffer);
198
12
    return T(reader, std::forward<ARGS>(arguments)...);
199
12
}
200
201
/**
202
 * Deserializes given bit buffer to instance of generated enum.
203
 *
204
 * Example:
205
 * \code{.cpp}
206
 *     #include <zserio/SerializeUtil.h>
207
 *
208
 *     const SomeZserioEnum enumValue = SomeZserioEnum::SomeEnumValue;
209
 *     const zserio::BitBuffer bitBuffer = zserio::serialize(enumValue);
210
 *     const SomeZserioEnum readEnumValue = zserio::deserialize<DummyEnum>(bitBuffer);
211
 * \endcode
212
 *
213
 * \param bitBuffer Bit buffer to use.
214
 *
215
 * \return Generated enum created from the given bit buffer.
216
 *
217
 * \throw CppRuntimeException When deserialization fails.
218
 */
219
template <typename T, typename ALLOC>
220
typename std::enable_if<std::is_enum<T>::value, T>::type deserialize(const BasicBitBuffer<ALLOC>& bitBuffer)
221
3
{
222
3
    BitStreamReader reader(bitBuffer);
223
3
    return zserio::read<T>(reader);
224
3
}
225
226
/**
227
 * Serializes given generated object to vector of bytes using given allocator.
228
 *
229
 * Before serialization, the method properly calls on the given zserio object methods `initialize()`
230
 * (if exits), `initializeChildren()` (if exists) and `initializeOffsets()`.
231
 *
232
 * Example:
233
 * \code{.cpp}
234
 *     #include <zserio/SerializeUtil.h>
235
 *     #include <zserio/pmr/PolymorphicAllocator.h>
236
 *
237
 *     const zserio::pmr::PolymorphicAllocator<> allocator;
238
 *     SomeZserioObject object(allocator);
239
 *     const zserio::vector<uint8_t, zserio::pmr::PolymorphicAllocator<>> buffer =
240
 *             zserio::serializeToBytes(object, allocator);
241
 * \endcode
242
 *
243
 * \param object Generated object to serialize.
244
 * \param allocator Allocator to use to allocate vector.
245
 * \param arguments Object's actual parameters for initialize() method (optional).
246
 *
247
 * \return Vector of bytes containing the serialized object.
248
 *
249
 * \throw CppRuntimeException When serialization fails.
250
 */
251
template <typename T, typename ALLOC, typename... ARGS,
252
        typename std::enable_if<!std::is_enum<T>::value && is_allocator<ALLOC>::value, int>::type = 0>
253
vector<uint8_t, ALLOC> serializeToBytes(T& object, const ALLOC& allocator, ARGS&&... arguments)
254
4
{
255
4
    const BasicBitBuffer<ALLOC> bitBuffer =
256
4
            detail::serialize(object, allocator, std::forward<ARGS>(arguments)...);
257
258
4
    return bitBuffer.getBytes();
259
4
}
260
261
/**
262
 * Serializes given generated object to vector of bytes using default allocator 'std::allocator<uint8_t>'.
263
 *
264
 * Before serialization, the method properly calls on the given zserio object methods `initialize()`
265
 * (if exits), `initializeChildren()` (if exists) and `initializeOffsets()`.
266
 *
267
 * However, it's still possible that not all bits of the last byte are used. In this case, only most
268
 * significant bits of the corresponding size are used.
269
 *
270
 * Example:
271
 * \code{.cpp}
272
 *     #include <zserio/SerializeUtil.h>
273
 *
274
 *     SomeZserioObject object;
275
 *     const zserio::vector<uint8_t> buffer = zserio::serializeToBytes(object);
276
 * \endcode
277
 *
278
 * \param object Generated object to serialize.
279
 * \param arguments Object's actual parameters for initialize() method (optional).
280
 *
281
 * \return Vector of bytes containing the serialized object.
282
 *
283
 * \throw CppRuntimeException When serialization fails.
284
 */
285
template <typename T, typename ALLOC = typename detail::allocator_chooser<T>::type, typename... ARGS,
286
        typename std::enable_if<!std::is_enum<T>::value &&
287
                        !is_first_allocator<typename std::decay<ARGS>::type...>::value,
288
                int>::type = 0>
289
vector<uint8_t, ALLOC> serializeToBytes(T& object, ARGS&&... arguments)
290
2
{
291
2
    const BasicBitBuffer<ALLOC> bitBuffer =
292
2
            detail::serialize(object, ALLOC(), std::forward<ARGS>(arguments)...);
293
294
2
    return bitBuffer.getBytes();
295
2
}
296
297
/**
298
 * Serializes given generated enum to vector of bytes.
299
 *
300
 * Example:
301
 * \code{.cpp}
302
 *     #include <zserio/SerializeUtil.h>
303
 *
304
 *     const SomeZserioEnum enumValue = SomeZserioEnum::SomeEnumValue;
305
 *     const zserio::vector<uint8_t> buffer = zserio::serializeToBytes(enumValue);
306
 * \endcode
307
 *
308
 * \param enumValue Generated enum to serialize.
309
 * \param allocator Allocator to use to allocate vector.
310
 *
311
 * \return Vector of bytes containing the serialized enum.
312
 *
313
 * \throw CppRuntimeException When serialization fails.
314
 */
315
template <typename T, typename ALLOC = std::allocator<uint8_t>,
316
        typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
317
vector<uint8_t, ALLOC> serializeToBytes(T enumValue, const ALLOC& allocator = ALLOC())
318
3
{
319
3
    const BasicBitBuffer<ALLOC> bitBuffer = serialize(enumValue, allocator);
320
321
3
    return bitBuffer.getBytes();
322
3
}
323
324
/**
325
 * Deserializes given vector of bytes to instance of generated object.
326
 *
327
 * This method can potentially use all bits of the last byte even if not all of them were written during
328
 * serialization (because there is no way how to specify exact number of bits). Thus, it could allow reading
329
 * behind stream (possibly in case of damaged data).
330
 *
331
 * Example:
332
 * \code{.cpp}
333
 *     #include <zserio/SerializeUtil.h>
334
 *
335
 *     SomeZserioObject object;
336
 *     const zserio::vector<uint8_t> buffer = zserio::serializeToBytes(object);
337
 *     SomeZserioObject readObject = zserio::deserializeFromBytes<SomeZserioObject>(buffer);
338
 * \endcode
339
 *
340
 * \param bitBuffer Vector of bytes to use.
341
 * \param arguments Object's actual parameters together with allocator for object's read constructor (optional).
342
 *
343
 * \return Generated object created from the given vector of bytes.
344
 *
345
 * \throw CppRuntimeException When deserialization fails.
346
 */
347
template <typename T, typename... ARGS>
348
typename std::enable_if<!std::is_enum<T>::value, T>::type deserializeFromBytes(
349
        Span<const uint8_t> buffer, ARGS&&... arguments)
350
9
{
351
9
    BitStreamReader reader(buffer);
352
9
    return T(reader, std::forward<ARGS>(arguments)...);
353
9
}
354
355
/**
356
 * Deserializes given vector of bytes to instance of generated enum.
357
 *
358
 * Example:
359
 * \code{.cpp}
360
 *     #include <zserio/SerializeUtil.h>
361
 *
362
 *     const SomeZserioEnum enumValue = SomeZserioEnum::SomeEnumValue;
363
 *     const zserio::vector<uint8_t> buffer = zserio::serializeToBytes(enumValue);
364
 *     const SomeZserioEnum readEnumValue = zserio::deserializeFromBytes<DummyEnum>(buffer);
365
 * \endcode
366
 *
367
 * \param bitBuffer Vector of bytes to use.
368
 *
369
 * \return Generated enum created from the given vector of bytes.
370
 *
371
 * \throw CppRuntimeException When deserialization fails.
372
 */
373
template <typename T>
374
typename std::enable_if<std::is_enum<T>::value, T>::type deserializeFromBytes(Span<const uint8_t> buffer)
375
3
{
376
3
    BitStreamReader reader(buffer);
377
3
    return zserio::read<T>(reader);
378
3
}
379
380
/**
381
 * Serializes given generated object to file.
382
 *
383
 * Example:
384
 * \code{.cpp}
385
 *     #include <zserio/SerializeUtil.h>
386
 *
387
 *     SomeZserioObject object;
388
 *     zserio::serializeToFile(object, "FileName.bin");
389
 * \endcode
390
 *
391
 * \param object Generated object to serialize.
392
 * \param fileName File name to write.
393
 *
394
 * \throw CppRuntimeException When serialization fails.
395
 */
396
template <typename T, typename... ARGS>
397
void serializeToFile(T& object, const std::string& fileName, ARGS&&... arguments)
398
3
{
399
3
    const auto bitBuffer = serialize(object, std::forward<ARGS>(arguments)...);
400
3
    writeBufferToFile(bitBuffer, fileName);
401
3
}
402
403
/**
404
 * Deserializes given file contents to instance of generated object.
405
 *
406
 * Example:
407
 * \code{.cpp}
408
 *     #include <zserio/SerializeUtil.h>
409
 *
410
 *     const std::string fileName = "FileName.bin";
411
 *     SomeZserioObject object;
412
 *     zserio::serializeToFile(object, fileName);
413
 *     SomeZserioObject readObject = zserio::deserializeFromFile<SomeZserioObject>(fileName);
414
 * \endcode
415
 *
416
 * \note Please note that BitBuffer is always allocated using 'std::allocator<uint8_t>'.
417
 *
418
 * \param fileName File to use.
419
 * \param arguments Object's arguments (optional).
420
 *
421
 * \return Generated object created from the given file contents.
422
 *
423
 * \throw CppRuntimeException When deserialization fails.
424
 */
425
template <typename T, typename... ARGS>
426
T deserializeFromFile(const std::string& fileName, ARGS&&... arguments)
427
3
{
428
3
    const BitBuffer bitBuffer = readBufferFromFile(fileName);
429
3
    return deserialize<T>(bitBuffer, std::forward<ARGS>(arguments)...);
430
3
}
431
432
} // namespace zserio
433
434
#endif // ZSERIO_SERIALIZE_UTIL_H_INC