Line data Source code
1 : /***************************************************************************\
2 : * Name : pb dumper *
3 : * Description : generate C++ src files for pb de/serialization *
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 :
11 : #include "dumper.h"
12 : #include "ast/ast-types.h"
13 : #include "ast/proto-field.h"
14 : #include "ast/proto-file.h"
15 : #include "template-h.h"
16 : #include <iterator>
17 : #include <sstream>
18 : #include <stdexcept>
19 : #include <string>
20 : #include <string_view>
21 :
22 : using namespace std::literals;
23 :
24 : namespace
25 : {
26 :
27 472 : auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
28 : {
29 472 : auto result = std::string( input );
30 472 : auto pos = size_t{ };
31 :
32 1382 : while( ( pos = result.find( what, pos ) ) != std::string::npos )
33 : {
34 910 : result.replace( pos, what.size( ), with );
35 910 : pos += with.size( );
36 : }
37 :
38 472 : return result;
39 0 : }
40 :
41 448 : void dump_prototypes( std::ostream & stream, std::string_view type )
42 : {
43 448 : stream << replace( file_pb_header_template, "$", type );
44 448 : }
45 :
46 448 : void dump_prototypes( std::ostream & stream, const proto_message & message,
47 : std::string_view parent )
48 : {
49 1344 : const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
50 448 : dump_prototypes( stream, message_with_parent );
51 448 : }
52 :
53 63 : void dump_prototypes( std::ostream & stream, const proto_messages & messages,
54 : std::string_view parent )
55 : {
56 511 : for( const auto & message : messages )
57 : {
58 448 : dump_prototypes( stream, message, parent );
59 : }
60 :
61 511 : for( const auto & message : messages )
62 : {
63 448 : if( message.messages.empty( ) )
64 : {
65 397 : continue;
66 : }
67 153 : const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
68 51 : dump_prototypes( stream, message.messages, message_with_parent );
69 51 : }
70 63 : }
71 :
72 12 : void dump_prototypes( std::ostream & stream, const proto_file & file )
73 : {
74 12 : const auto package_name = file.package.name.empty( )
75 12 : ? std::string( )
76 12 : : "::" + replace( file.package.name, ".", "::" );
77 12 : dump_prototypes( stream, file.package.messages, package_name );
78 12 : }
79 :
80 12 : void dump_cpp_includes( std::ostream & stream, std::string_view header_file_path )
81 : {
82 : stream << "#include \"" << header_file_path << "\"\n"
83 : << "#include <spb/pb.hpp>\n"
84 12 : "#include <type_traits>\n\n";
85 12 : }
86 :
87 472 : void dump_cpp_close_namespace( std::ostream & stream, std::string_view name )
88 : {
89 472 : stream << "} // namespace " << name << "\n";
90 472 : }
91 :
92 472 : void dump_cpp_open_namespace( std::ostream & stream, std::string_view name )
93 : {
94 472 : stream << "namespace " << name << "\n{\n";
95 472 : }
96 :
97 2636 : auto encoder_type_str( const proto_file & file, const proto_field & field ) -> std::string
98 : {
99 2636 : switch( field.type )
100 : {
101 1188 : case proto_field::Type::NONE:
102 : case proto_field::Type::BYTES:
103 : case proto_field::Type::MESSAGE:
104 : case proto_field::Type::STRING:
105 1188 : return { };
106 :
107 1042 : case proto_field::Type::BOOL:
108 : case proto_field::Type::ENUM:
109 : case proto_field::Type::INT32:
110 : case proto_field::Type::UINT32:
111 : case proto_field::Type::INT64:
112 : case proto_field::Type::UINT64:
113 2084 : return is_packed_array( file, field ) ? "scalar_encoder::varint | scalar_encoder::packed"
114 1042 : : "scalar_encoder::varint";
115 :
116 76 : case proto_field::Type::SINT32:
117 : case proto_field::Type::SINT64:
118 152 : return is_packed_array( file, field ) ? "scalar_encoder::svarint | scalar_encoder::packed"
119 76 : : "scalar_encoder::svarint";
120 :
121 116 : case proto_field::Type::FLOAT:
122 : case proto_field::Type::FIXED32:
123 : case proto_field::Type::SFIXED32:
124 232 : return is_packed_array( file, field ) ? "scalar_encoder::i32 | scalar_encoder::packed"
125 116 : : "scalar_encoder::i32";
126 :
127 214 : case proto_field::Type::DOUBLE:
128 : case proto_field::Type::FIXED64:
129 : case proto_field::Type::SFIXED64:
130 428 : return is_packed_array( file, field ) ? "scalar_encoder::i64 | scalar_encoder::packed"
131 214 : : "scalar_encoder::i64";
132 : }
133 0 : return { };
134 : }
135 :
136 2378 : auto encoder_type( const proto_file & file, const proto_field & field ) -> std::string
137 : {
138 2378 : const auto encoder = encoder_type_str( file, field );
139 2378 : if( encoder.empty( ) )
140 1126 : return { };
141 :
142 1252 : return "_as<" + encoder + ">";
143 2378 : }
144 :
145 104 : auto map_encoder_type( const proto_file & file, const proto_field & key, const proto_field & value )
146 : -> std::string
147 : {
148 104 : const auto key_encoder = encoder_type_str( file, key );
149 104 : const auto value_encoder = encoder_type_str( file, value );
150 :
151 104 : return "_as< scalar_encoder_combine( " +
152 332 : ( key_encoder.empty( ) ? std::string( "{}" ) : key_encoder ) + ", " +
153 458 : ( value_encoder.empty( ) ? std::string( "{}" ) : value_encoder ) + " ) >";
154 104 : }
155 :
156 50 : auto bitfield_encoder_type( const proto_file & file, const proto_field & field ) -> std::string
157 : {
158 50 : const auto encoder = encoder_type_str( file, field );
159 100 : return "_as< " + encoder + ", decltype( value." + std::string( field.name ) + ") >";
160 50 : }
161 :
162 4 : void dump_cpp_serialize_value( std::ostream & stream, const proto_file & file,
163 : const proto_oneof & oneof )
164 : {
165 4 : stream << "\t{\n\t\tconst auto index = value." << oneof.name << ".index( );\n";
166 4 : stream << "\t\tswitch( index )\n\t\t{\n";
167 15 : for( size_t i = 0; i < oneof.fields.size( ); ++i )
168 : {
169 11 : stream << "\t\t\tcase " << i << ":\n\t\t\t\treturn stream.serialize"
170 22 : << encoder_type( file, oneof.fields[ i ] ) << "( " << oneof.fields[ i ].number
171 11 : << ", std::get< " << i << " >( value." << oneof.name << ") );\n";
172 : }
173 4 : stream << "\t\t}\n\t}\n\n";
174 4 : }
175 :
176 448 : void dump_cpp_serialize_value( std::ostream & stream, const proto_file & file,
177 : const proto_message & message, std::string_view full_name )
178 : {
179 448 : if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
180 : {
181 9 : stream << "void serialize( detail::ostream & , const " << full_name << " & )\n{\n}\n\n";
182 9 : return;
183 : }
184 :
185 439 : stream << "void serialize( detail::ostream & stream, const " << full_name << " & value )\n{\n";
186 1642 : for( const auto & field : message.fields )
187 : {
188 2406 : stream << "\tstream.serialize" << encoder_type( file, field ) << "( " << field.number
189 1203 : << ", value." << field.name << " );\n";
190 : }
191 491 : for( const auto & map : message.maps )
192 : {
193 104 : stream << "\tstream.serialize" << map_encoder_type( file, map.key, map.value ) << "( "
194 52 : << map.number << ", value." << map.name << " );\n";
195 : }
196 443 : for( const auto & oneof : message.oneofs )
197 : {
198 4 : dump_cpp_serialize_value( stream, file, oneof );
199 : }
200 439 : stream << "}\n";
201 : }
202 :
203 448 : void dump_cpp_deserialize_value( std::ostream & stream, const proto_file & file,
204 : const proto_message & message, std::string_view full_name )
205 : {
206 448 : if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
207 : {
208 : stream << "void deserialize_value( detail::istream & stream, " << full_name
209 9 : << " &, uint32_t tag )\n{\n";
210 9 : stream << "\tstream.skip( tag );\n}\n\n";
211 9 : return;
212 : }
213 :
214 : stream << "void deserialize_value( detail::istream & stream, " << full_name
215 439 : << " & value, uint32_t tag )\n{\n";
216 439 : stream << "\tswitch( field_from_tag( tag ) )\n\t{\n";
217 :
218 1642 : for( const auto & field : message.fields )
219 : {
220 :
221 1203 : stream << "\t\tcase " << field.number << ":\n\t\t\t";
222 1203 : if( !field.bit_field.empty( ) )
223 : {
224 : stream << "value." << field.name << " = stream.deserialize_bitfield"
225 50 : << bitfield_encoder_type( file, field ) << "( " << field.bit_field
226 50 : << ", tag );\n";
227 50 : stream << "\t\t\treturn;\n";
228 : }
229 : else
230 : {
231 1153 : stream << "return stream.deserialize" << encoder_type( file, field ) << "( value."
232 1153 : << field.name << ", tag );\n";
233 : }
234 : }
235 491 : for( const auto & map : message.maps )
236 : {
237 52 : stream << "\t\tcase " << map.number << ":\n\t\t\treturn ";
238 52 : stream << "\tstream.deserialize" << map_encoder_type( file, map.key, map.value )
239 52 : << "( value." << map.name << ", tag );\n";
240 : }
241 443 : for( const auto & oneof : message.oneofs )
242 : {
243 15 : for( size_t i = 0; i < oneof.fields.size( ); ++i )
244 : {
245 11 : stream << "\t\tcase " << oneof.fields[ i ].number << ":\n\t\t\treturn ";
246 11 : auto type = encoder_type( file, oneof.fields[ i ] );
247 11 : if( type.empty( ) )
248 : {
249 8 : stream << "\tstream.deserialize_variant< " << i << ">( value." << oneof.name
250 8 : << ", tag );\n";
251 : }
252 : else
253 : {
254 3 : type.erase( 0, 4 );
255 3 : stream << "\tstream.deserialize_variant_as< " << i << ", " << type << "( value."
256 3 : << oneof.name << ", tag );\n";
257 : }
258 11 : }
259 : }
260 :
261 439 : stream << "\t\tdefault:\n\t\t\treturn stream.skip( tag );\t\n}\n}\n\n";
262 : }
263 :
264 : void dump_cpp_messages( std::ostream & stream, const proto_file & file,
265 : const proto_messages & messages, std::string_view parent );
266 :
267 448 : void dump_cpp_message( std::ostream & stream, const proto_file & file,
268 : const proto_message & message, std::string_view parent )
269 : {
270 1344 : const auto full_name = std::string( parent ) + "::" + std::string( message.name );
271 :
272 448 : dump_cpp_open_namespace( stream, "detail" );
273 448 : dump_cpp_serialize_value( stream, file, message, full_name );
274 448 : dump_cpp_deserialize_value( stream, file, message, full_name );
275 448 : dump_cpp_close_namespace( stream, "detail" );
276 :
277 448 : dump_cpp_messages( stream, file, message.messages, full_name );
278 448 : }
279 :
280 460 : void dump_cpp_messages( std::ostream & stream, const proto_file & file,
281 : const proto_messages & messages, std::string_view parent )
282 : {
283 908 : for( const auto & message : messages )
284 : {
285 448 : dump_cpp_message( stream, file, message, parent );
286 : }
287 460 : }
288 :
289 12 : void dump_cpp( std::ostream & stream, const proto_file & file )
290 : {
291 12 : const auto str_namespace = file.package.name.empty( )
292 12 : ? std::string( )
293 12 : : "::" + replace( file.package.name, ".", "::" );
294 12 : dump_cpp_messages( stream, file, file.package.messages, str_namespace );
295 12 : }
296 :
297 : }// namespace
298 :
299 12 : void dump_pb_header( const proto_file & file, std::ostream & stream )
300 : {
301 12 : dump_cpp_open_namespace( stream, "spb::pb::detail" );
302 12 : stream << "struct ostream;\nstruct istream;\n";
303 12 : dump_prototypes( stream, file );
304 12 : dump_cpp_close_namespace( stream, "spb::pb::detail" );
305 12 : }
306 :
307 12 : void dump_pb_cpp( const proto_file & file, const std::filesystem::path & header_file,
308 : std::ostream & stream )
309 : {
310 12 : dump_cpp_includes( stream, header_file.string( ) );
311 12 : dump_cpp_open_namespace( stream, "spb::pb" );
312 12 : dump_cpp( stream, file );
313 12 : dump_cpp_close_namespace( stream, "spb::pb" );
314 12 : }
|