Line data Source code
1 : /***************************************************************************\
2 : * Name : Public API for protobuf *
3 : * Description : all protobuf serialize and deserialize functions *
4 : * Author : antonin.kriz@gmail.com *
5 : * ------------------------------------------------------------------------- *
6 : * This is free software; you can redistribute it and/or modify it under the *
7 : * terms of the MIT license. A copy of the license can be found in the file *
8 : * "LICENSE" at the root of this distribution. *
9 : \***************************************************************************/
10 : #pragma once
11 :
12 : #include "concepts.h"
13 : #include "pb/deserialize.hpp"
14 : #include "pb/serialize.hpp"
15 : #include "spb/io/io.hpp"
16 : #include "spb/pb/wire-types.h"
17 : #include <cstdint>
18 : #include <cstdlib>
19 :
20 : namespace spb::pb
21 : {
22 :
23 : struct serialize_options
24 : {
25 : /**
26 : * @brief Writes the size of the message (as a varint) before the message itself.
27 : * Compatible with Google's `writeDelimitedTo` and NanoPb's PB_ENCODE_DELIMITED.
28 : */
29 : bool delimited = false;
30 : };
31 :
32 : struct deserialize_options
33 : {
34 : /**
35 : * @brief Expect the size of the message (encoded as a varint) to come before the message
36 : * itself. Compatible with Google's `parseDelimitedFrom` and NanoPb's PB_DECODE_DELIMITED. Will
37 : * return after having read the specified length; the spb::io::reader object can then be read
38 : * from again to get the next message (if any).
39 : */
40 : bool delimited = false;
41 : };
42 :
43 : /**
44 : * @brief serialize message via writer
45 : *
46 : * @param[in] message to be serialized
47 : * @param[in] on_write function for handling the writes
48 : * @param[in] options
49 : * @return serialized size in bytes
50 : * @throws exceptions only from `on_write`
51 : */
52 756 : size_t serialize(const auto &message, spb::io::writer on_write, const serialize_options &options = {})
53 : {
54 756 : auto stream = detail::ostream_writer{on_write};
55 756 : if (options.delimited)
56 378 : detail::serialize_varint(stream, detail::serialize_size(message));
57 :
58 756 : serialize_value(stream, message);
59 756 : return stream.size;
60 : }
61 :
62 : /**
63 : * @brief return protobuf serialized size in bytes
64 : *
65 : * @param[in] message to be serialized
66 : * @param[in] options
67 : * @return serialized size in bytes
68 : */
69 1512 : [[nodiscard]] size_t serialize_size(const auto &message, const serialize_options &options = {})
70 : {
71 1512 : const auto size = detail::serialize_size(message);
72 1512 : return (options.delimited) ? size + detail::serialize_varint_size(size) : size;
73 : }
74 :
75 : size_t serialize(const auto &message, void *buffer, const serialize_options &options = {})
76 : {
77 : const auto start = (uint8_t *)buffer;
78 : auto stream = detail::ostream_buffer((uint8_t *)buffer);
79 : if (options.delimited)
80 : detail::serialize_varint(stream, detail::serialize_size(message));
81 :
82 : serialize_value(stream, message);
83 : return stream.p_buffer - start;
84 : }
85 :
86 : /**
87 : * @brief serialize message into protobuf
88 : *
89 : * @param[in] message to be serialized
90 : * @param[in] options
91 : * @param[out] result serialized protobuf
92 : * @return serialized size in bytes
93 : * @throws std::runtime_error on error
94 : * @example `auto serialized = std::vector< std::byte >();`
95 : * `spb::pb::serialize( message, serialized );`
96 : */
97 : template <spb::resizable_container Container>
98 1594 : size_t serialize(const auto &message, Container &result, const serialize_options &options = {})
99 : {
100 : static_assert(sizeof(*result.data()) == sizeof(uint8_t));
101 :
102 1594 : const auto size = detail::serialize_size(message);
103 1594 : const auto serialized_size = options.delimited ? size + detail::serialize_varint_size(size) : size;
104 1594 : result.resize(serialized_size);
105 1594 : auto stream = detail::ostream_buffer((uint8_t *)result.data());
106 1594 : if (options.delimited)
107 758 : detail::serialize_varint(stream, size);
108 :
109 1594 : serialize_value(stream, message);
110 3056 : return result.size();
111 : }
112 :
113 : /**
114 : * @brief serialize message into protobuf
115 : *
116 : * @param[in] message to be serialized
117 : * @param[in] options
118 : * @return serialized protobuf
119 : * @throws std::runtime_error on error
120 : * @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
121 : */
122 : template <spb::resizable_container Container = std::string>
123 1594 : [[nodiscard]] Container serialize(const auto &message, const serialize_options &options = {})
124 : {
125 1594 : auto result = Container();
126 1594 : serialize(message, result, options);
127 1528 : return result;
128 66 : }
129 :
130 1952 : size_t deserialize(auto &message, const void *buffer, size_t size, const deserialize_options &options = {})
131 : {
132 1952 : detail::istream_buffer stream((const uint8_t *)buffer, size);
133 1952 : if (options.delimited)
134 : {
135 758 : const auto substream_length = read_varint<uint32_t>(stream);
136 758 : auto substream = stream.sub_stream(substream_length);
137 757 : deserialize<detail::serialize_mode{}>(substream, message);
138 : }
139 : else
140 : {
141 1194 : deserialize<detail::serialize_mode{}>(stream, message);
142 : }
143 3178 : return size - stream.size();
144 : }
145 :
146 : /**
147 : * @brief deserialize message from protobuf
148 : *
149 : * @param[in] reader function for handling reads
150 : * @param[in] options
151 : * @param[out] message deserialized message
152 : * @throws std::runtime_error on error
153 : */
154 756 : size_t deserialize(auto &message, spb::io::reader reader, const deserialize_options &options = {})
155 : {
156 756 : detail::istream_reader stream{reader};
157 756 : if (options.delimited)
158 : {
159 378 : const auto substream_length = read_varint<uint32_t>(stream);
160 378 : auto substream = stream.sub_stream(substream_length);
161 378 : deserialize<detail::serialize_mode{}>(substream, message);
162 : }
163 : else
164 : {
165 378 : deserialize<detail::serialize_mode{}>(stream, message);
166 : }
167 1512 : return stream.consumed_size();
168 : }
169 :
170 : /**
171 : * @brief deserialize message from protobuf
172 : *
173 : * @param[in] protobuf string with protobuf
174 : * @param[in] options
175 : * @param[out] message deserialized message
176 : * @throws std::runtime_error on error
177 : * @example `auto serialized = std::vector< std::byte >( ... );`
178 : * `auto message = Message();`
179 : * `spb::pb::deserialize( message, serialized );`
180 : */
181 : template <typename Message, spb::size_container Container>
182 756 : size_t deserialize(Message &message, const Container &protobuf, const deserialize_options &options = {})
183 : {
184 756 : return deserialize(message, protobuf.data(), protobuf.size(), options);
185 : }
186 :
187 : /**
188 : * @brief deserialize message from protobuf
189 : *
190 : * @param[in] protobuf serialized protobuf
191 : * @param[in] options
192 : * @return deserialized message
193 : * @throws std::runtime_error on error
194 : * @example `auto serialized = std::vector< std::byte >( ... );`
195 : * `auto message = spb::pb::deserialize< Message >( serialized );`
196 : */
197 : template <typename Message, spb::size_container Container>
198 1196 : [[nodiscard]] Message deserialize(const Container &protobuf, const deserialize_options &options = {})
199 : {
200 1162 : auto message = Message{};
201 1196 : deserialize(message, protobuf.data(), protobuf.size(), options);
202 836 : return message;
203 128 : }
204 :
205 : /**
206 : * @brief deserialize message from reader
207 : *
208 : * @param[in] reader function for handling reads
209 : * @param[in] options
210 : * @return deserialized message
211 : * @throws std::runtime_error on error
212 : */
213 : template <typename Message>
214 : [[nodiscard]] Message deserialize(spb::io::reader reader, const deserialize_options &options = {})
215 : {
216 : auto message = Message{};
217 : deserialize(message, reader, options);
218 : return message;
219 : }
220 : } // namespace spb::pb
|