LCOV - code coverage report
Current view: top level - json/impl - pointer.ipp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 79.5 % 307 244
Test Date: 2025-12-23 17:38:56 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          516 :     explicit iterator(char const* base) noexcept
      52          516 :         : base_(base)
      53              :     {
      54          516 :     }
      55              : 
      56          387 :     char operator*() const noexcept
      57              :     {
      58          387 :         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          385 :         default:
      67          385 :             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         1086 :     char const* base() const noexcept
      88              :     {
      89         1086 :         return base_;
      90              :     }
      91              : 
      92              : private:
      93              :     char const* base_;
      94              : };
      95              : 
      96          485 : bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
      97              : {
      98          485 :     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          258 : pointer_token::iterator pointer_token::begin() const noexcept
     107              : {
     108          258 :     return iterator(b_);
     109              : }
     110              : 
     111          258 : pointer_token::iterator pointer_token::end() const noexcept
     112              : {
     113          258 :     return iterator(e_);
     114              : }
     115              : 
     116          235 : bool operator==(pointer_token token, string_view sv) noexcept
     117              : {
     118          235 :     auto t_b = token.begin();
     119          235 :     auto const t_e = token.end();
     120          235 :     auto s_b = sv.begin();
     121          235 :     auto const s_e = sv.end();
     122          485 :     while( s_b != s_e )
     123              :     {
     124          356 :         if( t_e == t_b )
     125            4 :             return false;
     126          352 :         if( *t_b != *s_b )
     127          102 :             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              : std::pair<bool, string_view>
     419            0 : value::delete_at_pointer(
     420              :     string_view         sv,
     421              :     system::error_code& ec) 
     422              : {
     423            0 :     ec.clear();
     424              : 
     425              :     
     426            0 :     string_view previous_segment;
     427            0 :     string_view sv_copy = sv;
     428            0 :     string_view err_position;
     429            0 :     string_view segment = detail::next_segment(sv, ec);
     430            0 :     size_t shift = 0;
     431              :     
     432            0 :     auto result = this;
     433            0 :     auto previous_result = this;
     434              :     
     435              :     while (true) 
     436              :     {
     437            0 :         if (ec.failed())
     438            0 :             return {false, err_position};
     439              : 
     440            0 :         if (!result) 
     441              :         {
     442            0 :             BOOST_JSON_FAIL(ec, error::not_found);
     443            0 :             return {false, err_position};
     444              :         }
     445              : 
     446            0 :         if( segment.empty() )
     447            0 :             break;
     448              :         
     449            0 :         shift += segment.size();
     450            0 :         err_position = sv_copy.substr(0, shift);
     451              :         
     452            0 :         previous_segment = segment;
     453            0 :         previous_result = result;
     454              :         
     455            0 :         switch (result->kind()) 
     456              :         {
     457            0 :             case boost::json::kind::object: {
     458            0 :                 auto& obj = result->get_object();
     459              : 
     460            0 :                 detail::pointer_token const token(segment);
     461            0 :                 segment = detail::next_segment(sv, ec);
     462              : 
     463            0 :                 result = detail::if_contains_token(obj, token);
     464            0 :                 if( !result )
     465              :                 {
     466            0 :                     BOOST_JSON_FAIL(ec, error::not_found);
     467            0 :                     return {false, err_position};
     468              :                 }
     469            0 :                 break;
     470              :             }
     471            0 :             case boost::json::kind::array: {
     472            0 :                 auto const index = detail::parse_number_token(segment, ec);
     473            0 :                 segment          = detail::next_segment(sv, ec);
     474              : 
     475            0 :                 auto& arr = result->get_array();
     476            0 :                 result    = arr.if_contains(index);
     477            0 :                 if( !result )
     478              :                 {
     479            0 :                     BOOST_JSON_FAIL(ec, error::past_the_end);
     480            0 :                     return {false, err_position};
     481              :                 }
     482            0 :                 break;
     483              :             }
     484            0 :             default: {
     485            0 :                 BOOST_JSON_FAIL(ec, error::value_is_scalar);
     486            0 :                 return {false, err_position};
     487              :             }
     488              :         }
     489            0 :     }
     490              :     
     491            0 :     err_position = {};
     492              : 
     493            0 :     switch (previous_result->kind()) 
     494              :     {
     495            0 :         case boost::json::kind::object: {
     496            0 :             auto& obj = previous_result->get_object();
     497            0 :             detail::pointer_token const token(previous_segment);
     498            0 :             key_value_pair* kv = detail::find_in_object(obj, token).first;
     499            0 :             if (kv) {
     500            0 :                 obj.erase(kv);
     501            0 :                 return {true, err_position};
     502              :             }
     503            0 :             return {false,err_position};
     504              :         }
     505            0 :         case boost::json::kind::array: {
     506            0 :             auto const index = detail::parse_number_token(previous_segment, ec);
     507            0 :             auto& arr = previous_result->get_array();
     508            0 :             if (arr.if_contains(index)){
     509            0 :                 arr.erase(arr.begin() + index);
     510            0 :                 return {true, err_position};
     511              :             }
     512            0 :             return {false,err_position};
     513              :         }
     514            0 :         default: {
     515            0 :             BOOST_JSON_FAIL(ec, error::value_is_scalar);
     516            0 :             return {false, err_position};
     517              :         }
     518              :     }
     519              : }
     520              : 
     521              : value*
     522           28 : value::set_at_pointer(
     523              :     string_view sv,
     524              :     value_ref ref,
     525              :     system::error_code& ec,
     526              :     set_pointer_options const& opts )
     527              : {
     528           28 :     value* result = detail::walk_pointer(
     529              :         *this,
     530              :         sv,
     531              :         ec,
     532           33 :         []( object& obj, detail::pointer_token token)
     533              :         {
     534           33 :             if( !obj.empty() )
     535              :             {
     536           13 :                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
     537           13 :                 if( kv )
     538           12 :                     return &kv->value();
     539              :             }
     540              : 
     541           21 :             string key( token.begin(), token.end(), obj.storage() );
     542           21 :             return &obj.emplace( std::move(key), nullptr ).first->value();
     543           21 :         },
     544           20 :         [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
     545              :         {
     546           20 :             if( ec == error::past_the_end )
     547            6 :                 index = arr.size();
     548           14 :             else if( ec.failed() )
     549            2 :                 return nullptr;
     550              : 
     551           18 :             if( index >= arr.size() )
     552              :             {
     553           13 :                 std::size_t const n = index - arr.size();
     554           13 :                 if( n >= opts.max_created_elements )
     555            3 :                     return nullptr;
     556              : 
     557           10 :                 arr.resize( arr.size() + n + 1 );
     558              :             }
     559              : 
     560           15 :             ec.clear();
     561           15 :             return arr.data() + index;
     562              :         },
     563           24 :         [ &opts ]( value& jv, string_view segment )
     564              :         {
     565           24 :             if( jv.is_null() || opts.replace_any_scalar )
     566              :             {
     567           21 :                 if( opts.create_arrays )
     568              :                 {
     569           18 :                     system::error_code ec;
     570           18 :                     detail::parse_number_token( segment, ec );
     571           18 :                     if( !ec.failed() || ec == error::past_the_end )
     572              :                     {
     573            2 :                         jv = array( jv.storage() );
     574            2 :                         return true;
     575              :                     }
     576              :                 }
     577              : 
     578           19 :                 if( opts.create_objects )
     579              :                 {
     580           19 :                     jv = object( jv.storage() );
     581           19 :                     return true;
     582              :                 }
     583              :             }
     584              : 
     585            3 :             return false;
     586              :         });
     587              : 
     588           28 :     if( result )
     589           20 :         *result = ref.make_value( storage() );
     590           28 :     return result;
     591              : }
     592              : 
     593              : value*
     594            5 : value::set_at_pointer(
     595              :     string_view sv,
     596              :     value_ref ref,
     597              :     std::error_code& ec,
     598              :     set_pointer_options const& opts )
     599              : {
     600            5 :     system::error_code jec;
     601            5 :     value* result = set_at_pointer( sv, ref, jec, opts );
     602            5 :     ec = jec;
     603            5 :     return result;
     604              : }
     605              : 
     606              : system::result<value&>
     607           18 : value::try_set_at_pointer(
     608              :     string_view sv,
     609              :     value_ref ref,
     610              :     set_pointer_options const& opts )
     611              : {
     612           18 :     system::error_code ec;
     613           18 :     value* result = set_at_pointer( sv, ref, ec, opts );
     614           18 :     if( result )
     615           16 :         return *result;
     616            2 :     return ec;
     617              : }
     618              : 
     619              : value&
     620           17 : value::set_at_pointer(
     621              :     string_view sv, value_ref ref, set_pointer_options const& opts )
     622              : {
     623           17 :     return try_set_at_pointer(sv, ref, opts).value();
     624              : }
     625              : 
     626              : } // namespace json
     627              : } // namespace boost
     628              : 
     629              : #endif // BOOST_JSON_IMPL_POINTER_IPP
        

Generated by: LCOV version 2.1