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 % 158 156
Test Date: 2025-12-05 17:50:15 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 = 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 : }
        

Generated by: LCOV version 2.0-1