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