LCOV - code coverage report
Current view: top level - spb/json - serialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 97.9 % 188 184
Test Date: 2026-03-06 17:56:28 Functions: 32.4 % 1255 407

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : serialize library for json                                  *
       3              : * Description : all json serialization functions                            *
       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              : #pragma once
      12              : 
      13              : #include "../concepts.h"
      14              : 
      15              : #include "../to_from_chars.h"
      16              : #include "base64.h"
      17              : #include "spb/json/deserialize.hpp"
      18              : #include "spb/utf8.h"
      19              : #include <algorithm>
      20              : #include <array>
      21              : #include <cctype>
      22              : #include <charconv>
      23              : #include <cinttypes>
      24              : #include <cstddef>
      25              : #include <cstring>
      26              : #include <map>
      27              : #include <memory>
      28              : #include <spb/io/io.hpp>
      29              : #include <stdexcept>
      30              : #include <string>
      31              : #include <string_view>
      32              : #include <sys/types.h>
      33              : #include <type_traits>
      34              : 
      35              : namespace spb::json::detail
      36              : {
      37              : struct ostream
      38              : {
      39              : private:
      40              :     size_t bytes_written = 0;
      41              :     spb::io::writer on_write;
      42              : 
      43              : public:
      44              :     //- flag if put ',' before value
      45              :     bool put_comma = false;
      46              : 
      47              :     /**
      48              :      * @brief Construct a new ostream object
      49              :      *
      50              :      * @param writer if null, stream will skip all writes but will still count number of written
      51              :      * chars
      52              :      */
      53         6969 :     explicit ostream( spb::io::writer writer ) noexcept
      54         6969 :         : on_write( writer )
      55              :     {
      56         6969 :     }
      57              : 
      58      8756274 :     void write( char c ) noexcept
      59              :     {
      60      8756274 :         if( on_write )
      61              :         {
      62      4377405 :             on_write( &c, sizeof( c ) );
      63              :         }
      64              : 
      65      8756274 :         bytes_written += sizeof( c );
      66      8756274 :     }
      67              : 
      68           20 :     void write_unicode( uint32_t codepoint )
      69              :     {
      70           20 :         if( codepoint <= 0xffff )
      71              :         {
      72           15 :             char buffer[ 8 ] = { };
      73           15 :             auto size        = snprintf( buffer, sizeof( buffer ), "\\u%04" PRIx32, codepoint );
      74           15 :             return write( std::string_view( buffer, size ) );
      75              :         }
      76            5 :         if( codepoint <= 0x10FFFF )
      77              :         {
      78            5 :             codepoint -= 0x10000;
      79              : 
      80            5 :             auto high         = static_cast< uint16_t >( ( codepoint >> 10 ) + 0xD800 );
      81            5 :             auto low          = static_cast< uint16_t >( ( codepoint & 0x3FF ) + 0xDC00 );
      82            5 :             char buffer[ 16 ] = { };
      83              :             auto size =
      84            5 :                 snprintf( buffer, sizeof( buffer ), "\\u%04" PRIx16 "\\u%04" PRIx16, high, low );
      85            5 :             return write( std::string_view( buffer, size ) );
      86              :         }
      87            0 :         throw std::invalid_argument( "invalid utf8" );
      88              :     }
      89              : 
      90         4599 :     void write( std::string_view str )
      91              :     {
      92         4599 :         if( on_write )
      93              :         {
      94         1859 :             on_write( str.data( ), str.size( ) );
      95              :         }
      96              : 
      97         4599 :         bytes_written += str.size( );
      98         4599 :     }
      99              : 
     100         1688 :     void write_escaped( std::string_view str )
     101              :     {
     102         1688 :         if( !has_escape_chars( str ) )
     103              :         {
     104         1644 :             write( str );
     105         1644 :             return;
     106              :         }
     107              : 
     108              :         using namespace std::literals;
     109           44 :         uint32_t codepoint = 0;
     110           44 :         uint32_t state     = spb::detail::utf8::ok;
     111           44 :         bool decoding_utf8 = false;
     112          285 :         for( uint8_t c : str )
     113              :         {
     114          241 :             if( decoding_utf8 )
     115              :             {
     116           30 :                 if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
     117              :                     spb::detail::utf8::ok )
     118              :                 {
     119           15 :                     write_unicode( codepoint );
     120           15 :                     decoding_utf8 = false;
     121              :                 }
     122           30 :                 continue;
     123              :             }
     124          211 :             if( is_escape( c ) )
     125              :             {
     126          128 :                 switch( c )
     127              :                 {
     128           21 :                 case '"':
     129           21 :                     write( R"(\")"sv );
     130           21 :                     break;
     131           14 :                 case '\\':
     132           14 :                     write( R"(\\)"sv );
     133           14 :                     break;
     134            9 :                 case '\b':
     135            9 :                     write( R"(\b)"sv );
     136            9 :                     break;
     137            9 :                 case '\f':
     138            9 :                     write( R"(\f)"sv );
     139            9 :                     break;
     140           14 :                 case '\n':
     141           14 :                     write( R"(\n)"sv );
     142           14 :                     break;
     143           14 :                 case '\r':
     144           14 :                     write( R"(\r)"sv );
     145           14 :                     break;
     146           26 :                 case '\t':
     147           26 :                     write( R"(\t)"sv );
     148           26 :                     break;
     149           21 :                 default:
     150           21 :                     decoding_utf8 = true;
     151           21 :                     if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
     152              :                         spb::detail::utf8::ok )
     153              :                     {
     154            5 :                         write_unicode( codepoint );
     155            5 :                         decoding_utf8 = false;
     156              :                     }
     157              :                 }
     158              :             }
     159              :             else
     160              :             {
     161           83 :                 write( c );
     162              :             }
     163              :         }
     164           44 :         if( state != spb::detail::utf8::ok )
     165              :         {
     166            1 :             throw std::runtime_error( "invalid utf8" );
     167              :         }
     168              :     }
     169              : 
     170              :     void serialize( std::string_view key, const auto & value );
     171              :     void serialize( std::string_view value );
     172              : 
     173         4407 :     [[nodiscard]] auto size( ) const noexcept -> size_t
     174              :     {
     175         4407 :         return bytes_written;
     176              :     }
     177              : 
     178              : private:
     179         9275 :     static auto is_escape( uint8_t c ) -> bool
     180              :     {
     181              :         static constexpr std::string_view escape_chars = "\\\"\b\f\n\r\t<>";
     182         9275 :         return c <= 0x1f || c >= 0x7f || escape_chars.find( c ) != std::string_view::npos;
     183              :     }
     184              : 
     185         1688 :     static auto has_escape_chars( std::string_view str ) -> bool
     186              :     {
     187         1688 :         return std::any_of( str.begin( ), str.end( ), is_escape );
     188              :     }
     189              : };
     190              : 
     191              : using namespace std::literals;
     192              : 
     193         3939 : static inline void serialize_key( ostream & stream, std::string_view key )
     194              : {
     195         3939 :     if( stream.put_comma )
     196              :     {
     197          293 :         stream.write( ',' );
     198              :     }
     199         3939 :     stream.put_comma = true;
     200              : 
     201         3939 :     if( !key.empty( ) )
     202              :     {
     203         1425 :         stream.write( '"' );
     204         1425 :         stream.write_escaped( key );
     205         1425 :         stream.write( R"(":)"sv );
     206              :     }
     207         3939 : }
     208              : 
     209              : static inline void serialize( ostream & stream, std::string_view key, const bool & value );
     210              : static inline void serialize( ostream & stream, std::string_view key,
     211              :                               const spb::detail::proto_field_int_or_float auto & value );
     212              : static inline void serialize( ostream & stream, std::string_view key,
     213              :                               const spb::detail::proto_message auto & value );
     214              : static inline void serialize( ostream & stream, std::string_view key,
     215              :                               const spb::detail::proto_enum auto & value );
     216              : static inline void serialize( ostream & stream, std::string_view key,
     217              :                               const spb::detail::proto_field_string auto & value );
     218              : static inline void serialize( ostream & stream, std::string_view key,
     219              :                               const spb::detail::proto_field_bytes auto & value );
     220              : static inline void serialize( ostream & stream, std::string_view key,
     221              :                               const spb::detail::proto_label_repeated auto & value );
     222              : static inline void serialize( ostream & stream, std::string_view key,
     223              :                               const spb::detail::proto_label_repeated_fixed_size auto & value );
     224              : template < typename keyT, typename valueT >
     225              : static inline void serialize( ostream & stream, std::string_view key,
     226              :                               const std::map< keyT, valueT > & map );
     227              : 
     228              : static inline void serialize( ostream & stream, bool value );
     229              : static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value );
     230              : static inline void serialize( ostream & stream,
     231              :                               const spb::detail::proto_field_string auto & value );
     232              : 
     233           76 : static inline void serialize( ostream & stream, bool value )
     234              : {
     235           76 :     stream.write( value ? "true"sv : "false"sv );
     236           76 : }
     237              : 
     238          263 : static inline void serialize( ostream & stream, const std::string_view & value )
     239              : {
     240          263 :     stream.write( '"' );
     241          263 :     stream.write_escaped( value );
     242          262 :     stream.write( '"' );
     243          262 : }
     244              : 
     245          159 : static inline void serialize( ostream & stream, const spb::detail::proto_field_string auto & value )
     246              : {
     247          159 :     serialize( stream, std::string_view( value.data( ), value.size( ) ) );
     248          158 : }
     249              : 
     250         1319 : static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value )
     251              : {
     252         1319 :     auto buffer = std::array< char, 32 >( );
     253              : 
     254         3128 :     auto result = spb_std_emu::to_chars( buffer.data( ), buffer.data( ) + sizeof( buffer ), value );
     255         1319 :     stream.write(
     256         3957 :         std::string_view( buffer.data( ), static_cast< size_t >( result.ptr - buffer.data( ) ) ) );
     257         1319 : }
     258              : 
     259           76 : static inline void serialize( ostream & stream, std::string_view key, const bool & value )
     260              : {
     261           76 :     serialize_key( stream, key );
     262           76 :     serialize( stream, value );
     263           76 : }
     264              : 
     265         1311 : static inline void serialize( ostream & stream, std::string_view key,
     266              :                               const spb::detail::proto_field_int_or_float auto & value )
     267              : {
     268         1311 :     serialize_key( stream, key );
     269         1311 :     serialize( stream, value );
     270         1311 : }
     271              : 
     272          164 : static inline void serialize( ostream & stream, std::string_view key,
     273              :                               const spb::detail::proto_field_string auto & value )
     274              : {
     275          164 :     if( !value.empty( ) )
     276              :     {
     277          159 :         serialize_key( stream, key );
     278          159 :         serialize( stream, value );
     279              :     }
     280          163 : }
     281              : 
     282          134 : static inline void serialize( ostream & stream, std::string_view key,
     283              :                               const spb::detail::proto_field_bytes auto & value )
     284              : {
     285          134 :     if( !value.empty( ) )
     286              :     {
     287          119 :         serialize_key( stream, key );
     288          119 :         stream.write( '"' );
     289          119 :         base64_encode( stream, value );
     290          119 :         stream.write( '"' );
     291              :     }
     292          134 : }
     293          693 : static inline void serialize( ostream & stream, std::string_view key,
     294              :                               const spb::detail::proto_label_repeated auto & value )
     295              : {
     296          693 :     if( value.empty( ) )
     297              :     {
     298          270 :         return;
     299              :     }
     300              : 
     301          423 :     serialize_key( stream, key );
     302          423 :     stream.write( '[' );
     303          423 :     stream.put_comma = false;
     304         1076 :     for( const auto & v : value )
     305              :     {
     306              :         if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
     307              :                                       bool > )
     308              :         {
     309           34 :             serialize( stream, { }, bool( v ) );
     310              :         }
     311              :         else
     312              :         {
     313          619 :             serialize( stream, { }, v );
     314              :         }
     315              :     }
     316          423 :     stream.write( ']' );
     317          423 :     stream.put_comma = true;
     318              : }
     319              : 
     320           10 : static inline void serialize( ostream & stream, std::string_view key,
     321              :                               const spb::detail::proto_label_repeated_fixed_size auto & value )
     322              : {
     323           10 :     serialize_key( stream, key );
     324           10 :     stream.write( '[' );
     325           10 :     stream.put_comma = false;
     326           60 :     for( size_t i = 0; i < value.size( ); i++ )
     327              :     {
     328              :         if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
     329              :                                       bool > )
     330              :         {
     331              :             serialize( stream, { }, bool( value[ i ] ) );
     332              :         }
     333              :         else
     334              :         {
     335           40 :             serialize( stream, { }, value[ i ] );
     336              :         }
     337              :     }
     338           10 :     stream.write( ']' );
     339           10 :     stream.put_comma = true;
     340           10 : }
     341              : 
     342              : static constexpr std::string_view no_name = { };
     343              : 
     344              : template < typename keyT, typename valueT >
     345           20 : static inline void serialize( ostream & stream, std::string_view key,
     346              :                               const std::map< keyT, valueT > & map )
     347              : {
     348           20 :     if( map.empty( ) )
     349              :     {
     350            8 :         return;
     351              :     }
     352           12 :     serialize_key( stream, key );
     353           12 :     stream.write( '{' );
     354           12 :     stream.put_comma = false;
     355           26 :     for( auto & [ map_key, map_value ] : map )
     356              :     {
     357              :         if constexpr( std::is_same_v< keyT, std::string_view > ||
     358              :                       std::is_same_v< keyT, std::string > )
     359              :         {
     360            6 :             serialize_key( stream, map_key );
     361              :         }
     362              :         else
     363              :         {
     364            8 :             if( stream.put_comma )
     365              :             {
     366            2 :                 stream.write( ',' );
     367              :             }
     368              : 
     369            8 :             stream.write( '"' );
     370            8 :             serialize( stream, map_key );
     371            8 :             stream.write( R"(":)"sv );
     372              :         }
     373           14 :         stream.put_comma = false;
     374           14 :         serialize( stream, no_name, map_value );
     375           14 :         stream.put_comma = true;
     376              :     }
     377           12 :     stream.write( '}' );
     378           12 :     stream.put_comma = true;
     379              : }
     380              : 
     381          421 : static inline void serialize( ostream & stream, std::string_view key,
     382              :                               const spb::detail::proto_label_optional auto & p_value )
     383              : {
     384          421 :     if( p_value.has_value( ) )
     385              :     {
     386          319 :         return serialize( stream, key, *p_value );
     387              :     }
     388              : }
     389              : 
     390              : template < typename T >
     391            0 : static inline void serialize( ostream & stream, std::string_view key,
     392              :                               const std::unique_ptr< T > & p_value )
     393              : {
     394            0 :     if( p_value )
     395              :     {
     396            0 :         return serialize( stream, key, *p_value );
     397              :     }
     398              : }
     399              : 
     400         1719 : static inline void serialize( ostream & stream, std::string_view key,
     401              :                               const spb::detail::proto_message auto & value )
     402              : {
     403         1719 :     serialize_key( stream, key );
     404         1719 :     stream.write( '{' );
     405         1719 :     stream.put_comma = false;
     406              : 
     407              :     //
     408              :     //- serialize_value is generated by the spb-protoc
     409              :     //
     410         1719 :     serialize_value( stream, value );
     411         1719 :     stream.write( '}' );
     412         1719 :     stream.put_comma = true;
     413         1719 : }
     414              : 
     415          104 : static inline void serialize( ostream & stream, std::string_view key,
     416              :                               const spb::detail::proto_enum auto & value )
     417              : {
     418          104 :     serialize_key( stream, key );
     419              : 
     420              :     //
     421              :     //- serialize_value is generated by the spb-protoc
     422              :     //
     423          104 :     serialize_value( stream, value );
     424          104 : }
     425              : 
     426         1847 : static inline auto serialize( const auto & value, spb::io::writer on_write ) -> size_t
     427              : {
     428         1847 :     auto stream = ostream( on_write );
     429         1847 :     serialize( stream, no_name, value );
     430         3692 :     return stream.size( );
     431              : }
     432              : 
     433         1779 : void ostream::serialize( std::string_view key, const auto & value )
     434              : {
     435         1779 :     detail::serialize( *this, key, value );
     436         1779 : }
     437              : 
     438          104 : inline void ostream::serialize( std::string_view value )
     439              : {
     440          104 :     detail::serialize( *this, value );
     441          104 : }
     442              : 
     443              : }// namespace spb::json::detail
        

Generated by: LCOV version 2.0-1