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

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2022 Dmitry Arkhipov (grisumbras@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_POINTER_IPP
      11                 : #define BOOST_JSON_IMPL_POINTER_IPP
      12                 : 
      13                 : #include <boost/json/value.hpp>
      14                 : 
      15                 : namespace boost {
      16                 : namespace json {
      17                 : 
      18                 : namespace detail {
      19                 : 
      20                 : class pointer_token
      21                 : {
      22                 : public:
      23                 :     class iterator;
      24                 : 
      25 HIT         158 :     pointer_token(
      26                 :         string_view sv) noexcept
      27             158 :         : b_( sv.begin() + 1 )
      28             158 :         , e_( sv.end() )
      29                 :     {
      30             158 :         BOOST_ASSERT( !sv.empty() );
      31             158 :         BOOST_ASSERT( *sv.data() == '/' );
      32             158 :     }
      33                 : 
      34                 :     iterator begin() const noexcept;
      35                 :     iterator end() const noexcept;
      36                 : 
      37                 : private:
      38                 :     char const* b_;
      39                 :     char const* e_;
      40                 : };
      41                 : 
      42                 : class pointer_token::iterator
      43                 : {
      44                 : public:
      45                 :     using value_type = char;
      46                 :     using reference = char;
      47                 :     using pointer = value_type*;
      48                 :     using difference_type = std::ptrdiff_t;
      49                 :     using iterator_category = std::forward_iterator_tag;
      50                 : 
      51             576 :     explicit iterator(char const* base) noexcept
      52             576 :         : base_(base)
      53                 :     {
      54             576 :     }
      55                 : 
      56             625 :     char operator*() const noexcept
      57                 :     {
      58             625 :         switch( char c = *base_ )
      59                 :         {
      60               2 :         case '~':
      61               2 :             c = base_[1];
      62               2 :             if( '0' == c )
      63               1 :                 return '~';
      64               1 :             BOOST_ASSERT('1' == c);
      65               1 :             return '/';
      66             623 :         default:
      67             623 :             return c;
      68                 :         }
      69                 :     }
      70                 : 
      71             827 :     iterator& operator++() noexcept
      72                 :     {
      73             827 :         if( '~' == *base_ )
      74               2 :             base_ += 2;
      75                 :         else
      76             825 :             ++base_;
      77             827 :         return *this;
      78                 :     }
      79                 : 
      80              29 :     iterator operator++(int) noexcept
      81                 :     {
      82              29 :         iterator result = *this;
      83              29 :         ++(*this);
      84              29 :         return result;
      85                 :     }
      86                 : 
      87            1678 :     char const* base() const noexcept
      88                 :     {
      89            1678 :         return base_;
      90                 :     }
      91                 : 
      92                 : private:
      93                 :     char const* base_;
      94                 : };
      95                 : 
      96             619 : bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
      97                 : {
      98             619 :     return l.base() == r.base();
      99                 : }
     100                 : 
     101             220 : bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
     102                 : {
     103             220 :     return l.base() != r.base();
     104                 : }
     105                 : 
     106             288 : pointer_token::iterator pointer_token::begin() const noexcept
     107                 : {
     108             288 :     return iterator(b_);
     109                 : }
     110                 : 
     111             288 : pointer_token::iterator pointer_token::end() const noexcept
     112                 : {
     113             288 :     return iterator(e_);
     114                 : }
     115                 : 
     116             249 : bool operator==(pointer_token token, string_view sv) noexcept
     117                 : {
     118             249 :     auto t_b = token.begin();
     119             249 :     auto const t_e = token.end();
     120             249 :     auto s_b = sv.begin();
     121             249 :     auto const s_e = sv.end();
     122             619 :     while( s_b != s_e )
     123                 :     {
     124             474 :         if( t_e == t_b )
     125               4 :             return false;
     126             470 :         if( *t_b != *s_b )
     127             100 :             return false;
     128             370 :         ++t_b;
     129             370 :         ++s_b;
     130                 :     }
     131             145 :     return t_b == t_e;
     132                 : }
     133                 : 
     134              72 : bool is_invalid_zero(
     135                 :     char const* b,
     136                 :     char const* e) noexcept
     137                 : {
     138                 :     // in JSON Pointer only zero index can start character '0'
     139              72 :     if( *b != '0' )
     140              56 :         return false;
     141                 : 
     142                 :     // if an index token starts with '0', then it should not have any more
     143                 :     // characters: either the string should end, or new token should start
     144              16 :     ++b;
     145              16 :     if( b == e )
     146              13 :         return false;
     147                 : 
     148               3 :     BOOST_ASSERT( *b != '/' );
     149               3 :     return true;
     150                 : }
     151                 : 
     152              69 : bool is_past_the_end_token(
     153                 :     char const* b,
     154                 :     char const* e) noexcept
     155                 : {
     156              69 :     if( *b != '-' )
     157              57 :         return false;
     158                 : 
     159              12 :     ++b;
     160              12 :     BOOST_ASSERT( (b == e) || (*b != '/') );
     161              12 :     return b == e;
     162                 : }
     163                 : 
     164                 : std::size_t
     165              75 : parse_number_token(
     166                 :     string_view sv,
     167                 :     system::error_code& ec) noexcept
     168                 : {
     169              75 :     BOOST_ASSERT( !sv.empty() );
     170                 : 
     171              75 :     char const* b = sv.begin();
     172              75 :     BOOST_ASSERT( *b == '/' );
     173                 : 
     174              75 :     ++b;
     175              75 :     char const* const e = sv.end();
     176              75 :     if( ( b == e )
     177              75 :         || is_invalid_zero(b, e) )
     178                 :     {
     179               6 :         BOOST_JSON_FAIL(ec, error::token_not_number);
     180               6 :         return {};
     181                 :     }
     182                 : 
     183              69 :     if( is_past_the_end_token(b, e) )
     184                 :     {
     185              10 :         ++b;
     186              10 :         BOOST_JSON_FAIL(ec, error::past_the_end);
     187              10 :         return {};
     188                 :     }
     189                 : 
     190              59 :     std::size_t result = 0;
     191             133 :     for( ; b != e; ++b )
     192                 :     {
     193             104 :         char const c = *b;
     194             104 :         BOOST_ASSERT( c != '/' );
     195                 : 
     196             104 :         unsigned d = c - '0';
     197             104 :         if( d > 9 )
     198                 :         {
     199              28 :             BOOST_JSON_FAIL(ec, error::token_not_number);
     200              28 :             return {};
     201                 :         }
     202                 : 
     203              76 :         std::size_t new_result = result * 10 + d;
     204              76 :         if( new_result < result )
     205                 :         {
     206               2 :             BOOST_JSON_FAIL(ec, error::token_overflow);
     207               2 :             return {};
     208                 :         }
     209                 : 
     210              74 :         result = new_result;
     211                 : 
     212                 :     }
     213              29 :     return result;
     214                 : }
     215                 : 
     216                 : string_view
     217             344 : next_segment(
     218                 :     string_view& sv,
     219                 :     system::error_code& ec) noexcept
     220                 : {
     221             344 :     if( sv.empty() )
     222             108 :         return sv;
     223                 : 
     224             236 :     char const* const start = sv.begin();
     225             236 :     char const* b = start;
     226             236 :     if( *b++ != '/' )
     227                 :     {
     228               5 :         BOOST_JSON_FAIL( ec, error::missing_slash );
     229               5 :         return {};
     230                 :     }
     231                 : 
     232             231 :     char const* e = sv.end();
     233             768 :     for( ; b < e; ++b )
     234                 :     {
     235             655 :         char const c = *b;
     236             655 :         if( '/' == c )
     237             112 :             break;
     238                 : 
     239             543 :         if( '~' == c )
     240                 :         {
     241               8 :             if( ++b == e )
     242                 :             {
     243               3 :                 BOOST_JSON_FAIL( ec, error::invalid_escape );
     244               3 :                 break;
     245                 :             }
     246                 : 
     247               5 :             switch (*b)
     248                 :             {
     249               2 :             case '0': // fall through
     250                 :             case '1':
     251                 :                 // valid escape sequence
     252               2 :                 continue;
     253               3 :             default: {
     254               3 :                 BOOST_JSON_FAIL( ec, error::invalid_escape );
     255               3 :                 break;
     256                 :             }
     257               2 :             }
     258               3 :             break;
     259                 :         }
     260                 :     }
     261                 : 
     262             231 :     sv.remove_prefix( b - start );
     263             231 :     return string_view( start, b );
     264                 : }
     265                 : 
     266                 : value*
     267             125 : if_contains_token(object const& obj, pointer_token token)
     268                 : {
     269             125 :     if( obj.empty() )
     270               2 :         return nullptr;
     271                 : 
     272             123 :     auto const it = detail::find_in_object(obj, token).first;
     273             123 :     if( !it )
     274               5 :         return nullptr;
     275                 : 
     276             118 :     return &it->value();
     277                 : }
     278                 : 
     279                 : template<
     280                 :     class Value,
     281                 :     class OnObject,
     282                 :     class OnArray,
     283                 :     class OnScalar >
     284                 : Value*
     285             129 : walk_pointer(
     286                 :     Value& jv,
     287                 :     string_view sv,
     288                 :     system::error_code& ec,
     289                 :     OnObject on_object,
     290                 :     OnArray on_array,
     291                 :     OnScalar on_scalar)
     292                 : {
     293             129 :     ec.clear();
     294                 : 
     295             129 :     string_view segment = detail::next_segment( sv, ec );
     296                 : 
     297             129 :     Value* result = &jv;
     298             244 :     while( true )
     299                 :     {
     300             373 :         if( ec.failed() )
     301              43 :             return nullptr;
     302                 : 
     303             330 :         if( !result )
     304                 :         {
     305              12 :             BOOST_JSON_FAIL(ec, error::not_found);
     306              12 :             return nullptr;
     307                 :         }
     308                 : 
     309             318 :         if( segment.empty() )
     310              74 :             break;
     311                 : 
     312             244 :         switch( result->kind() )
     313                 :         {
     314             158 :         case kind::object: {
     315             158 :             auto& obj = result->get_object();
     316                 : 
     317             158 :             detail::pointer_token const token( segment );
     318             158 :             segment = detail::next_segment( sv, ec );
     319                 : 
     320             158 :             result = on_object( obj, token );
     321             158 :             break;
     322                 :         }
     323              57 :         case kind::array: {
     324              57 :             auto const index = detail::parse_number_token( segment, ec );
     325              57 :             segment = detail::next_segment( sv, ec );
     326                 : 
     327              57 :             auto& arr = result->get_array();
     328              57 :             result = on_array( arr, index, ec );
     329              57 :             break;
     330                 :         }
     331              29 :         default: {
     332              29 :             if( on_scalar( *result, segment ) )
     333              21 :                 break;
     334               8 :             BOOST_JSON_FAIL( ec, error::value_is_scalar );
     335                 :         }}
     336                 :     }
     337                 : 
     338              74 :     BOOST_ASSERT( result );
     339              74 :     return result;
     340                 : }
     341                 : 
     342                 : } // namespace detail
     343                 : 
     344                 : value const&
     345              56 : value::at_pointer(string_view ptr, source_location const& loc) const&
     346                 : {
     347              56 :     return try_at_pointer(ptr).value(loc);
     348                 : }
     349                 : 
     350                 : system::result<value const&>
     351              58 : value::try_at_pointer(string_view ptr) const noexcept
     352                 : {
     353              58 :     system::error_code ec;
     354              58 :     auto const found = find_pointer(ptr, ec);
     355              58 :     if( !found )
     356              10 :         return ec;
     357              48 :     return *found;
     358                 : }
     359                 : 
     360                 : system::result<value&>
     361               2 : value::try_at_pointer(string_view ptr) noexcept
     362                 : {
     363               2 :     system::error_code ec;
     364               2 :     auto const found = find_pointer(ptr, ec);
     365               2 :     if( !found )
     366               1 :         return ec;
     367               1 :     return *found;
     368                 : }
     369                 : 
     370                 : value const*
     371             101 : value::find_pointer( string_view sv, system::error_code& ec ) const noexcept
     372                 : {
     373             101 :     return detail::walk_pointer(
     374                 :         *this,
     375                 :         sv,
     376                 :         ec,
     377             125 :         []( object const& obj, detail::pointer_token token )
     378                 :         {
     379             125 :             return detail::if_contains_token(obj, token);
     380                 :         },
     381              37 :         []( array const& arr, std::size_t index, system::error_code& ec )
     382                 :             -> value const*
     383                 :         {
     384              37 :             if( ec )
     385              22 :                 return nullptr;
     386                 : 
     387              15 :             return arr.if_contains(index);
     388                 :         },
     389               5 :         []( value const&, string_view)
     390                 :         {
     391               5 :             return std::false_type();
     392             101 :         });
     393                 : }
     394                 : 
     395                 : value*
     396              22 : value::find_pointer(string_view ptr, system::error_code& ec) noexcept
     397                 : {
     398              22 :     value const& self = *this;
     399              22 :     return const_cast<value*>(self.find_pointer(ptr, ec));
     400                 : }
     401                 : 
     402                 : value const*
     403              20 : value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
     404                 : {
     405              20 :     system::error_code jec;
     406              20 :     value const* result = find_pointer(ptr, jec);
     407              20 :     ec = jec;
     408              20 :     return result;
     409                 : }
     410                 : 
     411                 : value*
     412              19 : value::find_pointer(string_view ptr, std::error_code& ec) noexcept
     413                 : {
     414              19 :     value const& self = *this;
     415              19 :     return const_cast<value*>(self.find_pointer(ptr, ec));
     416                 : }
     417                 : 
     418                 : value*
     419              28 : value::set_at_pointer(
     420                 :     string_view sv,
     421                 :     value_ref ref,
     422                 :     system::error_code& ec,
     423                 :     set_pointer_options const& opts )
     424                 : {
     425              28 :     value* result = detail::walk_pointer(
     426                 :         *this,
     427                 :         sv,
     428                 :         ec,
     429              33 :         []( object& obj, detail::pointer_token token)
     430                 :         {
     431              33 :             if( !obj.empty() )
     432                 :             {
     433              13 :                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
     434              13 :                 if( kv )
     435              12 :                     return &kv->value();
     436                 :             }
     437                 : 
     438              21 :             string key( token.begin(), token.end(), obj.storage() );
     439              21 :             return &obj.emplace( std::move(key), nullptr ).first->value();
     440              21 :         },
     441              20 :         [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
     442                 :         {
     443              20 :             if( ec == error::past_the_end )
     444               6 :                 index = arr.size();
     445              14 :             else if( ec.failed() )
     446               2 :                 return nullptr;
     447                 : 
     448              18 :             if( index >= arr.size() )
     449                 :             {
     450              13 :                 std::size_t const n = index - arr.size();
     451              13 :                 if( n >= opts.max_created_elements )
     452               3 :                     return nullptr;
     453                 : 
     454              10 :                 arr.resize( arr.size() + n + 1 );
     455                 :             }
     456                 : 
     457              15 :             ec.clear();
     458              15 :             return arr.data() + index;
     459                 :         },
     460              24 :         [ &opts ]( value& jv, string_view segment )
     461                 :         {
     462              24 :             if( jv.is_null() || opts.replace_any_scalar )
     463                 :             {
     464              21 :                 if( opts.create_arrays )
     465                 :                 {
     466              18 :                     system::error_code ec;
     467              18 :                     detail::parse_number_token( segment, ec );
     468              18 :                     if( !ec.failed() || ec == error::past_the_end )
     469                 :                     {
     470               2 :                         jv = array( jv.storage() );
     471               2 :                         return true;
     472                 :                     }
     473                 :                 }
     474                 : 
     475              19 :                 if( opts.create_objects )
     476                 :                 {
     477              19 :                     jv = object( jv.storage() );
     478              19 :                     return true;
     479                 :                 }
     480                 :             }
     481                 : 
     482               3 :             return false;
     483                 :         });
     484                 : 
     485              28 :     if( result )
     486              20 :         *result = ref.make_value( storage() );
     487              28 :     return result;
     488                 : }
     489                 : 
     490                 : value*
     491               5 : value::set_at_pointer(
     492                 :     string_view sv,
     493                 :     value_ref ref,
     494                 :     std::error_code& ec,
     495                 :     set_pointer_options const& opts )
     496                 : {
     497               5 :     system::error_code jec;
     498               5 :     value* result = set_at_pointer( sv, ref, jec, opts );
     499               5 :     ec = jec;
     500               5 :     return result;
     501                 : }
     502                 : 
     503                 : system::result<value&>
     504              18 : value::try_set_at_pointer(
     505                 :     string_view sv,
     506                 :     value_ref ref,
     507                 :     set_pointer_options const& opts )
     508                 : {
     509              18 :     system::error_code ec;
     510              18 :     value* result = set_at_pointer( sv, ref, ec, opts );
     511              18 :     if( result )
     512              16 :         return *result;
     513               2 :     return ec;
     514                 : }
     515                 : 
     516                 : value&
     517              17 : value::set_at_pointer(
     518                 :     string_view sv, value_ref ref, set_pointer_options const& opts )
     519                 : {
     520              17 :     return try_set_at_pointer(sv, ref, opts).value();
     521                 : }
     522                 : 
     523                 : } // namespace json
     524                 : } // namespace boost
     525                 : 
     526                 : #endif // BOOST_JSON_IMPL_POINTER_IPP
        

Generated by: LCOV version 2.3