LCOV - code coverage report
Current view: top level - spb/json - serialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 97.8 % 179 175
Test Date: 2025-05-23 14:18:13 Functions: 31.7 % 1242 394

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

Generated by: LCOV version 2.0-1