LCOV - code coverage report
Current view: top level - json/impl - pointer.ipp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 247 247
Test Date: 2026-07-17 10:23:07 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         160 :     pointer_token(
      26                 :         string_view sv) noexcept
      27             160 :         : b_( sv.begin() + 1 )
      28             160 :         , e_( sv.end() )
      29                 :     {
      30             160 :         BOOST_ASSERT( !sv.empty() );
      31             160 :         BOOST_ASSERT( *sv.data() == '/' );
      32             160 :     }
      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             580 :     explicit iterator(char const* base) noexcept
      52             580 :         : base_(base)
      53                 :     {
      54             580 :     }
      55                 : 
      56             631 :     char operator*() const noexcept
      57                 :     {
      58             631 :         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             629 :         default:
      67             629 :             return c;
      68                 :         }
      69                 :     }
      70                 : 
      71             833 :     iterator& operator++() noexcept
      72                 :     {
      73             833 :         if( '~' == *base_ )
      74               2 :             base_ += 2;
      75                 :         else
      76             831 :             ++base_;
      77             833 :         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            1694 :     char const* base() const noexcept
      88                 :     {
      89            1694 :         return base_;
      90                 :     }
      91                 : 
      92                 : private:
      93                 :     char const* base_;
      94                 : };
      95                 : 
      96             627 : bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
      97                 : {
      98             627 :     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             290 : pointer_token::iterator pointer_token::begin() const noexcept
     107                 : {
     108             290 :     return iterator(b_);
     109                 : }
     110                 : 
     111             290 : pointer_token::iterator pointer_token::end() const noexcept
     112                 : {
     113             290 :     return iterator(e_);
     114                 : }
     115                 : 
     116             251 : bool operator==(pointer_token token, string_view sv) noexcept
     117                 : {
     118             251 :     auto t_b = token.begin();
     119             251 :     auto const t_e = token.end();
     120             251 :     auto s_b = sv.begin();
     121             251 :     auto const s_e = sv.end();
     122             627 :     while( s_b != s_e )
     123                 :     {
     124             480 :         if( t_e == t_b )
     125               4 :             return false;
     126             476 :         if( *t_b != *s_b )
     127             100 :             return false;
     128             376 :         ++t_b;
     129             376 :         ++s_b;
     130                 :     }
     131             147 :     return t_b == t_e;
     132                 : }
     133                 : 
     134              74 : 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              74 :     if( *b != '0' )
     140              58 :         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              71 : bool is_past_the_end_token(
     153                 :     char const* b,
     154                 :     char const* e) noexcept
     155                 : {
     156              71 :     if( *b != '-' )
     157              59 :         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              77 : parse_number_token(
     166                 :     string_view sv,
     167                 :     system::error_code& ec) noexcept
     168                 : {
     169              77 :     BOOST_ASSERT( !sv.empty() );
     170                 : 
     171              77 :     char const* b = sv.begin();
     172              77 :     BOOST_ASSERT( *b == '/' );
     173                 : 
     174              77 :     ++b;
     175              77 :     char const* const e = sv.end();
     176              77 :     if( ( b == e )
     177              77 :         || is_invalid_zero(b, e) )
     178                 :     {
     179               6 :         BOOST_JSON_FAIL(ec, error::token_not_number);
     180               6 :         return {};
     181                 :     }
     182                 : 
     183              71 :     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              61 :     std::size_t result = 0;
     191             175 :     for( ; b != e; ++b )
     192                 :     {
     193             146 :         char const c = *b;
     194             146 :         BOOST_ASSERT( c != '/' );
     195                 : 
     196             146 :         unsigned d = c - '0';
     197             146 :         if( d > 9 )
     198                 :         {
     199              28 :             BOOST_JSON_FAIL(ec, error::token_not_number);
     200              28 :             return {};
     201                 :         }
     202                 : 
     203             118 :         if( result > std::size_t(-1) / 10 )
     204                 :         {
     205               2 :             BOOST_JSON_FAIL(ec, error::token_overflow);
     206               2 :             return {};
     207                 :         }
     208             116 :         result *= 10;
     209                 : 
     210             116 :         if( result > std::size_t(-1) - d )
     211                 :         {
     212               2 :             BOOST_JSON_FAIL(ec, error::token_overflow);
     213               2 :             return {};
     214                 :         }
     215             114 :         result += d;
     216                 :     }
     217              29 :     return result;
     218                 : }
     219                 : 
     220                 : string_view
     221             350 : next_segment(
     222                 :     string_view& sv,
     223                 :     system::error_code& ec) noexcept
     224                 : {
     225             350 :     if( sv.empty() )
     226             110 :         return sv;
     227                 : 
     228             240 :     char const* const start = sv.begin();
     229             240 :     char const* b = start;
     230             240 :     if( *b++ != '/' )
     231                 :     {
     232               5 :         BOOST_JSON_FAIL( ec, error::missing_slash );
     233               5 :         return {};
     234                 :     }
     235                 : 
     236             235 :     char const* e = sv.end();
     237             820 :     for( ; b < e; ++b )
     238                 :     {
     239             705 :         char const c = *b;
     240             705 :         if( '/' == c )
     241             114 :             break;
     242                 : 
     243             591 :         if( '~' == c )
     244                 :         {
     245               8 :             if( ++b == e )
     246                 :             {
     247               3 :                 BOOST_JSON_FAIL( ec, error::invalid_escape );
     248               3 :                 break;
     249                 :             }
     250                 : 
     251               5 :             switch (*b)
     252                 :             {
     253               2 :             case '0': // fall through
     254                 :             case '1':
     255                 :                 // valid escape sequence
     256               2 :                 continue;
     257               3 :             default: {
     258               3 :                 BOOST_JSON_FAIL( ec, error::invalid_escape );
     259               3 :                 break;
     260                 :             }
     261               2 :             }
     262               3 :             break;
     263                 :         }
     264                 :     }
     265                 : 
     266             235 :     sv.remove_prefix( b - start );
     267             235 :     return string_view( start, b );
     268                 : }
     269                 : 
     270                 : value*
     271             127 : if_contains_token(object const& obj, pointer_token token)
     272                 : {
     273             127 :     if( obj.empty() )
     274               2 :         return nullptr;
     275                 : 
     276             125 :     auto const it = detail::find_in_object(obj, token).first;
     277             125 :     if( !it )
     278               5 :         return nullptr;
     279                 : 
     280             120 :     return &it->value();
     281                 : }
     282                 : 
     283                 : template<
     284                 :     class Value,
     285                 :     class OnObject,
     286                 :     class OnArray,
     287                 :     class OnScalar >
     288                 : Value*
     289             131 : walk_pointer(
     290                 :     Value& jv,
     291                 :     string_view sv,
     292                 :     system::error_code& ec,
     293                 :     OnObject on_object,
     294                 :     OnArray on_array,
     295                 :     OnScalar on_scalar)
     296                 : {
     297             131 :     ec.clear();
     298                 : 
     299             131 :     string_view segment = detail::next_segment( sv, ec );
     300                 : 
     301             131 :     Value* result = &jv;
     302             248 :     while( true )
     303                 :     {
     304             379 :         if( ec.failed() )
     305              45 :             return nullptr;
     306                 : 
     307             334 :         if( !result )
     308                 :         {
     309              12 :             BOOST_JSON_FAIL(ec, error::not_found);
     310              12 :             return nullptr;
     311                 :         }
     312                 : 
     313             322 :         if( segment.empty() )
     314              74 :             break;
     315                 : 
     316             248 :         switch( result->kind() )
     317                 :         {
     318             160 :         case kind::object: {
     319             160 :             auto& obj = result->get_object();
     320                 : 
     321             160 :             detail::pointer_token const token( segment );
     322             160 :             segment = detail::next_segment( sv, ec );
     323                 : 
     324             160 :             result = on_object( obj, token );
     325             160 :             break;
     326                 :         }
     327              59 :         case kind::array: {
     328              59 :             auto const index = detail::parse_number_token( segment, ec );
     329              59 :             segment = detail::next_segment( sv, ec );
     330                 : 
     331              59 :             auto& arr = result->get_array();
     332              59 :             result = on_array( arr, index, ec );
     333              59 :             break;
     334                 :         }
     335              29 :         default: {
     336              29 :             if( on_scalar( *result, segment ) )
     337              21 :                 break;
     338               8 :             BOOST_JSON_FAIL( ec, error::value_is_scalar );
     339                 :         }}
     340                 :     }
     341                 : 
     342              74 :     BOOST_ASSERT( result );
     343              74 :     return result;
     344                 : }
     345                 : 
     346                 : } // namespace detail
     347                 : 
     348                 : value const&
     349              56 : value::at_pointer(string_view ptr, source_location const& loc) const&
     350                 : {
     351              56 :     return try_at_pointer(ptr).value(loc);
     352                 : }
     353                 : 
     354                 : system::result<value const&>
     355              58 : value::try_at_pointer(string_view ptr) const noexcept
     356                 : {
     357              58 :     system::error_code ec;
     358              58 :     auto const found = find_pointer(ptr, ec);
     359              58 :     if( !found )
     360              10 :         return ec;
     361              48 :     return *found;
     362                 : }
     363                 : 
     364                 : system::result<value&>
     365               2 : value::try_at_pointer(string_view ptr) noexcept
     366                 : {
     367               2 :     system::error_code ec;
     368               2 :     auto const found = find_pointer(ptr, ec);
     369               2 :     if( !found )
     370               1 :         return ec;
     371               1 :     return *found;
     372                 : }
     373                 : 
     374                 : value const*
     375             103 : value::find_pointer( string_view sv, system::error_code& ec ) const noexcept
     376                 : {
     377             103 :     return detail::walk_pointer(
     378                 :         *this,
     379                 :         sv,
     380                 :         ec,
     381             127 :         []( object const& obj, detail::pointer_token token )
     382                 :         {
     383             127 :             return detail::if_contains_token(obj, token);
     384                 :         },
     385              39 :         []( array const& arr, std::size_t index, system::error_code& ec )
     386                 :             -> value const*
     387                 :         {
     388              39 :             if( ec )
     389              24 :                 return nullptr;
     390                 : 
     391              15 :             return arr.if_contains(index);
     392                 :         },
     393               5 :         []( value const&, string_view)
     394                 :         {
     395               5 :             return std::false_type();
     396             103 :         });
     397                 : }
     398                 : 
     399                 : value*
     400              23 : value::find_pointer(string_view ptr, system::error_code& ec) noexcept
     401                 : {
     402              23 :     value const& self = *this;
     403              23 :     return const_cast<value*>(self.find_pointer(ptr, ec));
     404                 : }
     405                 : 
     406                 : value const*
     407              21 : value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
     408                 : {
     409              21 :     system::error_code jec;
     410              21 :     value const* result = find_pointer(ptr, jec);
     411              21 :     ec = jec;
     412              21 :     return result;
     413                 : }
     414                 : 
     415                 : value*
     416              20 : value::find_pointer(string_view ptr, std::error_code& ec) noexcept
     417                 : {
     418              20 :     value const& self = *this;
     419              20 :     return const_cast<value*>(self.find_pointer(ptr, ec));
     420                 : }
     421                 : 
     422                 : value*
     423              28 : value::set_at_pointer(
     424                 :     string_view sv,
     425                 :     value_ref ref,
     426                 :     system::error_code& ec,
     427                 :     set_pointer_options const& opts )
     428                 : {
     429              28 :     value* result = detail::walk_pointer(
     430                 :         *this,
     431                 :         sv,
     432                 :         ec,
     433              33 :         []( object& obj, detail::pointer_token token)
     434                 :         {
     435              33 :             if( !obj.empty() )
     436                 :             {
     437              13 :                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
     438              13 :                 if( kv )
     439              12 :                     return &kv->value();
     440                 :             }
     441                 : 
     442              21 :             string key( token.begin(), token.end(), obj.storage() );
     443              21 :             return &obj.emplace( std::move(key), nullptr ).first->value();
     444              21 :         },
     445              20 :         [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
     446                 :         {
     447              20 :             if( ec == error::past_the_end )
     448               6 :                 index = arr.size();
     449              14 :             else if( ec.failed() )
     450               2 :                 return nullptr;
     451                 : 
     452              18 :             if( index >= arr.size() )
     453                 :             {
     454              13 :                 std::size_t const n = index - arr.size();
     455              13 :                 if( n >= opts.max_created_elements )
     456               3 :                     return nullptr;
     457                 : 
     458              10 :                 arr.resize( arr.size() + n + 1 );
     459                 :             }
     460                 : 
     461              15 :             ec.clear();
     462              15 :             return arr.data() + index;
     463                 :         },
     464              24 :         [ &opts ]( value& jv, string_view segment )
     465                 :         {
     466              24 :             if( jv.is_null() || opts.replace_any_scalar )
     467                 :             {
     468              21 :                 if( opts.create_arrays )
     469                 :                 {
     470              18 :                     system::error_code ec;
     471              18 :                     detail::parse_number_token( segment, ec );
     472              18 :                     if( !ec.failed() || ec == error::past_the_end )
     473                 :                     {
     474               2 :                         jv = array( jv.storage() );
     475               2 :                         return true;
     476                 :                     }
     477                 :                 }
     478                 : 
     479              19 :                 if( opts.create_objects )
     480                 :                 {
     481              19 :                     jv = object( jv.storage() );
     482              19 :                     return true;
     483                 :                 }
     484                 :             }
     485                 : 
     486               3 :             return false;
     487                 :         });
     488                 : 
     489              28 :     if( result )
     490              20 :         *result = ref.make_value( storage() );
     491              28 :     return result;
     492                 : }
     493                 : 
     494                 : value*
     495               5 : value::set_at_pointer(
     496                 :     string_view sv,
     497                 :     value_ref ref,
     498                 :     std::error_code& ec,
     499                 :     set_pointer_options const& opts )
     500                 : {
     501               5 :     system::error_code jec;
     502               5 :     value* result = set_at_pointer( sv, ref, jec, opts );
     503               5 :     ec = jec;
     504               5 :     return result;
     505                 : }
     506                 : 
     507                 : system::result<value&>
     508              18 : value::try_set_at_pointer(
     509                 :     string_view sv,
     510                 :     value_ref ref,
     511                 :     set_pointer_options const& opts )
     512                 : {
     513              18 :     system::error_code ec;
     514              18 :     value* result = set_at_pointer( sv, ref, ec, opts );
     515              18 :     if( result )
     516              16 :         return *result;
     517               2 :     return ec;
     518                 : }
     519                 : 
     520                 : value&
     521              17 : value::set_at_pointer(
     522                 :     string_view sv, value_ref ref, set_pointer_options const& opts )
     523                 : {
     524              17 :     return try_set_at_pointer(sv, ref, opts).value();
     525                 : }
     526                 : 
     527                 : } // namespace json
     528                 : } // namespace boost
     529                 : 
     530                 : #endif // BOOST_JSON_IMPL_POINTER_IPP
        

Generated by: LCOV version 2.3