LCOV - code coverage report
Current view: top level - json/impl - serializer.ipp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 261 261
Test Date: 2026-02-25 20:43:10 Functions: 100.0 % 36 36

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/boostorg/json
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_JSON_IMPL_SERIALIZER_IPP
      11                 : #define BOOST_JSON_IMPL_SERIALIZER_IPP
      12                 : 
      13                 : #include <boost/core/detail/static_assert.hpp>
      14                 : #include <boost/json/serializer.hpp>
      15                 : #include <boost/json/detail/format.hpp>
      16                 : #include <boost/json/detail/sse2.hpp>
      17                 : 
      18                 : #ifdef _MSC_VER
      19                 : #pragma warning(push)
      20                 : #pragma warning(disable: 4127) // conditional expression is constant
      21                 : #endif
      22                 : 
      23                 : namespace boost {
      24                 : namespace json {
      25                 : namespace detail {
      26                 : 
      27                 : struct int64_formatter
      28                 : {
      29                 :     std::int64_t i;
      30                 : 
      31                 :     std::size_t
      32 HIT        3188 :     operator()(char* dst) const noexcept
      33                 :     {
      34            3188 :         return format_int64(dst, i);
      35                 :     }
      36                 : };
      37                 : 
      38                 : struct uint64_formatter
      39                 : {
      40                 :     std::uint64_t u;
      41                 : 
      42                 :     std::size_t
      43             425 :     operator()(char* dst) const noexcept
      44                 :     {
      45             425 :         return format_uint64(dst, u);
      46                 :     }
      47                 : };
      48                 : 
      49                 : struct double_formatter
      50                 : {
      51                 :     double d;
      52                 :     bool allow_infinity_and_nan;
      53                 : 
      54                 :     std::size_t
      55             477 :     operator()(char* dst) const noexcept
      56                 :     {
      57             477 :         return format_double(dst, d, allow_infinity_and_nan);
      58                 :     }
      59                 : };
      60                 : 
      61           21245 : writer::
      62                 : writer(
      63                 :     storage_ptr sp,
      64                 :     unsigned char* buf,
      65                 :     std::size_t buf_size,
      66           21245 :     serialize_options const& opts) noexcept
      67           21245 :     : st_(
      68           21245 :         std::move(sp),
      69                 :         buf,
      70                 :         buf_size)
      71           21245 :     , opts_(opts)
      72                 : {
      73                 :     // ensure room for \uXXXX escape plus one
      74                 :     BOOST_CORE_STATIC_ASSERT( sizeof(buf_) >= 7 );
      75           21245 : }
      76                 : 
      77                 : bool
      78                 : BOOST_FORCEINLINE
      79                 : write_buffer(writer& w, stream& ss0)
      80                 : {
      81            1444 :     local_stream ss(ss0);
      82            2578 :     auto const n = ss.remain();
      83            2578 :     if( n < w.cs0_.remain() )
      84                 :     {
      85            1334 :         ss.append(w.cs0_.data(), n);
      86            1334 :         w.cs0_.skip(n);
      87            1334 :         return w.suspend(writer::state::lit);
      88                 :     }
      89            1244 :     ss.append( w.cs0_.data(), w.cs0_.remain() );
      90            1244 :     return true;
      91            2578 : }
      92                 : 
      93                 : template< class F >
      94                 : bool
      95            4090 : write_buffer(writer& w, stream& ss0, F f)
      96                 : {
      97            4090 :     BOOST_ASSERT( w.st_.empty() );
      98                 : 
      99            4090 :     local_stream ss(ss0);
     100            4090 :     if(BOOST_JSON_LIKELY( ss.remain() >= detail::max_number_chars ))
     101                 :     {
     102            2956 :         ss.advance( f(ss.data()) );
     103            2956 :         return true;
     104                 :     }
     105                 : 
     106            1134 :     w.cs0_ = { w.buf_, f(w.buf_) };
     107            1134 :     return write_buffer(w, ss);
     108            4090 : }
     109                 : 
     110                 : template<literals Lit>
     111                 : bool
     112            4725 : write_literal(writer& w, stream& ss)
     113                 : {
     114            4725 :     constexpr std::size_t index = literal_index(Lit);
     115            4725 :     constexpr char const* literal = literal_strings[index];
     116            4725 :     constexpr std::size_t sz = literal_sizes[index];
     117                 : 
     118            4725 :     std::size_t const n = ss.remain();
     119            4725 :     if(BOOST_JSON_LIKELY( n >= sz ))
     120                 :     {
     121            4613 :         ss.append( literal, sz );
     122            4613 :         return true;
     123                 :     }
     124                 : 
     125             112 :     ss.append(literal, n);
     126                 : 
     127             112 :     w.cs0_ = {literal + n, sz - n};
     128             112 :     return w.suspend(writer::state::lit);
     129                 : }
     130                 : 
     131                 : bool
     132             197 : write_true(writer& w, stream& ss)
     133                 : {
     134             197 :     return write_literal<literals::true_>(w, ss);
     135                 : }
     136                 : 
     137                 : bool
     138             176 : write_false(writer& w, stream& ss)
     139                 : {
     140             176 :     return write_literal<literals::false_>(w, ss);
     141                 : }
     142                 : 
     143                 : bool
     144            4352 : write_null(writer& w, stream& ss)
     145                 : {
     146            4352 :     return write_literal<literals::null>(w, ss);
     147                 : }
     148                 : 
     149                 : bool
     150            3188 : write_int64(writer& w, stream& ss0, std::int64_t i)
     151                 : {
     152            3188 :     return write_buffer( w, ss0, int64_formatter{i} );
     153                 : }
     154                 : 
     155                 : bool
     156             425 : write_uint64(writer& w, stream& ss0, std::uint64_t u)
     157                 : {
     158             425 :     return write_buffer( w, ss0, uint64_formatter{u} );
     159                 : }
     160                 : 
     161                 : bool
     162             477 : write_double(writer& w, stream& ss0, double d)
     163                 : {
     164             954 :     return write_buffer(
     165             477 :         w, ss0, double_formatter{d, w.opts_.allow_infinity_and_nan} );
     166                 : }
     167                 : 
     168                 : bool
     169            1444 : resume_buffer(writer& w, stream& ss0)
     170                 : {
     171            1444 :     BOOST_ASSERT( !w.st_.empty() );
     172                 :     writer::state st;
     173            1444 :     w.st_.pop(st);
     174            1444 :     BOOST_ASSERT(st == writer::state::lit);
     175                 : 
     176            2888 :     return write_buffer(w, ss0);
     177                 : }
     178                 : 
     179                 : template<bool StackEmpty>
     180                 : bool
     181           44285 : do_write_string(writer& w, stream& ss0)
     182                 : {
     183           44285 :     local_stream ss(ss0);
     184           44285 :     local_const_stream cs(w.cs0_);
     185            9812 :     if(! StackEmpty && ! w.st_.empty())
     186                 :     {
     187                 :         writer::state st;
     188            9812 :         w.st_.pop(st);
     189            9812 :         switch(st)
     190                 :         {
     191             170 :         default:
     192             170 :         case writer::state::str1: goto do_str1;
     193             268 :         case writer::state::str2: goto do_str2;
     194            9082 :         case writer::state::str3: goto do_str3;
     195              52 :         case writer::state::esc1: goto do_esc1;
     196              48 :         case writer::state::utf1: goto do_utf1;
     197              48 :         case writer::state::utf2: goto do_utf2;
     198              48 :         case writer::state::utf3: goto do_utf3;
     199              48 :         case writer::state::utf4: goto do_utf4;
     200              48 :         case writer::state::utf5: goto do_utf5;
     201                 :         }
     202                 :     }
     203                 :     static constexpr char hex[] = "0123456789abcdef";
     204                 :     static constexpr char esc[] =
     205                 :         "uuuuuuuubtnufruuuuuuuuuuuuuuuuuu"
     206                 :         "\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     207                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0"
     208                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     209                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     210                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     211                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     212                 :         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
     213                 : 
     214                 :     // opening quote
     215           34473 : do_str1:
     216           34643 :     if(BOOST_JSON_LIKELY(ss))
     217           34473 :         ss.append('\x22'); // '"'
     218                 :     else
     219             170 :         return w.suspend(writer::state::str1);
     220                 : 
     221                 :     // fast loop,
     222                 :     // copy unescaped
     223           34741 : do_str2:
     224           34741 :     if(BOOST_JSON_LIKELY(ss))
     225                 :     {
     226           34485 :         std::size_t n = cs.remain();
     227           34485 :         if(BOOST_JSON_LIKELY(n > 0))
     228                 :         {
     229           34441 :             if(ss.remain() > n)
     230           25766 :                 n = detail::count_unescaped(
     231                 :                     cs.data(), n);
     232                 :             else
     233            8675 :                 n = detail::count_unescaped(
     234                 :                     cs.data(), ss.remain());
     235           34441 :             if(n > 0)
     236                 :             {
     237           25225 :                 ss.append(cs.data(), n);
     238           25225 :                 cs.skip(n);
     239           25225 :                 if(! ss)
     240              12 :                     return w.suspend(writer::state::str2);
     241                 :             }
     242                 :         }
     243                 :         else
     244                 :         {
     245              44 :             ss.append('\x22'); // '"'
     246              44 :             return true;
     247                 :         }
     248                 :     }
     249                 :     else
     250                 :     {
     251             256 :         return w.suspend(writer::state::str2);
     252                 :     }
     253                 : 
     254                 :     // slow loop,
     255                 :     // handle escapes
     256           43707 : do_str3:
     257        31461016 :     while(BOOST_JSON_LIKELY(ss))
     258                 :     {
     259        31451934 :         if(BOOST_JSON_LIKELY(cs))
     260                 :         {
     261        31417505 :             auto const ch = *cs;
     262        31417505 :             auto const c = esc[static_cast<
     263                 :                 unsigned char>(ch)];
     264        31417505 :             ++cs;
     265        31417505 :             if(! c)
     266                 :             {
     267        31416777 :                 ss.append(ch);
     268                 :             }
     269             728 :             else if(c != 'u')
     270                 :             {
     271             376 :                 ss.append('\\');
     272             376 :                 if(BOOST_JSON_LIKELY(ss))
     273                 :                 {
     274             324 :                     ss.append(c);
     275                 :                 }
     276                 :                 else
     277                 :                 {
     278              52 :                     w.buf_[0] = c;
     279              52 :                     return w.suspend(
     280              52 :                         writer::state::esc1);
     281                 :                 }
     282                 :             }
     283                 :             else
     284                 :             {
     285             352 :                 if(BOOST_JSON_LIKELY(
     286                 :                     ss.remain() >= 6))
     287                 :                 {
     288             208 :                     ss.append("\\u00", 4);
     289             208 :                     ss.append(hex[static_cast<
     290             208 :                         unsigned char>(ch) >> 4]);
     291             208 :                     ss.append(hex[static_cast<
     292             208 :                         unsigned char>(ch) & 15]);
     293                 :                 }
     294                 :                 else
     295                 :                 {
     296             144 :                     ss.append('\\');
     297             144 :                     w.buf_[0] = hex[static_cast<
     298             144 :                         unsigned char>(ch) >> 4];
     299             144 :                     w.buf_[1] = hex[static_cast<
     300             144 :                         unsigned char>(ch) & 15];
     301             144 :                     goto do_utf1;
     302                 :                 }
     303                 :             }
     304                 :         }
     305                 :         else
     306                 :         {
     307           34429 :             ss.append('\x22'); // '"'
     308           34429 :             return true;
     309                 :         }
     310                 :     }
     311            9082 :     return w.suspend(writer::state::str3);
     312                 : 
     313              52 : do_esc1:
     314              52 :     BOOST_ASSERT(ss);
     315              52 :     ss.append(w.buf_[0]);
     316              52 :     goto do_str3;
     317                 : 
     318             192 : do_utf1:
     319             192 :     if(BOOST_JSON_LIKELY(ss))
     320             144 :         ss.append('u');
     321                 :     else
     322              48 :         return w.suspend(writer::state::utf1);
     323             192 : do_utf2:
     324             192 :     if(BOOST_JSON_LIKELY(ss))
     325             144 :         ss.append('0');
     326                 :     else
     327              48 :         return w.suspend(writer::state::utf2);
     328             192 : do_utf3:
     329             192 :     if(BOOST_JSON_LIKELY(ss))
     330             144 :         ss.append('0');
     331                 :     else
     332              48 :         return w.suspend(writer::state::utf3);
     333             192 : do_utf4:
     334             192 :     if(BOOST_JSON_LIKELY(ss))
     335             144 :         ss.append(w.buf_[0]);
     336                 :     else
     337              48 :         return w.suspend(writer::state::utf4);
     338             192 : do_utf5:
     339             192 :     if(BOOST_JSON_LIKELY(ss))
     340             144 :         ss.append(w.buf_[1]);
     341                 :     else
     342              48 :         return w.suspend(writer::state::utf5);
     343             144 :     goto do_str3;
     344           44285 : }
     345                 : 
     346                 : bool
     347           19827 : write_string(writer& w, stream& ss0)
     348                 : {
     349           19827 :     return do_write_string<true>(w, ss0);
     350                 : }
     351                 : 
     352                 : bool
     353             408 : resume_string(writer& w, stream& ss0)
     354                 : {
     355             408 :     return do_write_string<false>(w, ss0);
     356                 : }
     357                 : 
     358                 : template<bool StackEmpty>
     359                 : bool
     360                 : write_value(writer& w, stream& ss);
     361                 : 
     362                 : template< class T, bool StackEmpty >
     363                 : BOOST_FORCEINLINE
     364                 : bool
     365                 : write_impl(no_conversion_tag, writer& w, stream& ss)
     366                 : {
     367           34536 :     return write_value<StackEmpty>(w, ss);
     368                 : }
     369                 : 
     370                 : template<bool StackEmpty>
     371                 : bool
     372            6041 : write_array(writer& w, stream& ss)
     373                 : {
     374            6040 :     return write_impl<array, StackEmpty>(sequence_conversion_tag(), w, ss);
     375                 : }
     376                 : 
     377                 : template<bool StackEmpty>
     378                 : bool
     379           27194 : write_object(writer& w, stream& ss)
     380                 : {
     381           27194 :     return write_impl<object, StackEmpty>(map_like_conversion_tag(), w, ss);
     382                 : }
     383                 : 
     384                 : template<bool StackEmpty>
     385                 : bool
     386           66927 : write_value(writer& w, stream& ss)
     387                 : {
     388           22905 :     if(StackEmpty || w.st_.empty())
     389                 :     {
     390           44503 :         BOOST_ASSERT( w.p_ );
     391           44503 :         auto const pv = reinterpret_cast<value const*>(w.p_);
     392           44503 :         switch(pv->kind())
     393                 :         {
     394           18083 :         default:
     395                 :         case kind::object:
     396           18083 :             w.p_ = &pv->get_object();
     397           18083 :             return write_object<true>(w, ss);
     398                 : 
     399            3400 :         case kind::array:
     400            3400 :             w.p_ = &pv->get_array();
     401            3400 :             return write_array<true>(w, ss);
     402                 : 
     403           14643 :         case kind::string:
     404                 :         {
     405           14643 :             auto const& js = pv->get_string();
     406           14643 :             w.cs0_ = { js.data(), js.size() };
     407           14643 :             return do_write_string<true>(w, ss);
     408                 :         }
     409                 : 
     410            3182 :         case kind::int64:
     411            3182 :             return write_int64( w, ss, pv->get_int64() );
     412              91 :         case kind::uint64:
     413              91 :             return write_uint64( w, ss, pv->get_uint64() );
     414             467 :         case kind::double_:
     415             467 :             return write_double( w, ss, pv->get_double() );
     416                 : 
     417             306 :         case kind::bool_:
     418             306 :             if( pv->get_bool() )
     419             139 :                 return write_true(w, ss);
     420                 :             else
     421             167 :                 return write_false(w, ss);
     422                 : 
     423            4331 :         case kind::null:
     424            4331 :             return write_null(w, ss);
     425                 :         }
     426                 :     }
     427                 :     else
     428                 :     {
     429                 :         writer::state st;
     430           22424 :         w.st_.peek(st);
     431           22424 :         switch(st)
     432                 :         {
     433            1324 :         default:
     434                 :         case writer::state::lit:
     435            1324 :             return resume_buffer(w, ss);
     436                 : 
     437            9404 :         case writer::state::str1: case writer::state::str2:
     438                 :         case writer::state::str3: case writer::state::esc1:
     439                 :         case writer::state::utf1: case writer::state::utf2:
     440                 :         case writer::state::utf3: case writer::state::utf4:
     441                 :         case writer::state::utf5:
     442            9404 :             return do_write_string<false>(w, ss);
     443                 : 
     444            2636 :         case writer::state::arr1: case writer::state::arr2:
     445                 :         case writer::state::arr3: case writer::state::arr4:
     446            2636 :             return write_array<StackEmpty>(w, ss);
     447                 : 
     448            9060 :         case writer::state::obj1: case writer::state::obj2:
     449                 :         case writer::state::obj3: case writer::state::obj4:
     450                 :         case writer::state::obj5: case writer::state::obj6:
     451            9060 :             return write_object<StackEmpty>(w, ss);
     452                 :         }
     453                 :     }
     454                 : }
     455                 : 
     456                 : } // namespace detail
     457                 : 
     458            2348 : serializer::
     459            2348 : serializer(serialize_options const& opts) noexcept
     460            2348 :     : serializer({}, nullptr, 0, opts)
     461            2348 : {}
     462                 : 
     463           21245 : serializer::
     464                 : serializer(
     465                 :     storage_ptr sp,
     466                 :     unsigned char* buf,
     467                 :     std::size_t buf_size,
     468           21245 :     serialize_options const& opts) noexcept
     469           21245 :     : detail::writer(std::move(sp), buf, buf_size, opts)
     470           21245 : {}
     471                 : 
     472                 : void
     473           20961 : serializer::
     474                 : reset(value const* p) noexcept
     475                 : {
     476           20961 :     p_ = p;
     477           20961 :     fn0_ = &detail::write_value<true>;
     478           20961 :     fn1_ = &detail::write_value<false>;
     479           20961 :     st_.clear();
     480           20961 :     done_ = false;
     481           20961 : }
     482                 : 
     483                 : void
     484               5 : serializer::
     485                 : reset(array const* p) noexcept
     486                 : {
     487               5 :     p_ = p;
     488               5 :     fn0_ = &detail::write_array<true>;
     489               5 :     fn1_ = &detail::write_array<false>;
     490               5 :     st_.clear();
     491               5 :     done_ = false;
     492               5 : }
     493                 : 
     494                 : void
     495              51 : serializer::
     496                 : reset(object const* p) noexcept
     497                 : {
     498              51 :     p_ = p;
     499              51 :     fn0_ = &detail::write_object<true>;
     500              51 :     fn1_ = &detail::write_object<false>;
     501              51 :     st_.clear();
     502              51 :     done_ = false;
     503              51 : }
     504                 : 
     505                 : void
     506               2 : serializer::
     507                 : reset(string const* p) noexcept
     508                 : {
     509               2 :     cs0_ = { p->data(), p->size() };
     510               2 :     fn0_ = &detail::do_write_string<true>;
     511               2 :     fn1_ = &detail::do_write_string<false>;
     512               2 :     st_.clear();
     513               2 :     done_ = false;
     514               2 : }
     515                 : 
     516                 : void
     517               1 : serializer::
     518                 : reset(string_view sv) noexcept
     519                 : {
     520               1 :     cs0_ = { sv.data(), sv.size() };
     521               1 :     fn0_ = &detail::do_write_string<true>;
     522               1 :     fn1_ = &detail::do_write_string<false>;
     523               1 :     st_.clear();
     524               1 :     done_ = false;
     525               1 : }
     526                 : 
     527                 : void
     528               6 : serializer::reset(std::nullptr_t) noexcept
     529                 : {
     530               6 :     p_ = nullptr;
     531               6 :     fn0_ = &detail::write_impl<std::nullptr_t, true>;
     532               6 :     fn1_ = &detail::write_impl<std::nullptr_t, false>;
     533               6 :     st_.clear();
     534               6 :     done_ = false;
     535               6 : }
     536                 : 
     537                 : string_view
     538           32983 : serializer::
     539                 : read(char* dest, std::size_t size)
     540                 : {
     541           32983 :     if( !fn0_ )
     542               6 :         reset(nullptr);
     543                 : 
     544           32983 :     if(BOOST_JSON_UNLIKELY(size == 0))
     545               1 :         return {dest, 0};
     546                 : 
     547           32982 :     detail::stream ss(dest, size);
     548           32982 :     if(st_.empty())
     549           21244 :         fn0_(*this, ss);
     550                 :     else
     551           11738 :         fn1_(*this, ss);
     552           32980 :     if(st_.empty())
     553                 :     {
     554           21242 :         done_ = true;
     555           21242 :         fn0_ = nullptr;
     556           21242 :         p_ = nullptr;
     557                 :     }
     558           32980 :     return string_view(
     559           32980 :         dest, ss.used(dest));
     560                 : }
     561                 : 
     562                 : } // namespace json
     563                 : } // namespace boost
     564                 : 
     565                 : #ifdef _MSC_VER
     566                 : #pragma warning(pop)
     567                 : #endif
     568                 : 
     569                 : #endif
        

Generated by: LCOV version 2.3