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 1674 : static inline auto serialize( const auto & message, spb::io::writer on_write,
51 : const serialize_options & options = { } ) -> size_t
52 : {
53 1674 : auto stream = detail::ostream{ on_write };
54 1674 : if( options.delimited )
55 : {
56 4 : detail::serialize_varint( stream, detail::serialize_size( message ) );
57 : }
58 1674 : serialize( stream, message );
59 3348 : 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 1004 : [[nodiscard]] static inline auto serialize_size( const auto & message,
70 : const serialize_options & options = { } ) -> size_t
71 : {
72 1004 : return serialize( message, spb::io::writer( nullptr ), options );
73 : }
74 :
75 : /**
76 : * @brief serialize message into protobuf
77 : *
78 : * @param[in] message to be serialized
79 : * @param[in] options
80 : * @param[out] result serialized protobuf
81 : * @return serialized size in bytes
82 : * @throws std::runtime_error on error
83 : * @example `auto serialized = std::vector< std::byte >();`
84 : * `spb::pb::serialize( message, serialized );`
85 : */
86 : template < typename Message, spb::resizable_container Container >
87 670 : static inline auto serialize( const Message & message, Container & result,
88 : const serialize_options & options = { } ) -> size_t
89 : {
90 670 : const auto size = serialize_size( message, options );
91 670 : result.resize( size );
92 2054 : auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
93 : {
94 1384 : memcpy( ptr, data, size );
95 1384 : ptr += size;
96 : };
97 :
98 670 : serialize( message, writer, options );
99 670 : return size;
100 : }
101 :
102 : /**
103 : * @brief serialize message into protobuf
104 : *
105 : * @param[in] message to be serialized
106 : * @param[in] options
107 : * @return serialized protobuf
108 : * @throws std::runtime_error on error
109 : * @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
110 : */
111 : template < spb::resizable_container Container = std::string, typename Message >
112 670 : [[nodiscard]] static inline auto serialize( const Message & message,
113 : const serialize_options & options = { } ) -> Container
114 : {
115 670 : auto result = Container( );
116 670 : serialize< Message, Container >( message, result, options );
117 670 : return result;
118 0 : }
119 :
120 : /**
121 : * @brief deserialize message from protobuf
122 : *
123 : * @param[in] reader function for handling reads
124 : * @param[in] options
125 : * @param[out] message deserialized message
126 : * @throws std::runtime_error on error
127 : */
128 1034 : static inline void deserialize( auto & message, spb::io::reader reader,
129 : const deserialize_options & options = { } )
130 : {
131 1034 : detail::istream stream{ reader };
132 1034 : if( options.delimited )
133 : {
134 2 : const auto substream_length = read_varint< uint32_t >( stream );
135 2 : auto substream = stream.sub_stream( substream_length );
136 2 : return deserialize_main( substream, message );
137 : }
138 : else
139 : {
140 1032 : return deserialize_main( stream, message );
141 : }
142 : }
143 :
144 : /**
145 : * @brief deserialize message from protobuf
146 : *
147 : * @param[in] protobuf string with protobuf
148 : * @param[in] options
149 : * @param[out] message deserialized message
150 : * @throws std::runtime_error on error
151 : * @example `auto serialized = std::vector< std::byte >( ... );`
152 : * `auto message = Message();`
153 : * `spb::pb::deserialize( message, serialized );`
154 : */
155 : template < typename Message, spb::size_container Container >
156 1034 : static inline void deserialize( Message & message, const Container & protobuf,
157 : const deserialize_options & options = { } )
158 : {
159 4602 : auto reader = [ ptr = protobuf.data( ), end = protobuf.data( ) + protobuf.size( ) ](
160 : void * data, size_t size ) mutable -> size_t
161 : {
162 3568 : size_t bytes_left = end - ptr;
163 :
164 3568 : size = std::min( size, bytes_left );
165 3568 : memcpy( data, ptr, size );
166 3568 : ptr += size;
167 3568 : return size;
168 : };
169 1779 : return deserialize( message, reader, options );
170 : }
171 :
172 : /**
173 : * @brief deserialize message from protobuf
174 : *
175 : * @param[in] protobuf serialized protobuf
176 : * @param[in] options
177 : * @return deserialized message
178 : * @throws std::runtime_error on error
179 : * @example `auto serialized = std::vector< std::byte >( ... );`
180 : * `auto message = spb::pb::deserialize< Message >( serialized );`
181 : */
182 : template < typename Message, spb::size_container Container >
183 700 : [[nodiscard]] static inline auto deserialize( const Container & protobuf,
184 : const deserialize_options & options = { } ) -> Message
185 : {
186 666 : auto message = Message{ };
187 700 : deserialize( message, protobuf, options );
188 414 : return message;
189 62 : }
190 :
191 : /**
192 : * @brief deserialize message from reader
193 : *
194 : * @param[in] reader function for handling reads
195 : * @param[in] options
196 : * @return deserialized message
197 : * @throws std::runtime_error on error
198 : */
199 : template < typename Message >
200 : [[nodiscard]] static inline auto deserialize( spb::io::reader reader,
201 : const deserialize_options & options = { } ) -> Message
202 : {
203 : auto message = Message{ };
204 : deserialize( message, reader, options );
205 : return message;
206 : }
207 :
208 : }// namespace spb::pb
|