LCOV - code coverage report
Current view: top level - spb-proto-compiler/dumper/json - dumper.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.9 % 271 260
Test Date: 2025-05-23 14:18:14 Functions: 100.0 % 26 26

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : json dumper                                                 *
       3              : * Description : generate C++ src files for json 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.h"
      13              : #include "ast/proto-field.h"
      14              : #include "ast/proto-file.h"
      15              : #include "io/file.h"
      16              : #include "parser/parser.h"
      17              : #include "template-h.h"
      18              : #include <algorithm>
      19              : #include <array>
      20              : #include <cctype>
      21              : #include <cstdint>
      22              : #include <map>
      23              : #include <set>
      24              : #include <spb/json/deserialize.hpp>
      25              : #include <sstream>
      26              : #include <stdexcept>
      27              : #include <string>
      28              : #include <string_view>
      29              : 
      30              : using namespace std::literals;
      31              : 
      32              : namespace
      33              : {
      34              : 
      35          555 : auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
      36              : {
      37          555 :     auto result = std::string( input );
      38          555 :     auto pos    = size_t{ };
      39              : 
      40         1631 :     while( ( pos = result.find( what, pos ) ) != std::string::npos )
      41              :     {
      42         1076 :         result.replace( pos, what.size( ), with );
      43         1076 :         pos += with.size( );
      44              :     }
      45              : 
      46          555 :     return result;
      47            0 : }
      48              : 
      49          531 : void dump_prototypes( std::ostream & stream, std::string_view type )
      50              : {
      51          531 :     stream << replace( file_json_header_template, "$", type );
      52          531 : }
      53              : 
      54         2480 : auto json_name_from_options( const proto_options & options ) -> std::string_view
      55              : {
      56         2480 :     if( auto p_option = options.find( "json_name" ); p_option != options.end( ) )
      57              :     {
      58            0 :         return p_option->second;
      59              :     }
      60              : 
      61         2480 :     return ""sv;
      62              : }
      63              : 
      64         1266 : auto convert_to_camelCase( std::string_view input ) -> std::string
      65              : {
      66         1266 :     auto result = std::string( input );
      67         1266 :     if( !result.empty( ) )
      68              :     {
      69         1266 :         result[ 0 ] = char( std::tolower( result[ 0 ] ) );
      70              :     }
      71              : 
      72         1266 :     if( input.find( '_' ) == std::string::npos )
      73              :     {
      74          797 :         return result;
      75              :     }
      76              : 
      77          469 :     auto index = 0U;
      78          469 :     auto up    = false;
      79         7624 :     for( auto c : input )
      80              :     {
      81         7155 :         if( c == '_' )
      82              :         {
      83          703 :             up = true;
      84              :         }
      85              :         else
      86              :         {
      87         6452 :             result[ index ] = char( ( up && index > 0 ) ? std::toupper( c ) : std::tolower( c ) );
      88         6452 :             index += 1;
      89         6452 :             up = false;
      90              :         }
      91              :     }
      92          469 :     result.resize( index );
      93          469 :     return result;
      94            0 : }
      95              : 
      96         1214 : auto json_field_name( const proto_base & field ) -> std::string
      97              : {
      98         1214 :     if( const auto result = json_name_from_options( field.options ); !result.empty( ) )
      99              :     {
     100            0 :         return std::string( result );
     101              :     }
     102              : 
     103         2428 :     return std::string( field.name );
     104              : }
     105              : 
     106         1266 : auto json_field_name_or_camelCase( const proto_base & field ) -> std::string
     107              : {
     108         1266 :     if( const auto result = json_name_from_options( field.options ); !result.empty( ) )
     109              :     {
     110            0 :         return std::string( result );
     111              :     }
     112              : 
     113         1266 :     return convert_to_camelCase( field.name );
     114              : }
     115              : 
     116          448 : void dump_prototypes( std::ostream & stream, const proto_message & message,
     117              :                       std::string_view parent )
     118              : {
     119         1344 :     const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
     120          448 :     dump_prototypes( stream, message_with_parent );
     121          448 : }
     122              : 
     123           83 : void dump_prototypes( std::ostream & stream, const proto_enum & my_enum, std::string_view parent )
     124              : {
     125          249 :     const auto enum_with_parent = std::string( parent ) + "::" + std::string( my_enum.name );
     126           83 :     dump_prototypes( stream, enum_with_parent );
     127           83 : }
     128              : 
     129           75 : void dump_prototypes( std::ostream & stream, const proto_enums & enums, std::string_view parent )
     130              : {
     131          158 :     for( const auto & my_enum : enums )
     132              :     {
     133           83 :         dump_prototypes( stream, my_enum, parent );
     134              :     }
     135           75 : }
     136              : 
     137           63 : void dump_prototypes( std::ostream & stream, const proto_messages & messages,
     138              :                       std::string_view parent )
     139              : {
     140          511 :     for( const auto & message : messages )
     141              :     {
     142          448 :         dump_prototypes( stream, message, parent );
     143              :     }
     144              : 
     145          511 :     for( const auto & message : messages )
     146              :     {
     147          448 :         if( message.messages.empty( ) )
     148              :         {
     149          397 :             continue;
     150              :         }
     151          153 :         const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
     152           51 :         dump_prototypes( stream, message.messages, message_with_parent );
     153           51 :     }
     154              : 
     155          511 :     for( const auto & message : messages )
     156              :     {
     157          448 :         if( message.enums.empty( ) )
     158              :         {
     159          385 :             continue;
     160              :         }
     161          189 :         const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
     162           63 :         dump_prototypes( stream, message.enums, message_with_parent );
     163           63 :     }
     164           63 : }
     165              : 
     166           12 : void dump_prototypes( std::ostream & stream, const proto_file & file )
     167              : {
     168           12 :     const auto package_name = replace( file.package.name, ".", "::" );
     169           12 :     dump_prototypes( stream, file.package.messages, package_name );
     170           12 :     dump_prototypes( stream, file.package.enums, package_name );
     171           12 : }
     172              : 
     173           12 : void dump_cpp_includes( std::ostream & stream, std::string_view header_file_path )
     174              : {
     175              :     stream << "#include \"" << header_file_path << "\"\n"
     176              :            << "#include <spb/json.hpp>\n"
     177              :               "#include <system_error>\n"
     178           12 :               "#include <type_traits>\n\n";
     179           12 : }
     180              : 
     181          555 : void dump_cpp_close_namespace( std::ostream & stream, std::string_view name )
     182              : {
     183          555 :     stream << "} // namespace " << name << "\n";
     184          555 : }
     185              : 
     186          555 : void dump_cpp_open_namespace( std::ostream & stream, std::string_view name )
     187              : {
     188          555 :     stream << "namespace " << name << "\n{\n";
     189          555 : }
     190              : 
     191            4 : void dump_cpp_serialize_value( std::ostream & stream, const proto_oneof & oneof )
     192              : {
     193            4 :     stream << "\t{\n\t\tconst auto index = value." << oneof.name << ".index( );\n";
     194            4 :     stream << "\t\tswitch( index )\n\t\t{\n";
     195           15 :     for( size_t i = 0; i < oneof.fields.size( ); ++i )
     196              :     {
     197           11 :         stream << "\t\t\tcase " << i << ":\n\t\t\t\treturn stream.serialize( \""
     198           22 :                << json_field_name( oneof.fields[ i ] ) << "\"sv, std::get< " << i << " >( value."
     199           11 :                << oneof.name << ") );\n";
     200              :     }
     201            4 :     stream << "\t\t}\n\t}\n\n";
     202            4 : }
     203              : 
     204           83 : void dump_cpp_serialize_value( std::ostream & stream, const proto_enum & my_enum,
     205              :                                std::string_view full_name )
     206              : {
     207           83 :     if( my_enum.fields.empty( ) )
     208              :     {
     209            0 :         stream << "void serialize_value( detail::ostream &, const " << full_name << " & )\n{\n";
     210            0 :         stream << "\treturn ;\n}\n\n";
     211            0 :         return;
     212              :     }
     213              : 
     214              :     stream << "void serialize_value( detail::ostream & stream, const " << full_name
     215           83 :            << " & value )\n{\n";
     216           83 :     stream << "\tswitch( value )\n\t{\n";
     217              : 
     218           83 :     std::set< int32_t > numbers_taken;
     219          521 :     for( const auto & field : my_enum.fields )
     220              :     {
     221          438 :         if( !numbers_taken.insert( field.number ).second )
     222              :         {
     223            4 :             continue;
     224              :         }
     225              : 
     226              :         stream << "\tcase " << full_name << "::" << field.name
     227          434 :                << ":\n\t\treturn stream.serialize( \"" << field.name << "\"sv);\n";
     228              :     }
     229              :     stream << "\tdefault:\n\t\tthrow std::system_error( std::make_error_code( "
     230           83 :               "std::errc::invalid_argument ) );\n";
     231           83 :     stream << "\t}\n}\n\n";
     232           83 : }
     233              : 
     234           83 : void dump_cpp_deserialize_value( std::ostream & stream, const proto_enum & my_enum,
     235              :                                  std::string_view full_name )
     236              : {
     237           83 :     if( my_enum.fields.empty( ) )
     238              :     {
     239            0 :         stream << "void deserialize_value( detail::istream &, " << full_name << " & )\n{\n";
     240            0 :         stream << "\n}\n\n";
     241            0 :         return;
     242              :     }
     243              : 
     244           83 :     size_t key_size_min = UINT32_MAX;
     245           83 :     size_t key_size_max = 0;
     246              : 
     247           83 :     auto name_map = std::multimap< uint32_t, std::string_view >( );
     248          521 :     for( const auto & field : my_enum.fields )
     249              :     {
     250          438 :         name_map.emplace( spb::json::detail::djb2_hash( field.name ), field.name );
     251          438 :         key_size_min = std::min( key_size_min, field.name.size( ) );
     252          438 :         key_size_max = std::max( key_size_max, field.name.size( ) );
     253              :     }
     254              : 
     255              :     stream << "void deserialize_value( detail::istream & stream, " << full_name
     256           83 :            << " & value )\n{\n";
     257           83 :     stream << "\tauto enum_value = stream.deserialize_string_or_int( " << key_size_min << ", "
     258           83 :            << key_size_max << " );\n";
     259           83 :     stream << "\tstd::visit( detail::overloaded{\n\t\t[&]( std::string_view enum_str )\n\t\t{\n";
     260           83 :     stream << "\t\t\tconst auto enum_hash = djb2_hash( enum_str );\n";
     261           83 :     stream << "\t\t\tswitch( enum_hash )\n\t\t\t{\n";
     262           83 :     auto last_hash = name_map.begin( )->first + 1;
     263           83 :     auto put_break = false;
     264          521 :     for( const auto & [ hash, name ] : name_map )
     265              :     {
     266          438 :         if( hash != last_hash )
     267              :         {
     268          438 :             last_hash = hash;
     269          438 :             if( put_break )
     270              :             {
     271          355 :                 stream << "\t\t\t\tbreak ;\n";
     272              :             }
     273          438 :             put_break = true;
     274          438 :             stream << "\t\t\tcase detail::djb2_hash( \"" << name << "\"sv ):\n";
     275              :         }
     276              :         stream << "\t\t\t\tif( enum_str == \"" << name << "\"sv ){\n\t\t\t\t\tvalue = " << full_name
     277          438 :                << "::" << name << ";\n\t\t\t\t\treturn ;\t\t\t\t}\n";
     278              :     }
     279           83 :     stream << "\t\t\t\tbreak ;\n";
     280              :     stream << "\t\t\t}\n\t\t\tthrow std::system_error( std::make_error_code( "
     281           83 :               "std::errc::invalid_argument ) );\n";
     282              :     stream << "\t\t},\n\t\t[&]( int32_t enum_int )\n\t\t{\n\t\t\tswitch( " << full_name
     283           83 :            << "( enum_int ) )\n\t\t\t{\n";
     284           83 :     std::set< int32_t > numbers_taken;
     285          521 :     for( const auto & field : my_enum.fields )
     286              :     {
     287          438 :         if( !numbers_taken.insert( field.number ).second )
     288              :         {
     289            4 :             continue;
     290              :         }
     291              : 
     292          434 :         stream << "\t\t\tcase " << full_name << "::" << field.name << ":\n";
     293              :     }
     294           83 :     stream << "\t\t\t\tvalue = " << full_name << "( enum_int );\n\t\t\t\treturn ;\n";
     295              :     stream << "\t\t\t}\n\t\t\tthrow std::system_error( std::make_error_code( "
     296           83 :               "std::errc::invalid_argument ) );\n";
     297           83 :     stream << "\t\t}\n\t}, enum_value );\n}\n\n";
     298           83 : }
     299              : 
     300          448 : void dump_cpp_serialize_value( std::ostream & stream, const proto_message & message,
     301              :                                std::string_view full_name )
     302              : {
     303          448 :     if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
     304              :     {
     305              :         stream << "void serialize_value( detail::ostream & , const " << full_name
     306            9 :                << " & )\n{\n}\n\n";
     307            9 :         return;
     308              :     }
     309              : 
     310              :     stream << "void serialize_value( detail::ostream & stream, const " << full_name
     311          439 :            << " & value )\n{\n";
     312         1642 :     for( const auto & field : message.fields )
     313              :     {
     314         1203 :         stream << "\tstream.serialize( \"" << json_field_name( field ) << "\"sv, value."
     315         1203 :                << field.name << " );\n";
     316              :     }
     317          491 :     for( const auto & map : message.maps )
     318              :     {
     319           52 :         stream << "\tstream.serialize( \"" << map.name << "\"sv, value." << map.name << " );\n";
     320              :     }
     321          443 :     for( const auto & oneof : message.oneofs )
     322              :     {
     323            4 :         dump_cpp_serialize_value( stream, oneof );
     324              :     }
     325          439 :     stream << "}\n";
     326              : }
     327              : 
     328          448 : void dump_cpp_deserialize_value( std::ostream & stream, const proto_message & message,
     329              :                                  std::string_view full_name )
     330              : {
     331          448 :     if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
     332              :     {
     333            9 :         stream << "void deserialize_value( detail::istream &, " << full_name << " & )\n{\n";
     334            9 :         stream << "\n}\n\n";
     335            9 :         return;
     336              :     }
     337              : 
     338              :     //- json deserializer needs to accept both camelCase (parsed_name) and the original field name
     339              :     struct one_field
     340              :     {
     341              :         std::string parsed_name;
     342              :         std::string_view name;
     343              :         size_t oneof_index = SIZE_MAX;
     344              :         std::string_view bitfield;
     345              :     };
     346              : 
     347          439 :     size_t key_size_min = UINT32_MAX;
     348          439 :     size_t key_size_max = 0;
     349              : 
     350          439 :     auto name_map = std::multimap< uint32_t, one_field >( );
     351         1642 :     for( const auto & field : message.fields )
     352              :     {
     353         1203 :         key_size_min = std::min( key_size_min, field.name.size( ) );
     354         1203 :         key_size_max = std::max( key_size_max, field.name.size( ) );
     355              : 
     356         1203 :         const auto field_name = json_field_name_or_camelCase( field );
     357         1203 :         name_map.emplace( spb::json::detail::djb2_hash( field_name ),
     358         2406 :                           one_field{
     359              :                               .parsed_name = field_name,
     360              :                               .name        = field.name,
     361              :                               .bitfield    = field.bit_field,
     362              :                           } );
     363         1203 :         if( field_name != field.name )
     364              :         {
     365          482 :             key_size_min = std::min( key_size_min, field_name.size( ) );
     366          482 :             key_size_max = std::max( key_size_max, field_name.size( ) );
     367              : 
     368          482 :             name_map.emplace( spb::json::detail::djb2_hash( field.name ),
     369         1446 :                               one_field{
     370          964 :                                   .parsed_name = std::string( field.name ),
     371              :                                   .name        = field.name,
     372              :                                   .bitfield    = field.bit_field,
     373              :                               } );
     374              :         }
     375         1203 :     }
     376          491 :     for( const auto & field : message.maps )
     377              :     {
     378           52 :         key_size_min = std::min( key_size_min, field.name.size( ) );
     379           52 :         key_size_max = std::max( key_size_max, field.name.size( ) );
     380              : 
     381           52 :         const auto field_name = json_field_name_or_camelCase( field );
     382           52 :         name_map.emplace( spb::json::detail::djb2_hash( field_name ),
     383          104 :                           one_field{
     384              :                               .parsed_name = field_name,
     385              :                               .name        = field.name,
     386              :                           } );
     387           52 :         if( field_name != field.name )
     388              :         {
     389           48 :             key_size_min = std::min( key_size_min, field_name.size( ) );
     390           48 :             key_size_max = std::max( key_size_max, field_name.size( ) );
     391              : 
     392           48 :             name_map.emplace(
     393           48 :                 spb::json::detail::djb2_hash( field.name ),
     394          192 :                 one_field{ .parsed_name = std::string( field.name ), .name = field.name } );
     395              :         }
     396           52 :     }
     397          443 :     for( const auto & oneof : message.oneofs )
     398              :     {
     399           15 :         for( size_t i = 0; i < oneof.fields.size( ); ++i )
     400              :         {
     401           11 :             key_size_min = std::min( key_size_min, oneof.fields[ i ].name.size( ) );
     402           11 :             key_size_max = std::max( key_size_max, oneof.fields[ i ].name.size( ) );
     403              : 
     404           11 :             const auto field_name = json_field_name_or_camelCase( oneof.fields[ i ] );
     405           11 :             name_map.emplace( spb::json::detail::djb2_hash( field_name ),
     406           22 :                               one_field{
     407              :                                   .parsed_name = field_name,
     408              :                                   .name        = oneof.name,
     409              :                                   .oneof_index = i,
     410              :                               } );
     411           11 :             if( field_name != oneof.fields[ i ].name )
     412              :             {
     413            8 :                 key_size_min = std::min( key_size_min, field_name.size( ) );
     414            8 :                 key_size_max = std::max( key_size_max, field_name.size( ) );
     415              : 
     416            8 :                 name_map.emplace( spb::json::detail::djb2_hash( oneof.fields[ i ].name ),
     417           24 :                                   one_field{
     418           16 :                                       .parsed_name = std::string( oneof.fields[ i ].name ),
     419              :                                       .name        = oneof.name,
     420              :                                       .oneof_index = i,
     421              :                                   } );
     422              :             }
     423           11 :         }
     424              :     }
     425              : 
     426              :     stream << "void deserialize_value( detail::istream & stream, " << full_name
     427          439 :            << " & value )\n{\n";
     428          439 :     stream << "\tauto key = stream.deserialize_key( " << key_size_min << ", " << key_size_max
     429          439 :            << " );\n";
     430          439 :     stream << "\tswitch( djb2_hash( key ) )\n\t{\n";
     431              : 
     432          439 :     auto last_hash = name_map.begin( )->first + 1;
     433          439 :     auto put_break = false;
     434         2243 :     for( const auto & [ hash, field ] : name_map )
     435              :     {
     436         1804 :         if( hash != last_hash )
     437              :         {
     438         1803 :             if( put_break )
     439              :             {
     440         1364 :                 stream << "\t\t\t\tbreak;\n";
     441              :             }
     442         1803 :             put_break = true;
     443         1803 :             last_hash = hash;
     444         1803 :             stream << "\t\tcase detail::djb2_hash( \"" << field.parsed_name << "\"sv ):\n";
     445              :         }
     446         1804 :         stream << "\t\t\tif( key == \"" << field.parsed_name << "\"sv )\n\t\t\t{\n";
     447         1804 :         if( field.oneof_index == SIZE_MAX )
     448              :         {
     449         1785 :             if( !field.bitfield.empty( ) )
     450              :             {
     451              : 
     452              :                 stream << "\t\t\t\tvalue." << field.name
     453              :                        << " = stream.deserialize_bitfield< decltype( value." << field.name
     454           50 :                        << " ) >( " << field.bitfield << " );\n\t\t\t\treturn ;\n";
     455              :             }
     456              :             else
     457              :             {
     458         1735 :                 stream << "\t\t\t\treturn stream.deserialize( value." << field.name << " );\n";
     459              :             }
     460              :         }
     461              :         else
     462              :         {
     463           19 :             stream << "\t\t\t\treturn stream.deserialize_variant<" << field.oneof_index
     464           19 :                    << ">( value." << field.name << " );\n";
     465              :         }
     466         1804 :         stream << "\t\t\t}\n";
     467              :     }
     468          439 :     stream << "\t\t\tbreak;\n\t}\n\treturn stream.skip_value( );\n}\n";
     469          439 : }
     470              : 
     471           83 : void dump_cpp_enum( std::ostream & stream, const proto_enum & my_enum, std::string_view parent )
     472              : {
     473          249 :     const auto full_name = std::string( parent ) + "::" + std::string( my_enum.name );
     474           83 :     dump_cpp_open_namespace( stream, "detail" );
     475           83 :     dump_cpp_serialize_value( stream, my_enum, full_name );
     476           83 :     dump_cpp_deserialize_value( stream, my_enum, full_name );
     477           83 :     dump_cpp_close_namespace( stream, "detail" );
     478           83 : }
     479              : 
     480          460 : void dump_cpp_enums( std::ostream & stream, const proto_enums & enums, std::string_view parent )
     481              : {
     482          543 :     for( const auto & my_enum : enums )
     483              :     {
     484           83 :         dump_cpp_enum( stream, my_enum, parent );
     485              :     }
     486          460 : }
     487              : 
     488              : void dump_cpp_messages( std::ostream & stream, const proto_messages & messages,
     489              :                         std::string_view parent );
     490              : 
     491          448 : void dump_cpp_message( std::ostream & stream, const proto_message & message,
     492              :                        std::string_view parent )
     493              : {
     494         1344 :     const auto full_name = std::string( parent ) + "::" + std::string( message.name );
     495              : 
     496          448 :     dump_cpp_enums( stream, message.enums, full_name );
     497              : 
     498          448 :     dump_cpp_open_namespace( stream, "detail" );
     499          448 :     dump_cpp_serialize_value( stream, message, full_name );
     500          448 :     dump_cpp_deserialize_value( stream, message, full_name );
     501          448 :     dump_cpp_close_namespace( stream, "detail" );
     502              : 
     503          448 :     dump_cpp_messages( stream, message.messages, full_name );
     504          448 : }
     505              : 
     506          460 : void dump_cpp_messages( std::ostream & stream, const proto_messages & messages,
     507              :                         std::string_view parent )
     508              : {
     509          908 :     for( const auto & message : messages )
     510              :     {
     511          448 :         dump_cpp_message( stream, message, parent );
     512              :     }
     513          460 : }
     514              : 
     515           12 : void dump_cpp( std::ostream & stream, const proto_file & file )
     516              : {
     517           12 :     const auto str_namespace = "::" + replace( file.package.name, ".", "::" );
     518           12 :     dump_cpp_enums( stream, file.package.enums, str_namespace );
     519           12 :     dump_cpp_messages( stream, file.package.messages, str_namespace );
     520           12 : }
     521              : 
     522              : }// namespace
     523              : 
     524           12 : void dump_json_header( const proto_file & file, std::ostream & stream )
     525              : {
     526           12 :     dump_cpp_open_namespace( stream, "spb::json::detail" );
     527           12 :     stream << "struct ostream;\nstruct istream;\n";
     528           12 :     dump_prototypes( stream, file );
     529           12 :     dump_cpp_close_namespace( stream, "spb::json::detail" );
     530           12 : }
     531              : 
     532           12 : void dump_json_cpp( const proto_file & file, const std::filesystem::path & header_file,
     533              :                     std::ostream & stream )
     534              : {
     535           12 :     dump_cpp_includes( stream, header_file.string( ) );
     536           12 :     dump_cpp_open_namespace( stream, "spb::json" );
     537           12 :     dump_cpp( stream, file );
     538           12 :     dump_cpp_close_namespace( stream, "spb::json" );
     539           12 : }
        

Generated by: LCOV version 2.0-1