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
|