Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:39:02

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     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     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     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) const&
0346 {
0347     error_code ec;
0348     auto const found = find_pointer(ptr, ec);
0349     if( !found )
0350         detail::throw_system_error( ec );
0351     return *found;
0352 }
0353 
0354 value const*
0355 value::find_pointer( string_view sv, error_code& ec ) const noexcept
0356 {
0357     return detail::walk_pointer(
0358         *this,
0359         sv,
0360         ec,
0361         []( object const& obj, detail::pointer_token token )
0362         {
0363             return detail::if_contains_token(obj, token);
0364         },
0365         []( array const& arr, std::size_t index, error_code& ec )
0366             -> value const*
0367         {
0368             if( ec )
0369                 return nullptr;
0370 
0371             return arr.if_contains(index);
0372         },
0373         []( value const&, string_view)
0374         {
0375             return std::false_type();
0376         });
0377 }
0378 
0379 value*
0380 value::find_pointer(string_view ptr, error_code& ec) noexcept
0381 {
0382     value const& self = *this;
0383     return const_cast<value*>(self.find_pointer(ptr, ec));
0384 }
0385 
0386 value const*
0387 value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
0388 {
0389     error_code jec;
0390     value const* result = find_pointer(ptr, jec);
0391     ec = jec;
0392     return result;
0393 }
0394 
0395 value*
0396 value::find_pointer(string_view ptr, std::error_code& ec) noexcept
0397 {
0398     value const& self = *this;
0399     return const_cast<value*>(self.find_pointer(ptr, ec));
0400 }
0401 
0402 value*
0403 value::set_at_pointer(
0404     string_view sv,
0405     value_ref ref,
0406     error_code& ec,
0407     set_pointer_options const& opts )
0408 {
0409     value* result = detail::walk_pointer(
0410         *this,
0411         sv,
0412         ec,
0413         []( object& obj, detail::pointer_token token)
0414         {
0415             if( !obj.empty() )
0416             {
0417                 key_value_pair* kv = detail::find_in_object( obj, token ).first;
0418                 if( kv )
0419                     return &kv->value();
0420             }
0421 
0422             string key( token.begin(), token.end(), obj.storage() );
0423             return &obj.emplace( std::move(key), nullptr ).first->value();
0424         },
0425         [ &opts ]( array& arr, std::size_t index, error_code& ec ) -> value*
0426         {
0427             if( ec == error::past_the_end )
0428                 index = arr.size();
0429             else if( ec.failed() )
0430                 return nullptr;
0431 
0432             if( index >= arr.size() )
0433             {
0434                 std::size_t const n = index - arr.size();
0435                 if( n >= opts.max_created_elements )
0436                     return nullptr;
0437 
0438                 arr.resize( arr.size() + n + 1 );
0439             }
0440 
0441             ec.clear();
0442             return arr.data() + index;
0443         },
0444         [ &opts ]( value& jv, string_view segment )
0445         {
0446             if( jv.is_null() || opts.replace_any_scalar )
0447             {
0448                 if( opts.create_arrays )
0449                 {
0450                     error_code ec;
0451                     detail::parse_number_token( segment, ec );
0452                     if( !ec.failed() || ec == error::past_the_end )
0453                     {
0454                         jv = array( jv.storage() );
0455                         return true;
0456                     }
0457                 }
0458 
0459                 if( opts.create_objects )
0460                 {
0461                     jv = object( jv.storage() );
0462                     return true;
0463                 }
0464             }
0465 
0466             return false;
0467         });
0468 
0469     if( result )
0470         *result = ref.make_value( storage() );
0471     return result;
0472 }
0473 
0474 value*
0475 value::set_at_pointer(
0476     string_view sv,
0477     value_ref ref,
0478     std::error_code& ec,
0479     set_pointer_options const& opts )
0480 {
0481     error_code jec;
0482     value* result = set_at_pointer( sv, ref, jec, opts );
0483     ec = jec;
0484     return result;
0485 }
0486 
0487 value&
0488 value::set_at_pointer(
0489     string_view sv, value_ref ref, set_pointer_options const& opts )
0490 {
0491     error_code ec;
0492     value* result = set_at_pointer( sv, ref, ec, opts );
0493     if( !result )
0494         detail::throw_system_error( ec );
0495     return *result;
0496 }
0497 
0498 } // namespace json
0499 } // namespace boost
0500 
0501 #endif // BOOST_JSON_IMPL_POINTER_IPP