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