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