LCOV - code coverage report
Current view: top level - json/impl - value_stack.ipp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 99.5 % 200 199 1
Test Date: 2026-08-28 19:50:29 Functions: 100.0 % 41 41

           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_VALUE_STACK_IPP
      11                 : #define BOOST_JSON_IMPL_VALUE_STACK_IPP
      12                 : 
      13                 : #include <boost/json/value_stack.hpp>
      14                 : #include <cstring>
      15                 : #include <memory>
      16                 : #include <stdexcept>
      17                 : #include <utility>
      18                 : 
      19                 : namespace boost {
      20                 : namespace json {
      21                 : 
      22                 : //--------------------------------------
      23                 : 
      24 HIT     2076904 : value_stack::
      25                 : stack::
      26                 : ~stack()
      27                 : {
      28         2076904 :     clear();
      29         2076904 :     if( begin_ != temp_ &&
      30           75341 :         begin_ != nullptr)
      31           75333 :         sp_->deallocate(
      32           75333 :             begin_,
      33           75333 :             (end_ - begin_) *
      34                 :                 sizeof(value));
      35         2076904 : }
      36                 : 
      37         2076904 : value_stack::
      38                 : stack::
      39                 : stack(
      40                 :     storage_ptr sp,
      41                 :     void* temp,
      42         2076904 :     std::size_t size) noexcept
      43         2076904 :     : sp_(std::move(sp))
      44                 : {
      45                 :     // the buffer stores `value`s, so it has to be aligned for one;
      46                 :     // align it up the same way static_resource does for its buffer
      47         2076904 :     if(std::align(
      48                 :         alignof(value),
      49                 :         min_size_ * sizeof(value),
      50                 :         temp, size))
      51                 :     {
      52         2001525 :         temp_ = temp;
      53         2001525 :         begin_ = reinterpret_cast<
      54                 :             value*>(temp);
      55         2001525 :         top_ = begin_;
      56         2001525 :         end_ = begin_ +
      57         2001525 :             size / sizeof(value);
      58                 :     }
      59                 :     else
      60                 :     {
      61           75379 :         temp_ = temp;
      62           75379 :         begin_ = nullptr;
      63           75379 :         top_ = nullptr;
      64           75379 :         end_ = nullptr;
      65                 :     }
      66         2076904 : }
      67                 : 
      68                 : void
      69         4153141 : value_stack::
      70                 : stack::
      71                 : run_dtors(bool b) noexcept
      72                 : {
      73         4153141 :     run_dtors_ = b;
      74         4153141 : }
      75                 : 
      76                 : std::size_t
      77         4213818 : value_stack::
      78                 : stack::
      79                 : size() const noexcept
      80                 : {
      81         4213818 :     return top_ - begin_;
      82                 : }
      83                 : 
      84                 : bool
      85           64466 : value_stack::
      86                 : stack::
      87                 : has_chars()
      88                 : {
      89           64466 :     return chars_ != 0;
      90                 : }
      91                 : 
      92                 : //--------------------------------------
      93                 : 
      94                 : // destroy the values but
      95                 : // not the stack allocation.
      96                 : void
      97         6230045 : value_stack::
      98                 : stack::
      99                 : clear() noexcept
     100                 : {
     101         6230045 :     if(top_ != begin_)
     102                 :     {
     103              86 :         if(run_dtors_)
     104              86 :             for(auto it = top_;
     105             339 :                 it-- != begin_;)
     106             253 :                 it->~value();
     107              86 :         top_ = begin_;
     108                 :     }
     109         6230045 :     chars_ = 0;
     110         6230045 : }
     111                 : 
     112                 : void
     113            1868 : value_stack::
     114                 : stack::
     115                 : maybe_grow()
     116                 : {
     117            1868 :     if(top_ >= end_)
     118             266 :         grow_one();
     119            1868 : }
     120                 : 
     121                 : // make room for at least one more value
     122                 : void
     123           66499 : value_stack::
     124                 : stack::
     125                 : grow_one()
     126                 : {
     127           66499 :     BOOST_ASSERT(chars_ == 0);
     128           66499 :     std::size_t const capacity =
     129           66499 :         end_ - begin_;
     130           66499 :     std::size_t new_cap = min_size_;
     131                 :     // VFALCO check overflow here
     132           66541 :     while(new_cap < capacity + 1)
     133              42 :         new_cap <<= 1;
     134                 :     auto const begin =
     135                 :         reinterpret_cast<value*>(
     136           66499 :             sp_->allocate(
     137                 :                 new_cap * sizeof(value)));
     138           66499 :     std::size_t const cur_size = top_ - begin_;
     139           66499 :     if(begin_)
     140                 :     {
     141              11 :         std::memcpy(
     142                 :             reinterpret_cast<char*>(begin),
     143              11 :             reinterpret_cast<char*>(begin_),
     144              11 :             size() * sizeof(value));
     145              11 :         if(begin_ != temp_)
     146               9 :             sp_->deallocate(begin_,
     147                 :                 capacity * sizeof(value));
     148                 :     }
     149                 :     // book-keeping
     150           66499 :     top_ = begin + cur_size;
     151           66499 :     end_ = begin + new_cap;
     152           66499 :     begin_ = begin;
     153           66499 : }
     154                 : 
     155                 : // make room for nchars additional characters.
     156                 : void
     157           16171 : value_stack::
     158                 : stack::
     159                 : grow(std::size_t nchars)
     160                 : {
     161                 :     // needed capacity in values
     162                 :     std::size_t const needed =
     163           16171 :         size() +
     164           16171 :         1 +
     165           16171 :         ((chars_ + nchars +
     166           16171 :             sizeof(value) - 1) /
     167           16171 :                 sizeof(value));
     168           16171 :     std::size_t const capacity =
     169           16171 :         end_ - begin_;
     170           16171 :     BOOST_ASSERT(
     171                 :         needed > capacity);
     172           16171 :     std::size_t new_cap = min_size_;
     173                 :     // VFALCO check overflow here
     174           57824 :     while(new_cap < needed)
     175           41653 :         new_cap <<= 1;
     176                 :     auto const begin =
     177                 :         reinterpret_cast<value*>(
     178           16171 :             sp_->allocate(
     179                 :                 new_cap * sizeof(value)));
     180           16171 :     std::size_t const cur_size = top_ - begin_;
     181           16171 :     if(begin_)
     182                 :     {
     183                 :         std::size_t amount =
     184            7328 :             size() * sizeof(value);
     185            7328 :         if(chars_ > 0)
     186 MIS           0 :             amount += sizeof(value) + chars_;
     187 HIT        7328 :         std::memcpy(
     188                 :             reinterpret_cast<char*>(begin),
     189            7328 :             reinterpret_cast<char*>(begin_),
     190                 :             amount);
     191            7328 :         if(begin_ != temp_)
     192            7328 :             sp_->deallocate(begin_,
     193                 :                 capacity * sizeof(value));
     194                 :     }
     195                 :     // book-keeping
     196           16171 :     top_ = begin + cur_size;
     197           16171 :     end_ = begin + new_cap;
     198           16171 :     begin_ = begin;
     199           16171 : }
     200                 : 
     201                 : //--------------------------------------
     202                 : 
     203                 : void
     204           17149 : value_stack::
     205                 : stack::
     206                 : append(string_view s)
     207                 : {
     208           17149 :     std::size_t const bytes_avail =
     209                 :         reinterpret_cast<
     210           17149 :             char const*>(end_) -
     211                 :         reinterpret_cast<
     212           17149 :             char const*>(top_);
     213                 :     // make sure there is room for
     214                 :     // pushing one more value without
     215                 :     // clobbering the string.
     216           34298 :     if(sizeof(value) + chars_ +
     217           17149 :             s.size() > bytes_avail)
     218           16171 :         grow(s.size());
     219                 : 
     220                 :     // copy the new piece
     221           17149 :     std::memcpy(
     222                 :         reinterpret_cast<char*>(
     223           17149 :             top_ + 1) + chars_,
     224           17149 :         s.data(), s.size());
     225           17149 :     chars_ += s.size();
     226                 : 
     227                 :     // ensure a pushed value cannot
     228                 :     // clobber the released string.
     229           17149 :     BOOST_ASSERT(
     230                 :         reinterpret_cast<char*>(
     231                 :             top_ + 1) + chars_ <=
     232                 :         reinterpret_cast<char*>(
     233                 :             end_));
     234           17149 : }
     235                 : 
     236                 : string_view
     237           17021 : value_stack::
     238                 : stack::
     239                 : release_string() noexcept
     240                 : {
     241                 :     // ensure a pushed value cannot
     242                 :     // clobber the released string.
     243           17021 :     BOOST_ASSERT(
     244                 :         reinterpret_cast<char*>(
     245                 :             top_ + 1) + chars_ <=
     246                 :         reinterpret_cast<char*>(
     247                 :             end_));
     248           17021 :     auto const n = chars_;
     249           17021 :     chars_ = 0;
     250                 :     return { reinterpret_cast<
     251           17021 :         char const*>(top_ + 1), n };
     252                 : }
     253                 : 
     254                 : // transfer ownership of the top n
     255                 : // elements of the stack to the caller
     256                 : value*
     257         2113658 : value_stack::
     258                 : stack::
     259                 : release(std::size_t n) noexcept
     260                 : {
     261         2113658 :     BOOST_ASSERT(n <= size());
     262         2113658 :     BOOST_ASSERT(chars_ == 0);
     263         2113658 :     top_ -= n;
     264         2113658 :     return top_;
     265                 : }
     266                 : 
     267                 : template<class... Args>
     268                 : value&
     269         2119961 : value_stack::
     270                 : stack::
     271                 : push(Args&&... args)
     272                 : {
     273         2119961 :     BOOST_ASSERT(chars_ == 0);
     274         2119961 :     if(top_ >= end_)
     275           66233 :         grow_one();
     276                 :     value& jv = detail::access::
     277         2119961 :         construct_value(top_,
     278                 :             std::forward<Args>(args)...);
     279         2119897 :     ++top_;
     280         2119897 :     return jv;
     281                 : }
     282                 : 
     283                 : template<class Unchecked>
     284                 : void
     285           37008 : value_stack::
     286                 : stack::
     287                 : exchange(Unchecked&& u)
     288                 : {
     289           37008 :     BOOST_ASSERT(chars_ == 0);
     290                 :     union U
     291                 :     {
     292                 :         value v;
     293           37008 :         U() {}
     294           37008 :         ~U() {}
     295           37008 :     } jv;
     296                 :     // construct value on the stack
     297                 :     // to avoid clobbering top_[0],
     298                 :     // which belongs to `u`.
     299                 :     detail::access::
     300           37008 :         construct_value(
     301           37008 :             &jv.v, std::move(u));
     302           36931 :     std::memcpy(
     303                 :         reinterpret_cast<
     304           36931 :             char*>(top_),
     305                 :         &jv.v, sizeof(value));
     306           36931 :     ++top_;
     307           37008 : }
     308                 : 
     309                 : //----------------------------------------------------------
     310                 : 
     311         2076904 : value_stack::
     312                 : ~value_stack()
     313                 : {
     314                 :     // default dtor is here so the
     315                 :     // definition goes in the library
     316                 :     // instead of the caller's TU.
     317         2076904 : }
     318                 : 
     319         2076904 : value_stack::
     320                 : value_stack(
     321                 :     storage_ptr sp,
     322                 :     unsigned char* temp_buffer,
     323         2076904 :     std::size_t temp_size) noexcept
     324         2076904 :     : st_(
     325         2076904 :         std::move(sp),
     326                 :         temp_buffer,
     327         2076904 :         temp_size)
     328                 : {
     329         2076904 : }
     330                 : 
     331                 : void
     332         4153141 : value_stack::
     333                 : reset(storage_ptr sp) noexcept
     334                 : {
     335         4153141 :     st_.clear();
     336                 : 
     337         4153141 :     sp_.~storage_ptr();
     338        12459415 :     ::new(&sp_) storage_ptr(
     339         4153141 :         pilfer(sp));
     340                 : 
     341                 :     // `stack` needs this
     342                 :     // to clean up correctly
     343         8306282 :     st_.run_dtors(
     344         4153141 :         ! sp_.is_not_shared_and_deallocate_is_trivial());
     345         4153141 : }
     346                 : 
     347                 : value
     348         2076650 : value_stack::
     349                 : release() noexcept
     350                 : {
     351                 :     // This means the caller did not
     352                 :     // cause a single top level element
     353                 :     // to be produced.
     354         2076650 :     BOOST_ASSERT(st_.size() == 1);
     355                 : 
     356                 :     // give up shared ownership
     357         2076650 :     sp_ = {};
     358                 : 
     359         2076650 :     return pilfer(*st_.release(1));
     360                 : }
     361                 : 
     362                 : //----------------------------------------------------------
     363                 : 
     364                 : void
     365            2128 : value_stack::
     366                 : push_array(std::size_t n)
     367                 : {
     368                 :     // we already have room if n > 0
     369            2128 :     if(BOOST_JSON_UNLIKELY(n == 0))
     370             819 :         st_.maybe_grow();
     371                 :     detail::unchecked_array ua(
     372            2128 :         st_.release(n), n, sp_);
     373            2128 :     st_.exchange(std::move(ua));
     374            2128 : }
     375                 : 
     376                 : void
     377           34880 : value_stack::
     378                 : push_object(std::size_t n)
     379                 : {
     380                 :     // we already have room if n > 0
     381           34880 :     if(BOOST_JSON_UNLIKELY(n == 0))
     382            1049 :         st_.maybe_grow();
     383                 :     detail::unchecked_object uo(
     384           34880 :         st_.release(n * 2), n, sp_);
     385           34880 :     st_.exchange(std::move(uo));
     386           34880 : }
     387                 : 
     388                 : void
     389           17149 : value_stack::
     390                 : push_chars(
     391                 :     string_view s)
     392                 : {
     393           17149 :     st_.append(s);
     394           17149 : }
     395                 : 
     396                 : void
     397           38358 : value_stack::
     398                 : push_key(
     399                 :     string_view s)
     400                 : {
     401           38358 :     if(! st_.has_chars())
     402                 :     {
     403           30297 :         st_.push(detail::key_t{}, s, sp_);
     404           30237 :         return;
     405                 :     }
     406            8061 :     auto part = st_.release_string();
     407            8061 :     st_.push(detail::key_t{}, part, s, sp_);
     408                 : }
     409                 : 
     410                 : void
     411           26108 : value_stack::
     412                 : push_string(
     413                 :     string_view s)
     414                 : {
     415           26108 :     if(! st_.has_chars())
     416                 :     {
     417                 :         // fast path
     418           17148 :         st_.push(s, sp_);
     419           17144 :         return;
     420                 :     }
     421                 : 
     422                 :     // VFALCO We could add a special
     423                 :     // private ctor to string that just
     424                 :     // creates uninitialized space,
     425                 :     // to reduce member function calls.
     426            8960 :     auto part = st_.release_string();
     427           17920 :     auto& str = st_.push(
     428            8960 :         string_kind, sp_).get_string();
     429            8960 :     str.reserve(
     430            8960 :         part.size() + s.size());
     431            8960 :     std::memcpy(
     432            8960 :         str.data(),
     433            8960 :         part.data(), part.size());
     434            8960 :     std::memcpy(
     435            8960 :         str.data() + part.size(),
     436            8960 :         s.data(), s.size());
     437            8960 :     str.grow(part.size() + s.size());
     438                 : }
     439                 : 
     440                 : void
     441            5839 : value_stack::
     442                 : push_int64(
     443                 :     int64_t i)
     444                 : {
     445            5839 :     st_.push(i, sp_);
     446            5839 : }
     447                 : 
     448                 : void
     449              70 : value_stack::
     450                 : push_uint64(
     451                 :     uint64_t u)
     452                 : {
     453              70 :     st_.push(u, sp_);
     454              70 : }
     455                 : 
     456                 : void
     457         2039817 : value_stack::
     458                 : push_double(
     459                 :     double d)
     460                 : {
     461         2039817 :     st_.push(d, sp_);
     462         2039817 : }
     463                 : 
     464                 : void
     465             400 : value_stack::
     466                 : push_bool(
     467                 :     bool b)
     468                 : {
     469             400 :     st_.push(b, sp_);
     470             400 : }
     471                 : 
     472                 : void
     473            9369 : value_stack::
     474                 : push_null()
     475                 : {
     476            9369 :     st_.push(nullptr, sp_);
     477            9369 : }
     478                 : 
     479                 : } // namespace json
     480                 : } // namespace boost
     481                 : 
     482                 : #endif
        

Generated by: LCOV version 2.3