LCOV - code coverage report
Current view: top level - spb/json - deserialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.4 % 354 352
Test Date: 2025-05-23 14:18:13 Functions: 32.8 % 1295 425

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : deserialize library for json                                *
       3              : * Description : all json deserialization 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 "../bits.h"
      14              : #include "../concepts.h"
      15              : #include "../to_from_chars.h"
      16              : #include "../utf8.h"
      17              : #include "base64.h"
      18              : #include <algorithm>
      19              : #include <cctype>
      20              : #include <cstddef>
      21              : #include <cstdint>
      22              : #include <cstring>
      23              : #include <map>
      24              : #include <memory>
      25              : #include <spb/io/buffer-io.hpp>
      26              : #include <spb/io/io.hpp>
      27              : #include <stdexcept>
      28              : #include <string>
      29              : #include <string_view>
      30              : #include <type_traits>
      31              : #include <variant>
      32              : 
      33              : namespace spb::json::detail
      34              : {
      35              : using namespace std::literals;
      36              : 
      37              : static const auto escape = '\\';
      38              : 
      39              : /**
      40              :  * @brief helper for std::variant visit
      41              :  *        https://en.cppreference.com/w/cpp/utility/variant/visit
      42              :  *
      43              :  */
      44              : template < class... Ts >
      45              : struct overloaded : Ts...
      46              : {
      47              :     using Ts::operator( )...;
      48              : };
      49              : // explicit deduction guide (not needed as of C++20)
      50              : template < class... Ts >
      51              : overloaded( Ts... ) -> overloaded< Ts... >;
      52              : 
      53              : /**
      54              :  * @brief djb2_hash for strings
      55              :  *        http://www.cse.yorku.ca/~oz/hash.html
      56              :  *
      57              :  * @param str
      58              :  * @return uint32_t
      59              :  */
      60         2898 : static constexpr inline auto djb2_hash( std::string_view str ) noexcept -> uint32_t
      61              : {
      62         2898 :     uint32_t hash = 5381U;
      63              : 
      64        30794 :     for( auto c : str )
      65              :     {
      66        27896 :         hash = ( ( hash << 5U ) + hash ) + uint8_t( c ); /* hash * 33 + c */
      67              :     }
      68              : 
      69         2898 :     return hash;
      70              : }
      71              : 
      72              : static constexpr inline auto fnv1a_hash( std::string_view str ) noexcept -> uint64_t
      73              : {
      74              :     uint64_t hash        = 14695981039346656037ULL;
      75              :     const uint64_t prime = 1099511628211ULL;
      76              : 
      77              :     for( auto c : str )
      78              :     {
      79              :         hash *= prime;
      80              :         hash ^= c;
      81              :     }
      82              : 
      83              :     return hash;
      84              : }
      85              : 
      86              : template < spb::detail::proto_field_bytes T >
      87            2 : void clear( T & container )
      88              : {
      89              :     if constexpr( spb::detail::proto_field_bytes_resizable< T > )
      90              :     {
      91            2 :         container.clear( );
      92              :     }
      93              :     else
      94              :     {
      95            0 :         std::fill( container.begin( ), container.end( ), typename T::value_type( ) );
      96              :     }
      97            2 : }
      98              : 
      99              : struct istream
     100              : {
     101              : private:
     102              :     spb::io::buffered_reader reader;
     103              : 
     104              :     //- current char
     105              :     int m_current = -1;
     106              : 
     107              :     std::string_view m_current_key;
     108              : 
     109              :     /**
     110              :      * @brief gets the next char from the stream
     111              :      *
     112              :      * @param skip_white_space if true, skip white spaces
     113              :      */
     114        11390 :     void update_current( bool skip_white_space )
     115              :     {
     116              :         for( ;; )
     117              :         {
     118        11391 :             auto view = reader.view( 1 );
     119        11391 :             if( view.empty( ) )
     120              :             {
     121          805 :                 m_current = 0;
     122        11390 :                 return;
     123              :             }
     124        10586 :             m_current = view[ 0 ];
     125        10586 :             if( !skip_white_space )
     126              :             {
     127         3273 :                 return;
     128              :             }
     129         7313 :             size_t spaces = 0;
     130         7749 :             for( auto c : view )
     131              :             {
     132         7748 :                 if( !isspace( c ) )
     133              :                 {
     134         7312 :                     m_current = c;
     135         7312 :                     reader.skip( spaces );
     136         7312 :                     return;
     137              :                 }
     138          436 :                 spaces += 1;
     139              :             }
     140            1 :             reader.skip( spaces );
     141            1 :         }
     142              :     }
     143              : 
     144              :     [[nodiscard]] auto eof( ) -> bool
     145              :     {
     146              :         return current_char( ) == 0;
     147              :     }
     148              : 
     149              : public:
     150         3795 :     istream( spb::io::reader reader )
     151         3795 :         : reader( reader )
     152              :     {
     153         3795 :     }
     154              : 
     155              :     void deserialize( auto & value );
     156              :     template < size_t ordinal, typename T >
     157              :     void deserialize_variant( T & variant );
     158              :     template < typename T >
     159              :     [[nodiscard]] auto deserialize_bitfield( uint32_t bits ) -> T;
     160              :     [[nodiscard]] auto deserialize_int( ) -> int32_t;
     161              :     [[nodiscard]] auto deserialize_string_or_int( size_t min_size, size_t max_size )
     162              :         -> std::variant< std::string_view, int32_t >;
     163              :     [[nodiscard]] auto deserialize_key( size_t min_size, size_t max_size ) -> std::string_view;
     164              :     [[nodiscard]] auto current_key( ) const -> std::string_view;
     165              : 
     166        12130 :     [[nodiscard]] auto current_char( ) -> char
     167              :     {
     168        12130 :         if( m_current < 0 )
     169              :         {
     170         5422 :             update_current( true );
     171              :         }
     172              : 
     173        12130 :         return m_current;
     174              :     }
     175              :     /**
     176              :      * @brief consumes `current char` if its equal to c
     177              :      *
     178              :      * @param c consumed char
     179              :      * @return true if char was consumed
     180              :      */
     181         6818 :     [[nodiscard]] auto consume( char c ) -> bool
     182              :     {
     183         6818 :         if( current_char( ) == c )
     184              :         {
     185         2605 :             consume_current_char( true );
     186         2605 :             return true;
     187              :         }
     188         4213 :         return false;
     189              :     }
     190              : 
     191              :     /**
     192              :      * @brief consumes an `token`
     193              :      *
     194              :      * @param token consumed `token` (whole word)
     195              :      * @return true if `token` was consumed
     196              :      */
     197          576 :     [[nodiscard]] auto consume( std::string_view token ) -> bool
     198              :     {
     199          576 :         assert( !token.empty( ) );
     200              : 
     201          576 :         if( current_char( ) != token[ 0 ] )
     202              :         {
     203          502 :             return false;
     204              :         }
     205              : 
     206           74 :         if( !reader.view( token.size( ) ).starts_with( token ) )
     207              :         {
     208            3 :             return false;
     209              :         }
     210           71 :         auto token_view = reader.view( token.size( ) + 1 ).substr( 0, token.size( ) + 1 );
     211          105 :         if( token_view.size( ) == token.size( ) || isspace( token_view.back( ) ) ||
     212           34 :             ( !isalnum( token_view.back( ) ) && token_view.back( ) != '_' ) )
     213              :         {
     214           65 :             reader.skip( token.size( ) );
     215           65 :             update_current( true );
     216           65 :             return true;
     217              :         }
     218            6 :         return false;
     219              :     }
     220              : 
     221        23040 :     [[nodiscard]] auto view( size_t size ) -> std::string_view
     222              :     {
     223        23040 :         auto result = reader.view( size );
     224        23040 :         if( result.empty( ) )
     225              :         {
     226         2584 :             throw std::runtime_error( "unexpected end of stream" );
     227              :         }
     228        20456 :         return result;
     229              :     }
     230              : 
     231         5903 :     void consume_current_char( bool skip_white_space ) noexcept
     232              :     {
     233         5903 :         reader.skip( 1 );
     234         5903 :         update_current( skip_white_space );
     235         5903 :     }
     236              : 
     237        22981 :     void skip( size_t size )
     238              :     {
     239        22981 :         reader.skip( size );
     240        22981 :         m_current = -1;
     241        22981 :     }
     242              :     void skip_value( );
     243              : };
     244              : 
     245           50 : static inline void deserialize( istream & stream, spb::detail::proto_enum auto & value )
     246              : {
     247           50 :     deserialize_value( stream, value );
     248           48 : }
     249              : 
     250           20 : static inline void ignore_string( istream & stream )
     251              : {
     252           20 :     if( stream.current_char( ) != '"' )
     253              :     {
     254            1 :         throw std::runtime_error( "expecting '\"'" );
     255              :     }
     256              : 
     257           19 :     auto last = escape;
     258              :     for( ;; )
     259              :     {
     260           21 :         auto view   = stream.view( UINT32_MAX );
     261           21 :         auto length = 0U;
     262          921 :         for( auto current : view )
     263              :         {
     264          919 :             length += 1;
     265          919 :             if( current == '"' && last != escape )
     266              :             {
     267           19 :                 stream.skip( length );
     268           19 :                 return;
     269              :             }
     270              :             //- handle \\"
     271          900 :             last = current != escape || last != escape ? current : ' ';
     272              :         }
     273            2 :         stream.skip( view.size( ) );
     274            2 :     }
     275              : }
     276              : 
     277          682 : static inline auto deserialize_string_view( istream & stream, size_t min_size, size_t max_size )
     278              :     -> std::string_view
     279              : {
     280          682 :     if( stream.current_char( ) != '"' )
     281              :     {
     282            1 :         throw std::runtime_error( "expecting '\"'" );
     283              :     }
     284              : 
     285              :     //- +2 for '"'
     286          681 :     auto view   = stream.view( max_size + 2 );
     287          681 :     auto last   = escape;
     288          681 :     auto length = size_t( 0 );
     289         5269 :     for( auto current : view )
     290              :     {
     291         5268 :         length += 1;
     292              : 
     293         5268 :         if( current == '"' && last != escape )
     294              :         {
     295          680 :             stream.skip( length );
     296              : 
     297          680 :             if( ( length - 2 ) >= min_size && ( length - 2 ) <= max_size )
     298              :             {
     299          671 :                 return view.substr( 1, length - 2 );
     300              :             }
     301              : 
     302            9 :             return { };
     303              :         }
     304              :         //- handle \\"
     305         4588 :         last = current != escape || last != escape ? current : ' ';
     306              :     }
     307              : 
     308            1 :     ignore_string( stream );
     309            1 :     return { };
     310              : }
     311              : 
     312           21 : static inline auto unicode_from_hex( istream & stream ) -> uint16_t
     313              : {
     314           21 :     const auto esc_size = 4U;
     315           21 :     auto unicode_view   = stream.view( esc_size );
     316           21 :     if( unicode_view.size( ) < esc_size )
     317              :     {
     318            4 :         throw std::runtime_error( "invalid escape sequence" );
     319              :     }
     320           17 :     auto value = uint16_t( 0 );
     321              :     auto result =
     322           17 :         spb_std_emu::from_chars( unicode_view.data( ), unicode_view.data( ) + esc_size, value, 16 );
     323           17 :     if( result.ec != std::errc{ } || result.ptr != unicode_view.data( ) + esc_size )
     324              :     {
     325            1 :         throw std::runtime_error( "invalid escape sequence" );
     326              :     }
     327           16 :     stream.skip( esc_size );
     328           16 :     return value;
     329              : }
     330              : 
     331           15 : static inline auto unescape_unicode( istream & stream, char utf8[ 4 ] ) -> uint32_t
     332              : {
     333           15 :     auto value = uint32_t( unicode_from_hex( stream ) );
     334           12 :     if( value >= 0xD800 && value <= 0xDBFF && stream.view( 2 ).starts_with( "\\u"sv ) )
     335              :     {
     336            6 :         stream.skip( 2 );
     337            6 :         auto low = unicode_from_hex( stream );
     338              : 
     339            4 :         if( low < 0xDC00 || low > 0xDFFF )
     340              :         {
     341            2 :             throw std::invalid_argument( "invalid escape sequence" );
     342              :         }
     343            2 :         value = ( ( value - 0xD800 ) << 10 ) + ( low - 0xDC00 ) + 0x10000;
     344              :     }
     345            8 :     if( auto result = spb::detail::utf8::encode_point( value, utf8 ); result != 0 )
     346              :     {
     347            8 :         return result;
     348              :     }
     349            0 :     throw std::runtime_error( "invalid escape sequence" );
     350              : }
     351          309 : static inline auto unescape( istream & stream, char utf8[ 4 ] ) -> uint32_t
     352              : {
     353          309 :     auto c = stream.current_char( );
     354          309 :     stream.consume_current_char( false );
     355          309 :     switch( c )
     356              :     {
     357           15 :     case '"':
     358           15 :         utf8[ 0 ] = '"';
     359           15 :         return 1;
     360            5 :     case '\\':
     361            5 :         utf8[ 0 ] = '\\';
     362            5 :         return 1;
     363            1 :     case '/':
     364            1 :         utf8[ 0 ] = '/';
     365            1 :         return 1;
     366            4 :     case 'b':
     367            4 :         utf8[ 0 ] = '\b';
     368            4 :         return 1;
     369            4 :     case 'f':
     370            4 :         utf8[ 0 ] = '\f';
     371            4 :         return 1;
     372            7 :     case 'n':
     373            7 :         utf8[ 0 ] = '\n';
     374            7 :         return 1;
     375            6 :     case 'r':
     376            6 :         utf8[ 0 ] = '\r';
     377            6 :         return 1;
     378           11 :     case 't':
     379           11 :         utf8[ 0 ] = '\t';
     380           11 :         return 1;
     381           15 :     case 'u':
     382           15 :         return unescape_unicode( stream, utf8 );
     383          241 :     default:
     384          241 :         throw std::runtime_error( "invalid escape sequence" );
     385              :     }
     386              : }
     387              : 
     388          353 : static inline void deserialize( istream & stream, spb::detail::proto_field_string auto & value )
     389              : {
     390          353 :     if( stream.current_char( ) != '"' )
     391              :     {
     392            7 :         throw std::runtime_error( "expecting '\"'" );
     393              :     }
     394              : 
     395          346 :     stream.consume_current_char( false );
     396              : 
     397              :     if constexpr( spb::detail::proto_field_string_resizable< decltype( value ) > )
     398              :     {
     399          326 :         value.clear( );
     400              :     }
     401          346 :     auto index           = size_t( 0 );
     402          388 :     auto append_to_value = [ & ]( const char * str, size_t size )
     403              :     {
     404              :         if constexpr( spb::detail::proto_field_string_resizable< decltype( value ) > )
     405              :         {
     406          418 :             value.append( str, size );
     407              :         }
     408              :         else
     409              :         {
     410           88 :             if( auto space_left = value.size( ) - index; size <= space_left ) [[likely]]
     411              :             {
     412           42 :                 memcpy( value.data( ) + index, str, size );
     413           42 :                 index += size;
     414              :             }
     415              :             else
     416              :             {
     417            2 :                 throw std::runtime_error( "invalid string size" );
     418              :             }
     419              :         }
     420              :     };
     421              : 
     422           62 :     for( ;; )
     423              :     {
     424          408 :         auto view  = stream.view( UINT32_MAX );
     425          401 :         auto found = view.find_first_of( R"("\)" );
     426          401 :         if( found == view.npos ) [[unlikely]]
     427              :         {
     428            1 :             append_to_value( view.data( ), view.size( ) );
     429            1 :             stream.skip( view.size( ) );
     430            1 :             continue;
     431              :         }
     432              : 
     433          400 :         append_to_value( view.data( ), found );
     434              :         // +1 for '"' or '\'
     435          398 :         stream.skip( found + 1 );
     436          398 :         if( view[ found ] == '"' ) [[likely]]
     437              :         {
     438              :             if constexpr( !spb::detail::proto_field_string_resizable< decltype( value ) > )
     439              :             {
     440           18 :                 if( index != value.size( ) )
     441              :                 {
     442            2 :                     throw std::runtime_error( "invalid string size" );
     443              :                 }
     444              :             }
     445           87 :             return;
     446              :         }
     447              :         char utf8_buffer[ 4 ];
     448          309 :         auto utf8_size = unescape( stream, utf8_buffer );
     449           61 :         append_to_value( utf8_buffer, utf8_size );
     450              :     }
     451              :     spb::detail::utf8::validate( std::string_view( value.data( ), value.size( ) ) );
     452              : }
     453              : 
     454          641 : static inline void deserialize( istream & stream,
     455              :                                 spb::detail::proto_field_int_or_float auto & value )
     456              : {
     457          641 :     if( stream.current_char( ) == '"' ) [[unlikely]]
     458              :     {
     459              :         //- https://protobuf.dev/programming-guides/proto2/#json
     460              :         //- number can be a string
     461           16 :         auto view   = deserialize_string_view( stream, 1, UINT32_MAX );
     462           16 :         auto result = spb_std_emu::from_chars( view.data( ), view.data( ) + view.size( ), value );
     463           16 :         if( result.ec != std::errc{ } )
     464              :         {
     465            8 :             throw std::runtime_error( "invalid number" );
     466              :         }
     467            8 :         return;
     468              :     }
     469          625 :     auto view   = stream.view( UINT32_MAX );
     470          612 :     auto result = spb_std_emu::from_chars( view.data( ), view.data( ) + view.size( ), value );
     471          612 :     if( result.ec != std::errc{ } )
     472              :     {
     473           21 :         throw std::runtime_error( "invalid number" );
     474              :     }
     475          591 :     stream.skip( result.ptr - view.data( ) );
     476              : }
     477              : 
     478           64 : static inline void deserialize( istream & stream, bool & value )
     479              : {
     480           64 :     if( stream.consume( "true"sv ) )
     481              :     {
     482           25 :         value = true;
     483              :     }
     484           39 :     else if( stream.consume( "false"sv ) )
     485              :     {
     486           22 :         value = false;
     487              :     }
     488              :     else
     489              :     {
     490           17 :         throw std::runtime_error( "expecting 'true' or 'false'" );
     491              :     }
     492           47 : }
     493              : 
     494              : static inline void deserialize( istream & stream, auto & value );
     495              : 
     496              : template < typename keyT, typename valueT >
     497              : static inline void deserialize( istream & stream, std::map< keyT, valueT > & value );
     498              : 
     499              : static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & value );
     500              : 
     501              : template < spb::detail::proto_label_repeated C >
     502          208 : static inline void deserialize( istream & stream, C & value )
     503              : {
     504          208 :     if( stream.consume( "null"sv ) )
     505              :     {
     506            5 :         value.clear( );
     507            5 :         return;
     508              :     }
     509              : 
     510          203 :     if( !stream.consume( '[' ) )
     511              :     {
     512           10 :         throw std::runtime_error( "expecting '['" );
     513              :     }
     514              : 
     515          193 :     if( stream.consume( ']' ) )
     516              :     {
     517            5 :         return;
     518              :     }
     519              : 
     520              :     do
     521              :     {
     522              :         if constexpr( std::is_same_v< typename C::value_type, bool > )
     523              :         {
     524           16 :             auto b = false;
     525           16 :             deserialize( stream, b );
     526           14 :             value.push_back( b );
     527              :         }
     528              :         else
     529              :         {
     530          277 :             deserialize( stream, value.emplace_back( ) );
     531              :         }
     532          283 :     } while( stream.consume( ',' ) );
     533              : 
     534          178 :     if( !stream.consume( ']' ) )
     535              :     {
     536            2 :         throw std::runtime_error( "expecting ']'" );
     537              :     }
     538              : }
     539              : 
     540           62 : static inline void deserialize( istream & stream, spb::detail::proto_field_bytes auto & value )
     541              : {
     542           62 :     if( stream.consume( "null"sv ) )
     543              :     {
     544            2 :         clear( value );
     545            2 :         return;
     546              :     }
     547              : 
     548           60 :     base64_decode_string( value, stream );
     549              : }
     550              : 
     551              : template < typename T >
     552           16 : void deserialize_map_key( istream & stream, T & map_key )
     553              : {
     554              :     if constexpr( std::is_same_v< T, std::string > )
     555              :     {
     556            8 :         return deserialize( stream, map_key );
     557              :     }
     558            8 :     auto str_key_map = deserialize_string_view( stream, 1, UINT32_MAX );
     559           24 :     auto reader = [ ptr = str_key_map.data( ), end = str_key_map.data( ) + str_key_map.size( ) ](
     560              :                       void * data, size_t size ) mutable -> size_t
     561              :     {
     562           16 :         size_t bytes_left = end - ptr;
     563           16 :         size              = std::min( size, bytes_left );
     564           16 :         memcpy( data, ptr, size );
     565           16 :         ptr += size;
     566           16 :         return size;
     567              :     };
     568            8 :     auto key_stream = istream( reader );
     569            8 :     deserialize( key_stream, map_key );
     570            6 : }
     571              : 
     572              : template < typename keyT, typename valueT >
     573           28 : static inline void deserialize( istream & stream, std::map< keyT, valueT > & value )
     574              : {
     575           28 :     if( stream.consume( "null"sv ) )
     576              :     {
     577            4 :         value.clear( );
     578            4 :         return;
     579              :     }
     580           24 :     if( !stream.consume( '{' ) )
     581              :     {
     582            5 :         throw std::runtime_error( "expecting '{'" );
     583              :     }
     584              : 
     585           19 :     if( stream.consume( '}' ) )
     586              :     {
     587            4 :         return;
     588              :     }
     589              : 
     590              :     do
     591              :     {
     592           16 :         auto map_key = keyT( );
     593           16 :         deserialize_map_key( stream, map_key );
     594           14 :         if( !stream.consume( ':' ) )
     595              :         {
     596            1 :             throw std::runtime_error( "expecting ':'" );
     597              :         }
     598           13 :         auto map_value = valueT( );
     599           13 :         deserialize( stream, map_value );
     600            8 :         value.emplace( std::move( map_key ), std::move( map_value ) );
     601           15 :     } while( stream.consume( ',' ) );
     602              : 
     603            7 :     if( !stream.consume( '}' ) )
     604              :     {
     605            1 :         throw std::runtime_error( "expecting '}'" );
     606              :     }
     607              : }
     608              : 
     609          160 : static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & p_value )
     610              : {
     611          160 :     if( stream.consume( "null"sv ) )
     612              :     {
     613            4 :         p_value.reset( );
     614            4 :         return;
     615              :     }
     616              : 
     617          156 :     if( p_value.has_value( ) )
     618              :     {
     619            4 :         deserialize( stream, *p_value );
     620              :     }
     621              :     else
     622              :     {
     623          152 :         deserialize(
     624              :             stream,
     625          314 :             p_value.emplace( typename std::decay_t< decltype( p_value ) >::value_type( ) ) );
     626              :     }
     627              : }
     628              : 
     629              : template < typename T >
     630           13 : static inline void deserialize( istream & stream, std::unique_ptr< T > & value )
     631              : {
     632           13 :     if( stream.consume( "null"sv ) )
     633              :     {
     634            2 :         value.reset( );
     635            2 :         return;
     636              :     }
     637              : 
     638           11 :     if( value )
     639              :     {
     640            5 :         deserialize( stream, *value );
     641              :     }
     642              :     else
     643              :     {
     644            6 :         value = std::make_unique< T >( );
     645            6 :         deserialize( stream, *value );
     646              :     }
     647              : }
     648              : 
     649              : static inline void ignore_value( istream & stream );
     650           10 : static inline void ignore_key_and_value( istream & stream )
     651              : {
     652           10 :     ignore_string( stream );
     653            9 :     if( !stream.consume( ':' ) )
     654              :     {
     655            1 :         throw std::runtime_error( "expecting ':'" );
     656              :     }
     657            8 :     ignore_value( stream );
     658            7 : }
     659              : 
     660            8 : static inline void ignore_object( istream & stream )
     661              : {
     662              :     //- '{' was already checked by caller
     663            8 :     stream.consume_current_char( true );
     664              : 
     665            8 :     if( stream.consume( '}' ) )
     666              :     {
     667            1 :         return;
     668              :     }
     669              : 
     670              :     do
     671              :     {
     672           10 :         ignore_key_and_value( stream );
     673            7 :     } while( stream.consume( ',' ) );
     674              : 
     675            4 :     if( !stream.consume( '}' ) )
     676              :     {
     677            1 :         throw std::runtime_error( "expecting '}'" );
     678              :     }
     679              : }
     680              : 
     681            9 : static inline void ignore_array( istream & stream )
     682              : {
     683              :     //- '[' was already checked by caller
     684            9 :     stream.consume_current_char( true );
     685              : 
     686            9 :     if( stream.consume( ']' ) )
     687              :     {
     688            2 :         return;
     689              :     }
     690              : 
     691              :     do
     692              :     {
     693            9 :         ignore_value( stream );
     694            6 :     } while( stream.consume( ',' ) );
     695              : 
     696            4 :     if( !stream.consume( ']' ) )
     697              :     {
     698            1 :         throw std::runtime_error( "expecting ']" );
     699              :     }
     700              : }
     701              : 
     702           13 : static inline void ignore_number( istream & stream )
     703              : {
     704           13 :     auto value = double{ };
     705           13 :     deserialize( stream, value );
     706            9 : }
     707              : 
     708            5 : static inline void ignore_bool( istream & stream )
     709              : {
     710            5 :     auto value = bool{ };
     711            5 :     deserialize( stream, value );
     712            3 : }
     713              : 
     714            2 : static inline void ignore_null( istream & stream )
     715              : {
     716            2 :     if( !stream.consume( "null"sv ) )
     717              :     {
     718            1 :         throw std::runtime_error( "expecting 'null'" );
     719              :     }
     720            1 : }
     721              : 
     722           46 : static inline void ignore_value( istream & stream )
     723              : {
     724           46 :     switch( stream.current_char( ) )
     725              :     {
     726            8 :     case '{':
     727            8 :         return ignore_object( stream );
     728            9 :     case '[':
     729            9 :         return ignore_array( stream );
     730            9 :     case '"':
     731            9 :         return ignore_string( stream );
     732            2 :     case 'n':
     733            2 :         return ignore_null( stream );
     734            5 :     case 't':
     735              :     case 'f':
     736            5 :         return ignore_bool( stream );
     737           13 :     default:
     738           13 :         return ignore_number( stream );
     739              :     }
     740              : }
     741              : 
     742              : template < typename T >
     743          159 : inline auto deserialize_bitfield( istream & stream, uint32_t bits ) -> T
     744              : {
     745          159 :     auto value = T( );
     746          159 :     deserialize( stream, value );
     747          159 :     spb::detail::check_if_value_fit_in_bits( value, bits );
     748          158 :     return value;
     749              : }
     750              : 
     751              : template < size_t ordinal, typename T >
     752           12 : static inline void deserialize_variant( istream & stream, T & variant )
     753              : {
     754           12 :     deserialize( stream, variant.template emplace< ordinal >( ) );
     755           12 : }
     756              : 
     757          723 : static inline void deserialize( istream & stream, auto & value )
     758              : {
     759          723 :     if( !stream.consume( '{' ) )
     760              :     {
     761            2 :         throw std::runtime_error( "expecting '{'" );
     762              :     }
     763              : 
     764          721 :     if( stream.consume( '}' ) )
     765              :     {
     766          134 :         return;
     767              :     }
     768              : 
     769              :     for( ;; )
     770              :     {
     771              :         //
     772              :         //- deserialize_value is generated by the sprotoc
     773              :         //
     774          611 :         deserialize_value( stream, value );
     775              : 
     776          593 :         if( stream.consume( ',' ) )
     777              :         {
     778           24 :             continue;
     779              :         }
     780              : 
     781          569 :         if( stream.consume( '}' ) )
     782              :         {
     783          568 :             return;
     784              :         }
     785              : 
     786            1 :         throw std::runtime_error( "expecting '}' or ','" );
     787              :     }
     788              : }
     789              : 
     790          611 : inline auto istream::deserialize_key( size_t min_size, size_t max_size ) -> std::string_view
     791              : {
     792          611 :     m_current_key = deserialize_string_view( *this, min_size, max_size );
     793          610 :     if( !consume( ':' ) )
     794              :     {
     795            1 :         throw std::runtime_error( "expecting ':'" );
     796              :     }
     797          609 :     return m_current_key;
     798              : }
     799              : 
     800          409 : inline void istream::deserialize( auto & value )
     801              : {
     802          409 :     return detail::deserialize( *this, value );
     803              : }
     804              : 
     805              : template < size_t ordinal, typename T >
     806           12 : inline void istream::deserialize_variant( T & variant )
     807              : {
     808           12 :     return detail::deserialize_variant< ordinal >( *this, variant );
     809              : }
     810              : 
     811              : template < typename T >
     812          159 : inline auto istream::deserialize_bitfield( uint32_t bits ) -> T
     813              : {
     814          159 :     return detail::deserialize_bitfield< T >( *this, bits );
     815              : }
     816              : 
     817           50 : inline auto istream::deserialize_string_or_int( size_t min_size, size_t max_size )
     818              :     -> std::variant< std::string_view, int32_t >
     819              : {
     820           50 :     if( current_char( ) == '"' )
     821              :     {
     822           47 :         return deserialize_string_view( *this, min_size, max_size );
     823              :     }
     824            3 :     return deserialize_int( );
     825              : }
     826              : 
     827            3 : inline auto istream::deserialize_int( ) -> int32_t
     828              : {
     829            3 :     auto result = int32_t{ };
     830            3 :     detail::deserialize( *this, result );
     831            2 :     return result;
     832              : }
     833              : 
     834           29 : inline void istream::skip_value( )
     835              : {
     836           29 :     return detail::ignore_value( *this );
     837              : }
     838              : 
     839         1212 : static inline void deserialize( auto & value, spb::io::reader reader )
     840              : {
     841         1212 :     auto stream = detail::istream( reader );
     842         2056 :     return detail::deserialize( stream, value );
     843              : }
     844              : 
     845              : }// namespace spb::json::detail
        

Generated by: LCOV version 2.0-1