LCOV - code coverage report
Current view: top level - spb/json - deserialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.1 % 374 367
Test Date: 2026-05-30 19:41:37 Functions: 90.1 % 563 507

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

Generated by: LCOV version 2.0-1