LCOV - code coverage report
Current view: top level - json/impl - pointer.ipp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 80.3 % 304 244
Test Date: 2026-01-06 03:55:47 Functions: 97.2 % 36 35

            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          142 :     pointer_token(
      26              :         string_view sv) noexcept
      27          142 :         : b_( sv.begin() + 1 )
      28          142 :         , e_( sv.end() )
      29              :     {
      30          142 :         BOOST_ASSERT( !sv.empty() );
      31          142 :         BOOST_ASSERT( *sv.data() == '/' );
      32          142 :     }
      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          520 :     explicit iterator(char const* base) noexcept
      52          520 :         : base_(base)
      53              :     {
      54          520 :     }
      55              : 
      56          389 :     char operator*() const noexcept
      57              :     {
      58          389 :         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          387 :         default:
      67          387 :             return c;
      68              :         }
      69              :     }
      70              : 
      71          285 :     iterator& operator++() noexcept
      72              :     {
      73          285 :         if( '~' == *base_ )
      74            2 :             base_ += 2;
      75              :         else
      76          283 :             ++base_;
      77          285 :         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         1090 :     char const* base() const noexcept
      88              :     {
      89         1090 :         return base_;
      90              :     }
      91              : 
      92              : private:
      93              :     char const* base_;
      94              : };
      95              : 
      96          487 : bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
      97              : {
      98          487 :     return l.base() == r.base();
      99              : }
     100              : 
     101           58 : bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
     102              : {
     103           58 :     return l.base() != r.base();
     104              : }
     105              : 
     106          260 : pointer_token::iterator pointer_token::begin() const noexcept
     107              : {
     108          260 :     return iterator(b_);
     109              : }
     110              : 
     111          260 : pointer_token::iterator pointer_token::end() const noexcept
     112              : {
     113          260 :     return iterator(e_);
     114              : }
     115              : 
     116          237 : bool operator==(pointer_token token, string_view sv) noexcept
     117              : {
     118          237 :     auto t_b = token.begin();
     119          237 :     auto const t_e = token.end();
     120          237 :     auto s_b = sv.begin();
     121          237 :     auto const s_e = sv.end();
     122          487 :     while( s_b != s_e )
     123              :     {
     124          358 :         if( t_e == t_b )
     125            4 :             return false;
     126          354 :         if( *t_b != *s_b )
     127          104 :             return false;
     128          250 :         ++t_b;
     129          250 :         ++s_b;
     130              :     }
     131          129 :     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          312 : next_segment(
     218              :     string_view& sv,
     219              :     system::error_code& ec) noexcept
     220              : {
     221          312 :     if( sv.empty() )
     222           92 :         return sv;
     223              : 
     224          220 :     char const* const start = sv.begin();
     225          220 :     char const* b = start;
     226          220 :     if( *b++ != '/' )
     227              :     {
     228            5 :         BOOST_JSON_FAIL( ec, error::missing_slash );
     229            5 :         return {};
     230              :     }
     231              : 
     232          215 :     char const* e = sv.end();
     233          632 :     for( ; b < e; ++b )
     234              :     {
     235          535 :         char const c = *b;
     236          535 :         if( '/' == c )
     237          112 :             break;
     238              : 
     239          423 :         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          215 :     sv.remove_prefix( b - start );
     263          215 :     return string_view( start, b );
     264              : }
     265              : 
     266              : value*
     267          109 : if_contains_token(object const& obj, pointer_token token)
     268              : {
     269          109 :     if( obj.empty() )
     270            2 :         return nullptr;
     271              : 
     272          107 :     auto const it = detail::find_in_object(obj, token).first;
     273          107 :     if( !it )
     274            5 :         return nullptr;
     275              : 
     276          102 :     return &it->value();
     277              : }
     278              : 
     279              : template<
     280              :     class Value,
     281              :     class OnObject,
     282              :     class OnArray,
     283              :     class OnScalar >
     284              : Value*
     285          113 : 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          113 :     ec.clear();
     294              : 
     295          113 :     string_view segment = detail::next_segment( sv, ec );
     296              : 
     297          113 :     Value* result = &jv;
     298          228 :     while( true )
     299              :     {
     300          341 :         if( ec.failed() )
     301           43 :             return nullptr;
     302              : 
     303          298 :         if( !result )
     304              :         {
     305           12 :             BOOST_JSON_FAIL(ec, error::not_found);
     306           12 :             return nullptr;
     307              :         }
     308              : 
     309          286 :         if( segment.empty() )
     310           58 :             break;
     311              : 
     312          228 :         switch( result->kind() )
     313              :         {
     314          142 :         case kind::object: {
     315          142 :             auto& obj = result->get_object();
     316              : 
     317          142 :             detail::pointer_token const token( segment );
     318          142 :             segment = detail::next_segment( sv, ec );
     319              : 
     320          142 :             result = on_object( obj, token );
     321          142 :             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           58 :     BOOST_ASSERT( result );
     339           58 :     return result;
     340              : }
     341              : 
     342              : } // namespace detail
     343              : 
     344              : value const&
     345           40 : value::at_pointer(string_view ptr, source_location const& loc) const&
     346              : {
     347           40 :     return try_at_pointer(ptr).value(loc);
     348              : }
     349              : 
     350              : system::result<value const&>
     351           42 : value::try_at_pointer(string_view ptr) const noexcept
     352              : {
     353           42 :     system::error_code ec;
     354           42 :     auto const found = find_pointer(ptr, ec);
     355           42 :     if( !found )
     356           10 :         return ec;
     357           32 :     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           85 : value::find_pointer( string_view sv, system::error_code& ec ) const noexcept
     372              : {
     373           85 :     return detail::walk_pointer(
     374              :         *this,
     375              :         sv,
     376              :         ec,
     377          109 :         []( object const& obj, detail::pointer_token token )
     378              :         {
     379          109 :             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           85 :         });
     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              : bool
     419            0 : value::erase_at_pointer(
     420              :     string_view         sv,
     421              :     system::error_code& ec) 
     422              : {
     423            0 :     ec.clear();
     424            0 :     if(sv.empty()){
     425            0 :         BOOST_JSON_FAIL(ec, error::syntax);
     426            0 :         return false;
     427              :     }
     428              :     
     429            0 :     string_view previous_segment;
     430              :     
     431            0 :     string_view segment = detail::next_segment(sv, ec);
     432              :     
     433            0 :     auto result = this;
     434            0 :     auto previous_result = this;
     435              :     
     436              :     while (true) 
     437              :     {
     438            0 :         if (ec.failed())
     439            0 :             return false;
     440              : 
     441            0 :         if (!result) 
     442              :         {
     443            0 :             BOOST_JSON_FAIL(ec, error::not_found);
     444            0 :             return false;
     445              :         }
     446              : 
     447            0 :         if( segment.empty() )
     448            0 :             break;
     449              :         
     450            0 :         previous_segment = segment;
     451            0 :         previous_result = result;
     452              :         
     453            0 :         switch (result->kind()) 
     454              :         {
     455            0 :             case boost::json::kind::object: {
     456            0 :                 auto& obj = result->get_object();
     457              : 
     458            0 :                 detail::pointer_token const token(segment);
     459            0 :                 segment = detail::next_segment(sv, ec);
     460              : 
     461            0 :                 result = detail::if_contains_token(obj, token);
     462            0 :                 if( !result )
     463              :                 {
     464            0 :                     BOOST_JSON_FAIL(ec, error::not_found);
     465            0 :                     return false;
     466              :                 }
     467            0 :                 break;
     468              :             }
     469            0 :             case boost::json::kind::array: {
     470            0 :                 auto const index = detail::parse_number_token(segment, ec);
     471            0 :                 segment          = detail::next_segment(sv, ec);
     472              : 
     473            0 :                 auto& arr = result->get_array();
     474            0 :                 result    = arr.if_contains(index);
     475            0 :                 if( !result )
     476              :                 {
     477            0 :                     BOOST_JSON_FAIL(ec, error::past_the_end);
     478            0 :                     return false;
     479              :                 }
     480            0 :                 break;
     481              :             }
     482            0 :             default: {
     483            0 :                 BOOST_JSON_FAIL(ec, error::value_is_scalar);
     484            0 :                 return false;
     485              :             }
     486              :         }
     487            0 :     }
     488              :     
     489            0 :     switch (previous_result->kind()) 
     490              :     {
     491            0 :         case boost::json::kind::object: {
     492            0 :             auto& obj = previous_result->get_object();
     493            0 :             detail::pointer_token const token(previous_segment);
     494            0 :             key_value_pair* kv = detail::find_in_object(obj, token).first;
     495            0 :             if (kv) {
     496            0 :                 obj.erase(kv);
     497            0 :                 return true;
     498              :             }
     499            0 :             return false;
     500              :         }
     501            0 :         case boost::json::kind::array: {
     502            0 :             auto const index = detail::parse_number_token(previous_segment, ec);
     503            0 :             auto& arr = previous_result->get_array();
     504            0 :             if (arr.if_contains(index)){
     505            0 :                 arr.erase(arr.begin() + index);
     506            0 :                 return true;
     507              :             }
     508            0 :             return false;
     509              :         }
     510            0 :         default: {
     511            0 :             BOOST_JSON_FAIL(ec, error::value_is_scalar);
     512            0 :             return false;
     513              :         }
     514              :     }
     515              : }
     516              : 
     517              : value*
     518           28 : value::set_at_pointer(
     519              :     string_view sv,
     520              :     value_ref ref,
     521              :     system::error_code& ec,
     522              :     set_pointer_options const& opts )
     523              : {
     524           28 :     value* result = detail::walk_pointer(
     525              :         *this,
     526              :         sv,
     527              :         ec,
     528           33 :         []( object& obj, detail::pointer_token token)
     529              :         {
     530           33 :             if( !obj.empty() )
     531              :             {
     532           13 :                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
     533           13 :                 if( kv )
     534           12 :                     return &kv->value();
     535              :             }
     536              : 
     537           21 :             string key( token.begin(), token.end(), obj.storage() );
     538           21 :             return &obj.emplace( std::move(key), nullptr ).first->value();
     539           21 :         },
     540           20 :         [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
     541              :         {
     542           20 :             if( ec == error::past_the_end )
     543            6 :                 index = arr.size();
     544           14 :             else if( ec.failed() )
     545            2 :                 return nullptr;
     546              : 
     547           18 :             if( index >= arr.size() )
     548              :             {
     549           13 :                 std::size_t const n = index - arr.size();
     550           13 :                 if( n >= opts.max_created_elements )
     551            3 :                     return nullptr;
     552              : 
     553           10 :                 arr.resize( arr.size() + n + 1 );
     554              :             }
     555              : 
     556           15 :             ec.clear();
     557           15 :             return arr.data() + index;
     558              :         },
     559           24 :         [ &opts ]( value& jv, string_view segment )
     560              :         {
     561           24 :             if( jv.is_null() || opts.replace_any_scalar )
     562              :             {
     563           21 :                 if( opts.create_arrays )
     564              :                 {
     565           18 :                     system::error_code ec;
     566           18 :                     detail::parse_number_token( segment, ec );
     567           18 :                     if( !ec.failed() || ec == error::past_the_end )
     568              :                     {
     569            2 :                         jv = array( jv.storage() );
     570            2 :                         return true;
     571              :                     }
     572              :                 }
     573              : 
     574           19 :                 if( opts.create_objects )
     575              :                 {
     576           19 :                     jv = object( jv.storage() );
     577           19 :                     return true;
     578              :                 }
     579              :             }
     580              : 
     581            3 :             return false;
     582              :         });
     583              : 
     584           28 :     if( result )
     585           20 :         *result = ref.make_value( storage() );
     586           28 :     return result;
     587              : }
     588              : 
     589              : value*
     590            5 : value::set_at_pointer(
     591              :     string_view sv,
     592              :     value_ref ref,
     593              :     std::error_code& ec,
     594              :     set_pointer_options const& opts )
     595              : {
     596            5 :     system::error_code jec;
     597            5 :     value* result = set_at_pointer( sv, ref, jec, opts );
     598            5 :     ec = jec;
     599            5 :     return result;
     600              : }
     601              : 
     602              : system::result<value&>
     603           18 : value::try_set_at_pointer(
     604              :     string_view sv,
     605              :     value_ref ref,
     606              :     set_pointer_options const& opts )
     607              : {
     608           18 :     system::error_code ec;
     609           18 :     value* result = set_at_pointer( sv, ref, ec, opts );
     610           18 :     if( result )
     611           16 :         return *result;
     612            2 :     return ec;
     613              : }
     614              : 
     615              : value&
     616           17 : value::set_at_pointer(
     617              :     string_view sv, value_ref ref, set_pointer_options const& opts )
     618              : {
     619           17 :     return try_set_at_pointer(sv, ref, opts).value();
     620              : }
     621              : 
     622              : } // namespace json
     623              : } // namespace boost
     624              : 
     625              : #endif // BOOST_JSON_IMPL_POINTER_IPP
        

Generated by: LCOV version 2.1