LCOV - code coverage report
Current view: top level - spb/json - serialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.1 % 226 224
Test Date: 2026-10-01 11:28:31 Functions: 90.2 % 1287 1161

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : serialize library for json                                  *
       3              : * Description : all json serialization functions                            *
       4              : * Author      : antonin.kriz@gmail.com                                      *
       5              : * ------------------------------------------------------------------------- *
       6              : * This is free software; you can redistribute it and/or modify it under the *
       7              : * terms of the MIT license. A copy of the license can be found in the file  *
       8              : * "LICENSE" at the root of this distribution.                               *
       9              : \***************************************************************************/
      10              : 
      11              : #pragma once
      12              : 
      13              : #include "../concepts.h"
      14              : 
      15              : #include "../to_from_chars.h"
      16              : #include "base64.h"
      17              : #include "field.hpp"
      18              : #include "spb/utf8.h"
      19              : #include <algorithm>
      20              : #include <array>
      21              : #include <charconv>
      22              : #include <cinttypes>
      23              : #include <cstddef>
      24              : #include <cstdint>
      25              : #include <cstring>
      26              : #include <memory>
      27              : #include <spb/io/io.hpp>
      28              : #include <stdexcept>
      29              : #include <string>
      30              : #include <string_view>
      31              : #include <sys/types.h>
      32              : #include <type_traits>
      33              : 
      34              : namespace spb::json::detail
      35              : {
      36              : struct ostream_size
      37              : {
      38              :     static constexpr bool size_only = true;
      39              :     size_t size;
      40              :     bool put_comma = false;
      41              : 
      42      4381872 :     void write(uint8_t) noexcept
      43              :     {
      44      4381872 :         size += sizeof(uint8_t);
      45      4381872 :     }
      46              : 
      47         3786 :     void write(const void *, size_t data_size) noexcept
      48              :     {
      49         3786 :         size += data_size;
      50         3786 :     }
      51              : };
      52              : 
      53              : struct ostream_buffer
      54              : {
      55              :     static constexpr bool size_only = false;
      56              : 
      57              :     uint8_t *p_buffer;
      58              :     bool put_comma = false;
      59              : 
      60         3488 :     explicit ostream_buffer(void *buffer) : p_buffer((std::uint8_t *)buffer)
      61              :     {
      62         3488 :     }
      63              : 
      64      4379321 :     void write(uint8_t byte) noexcept
      65              :     {
      66      4379321 :         *p_buffer++ = byte;
      67      4379321 :     }
      68              : 
      69         2571 :     void write(const void *data, size_t data_size) noexcept
      70              :     {
      71         2571 :         memcpy(p_buffer, data, data_size);
      72         2571 :         p_buffer += data_size;
      73         2571 :     }
      74              : };
      75              : 
      76              : struct ostream_writer
      77              : {
      78              :     static constexpr bool size_only = false;
      79              :     spb::io::writer on_write;
      80              :     size_t size    = 0;
      81              :     bool put_comma = false;
      82              : 
      83          386 :     explicit ostream_writer(spb::io::writer writer) : on_write(writer)
      84              :     {
      85          386 :     }
      86              : 
      87         2054 :     void write(uint8_t byte)
      88              :     {
      89         2054 :         write(&byte, 1);
      90         2054 :     }
      91              : 
      92         3154 :     void write(const void *data, size_t data_size)
      93              :     {
      94         3154 :         on_write(data, data_size);
      95         3154 :         size += data_size;
      96         3154 :     }
      97              : };
      98              : 
      99              : template <field_attributes = field_attributes{}> size_t serialize_size(const auto &value);
     100              : 
     101           24 : void write_unicode(auto &stream, uint32_t codepoint)
     102              : {
     103           24 :     if (codepoint <= 0xffff)
     104              :     {
     105           18 :         char buffer[8] = {};
     106           18 :         auto size      = snprintf(buffer, sizeof(buffer), "\\u%04" PRIx32, codepoint);
     107           18 :         return stream.write(buffer, size);
     108              :     }
     109            6 :     if (codepoint <= 0x10FFFF)
     110              :     {
     111            6 :         codepoint -= 0x10000;
     112              : 
     113            6 :         auto high       = static_cast<uint16_t>((codepoint >> 10) + 0xD800);
     114            6 :         auto low        = static_cast<uint16_t>((codepoint & 0x3FF) + 0xDC00);
     115            6 :         char buffer[16] = {};
     116            6 :         auto size       = snprintf(buffer, sizeof(buffer), "\\u%04" PRIx16 "\\u%04" PRIx16, high, low);
     117            6 :         return stream.write(buffer, size);
     118              :     }
     119            0 :     throw std::invalid_argument("invalid utf8");
     120              : }
     121              : 
     122         2579 : template <size_t N> void write_string(auto &stream, const char (&string)[N])
     123              : {
     124         2579 :     stream.write(string, N - 1);
     125         2579 : }
     126              : 
     127         1910 : static constexpr auto is_escape(uint8_t c) -> bool
     128              : {
     129         1910 :     switch (c)
     130              :     {
     131           66 :     case '\\':
     132              :     case '\"':
     133              :     case '<':
     134              :     case '>':
     135           66 :         return true;
     136         1844 :     default:
     137         1844 :         return (c < ' ') | (c >= 0x7f);
     138              :     }
     139              : }
     140              : 
     141          609 : static constexpr auto has_escape_chars(std::string_view str) -> bool
     142              : {
     143          609 :     return std::any_of(str.begin(), str.end(), is_escape);
     144              : }
     145              : 
     146          609 : void write_escaped(auto &stream, std::string_view str)
     147              : {
     148          609 :     if (!has_escape_chars(str)) [[likely]]
     149              :     {
     150          560 :         stream.write(str.data(), str.size());
     151          560 :         return;
     152              :     }
     153              : 
     154           49 :     uint32_t codepoint = 0;
     155           49 :     uint32_t state     = spb::detail::utf8::ok;
     156           49 :     bool decoding_utf8 = false;
     157          329 :     for (uint8_t c : str)
     158              :     {
     159          280 :         if (decoding_utf8)
     160              :         {
     161           36 :             if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
     162              :             {
     163           18 :                 write_unicode(stream, codepoint);
     164           18 :                 decoding_utf8 = false;
     165              :             }
     166           36 :             continue;
     167              :         }
     168          244 :         if (is_escape(c))
     169              :         {
     170          147 :             switch (c)
     171              :             {
     172           24 :             case '"':
     173           24 :                 write_string(stream, R"(\")");
     174           24 :                 break;
     175           16 :             case '\\':
     176           16 :                 write_string(stream, R"(\\)");
     177           16 :                 break;
     178           10 :             case '\b':
     179           10 :                 write_string(stream, R"(\b)");
     180           10 :                 break;
     181           10 :             case '\f':
     182           10 :                 write_string(stream, R"(\f)");
     183           10 :                 break;
     184           16 :             case '\n':
     185           16 :                 write_string(stream, R"(\n)");
     186           16 :                 break;
     187           16 :             case '\r':
     188           16 :                 write_string(stream, R"(\r)");
     189           16 :                 break;
     190           30 :             case '\t':
     191           30 :                 write_string(stream, R"(\t)");
     192           30 :                 break;
     193           25 :             default:
     194           25 :                 decoding_utf8 = true;
     195           25 :                 if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
     196              :                 {
     197            6 :                     write_unicode(stream, codepoint);
     198            6 :                     decoding_utf8 = false;
     199              :                 }
     200              :             }
     201              :         }
     202              :         else
     203              :         {
     204           97 :             stream.write(c);
     205              :         }
     206              :     }
     207           49 :     if (state != spb::detail::utf8::ok) [[unlikely]]
     208              :     {
     209            1 :         throw std::runtime_error("invalid utf8");
     210              :     }
     211              : }
     212              : 
     213         3854 : void put_comma_if_needed(auto &stream)
     214              : {
     215         3854 :     if (stream.put_comma)
     216          876 :         stream.write(',');
     217              : 
     218         3854 :     stream.put_comma = true;
     219         3854 : }
     220              : 
     221         2331 : void serialize_key(auto &stream, std::string_view key)
     222              : {
     223         2331 :     put_comma_if_needed(stream);
     224              : 
     225         2331 :     stream.write('"');
     226         2331 :     stream.write(key.data(), key.size());
     227         2331 :     write_string(stream, R"(":)");
     228         2331 : }
     229              : 
     230              : template <field_attributes> void serialize(auto &stream, const bool &value);
     231              : template <field_attributes> void serialize(auto &stream, const bool &value, std::string_view field);
     232              : 
     233              : template <field_attributes>
     234              : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value);
     235              : template <field_attributes>
     236              : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value, std::string_view field);
     237              : 
     238              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_message auto &value);
     239              : template <field_attributes>
     240              : void serialize(auto &stream, const spb::detail::proto_message auto &value, std::string_view field);
     241              : 
     242              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_enum auto &value);
     243              : template <field_attributes>
     244              : void serialize(auto &stream, const spb::detail::proto_enum auto &value, std::string_view field);
     245              : 
     246              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_field_string auto &value);
     247              : template <field_attributes>
     248              : void serialize(auto &stream, const spb::detail::proto_field_string auto &value, std::string_view field);
     249              : 
     250              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value);
     251              : template <field_attributes>
     252              : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value, std::string_view field);
     253              : 
     254              : template <field_attributes attributes>
     255              : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value);
     256              : template <field_attributes attributes>
     257              : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value, std::string_view field);
     258              : 
     259              : template <field_attributes attributes, typename T>
     260              : void serialize(auto &stream, const std::unique_ptr<T> &p_value);
     261              : template <field_attributes attributes, typename T>
     262              : void serialize(auto &stream, const std::unique_ptr<T> &p_value, std::string_view field);
     263              : 
     264              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value);
     265              : template <field_attributes>
     266              : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value, std::string_view field);
     267              : 
     268              : template <field_attributes>
     269              : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value);
     270              : template <field_attributes>
     271              : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
     272              :                std::string_view field);
     273              : 
     274              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_map auto &map);
     275              : template <field_attributes>
     276              : void serialize(auto &stream, const spb::detail::proto_map auto &map, std::string_view field);
     277              : 
     278           88 : template <field_attributes> void serialize(auto &stream, const bool &value)
     279              : {
     280           88 :     return value ? write_string(stream, "true") : write_string(stream, "false");
     281              : }
     282              : 
     283          124 : void serialize_enum(auto &stream, std::string_view value)
     284              : {
     285          124 :     stream.write('"');
     286          124 :     stream.write(value.data(), value.size());
     287          124 :     stream.write('"');
     288          124 : }
     289              : 
     290          617 : template <field_attributes attributes> void serialize(auto &stream, const std::string_view &value)
     291              : {
     292              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     293              : 
     294              :     if constexpr (!stream_type::size_only && attributes.max_size)
     295           40 :         check_size(value.size(), attributes.max_size);
     296              : 
     297          609 :     stream.write('"');
     298          609 :     write_escaped(stream, value);
     299          608 :     stream.write('"');
     300          608 : }
     301              : 
     302              : template <field_attributes attributes>
     303          617 : void serialize(auto &stream, const spb::detail::proto_field_string auto &value)
     304              : {
     305          617 :     serialize<attributes>(stream, std::string_view(value.data(), value.size()));
     306          608 : }
     307              : 
     308              : template <field_attributes>
     309         1839 : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value)
     310              : {
     311         1839 :     auto buffer = std::array<char, 32>();
     312         5517 :     auto result = spb_std_emu::to_chars(buffer.data(), buffer.data() + sizeof(buffer), value);
     313         5517 :     stream.write(buffer.data(), static_cast<size_t>(result.ptr - buffer.data()));
     314         1839 : }
     315              : 
     316           36 : template <field_attributes attributes> void serialize(auto &stream, const bool &value, std::string_view field)
     317              : {
     318           36 :     serialize_key(stream, field);
     319           36 :     serialize<attributes>(stream, value);
     320           36 : }
     321              : 
     322              : template <field_attributes attributes>
     323          946 : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value, std::string_view field)
     324              : {
     325          946 :     serialize_key(stream, field);
     326          946 :     serialize<attributes>(stream, value);
     327          946 : }
     328              : 
     329              : template <field_attributes attributes>
     330          363 : void serialize(auto &stream, const spb::detail::proto_field_string auto &value, std::string_view field)
     331              : {
     332          363 :     if (value.empty())
     333            6 :         return;
     334              : 
     335          357 :     serialize_key(stream, field);
     336          357 :     serialize<attributes>(stream, value);
     337              : }
     338              : 
     339              : template <field_attributes attributes>
     340          388 : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value)
     341              : {
     342              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     343              : 
     344              :     if constexpr (!stream_type::size_only && attributes.max_size)
     345           40 :         check_size(value.size(), attributes.max_size);
     346              : 
     347          380 :     stream.write('"');
     348          380 :     base64_encode(stream, value);
     349          380 :     stream.write('"');
     350          380 : }
     351              : 
     352              : template <field_attributes attributes>
     353          152 : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value, std::string_view field)
     354              : {
     355          152 :     if (value.empty())
     356            6 :         return;
     357              : 
     358          146 :     serialize_key(stream, field);
     359          146 :     serialize<attributes>(stream, value);
     360              : }
     361              : 
     362              : template <field_attributes attributes>
     363          998 : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value, std::string_view field)
     364              : {
     365          998 :     if (value.empty())
     366          318 :         return;
     367              : 
     368          680 :     serialize_key(stream, field);
     369          680 :     serialize<attributes>(stream, value);
     370              : }
     371              : 
     372              : template <field_attributes attributes>
     373          706 : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value)
     374              : {
     375              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     376              : 
     377              :     if constexpr (!stream_type::size_only && attributes.max_count)
     378           85 :         check_size(value.size(), attributes.max_count);
     379              : 
     380          689 :     stream.write('[');
     381          689 :     stream.put_comma = false;
     382         2164 :     for (const auto &v : value)
     383              :     {
     384         1475 :         put_comma_if_needed(stream);
     385              : 
     386              :         if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
     387              :         {
     388           40 :             serialize<attributes>(stream, bool(v));
     389              :         }
     390              :         else
     391              :         {
     392         1435 :             serialize<attributes>(stream, v);
     393              :         }
     394              :     }
     395          689 :     stream.write(']');
     396          689 :     stream.put_comma = true;
     397          689 : }
     398              : 
     399              : template <field_attributes attributes>
     400           12 : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value)
     401              : {
     402           12 :     stream.write('[');
     403           12 :     stream.put_comma = false;
     404          120 :     for (size_t i = 0; i < value.size(); i++)
     405              :     {
     406           48 :         put_comma_if_needed(stream);
     407              : 
     408              :         if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
     409              :         {
     410              :             serialize<attributes>(stream, bool(value[i]));
     411              :         }
     412              :         else
     413              :         {
     414           48 :             serialize<attributes>(stream, value[i]);
     415              :         }
     416              :     }
     417           12 :     stream.write(']');
     418           12 :     stream.put_comma = true;
     419           12 : }
     420              : 
     421              : template <field_attributes attributes>
     422           12 : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
     423              :                std::string_view field)
     424              : {
     425           12 :     serialize_key(stream, field);
     426           12 :     serialize<attributes>(stream, value);
     427           12 : }
     428              : 
     429              : template <field_attributes attributes>
     430           54 : void serialize(auto &stream, const spb::detail::proto_map auto &map, std::string_view field)
     431              : {
     432           54 :     if (map.empty())
     433            0 :         return;
     434              : 
     435           54 :     serialize_key(stream, field);
     436           54 :     serialize<attributes>(stream, map);
     437              : }
     438              : 
     439           74 : template <field_attributes attributes> void serialize(auto &stream, const spb::detail::proto_map auto &map)
     440              : {
     441              :     using map_type = std::remove_cvref_t<decltype(map)>;
     442              :     using key_type = typename map_type::key_type;
     443              : 
     444           74 :     stream.write('{');
     445           74 :     stream.put_comma = false;
     446          154 :     for (const auto &[map_key, map_value] : map)
     447              :     {
     448              :         if constexpr (std::is_same_v<key_type, std::string_view> || std::is_same_v<key_type, std::string>)
     449              :         {
     450           42 :             serialize_key(stream, map_key);
     451              :         }
     452              :         else
     453              :         {
     454           38 :             if (stream.put_comma)
     455            8 :                 stream.write(',');
     456              : 
     457           38 :             stream.write('"');
     458           38 :             serialize<attributes>(stream, map_key);
     459           38 :             write_string(stream, R"(":)");
     460              :         }
     461           80 :         stream.put_comma = false;
     462           80 :         serialize<attributes>(stream, map_value);
     463           80 :         stream.put_comma = true;
     464              :     }
     465           74 :     stream.write('}');
     466           74 :     stream.put_comma = true;
     467           74 : }
     468              : 
     469              : template <field_attributes attributes>
     470           34 : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value)
     471              : {
     472           34 :     if (p_value.has_value())
     473           22 :         return serialize<attributes>(stream, *p_value);
     474              : }
     475              : 
     476              : template <field_attributes attributes>
     477          724 : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value, std::string_view field)
     478              : {
     479          724 :     if (p_value.has_value())
     480          612 :         return serialize<attributes>(stream, *p_value, field);
     481              : }
     482              : 
     483              : template <field_attributes attributes, typename T>
     484              : void serialize(auto &stream, const std::unique_ptr<T> &p_value)
     485              : {
     486              :     if (p_value)
     487              :         return serialize<attributes>(stream, *p_value);
     488              : }
     489              : 
     490              : template <field_attributes attributes, typename T>
     491              : void serialize(auto &stream, const std::unique_ptr<T> &p_value, std::string_view field)
     492              : {
     493              :     if (p_value)
     494              :         return serialize<attributes>(stream, *p_value, field);
     495              : }
     496              : 
     497         2698 : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_message auto &value)
     498              : {
     499         2698 :     stream.write('{');
     500         2698 :     stream.put_comma = false;
     501         2698 :     serialize_value(stream, value);
     502         2664 :     stream.write('}');
     503         2664 :     stream.put_comma = true;
     504         2664 : }
     505              : 
     506              : template <field_attributes attributes>
     507           26 : void serialize(auto &stream, const spb::detail::proto_message auto &value, std::string_view field)
     508              : {
     509           26 :     serialize_key(stream, field);
     510           26 :     serialize<attributes>(stream, value);
     511           26 : }
     512              : 
     513           92 : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_enum auto &value)
     514              : {
     515           92 :     serialize_value(stream, value);
     516           92 : }
     517              : 
     518              : template <field_attributes>
     519           32 : void serialize(auto &stream, const spb::detail::proto_enum auto &value, std::string_view field)
     520              : {
     521           32 :     serialize_key(stream, field);
     522           32 :     serialize_value(stream, value);
     523           32 : }
     524              : 
     525         1315 : template <field_attributes attributes> size_t serialize_size(const auto &value)
     526              : {
     527         1315 :     auto stream = ostream_size();
     528         1315 :     serialize<attributes>(stream, value);
     529         1314 :     return stream.size;
     530              : }
     531              : 
     532              : } // namespace spb::json::detail
        

Generated by: LCOV version 2.0-1