userver: userver/dump/operations.hpp Source File
Loading...
Searching...
No Matches
operations.hpp
1#pragma once
2
3#include <stdexcept>
4#include <string>
5#include <string_view>
6#include <type_traits>
7
8#include <userver/dump/fwd.hpp>
9#include <userver/dump/meta.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace dump {
14
15/// Indicates a failure reading or writing a dump. No further operations
16/// should be performed with a failed dump.
17class Error final : public std::runtime_error {
18public:
19 explicit Error(std::string message) : std::runtime_error(message) {}
20};
21
22/// A general interface for binary data output
23class Writer {
24public:
25 virtual ~Writer() = default;
26
27 /// @brief Writes binary data
28 /// @details Calls ADL-found `Write(writer, data)`
29 /// @throws `Error` and any user-thrown `std::exception`
30 template <typename T>
31 void Write(const T& data);
32
33 /// @brief Must be called once all data has been written
34 /// @warning This method must not be called from within `Write`/`Read`
35 /// @throws `Error` on write operation failure
36 virtual void Finish() = 0;
37
38protected:
39 /// @brief Writes binary data
40 /// @details Unlike `Write`, doesn't write the size of `data`
41 /// @throws `Error` on write operation failure
42 virtual void WriteRaw(std::string_view data) = 0;
43
44 friend void WriteStringViewUnsafe(Writer& writer, std::string_view value);
45};
46
47/// A general interface for binary data input
48class Reader {
49public:
50 virtual ~Reader() = default;
51
52 /// @brief Reads binary data
53 /// @details Calls ADL-found `Read(reader, To<T>)`
54 /// @throws `Error` and any user-thrown `std::exception`
55 template <typename T>
56 T Read();
57
58 /// @brief Must be called once all data has been read
59 /// @warning This method must not be called from within `Write`/`Read`
60 /// @throws `Error` on read operation failure or if there is leftover data
61 virtual void Finish() = 0;
62
63protected:
64 /// @brief Reads binary data
65 /// @note Invalidates the memory returned by the previous call of `ReadRaw`
66 /// @note Normally, exactly `max_size` bytes is returned. On end-of-file,
67 /// the amount of bytes returned can be less than `max_size`.
68 /// @throws `Error` on read operation failure
70
71 /// @brief Moves the internal cursor back by `size` bytes
72 /// so that the next call to @ref ReadRaw returns the same data again
73 /// @note If there has been no previous call to @ref ReadRaw,
74 /// or `size` is greater than the number of bytes returned from that call,
75 /// the behavior is undefined.
76 virtual void BackUp(std::size_t size);
77
79 friend void BackUpReadUnsafe(Reader& reader, std::size_t size);
80};
81
82namespace impl {
83
84template <typename T>
85void CallWrite(Writer& writer, const T& data) {
86 Write(writer, data);
87}
88
89template <typename T>
90// NOLINTNEXTLINE(readability-const-return-type)
91T CallRead(Reader& reader, To<T> to) {
92 return Read(reader, to);
93}
94
95} // namespace impl
96
97template <typename T>
98void Writer::Write(const T& data) {
99 if constexpr (kIsWritable<T>) {
100 impl::CallWrite(*this, data);
101 } else if constexpr (std::is_aggregate_v<T>) {
102 static_assert(
103 !sizeof(T),
104 "Serialization is not implemented for this type. You "
105 "either forgot to specialize IsDumpedAggregate for your type "
106 "(see <userver/dump/aggregates.hpp>)"
107 "or you've got a non-standard data type and need to implement "
108 "`void Write(dump::Writer& writer, const T& data);` and put it "
109 "in the namespace of `T` or in `dump`."
110 );
111 } else {
112 static_assert(
113 !sizeof(T),
114 "You either forgot to `#include <userver/dump/common_containers.hpp>`, "
115 "or you've got a non-standard data type and need to implement "
116 "`void Write(dump::Writer& writer, const T& data);` and put it "
117 "in the namespace of `T` or in `dump`."
118 );
119 }
120}
121
122template <typename T>
123// NOLINTNEXTLINE(readability-const-return-type)
124T Reader::Read() {
125 if constexpr (kIsReadable<T>) {
126 return impl::CallRead(*this, To<T>{});
127 } else if constexpr (std::is_aggregate_v<T>) {
128 static_assert(
129 !sizeof(T),
130 "Serialization is not implemented for this type. You "
131 "either forgot to specialize IsDumpedAggregate for your type"
132 "(see <userver/dump/aggregates.hpp>) "
133 "or you've got a non-standard data type and need to implement "
134 "`T Read(dump::Reader& reader, dump::To<T>);` and put it "
135 "in the namespace of `T` or in `dump`."
136 );
137 } else {
138 static_assert(
139 !sizeof(T),
140 "You either forgot to `#include <userver/dump/common_containers.hpp>`, "
141 "or you've got a non-standard data type and need to implement"
142 "`T Read(dump::Reader& reader, dump::To<T>);` and put it "
143 "in the namespace of `T` or in `dump`."
144 );
145 return T{};
146 }
147}
148
149} // namespace dump
150
151USERVER_NAMESPACE_END