LCOV - code coverage report
Current view: top level - json/impl - pointer.ipp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 244 244
Test Date: 2025-12-23 17:20:51 Functions: 100.0 % 35 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              : 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.1