LCOV - code coverage report
Current view: top level - spb-proto-compiler/dumper/pb - dumper.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.7 % 154 152
Test Date: 2025-05-23 14:18:14 Functions: 100.0 % 20 20

            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 : }
        

Generated by: LCOV version 2.0-1