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