Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-14 08:35:00

0001 //
0002 // Copyright (c) 2022 Dmitry Arkhipov (grisumbras@gmail.com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/json
0008 //
0009 
0010 #ifndef BOOST_JSON_IMPL_POINTER_IPP
0011 #define BOOST_JSON_IMPL_POINTER_IPP
0012 
0013 #include <boost/json/value.hpp>
0014 
0015 namespace boost {
0016 namespace json {
0017 
0018 namespace detail {
0019 
0020 class pointer_token
0021 {
0022 public:
0023     class iterator;
0024 
0025     pointer_token(
0026         string_view sv) noexcept
0027         : b_( sv.begin() + 1 )
0028         , e_( sv.end() )
0029     {
0030         BOOST_ASSERT( !sv.empty() );
0031         BOOST_ASSERT( *sv.data() == '/' );
0032     }
0033 
0034     iterator begin() const noexcept;
0035     iterator end() const noexcept;
0036 
0037 private:
0038     char const* b_;
0039     char const* e_;
0040 };
0041 
0042 class pointer_token::iterator
0043 {
0044 public:
0045     using value_type = char;
0046     using reference = char;
0047     using pointer = value_type*;
0048     using difference_type = std::ptrdiff_t;
0049     using iterator_category = std::forward_iterator_tag;
0050 
0051     explicit iterator(char const* base) noexcept
0052         : base_(base)
0053     {
0054     }
0055 
0056     char operator*() const noexcept
0057     {
0058         switch( char c = *base_ )
0059         {
0060         case '~':
0061             c = base_[1];
0062             if( '0' == c )
0063                 return '~';
0064             BOOST_ASSERT('1' == c);
0065             return '/';
0066         default:
0067             return c;
0068         }
0069     }
0070 
0071     iterator& operator++() noexcept
0072     {
0073         if( '~' == *base_ )
0074             base_ += 2;
0075         else
0076             ++base_;
0077         return *this;
0078     }
0079 
0080     iterator operator++(int) noexcept
0081     {
0082         iterator result = *this;
0083         ++(*this);
0084         return result;
0085     }
0086 
0087     char const* base() const noexcept
0088     {
0089         return base_;
0090     }
0091 
0092 private:
0093     char const* base_;
0094 };
0095 
0096 bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
0097 {
0098     return l.base() == r.base();
0099 }
0100 
0101 bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
0102 {
0103     return l.base() != r.base();
0104 }
0105 
0106 pointer_token::iterator pointer_token::begin() const noexcept
0107 {
0108     return iterator(b_);
0109 }
0110 
0111 pointer_token::iterator pointer_token::end() const noexcept
0112 {
0113     return iterator(e_);
0114 }
0115 
0116 bool operator==(pointer_token token, string_view sv) noexcept
0117 {
0118     auto t_b = token.begin();
0119     auto const t_e = token.end();
0120     auto s_b = sv.begin();
0121     auto const s_e = sv.end();
0122     while( s_b != s_e )
0123     {
0124         if( t_e == t_b )
0125             return false;
0126         if( *t_b != *s_b )
0127             return false;
0128         ++t_b;
0129         ++s_b;
0130     }
0131     return t_b == t_e;
0132 }
0133 
0134 bool is_invalid_zero(
0135     char const* b,
0136     char const* e) noexcept
0137 {
0138     // in JSON Pointer only zero index can start character '0'
0139     if( *b != '0' )
0140         return false;
0141 
0142     // if an index token starts with '0', then it should not have any more
0143     // characters: either the string should end, or new token should start
0144     ++b;
0145     if( b == e )
0146         return false;
0147 
0148     BOOST_ASSERT( *b != '/' );
0149     return true;
0150 }
0151 
0152 bool is_past_the_end_token(
0153     char const* b,
0154     char const* e) noexcept
0155 {
0156     if( *b != '-' )
0157         return false;
0158 
0159     ++b;
0160     BOOST_ASSERT( (b == e) || (*b != '/') );
0161     return b == e;
0162 }
0163 
0164 std::size_t
0165 parse_number_token(
0166     string_view sv,
0167     system::error_code& ec) noexcept
0168 {
0169     BOOST_ASSERT( !sv.empty() );
0170 
0171     char const* b = sv.begin();
0172     BOOST_ASSERT( *b == '/' );
0173 
0174     ++b;
0175     char const* const e = sv.end();
0176     if( ( b == e )
0177         || is_invalid_zero(b, e) )
0178     {
0179         BOOST_JSON_FAIL(ec, error::token_not_number);
0180         return {};
0181     }
0182 
0183     if( is_past_the_end_token(b, e) )
0184     {
0185         ++b;
0186         BOOST_JSON_FAIL(ec, error::past_the_end);
0187         return {};
0188     }
0189 
0190     std::size_t result = 0;
0191     for( ; b != e; ++b )
0192     {
0193         char const c = *b;
0194         BOOST_ASSERT( c != '/' );
0195 
0196         unsigned d = c - '0';
0197         if( d > 9 )
0198         {
0199             BOOST_JSON_FAIL(ec, error::token_not_number);
0200             return {};
0201         }
0202 
0203         std::size_t new_result = result * 10 + d;
0204         if( new_result < result )
0205         {
0206             BOOST_JSON_FAIL(ec, error::token_overflow);
0207             return {};
0208         }
0209 
0210         result = new_result;
0211 
0212     }
0213     return result;
0214 }
0215 
0216 string_view
0217 next_segment(
0218     string_view& sv,
0219     system::error_code& ec) noexcept
0220 {
0221     if( sv.empty() )
0222         return sv;
0223 
0224     char const* const start = sv.begin();
0225     char const* b = start;
0226     if( *b++ != '/' )
0227     {
0228         BOOST_JSON_FAIL( ec, error::missing_slash );
0229         return {};
0230     }
0231 
0232     char const* e = sv.end();
0233     for( ; b < e; ++b )
0234     {
0235         char const c = *b;
0236         if( '/' == c )
0237             break;
0238 
0239         if( '~' == c )
0240         {
0241             if( ++b == e )
0242             {
0243                 BOOST_JSON_FAIL( ec, error::invalid_escape );
0244                 break;
0245             }
0246 
0247             switch (*b)
0248             {
0249             case '0': // fall through
0250             case '1':
0251                 // valid escape sequence
0252                 continue;
0253             default: {
0254                 BOOST_JSON_FAIL( ec, error::invalid_escape );
0255                 break;
0256             }
0257             }
0258             break;
0259         }
0260     }
0261 
0262     sv.remove_prefix( b - start );
0263     return string_view( start, b );
0264 }
0265 
0266 value*
0267 if_contains_token(object const& obj, pointer_token token)
0268 {
0269     if( obj.empty() )
0270         return nullptr;
0271 
0272     auto const it = detail::find_in_object(obj, token).first;
0273     if( !it )
0274         return nullptr;
0275 
0276     return &it->value();
0277 }
0278 
0279 template<
0280     class Value,
0281     class OnObject,
0282     class OnArray,
0283     class OnScalar >
0284 Value*
0285 walk_pointer(
0286     Value& jv,
0287     string_view sv,
0288     system::error_code& ec,
0289     OnObject on_object,
0290     OnArray on_array,
0291     OnScalar on_scalar)
0292 {
0293     ec.clear();
0294 
0295     string_view segment = detail::next_segment( sv, ec );
0296 
0297     Value* result = &jv;
0298     while( true )
0299     {
0300         if( ec.failed() )
0301             return nullptr;
0302 
0303         if( !result )
0304         {
0305             BOOST_JSON_FAIL(ec, error::not_found);
0306             return nullptr;
0307         }
0308 
0309         if( segment.empty() )
0310             break;
0311 
0312         switch( result->kind() )
0313         {
0314         case kind::object: {
0315             auto& obj = result->get_object();
0316 
0317             detail::pointer_token const token( segment );
0318             segment = detail::next_segment( sv, ec );
0319 
0320             result = on_object( obj, token );
0321             break;
0322         }
0323         case kind::array: {
0324             auto const index = detail::parse_number_token( segment, ec );
0325             segment = detail::next_segment( sv, ec );
0326 
0327             auto& arr = result->get_array();
0328             result = on_array( arr, index, ec );
0329             break;
0330         }
0331         default: {
0332             if( on_scalar( *result, segment ) )
0333                 break;
0334             BOOST_JSON_FAIL( ec, error::value_is_scalar );
0335         }}
0336     }
0337 
0338     BOOST_ASSERT( result );
0339     return result;
0340 }
0341 
0342 } // namespace detail
0343 
0344 value const&
0345 value::at_pointer(string_view ptr, source_location const& loc) const&
0346 {
0347     return try_at_pointer(ptr).value(loc);
0348 }
0349 
0350 system::result<value const&>
0351 value::try_at_pointer(string_view ptr) const noexcept
0352 {
0353     system::error_code ec;
0354     auto const found = find_pointer(ptr, ec);
0355     if( !found )
0356         return ec;
0357     return *found;
0358 }
0359 
0360 system::result<value&>
0361 value::try_at_pointer(string_view ptr) noexcept
0362 {
0363     system::error_code ec;
0364     auto const found = find_pointer(ptr, ec);
0365     if( !found )
0366         return ec;
0367     return *found;
0368 }
0369 
0370 value const*
0371 value::find_pointer( string_view sv, system::error_code& ec ) const noexcept
0372 {
0373     return detail::walk_pointer(
0374         *this,
0375         sv,
0376         ec,
0377         []( object const& obj, detail::pointer_token token )
0378         {
0379             return detail::if_contains_token(obj, token);
0380         },
0381         []( array const& arr, std::size_t index, system::error_code& ec )
0382             -> value const*
0383         {
0384             if( ec )
0385                 return nullptr;
0386 
0387             return arr.if_contains(index);
0388         },
0389         []( value const&, string_view)
0390         {
0391             return std::false_type();
0392         });
0393 }
0394 
0395 value*
0396 value::find_pointer(string_view ptr, system::error_code& ec) noexcept
0397 {
0398     value const& self = *this;
0399     return const_cast<value*>(self.find_pointer(ptr, ec));
0400 }
0401 
0402 value const*
0403 value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
0404 {
0405     system::error_code jec;
0406     value const* result = find_pointer(ptr, jec);
0407     ec = jec;
0408     return result;
0409 }
0410 
0411 value*
0412 value::find_pointer(string_view ptr, std::error_code& ec) noexcept
0413 {
0414     value const& self = *this;
0415     return const_cast<value*>(self.find_pointer(ptr, ec));
0416 }
0417 
0418 value*
0419 value::set_at_pointer(
0420     string_view sv,
0421     value_ref ref,
0422     system::error_code& ec,
0423     set_pointer_options const& opts )
0424 {
0425     value* result = detail::walk_pointer(
0426         *this,
0427         sv,
0428         ec,
0429         []( object& obj, detail::pointer_token token)
0430         {
0431             if( !obj.empty() )
0432             {
0433                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
0434                 if( kv )
0435                     return &kv->value();
0436             }
0437 
0438             string key( token.begin(), token.end(), obj.storage() );
0439             return &obj.emplace( std::move(key), nullptr ).first->value();
0440         },
0441         [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
0442         {
0443             if( ec == error::past_the_end )
0444                 index = arr.size();
0445             else if( ec.failed() )
0446                 return nullptr;
0447 
0448             if( index >= arr.size() )
0449             {
0450                 std::size_t const n = index - arr.size();
0451                 if( n >= opts.max_created_elements )
0452                     return nullptr;
0453 
0454                 arr.resize( arr.size() + n + 1 );
0455             }
0456 
0457             ec.clear();
0458             return arr.data() + index;
0459         },
0460         [ &opts ]( value& jv, string_view segment )
0461         {
0462             if( jv.is_null() || opts.replace_any_scalar )
0463             {
0464                 if( opts.create_arrays )
0465                 {
0466                     system::error_code ec;
0467                     detail::parse_number_token( segment, ec );
0468                     if( !ec.failed() || ec == error::past_the_end )
0469                     {
0470                         jv = array( jv.storage() );
0471                         return true;
0472                     }
0473                 }
0474 
0475                 if( opts.create_objects )
0476                 {
0477                     jv = object( jv.storage() );
0478                     return true;
0479                 }
0480             }
0481 
0482             return false;
0483         });
0484 
0485     if( result )
0486         *result = ref.make_value( storage() );
0487     return result;
0488 }
0489 
0490 value*
0491 value::set_at_pointer(
0492     string_view sv,
0493     value_ref ref,
0494     std::error_code& ec,
0495     set_pointer_options const& opts )
0496 {
0497     system::error_code jec;
0498     value* result = set_at_pointer( sv, ref, jec, opts );
0499     ec = jec;
0500     return result;
0501 }
0502 
0503 system::result<value&>
0504 value::try_set_at_pointer(
0505     string_view sv,
0506     value_ref ref,
0507     set_pointer_options const& opts )
0508 {
0509     system::error_code ec;
0510     value* result = set_at_pointer( sv, ref, ec, opts );
0511     if( result )
0512         return *result;
0513     return ec;
0514 }
0515 
0516 value&
0517 value::set_at_pointer(
0518     string_view sv, value_ref ref, set_pointer_options const& opts )
0519 {
0520     return try_set_at_pointer(sv, ref, opts).value();
0521 }
0522 
0523 } // namespace json
0524 } // namespace boost
0525 
0526 #endif // BOOST_JSON_IMPL_POINTER_IPP