LCOV - code coverage report
Current view: top level - spb/json - deserialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 92.5 % 428 396
Test Date: 2026-07-19 10:31:31 Functions: 87.3 % 646 564

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

Generated by: LCOV version 2.0-1