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 <cstdlib>
17 :
18 : namespace spb::pb
19 : {
20 :
21 : struct serialize_options
22 : {
23 : /**
24 : * @brief Writes the size of the message (as a varint) before the message itself.
25 : * Compatible with Google's `writeDelimitedTo` and NanoPb's PB_ENCODE_DELIMITED.
26 : */
27 : bool delimited = false;
28 : };
29 :
30 : struct deserialize_options
31 : {
32 : /**
33 : * @brief Expect the size of the message (encoded as a varint) to come before the message
34 : * itself. Compatible with Google's `parseDelimitedFrom` and NanoPb's PB_DECODE_DELIMITED. Will
35 : * return after having read the specified length; the spb::io::reader object can then be read
36 : * from again to get the next message (if any).
37 : */
38 : bool delimited = false;
39 : };
40 :
41 : /**
42 : * @brief serialize message via writer
43 : *
44 : * @param[in] message to be serialized
45 : * @param[in] on_write function for handling the writes
46 : * @param[in] options
47 : * @return serialized size in bytes
48 : * @throws exceptions only from `on_write`
49 : */
50 1948 : static inline auto serialize(const auto &message, spb::io::writer on_write,
51 : const serialize_options &options = {}) -> size_t
52 : {
53 1948 : auto stream = detail::ostream{on_write};
54 1948 : if (options.delimited)
55 4 : detail::serialize_varint(stream, detail::serialize_size(message));
56 :
57 1948 : serialize(stream, message);
58 3764 : return stream.size();
59 : }
60 :
61 : /**
62 : * @brief return protobuf serialized size in bytes
63 : *
64 : * @param[in] message to be serialized
65 : * @param[in] options
66 : * @return serialized size in bytes
67 : */
68 1192 : [[nodiscard]] static inline auto serialize_size(const auto &message, const serialize_options &options = {})
69 : -> size_t
70 : {
71 1192 : return serialize(message, spb::io::writer(nullptr), options);
72 : }
73 :
74 : /**
75 : * @brief serialize message into protobuf
76 : *
77 : * @param[in] message to be serialized
78 : * @param[in] options
79 : * @param[out] result serialized protobuf
80 : * @return serialized size in bytes
81 : * @throws std::runtime_error on error
82 : * @example `auto serialized = std::vector< std::byte >();`
83 : * `spb::pb::serialize( message, serialized );`
84 : */
85 : template <typename Message, spb::resizable_container Container>
86 822 : static inline auto serialize(const Message &message, Container &result, const serialize_options &options = {})
87 : -> size_t
88 : {
89 822 : const auto size = serialize_size(message, options);
90 756 : result.resize(size);
91 2848 : auto writer = [ptr = result.data()](const void *data, size_t size) mutable
92 : {
93 2092 : memcpy(ptr, data, size);
94 2092 : ptr += size;
95 : };
96 :
97 756 : serialize(message, writer, options);
98 756 : return size;
99 : }
100 :
101 : /**
102 : * @brief serialize message into protobuf
103 : *
104 : * @param[in] message to be serialized
105 : * @param[in] options
106 : * @return serialized protobuf
107 : * @throws std::runtime_error on error
108 : * @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
109 : */
110 : template <spb::resizable_container Container = std::string, typename Message>
111 822 : [[nodiscard]] static inline auto serialize(const Message &message, const serialize_options &options = {})
112 : -> Container
113 : {
114 822 : auto result = Container();
115 822 : serialize<Message, Container>(message, result, options);
116 756 : return result;
117 66 : }
118 :
119 : /**
120 : * @brief deserialize message from protobuf
121 : *
122 : * @param[in] reader function for handling reads
123 : * @param[in] options
124 : * @param[out] message deserialized message
125 : * @throws std::runtime_error on error
126 : */
127 1180 : static inline void deserialize(auto &message, spb::io::reader reader, const deserialize_options &options = {})
128 : {
129 1180 : detail::istream stream{reader};
130 1180 : if (!options.delimited)
131 1178 : return deserialize_main(stream, message);
132 :
133 2 : const auto substream_length = read_varint<uint32_t>(stream);
134 2 : auto substream = stream.sub_stream(substream_length);
135 2 : return deserialize_main(substream, message);
136 : }
137 :
138 : /**
139 : * @brief deserialize message from protobuf
140 : *
141 : * @param[in] protobuf string with protobuf
142 : * @param[in] options
143 : * @param[out] message deserialized message
144 : * @throws std::runtime_error on error
145 : * @example `auto serialized = std::vector< std::byte >( ... );`
146 : * `auto message = Message();`
147 : * `spb::pb::deserialize( message, serialized );`
148 : */
149 : template <typename Message, spb::size_container Container>
150 1180 : static inline void deserialize(Message &message, const Container &protobuf,
151 : const deserialize_options &options = {})
152 : {
153 1180 : auto ptr = protobuf.data();
154 1180 : const auto end = protobuf.data() + protobuf.size();
155 :
156 5840 : auto reader = [ptr, end](void *data, size_t size) mutable -> size_t
157 : {
158 4660 : const size_t bytes_left = end - ptr;
159 :
160 4660 : size = std::min(size, bytes_left);
161 4660 : memcpy(data, ptr, size);
162 4660 : ptr += size;
163 4660 : return size;
164 : };
165 1997 : return deserialize(message, reader, options);
166 : }
167 :
168 : /**
169 : * @brief deserialize message from protobuf
170 : *
171 : * @param[in] protobuf serialized protobuf
172 : * @param[in] options
173 : * @return deserialized message
174 : * @throws std::runtime_error on error
175 : * @example `auto serialized = std::vector< std::byte >( ... );`
176 : * `auto message = spb::pb::deserialize< Message >( serialized );`
177 : */
178 : template <typename Message, spb::size_container Container>
179 810 : [[nodiscard]] static inline auto deserialize(const Container &protobuf,
180 : const deserialize_options &options = {}) -> Message
181 : {
182 776 : auto message = Message{};
183 810 : deserialize(message, protobuf, options);
184 450 : return message;
185 128 : }
186 :
187 : /**
188 : * @brief deserialize message from reader
189 : *
190 : * @param[in] reader function for handling reads
191 : * @param[in] options
192 : * @return deserialized message
193 : * @throws std::runtime_error on error
194 : */
195 : template <typename Message>
196 : [[nodiscard]] static inline auto deserialize(spb::io::reader reader, const deserialize_options &options = {})
197 : -> Message
198 : {
199 : auto message = Message{};
200 : deserialize(message, reader, options);
201 : return message;
202 : }
203 :
204 : } // namespace spb::pb
|