userver: userver/dump/common.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
common.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dump/common.hpp
4/// @brief Serialization and deserialization of integral, floating point,
5/// string, `std::chrono`, `enum` and `uuid` types for dumps.
6///
7/// @ingroup userver_dump_read_write
8
9#include <chrono>
10#include <cstdint>
11#include <stdexcept>
12#include <string>
13#include <string_view>
14#include <type_traits>
15
16#include <boost/numeric/conversion/cast.hpp>
17
18#include <userver/dump/operations.hpp>
19#include <userver/dump/unsafe.hpp>
20#include <userver/utils/meta_light.hpp>
21
22namespace boost::uuids {
23struct uuid;
24}
25
26USERVER_NAMESPACE_BEGIN
27
28namespace decimal64 {
29template <int Prec, typename RoundPolicy>
30class Decimal;
31} // namespace decimal64
32
33namespace formats::json {
34class Value;
35} // namespace formats::json
36
37namespace dump {
38
39/// @brief Reads the rest of the data from `reader`
40std::string ReadEntire(Reader& reader);
41
42namespace impl {
43
44/// @brief Helpers for serialization of trivially-copyable types
45template <typename T>
46void WriteTrivial(Writer& writer, T value) {
47 static_assert(std::is_trivially_copyable_v<T>);
48 // TODO: endianness
49 WriteStringViewUnsafe(
50 writer,
51 std::string_view{reinterpret_cast<const char*>(&value), sizeof(value)});
52}
53
54/// @brief Helpers for deserialization trivially-copyable types
55template <typename T>
56T ReadTrivial(Reader& reader) {
57 static_assert(std::is_trivially_copyable_v<T>);
58 T value{};
59 // TODO: endianness
60 ReadStringViewUnsafe(reader, sizeof(T))
61 .copy(reinterpret_cast<char*>(&value), sizeof(T));
62 return value;
63}
64
65void WriteInteger(Writer& writer, std::uint64_t value);
66
67std::uint64_t ReadInteger(Reader& reader);
68
69template <typename Duration>
70inline constexpr bool kIsDumpedAsNanoseconds =
71 std::is_integral_v<typename Duration::rep> &&
72 (Duration::period::num == 1) &&
73 (Duration{1} <= std::chrono::milliseconds{1}) &&
74 (1'000'000'000 % Duration::period::den == 0);
75
76} // namespace impl
77
78/// @brief Write-only `std::string_view` support
79/// @see `ReadStringViewUnsafe`
80void Write(Writer& writer, std::string_view value);
81
82/// @brief `std::string` serialization support
83void Write(Writer& writer, const std::string& value);
84
85/// @brief `std::string` deserialization support
86std::string Read(Reader& reader, To<std::string>);
87
88/// @brief Allows writing string literals
89void Write(Writer& writer, const char* value);
90
91/// @brief Integral types serialization support
92template <typename T>
94 if constexpr (sizeof(T) == 1) {
95 impl::WriteTrivial(writer, value);
96 } else {
97 impl::WriteInteger(writer, static_cast<std::uint64_t>(value));
98 }
99}
100
101/// @brief Integral types deserialization support
102template <typename T>
104 if constexpr (sizeof(T) == 1) {
105 return impl::ReadTrivial<T>(reader);
106 }
107
108 const auto raw = impl::ReadInteger(reader);
109
110 if constexpr (std::is_signed_v<T>) {
111 return boost::numeric_cast<T>(static_cast<std::int64_t>(raw));
112 } else {
113 return boost::numeric_cast<T>(raw);
114 }
115}
116
117/// @brief Floating-point serialization support
118template <typename T>
120 impl::WriteTrivial(writer, value);
121}
122
123/// @brief Floating-point deserialization support
124template <typename T>
126 return impl::ReadTrivial<T>(reader);
127}
128
129/// @brief bool serialization support
130void Write(Writer& writer, bool value);
131
132/// @brief bool deserialization support
133bool Read(Reader& reader, To<bool>);
134
135/// @brief enum serialization support
136template <typename T>
138 writer.Write(static_cast<std::underlying_type_t<T>>(value));
139}
140
141/// @brief enum deserialization support
142template <typename T>
144 return static_cast<T>(reader.Read<std::underlying_type_t<T>>());
145}
146
147/// @brief `std::chrono::duration` serialization support
148template <typename Rep, typename Period>
149void Write(Writer& writer, std::chrono::duration<Rep, Period> value) {
150 using std::chrono::duration, std::chrono::nanoseconds;
151
152 // Durations, which on some systems represent
153 // `std::chrono::*_clock::duration`, are serialized as `std::nanoseconds`
154 // to avoid system dependency
155 if constexpr (impl::kIsDumpedAsNanoseconds<duration<Rep, Period>>) {
156 const auto count = std::chrono::duration_cast<nanoseconds>(value).count();
157
158 if (nanoseconds{count} != value) {
159 throw std::logic_error(
160 "Trying to serialize a huge duration, it does not fit into "
161 "std::chrono::nanoseconds type");
162 }
163 impl::WriteTrivial(writer, count);
164 } else {
165 impl::WriteTrivial(writer, value.count());
166 }
167}
168
169/// @brief `std::chrono::duration` deserialization support
170template <typename Rep, typename Period>
171std::chrono::duration<Rep, Period> Read(
172 Reader& reader, To<std::chrono::duration<Rep, Period>>) {
173 using std::chrono::duration, std::chrono::nanoseconds;
174
175 if constexpr (impl::kIsDumpedAsNanoseconds<duration<Rep, Period>>) {
176 const auto count = impl::ReadTrivial<nanoseconds::rep>(reader);
177 return std::chrono::duration_cast<duration<Rep, Period>>(
178 nanoseconds{count});
179 } else {
180 const auto count = impl::ReadTrivial<Rep>(reader);
181 return duration<Rep, Period>{count};
182 }
183}
184
185/// @brief `std::chrono::time_point` serialization support
186/// @note Only `system_clock` is supported, because `steady_clock` can only
187/// be used within a single execution
188template <typename Duration>
189void Write(Writer& writer,
190 std::chrono::time_point<std::chrono::system_clock, Duration> value) {
191 writer.Write(value.time_since_epoch());
192}
193
194/// @brief `std::chrono::time_point` deserialization support
195/// @note Only `system_clock` is supported, because `steady_clock` can only
196/// be used within a single execution
197template <typename Duration>
198auto Read(Reader& reader,
199 To<std::chrono::time_point<std::chrono::system_clock, Duration>>) {
200 return std::chrono::time_point<std::chrono::system_clock, Duration>{
201 reader.Read<Duration>()};
202}
203
204/// @brief `boost::uuids::uuid` serialization support
205void Write(Writer& writer, const boost::uuids::uuid& value);
206
207/// @brief `boost::uuids::uuid` deserialization support
208boost::uuids::uuid Read(Reader& reader, To<boost::uuids::uuid>);
209
210/// @brief decimal64::Decimal serialization support
211template <int Prec, typename RoundPolicy>
212inline void Write(Writer& writer,
213 const decimal64::Decimal<Prec, RoundPolicy>& dec) {
214 writer.Write(dec.AsUnbiased());
215}
216
217/// @brief decimal64::Decimal deserialization support
218template <int Prec, typename RoundPolicy>
219auto Read(Reader& reader, dump::To<decimal64::Decimal<Prec, RoundPolicy>>) {
220 return decimal64::Decimal<Prec, RoundPolicy>::FromUnbiased(
221 reader.Read<int64_t>());
222}
223
224/// @brief formats::json::Value serialization support
225void Write(Writer& writer, const formats::json::Value& value);
226
227/// @brief formats::json::Value deserialization support
228formats::json::Value Read(Reader& reader, To<formats::json::Value>);
229
230} // namespace dump
231
232USERVER_NAMESPACE_END