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