userver: userver/dump/common.hpp Source File
Loading...
Searching...
No Matches
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