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-07-19 10:31:31 Functions: 90.4 % 1253 1133

            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      4381749 :     void write(uint8_t) noexcept
      43              :     {
      44      4381749 :         size += sizeof(uint8_t);
      45      4381749 :     }
      46              : 
      47         3747 :     void write(const void *, size_t data_size) noexcept
      48              :     {
      49         3747 :         size += data_size;
      50         3747 :     }
      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         3472 :     explicit ostream_buffer(void *buffer) : p_buffer((std::uint8_t *)buffer)
      61              :     {
      62         3472 :     }
      63              : 
      64      4379239 :     void write(uint8_t byte) noexcept
      65              :     {
      66      4379239 :         *p_buffer++ = byte;
      67      4379239 :     }
      68              : 
      69         2545 :     void write(const void *data, size_t data_size) noexcept
      70              :     {
      71         2545 :         memcpy(p_buffer, data, data_size);
      72         2545 :         p_buffer += data_size;
      73         2545 :     }
      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          378 :     explicit ostream_writer(spb::io::writer writer) : on_write(writer)
      84              :     {
      85          378 :     }
      86              : 
      87         2013 :     void write(uint8_t byte)
      88              :     {
      89         2013 :         write(&byte, 1);
      90         2013 :     }
      91              : 
      92         3100 :     void write(const void *data, size_t data_size)
      93              :     {
      94         3100 :         on_write(data, data_size);
      95         3100 :         size += data_size;
      96         3100 :     }
      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         2543 : template <size_t N> void write_string(auto &stream, const char (&string)[N])
     123              : {
     124         2543 :     stream.write(string, N - 1);
     125         2543 : }
     126              : 
     127         1910 : static constexpr auto is_escape(uint8_t c) -> bool
     128              : {
     129         1910 :     switch (c)
     130              :     {
     131           66 :     case '\\':
     132              :     case '\"':
     133           66 :         return true;
     134         1844 :     default:
     135         1844 :         return (c < ' ') | (c >= 0x80);
     136              :     }
     137              : }
     138              : 
     139          603 : static constexpr auto has_escape_chars(std::string_view str) -> bool
     140              : {
     141          603 :     return std::any_of(str.begin(), str.end(), is_escape);
     142              : }
     143              : 
     144          603 : void write_escaped(auto &stream, std::string_view str)
     145              : {
     146          603 :     if (!has_escape_chars(str)) [[likely]]
     147              :     {
     148          554 :         stream.write(str.data(), str.size());
     149          554 :         return;
     150              :     }
     151              : 
     152           49 :     uint32_t codepoint = 0;
     153           49 :     uint32_t state     = spb::detail::utf8::ok;
     154           49 :     bool decoding_utf8 = false;
     155          329 :     for (uint8_t c : str)
     156              :     {
     157          280 :         if (decoding_utf8)
     158              :         {
     159           36 :             if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
     160              :             {
     161           18 :                 write_unicode(stream, codepoint);
     162           18 :                 decoding_utf8 = false;
     163              :             }
     164           36 :             continue;
     165              :         }
     166          244 :         if (is_escape(c))
     167              :         {
     168          147 :             switch (c)
     169              :             {
     170           24 :             case '"':
     171           24 :                 write_string(stream, R"(\")");
     172           24 :                 break;
     173           16 :             case '\\':
     174           16 :                 write_string(stream, R"(\\)");
     175           16 :                 break;
     176           10 :             case '\b':
     177           10 :                 write_string(stream, R"(\b)");
     178           10 :                 break;
     179           10 :             case '\f':
     180           10 :                 write_string(stream, R"(\f)");
     181           10 :                 break;
     182           16 :             case '\n':
     183           16 :                 write_string(stream, R"(\n)");
     184           16 :                 break;
     185           16 :             case '\r':
     186           16 :                 write_string(stream, R"(\r)");
     187           16 :                 break;
     188           30 :             case '\t':
     189           30 :                 write_string(stream, R"(\t)");
     190           30 :                 break;
     191           25 :             default:
     192           25 :                 decoding_utf8 = true;
     193           25 :                 if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
     194              :                 {
     195            6 :                     write_unicode(stream, codepoint);
     196            6 :                     decoding_utf8 = false;
     197              :                 }
     198              :             }
     199              :         }
     200              :         else
     201              :         {
     202           97 :             stream.write(c);
     203              :         }
     204              :     }
     205           49 :     if (state != spb::detail::utf8::ok) [[unlikely]]
     206              :     {
     207            1 :         throw std::runtime_error("invalid utf8");
     208              :     }
     209              : }
     210              : 
     211         3800 : void put_comma_if_needed(auto &stream)
     212              : {
     213         3800 :     if (stream.put_comma)
     214          870 :         stream.write(',');
     215              : 
     216         3800 :     stream.put_comma = true;
     217         3800 : }
     218              : 
     219         2295 : void serialize_key(auto &stream, std::string_view key)
     220              : {
     221         2295 :     put_comma_if_needed(stream);
     222              : 
     223         2295 :     stream.write('"');
     224         2295 :     stream.write(key.data(), key.size());
     225         2295 :     write_string(stream, R"(":)");
     226         2295 : }
     227              : 
     228              : template <field_attributes> void serialize(auto &stream, const bool &value);
     229              : template <field_attributes> void serialize(auto &stream, const bool &value, std::string_view field);
     230              : 
     231              : template <field_attributes>
     232              : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value);
     233              : template <field_attributes>
     234              : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value, std::string_view field);
     235              : 
     236              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_message auto &value);
     237              : template <field_attributes>
     238              : void serialize(auto &stream, const spb::detail::proto_message auto &value, std::string_view field);
     239              : 
     240              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_enum auto &value);
     241              : template <field_attributes>
     242              : void serialize(auto &stream, const spb::detail::proto_enum auto &value, std::string_view field);
     243              : 
     244              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_field_string auto &value);
     245              : template <field_attributes>
     246              : void serialize(auto &stream, const spb::detail::proto_field_string auto &value, std::string_view field);
     247              : 
     248              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value);
     249              : template <field_attributes>
     250              : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value, std::string_view field);
     251              : 
     252              : template <field_attributes attributes>
     253              : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value);
     254              : template <field_attributes attributes>
     255              : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value, std::string_view field);
     256              : 
     257              : template <field_attributes attributes, typename T>
     258              : void serialize(auto &stream, const std::unique_ptr<T> &p_value);
     259              : template <field_attributes attributes, typename T>
     260              : void serialize(auto &stream, const std::unique_ptr<T> &p_value, std::string_view field);
     261              : 
     262              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value);
     263              : template <field_attributes>
     264              : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value, std::string_view field);
     265              : 
     266              : template <field_attributes>
     267              : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value);
     268              : template <field_attributes>
     269              : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
     270              :                std::string_view field);
     271              : 
     272              : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_map auto &map);
     273              : template <field_attributes>
     274              : void serialize(auto &stream, const spb::detail::proto_map auto &map, std::string_view field);
     275              : 
     276           88 : template <field_attributes> void serialize(auto &stream, const bool &value)
     277              : {
     278           88 :     return value ? write_string(stream, "true") : write_string(stream, "false");
     279              : }
     280              : 
     281          124 : void serialize_enum(auto &stream, std::string_view value)
     282              : {
     283          124 :     stream.write('"');
     284          124 :     stream.write(value.data(), value.size());
     285          124 :     stream.write('"');
     286          124 : }
     287              : 
     288          611 : template <field_attributes attributes> void serialize(auto &stream, const std::string_view &value)
     289              : {
     290              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     291              : 
     292              :     if constexpr (!stream_type::size_only && attributes.max_size)
     293           40 :         check_size(value.size(), attributes.max_size);
     294              : 
     295          603 :     stream.write('"');
     296          603 :     write_escaped(stream, value);
     297          602 :     stream.write('"');
     298          602 : }
     299              : 
     300              : template <field_attributes attributes>
     301          611 : void serialize(auto &stream, const spb::detail::proto_field_string auto &value)
     302              : {
     303          611 :     serialize<attributes>(stream, std::string_view(value.data(), value.size()));
     304          602 : }
     305              : 
     306              : template <field_attributes>
     307         1839 : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value)
     308              : {
     309         1839 :     auto buffer = std::array<char, 32>();
     310         5517 :     auto result = spb_std_emu::to_chars(buffer.data(), buffer.data() + sizeof(buffer), value);
     311         5517 :     stream.write(buffer.data(), static_cast<size_t>(result.ptr - buffer.data()));
     312         1839 : }
     313              : 
     314           36 : template <field_attributes attributes> void serialize(auto &stream, const bool &value, std::string_view field)
     315              : {
     316           36 :     serialize_key(stream, field);
     317           36 :     serialize<attributes>(stream, value);
     318           36 : }
     319              : 
     320              : template <field_attributes attributes>
     321          946 : void serialize(auto &stream, const spb::detail::proto_field_int_or_float auto &value, std::string_view field)
     322              : {
     323          946 :     serialize_key(stream, field);
     324          946 :     serialize<attributes>(stream, value);
     325          946 : }
     326              : 
     327              : template <field_attributes attributes>
     328          363 : void serialize(auto &stream, const spb::detail::proto_field_string auto &value, std::string_view field)
     329              : {
     330          363 :     if (value.empty())
     331            6 :         return;
     332              : 
     333          357 :     serialize_key(stream, field);
     334          357 :     serialize<attributes>(stream, value);
     335              : }
     336              : 
     337              : template <field_attributes attributes>
     338          388 : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value)
     339              : {
     340              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     341              : 
     342              :     if constexpr (!stream_type::size_only && attributes.max_size)
     343           40 :         check_size(value.size(), attributes.max_size);
     344              : 
     345          380 :     stream.write('"');
     346          380 :     base64_encode(stream, value);
     347          380 :     stream.write('"');
     348          380 : }
     349              : 
     350              : template <field_attributes attributes>
     351          152 : void serialize(auto &stream, const spb::detail::proto_field_bytes auto &value, std::string_view field)
     352              : {
     353          152 :     if (value.empty())
     354            6 :         return;
     355              : 
     356          146 :     serialize_key(stream, field);
     357          146 :     serialize<attributes>(stream, value);
     358              : }
     359              : 
     360              : template <field_attributes attributes>
     361          980 : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value, std::string_view field)
     362              : {
     363          980 :     if (value.empty())
     364          312 :         return;
     365              : 
     366          668 :     serialize_key(stream, field);
     367          668 :     serialize<attributes>(stream, value);
     368              : }
     369              : 
     370              : template <field_attributes attributes>
     371          694 : void serialize(auto &stream, const spb::detail::proto_label_repeated auto &value)
     372              : {
     373              :     using stream_type = std::remove_cvref_t<decltype(stream)>;
     374              : 
     375              :     if constexpr (!stream_type::size_only && attributes.max_count)
     376           85 :         check_size(value.size(), attributes.max_count);
     377              : 
     378          677 :     stream.write('[');
     379          677 :     stream.put_comma = false;
     380         2134 :     for (const auto &v : value)
     381              :     {
     382         1457 :         put_comma_if_needed(stream);
     383              : 
     384              :         if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
     385              :         {
     386           40 :             serialize<attributes>(stream, bool(v));
     387              :         }
     388              :         else
     389              :         {
     390         1417 :             serialize<attributes>(stream, v);
     391              :         }
     392              :     }
     393          677 :     stream.write(']');
     394          677 :     stream.put_comma = true;
     395          677 : }
     396              : 
     397              : template <field_attributes attributes>
     398           12 : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value)
     399              : {
     400           12 :     stream.write('[');
     401           12 :     stream.put_comma = false;
     402          120 :     for (size_t i = 0; i < value.size(); i++)
     403              :     {
     404           48 :         put_comma_if_needed(stream);
     405              : 
     406              :         if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
     407              :         {
     408              :             serialize<attributes>(stream, bool(value[i]));
     409              :         }
     410              :         else
     411              :         {
     412           48 :             serialize<attributes>(stream, value[i]);
     413              :         }
     414              :     }
     415           12 :     stream.write(']');
     416           12 :     stream.put_comma = true;
     417           12 : }
     418              : 
     419              : template <field_attributes attributes>
     420           12 : void serialize(auto &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
     421              :                std::string_view field)
     422              : {
     423           12 :     serialize_key(stream, field);
     424           12 :     serialize<attributes>(stream, value);
     425           12 : }
     426              : 
     427              : template <field_attributes attributes>
     428           48 : void serialize(auto &stream, const spb::detail::proto_map auto &map, std::string_view field)
     429              : {
     430           48 :     if (map.empty())
     431            0 :         return;
     432              : 
     433           48 :     serialize_key(stream, field);
     434           48 :     serialize<attributes>(stream, map);
     435              : }
     436              : 
     437           68 : template <field_attributes attributes> void serialize(auto &stream, const spb::detail::proto_map auto &map)
     438              : {
     439              :     using map_type = std::remove_cvref_t<decltype(map)>;
     440              :     using key_type = typename map_type::key_type;
     441              : 
     442           68 :     stream.write('{');
     443           68 :     stream.put_comma = false;
     444          142 :     for (const auto &[map_key, map_value] : map)
     445              :     {
     446              :         if constexpr (std::is_same_v<key_type, std::string_view> || std::is_same_v<key_type, std::string>)
     447              :         {
     448           36 :             serialize_key(stream, map_key);
     449              :         }
     450              :         else
     451              :         {
     452           38 :             if (stream.put_comma)
     453            8 :                 stream.write(',');
     454              : 
     455           38 :             stream.write('"');
     456           38 :             serialize<attributes>(stream, map_key);
     457           38 :             write_string(stream, R"(":)");
     458              :         }
     459           74 :         stream.put_comma = false;
     460           74 :         serialize<attributes>(stream, map_value);
     461           74 :         stream.put_comma = true;
     462              :     }
     463           68 :     stream.write('}');
     464           68 :     stream.put_comma = true;
     465           68 : }
     466              : 
     467              : template <field_attributes attributes>
     468           34 : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value)
     469              : {
     470           34 :     if (p_value.has_value())
     471           22 :         return serialize<attributes>(stream, *p_value);
     472              : }
     473              : 
     474              : template <field_attributes attributes>
     475          712 : void serialize(auto &stream, const spb::detail::proto_label_optional auto &p_value, std::string_view field)
     476              : {
     477          712 :     if (p_value.has_value())
     478          606 :         return serialize<attributes>(stream, *p_value, field);
     479              : }
     480              : 
     481              : template <field_attributes attributes, typename T>
     482              : void serialize(auto &stream, const std::unique_ptr<T> &p_value)
     483              : {
     484              :     if (p_value)
     485              :         return serialize<attributes>(stream, *p_value);
     486              : }
     487              : 
     488              : template <field_attributes attributes, typename T>
     489              : void serialize(auto &stream, const std::unique_ptr<T> &p_value, std::string_view field)
     490              : {
     491              :     if (p_value)
     492              :         return serialize<attributes>(stream, *p_value, field);
     493              : }
     494              : 
     495         2620 : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_message auto &value)
     496              : {
     497         2620 :     stream.write('{');
     498         2620 :     stream.put_comma = false;
     499         2620 :     serialize_value(stream, value);
     500         2586 :     stream.write('}');
     501         2586 :     stream.put_comma = true;
     502         2586 : }
     503              : 
     504              : template <field_attributes attributes>
     505           14 : void serialize(auto &stream, const spb::detail::proto_message auto &value, std::string_view field)
     506              : {
     507           14 :     serialize_key(stream, field);
     508           14 :     serialize<attributes>(stream, value);
     509           14 : }
     510              : 
     511           92 : template <field_attributes> void serialize(auto &stream, const spb::detail::proto_enum auto &value)
     512              : {
     513           92 :     serialize_value(stream, value);
     514           92 : }
     515              : 
     516              : template <field_attributes>
     517           32 : void serialize(auto &stream, const spb::detail::proto_enum auto &value, std::string_view field)
     518              : {
     519           32 :     serialize_key(stream, field);
     520           32 :     serialize_value(stream, value);
     521           32 : }
     522              : 
     523         1291 : template <field_attributes attributes> size_t serialize_size(const auto &value)
     524              : {
     525         1291 :     auto stream = ostream_size();
     526         1291 :     serialize<attributes>(stream, value);
     527         1290 :     return stream.size;
     528              : }
     529              : 
     530              : } // namespace spb::json::detail
        

Generated by: LCOV version 2.0-1