TLA 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 HIT 164 : pointer_token(
26 : string_view sv) noexcept
27 164 : : b_( sv.begin() + 1 )
28 164 : , e_( sv.end() )
29 : {
30 164 : BOOST_ASSERT( !sv.empty() );
31 164 : BOOST_ASSERT( *sv.data() == '/' );
32 164 : }
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 588 : explicit iterator(char const* base) noexcept
52 588 : : base_(base)
53 : {
54 588 : }
55 :
56 635 : char operator*() const noexcept
57 : {
58 635 : 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 633 : default:
67 633 : return c;
68 : }
69 : }
70 :
71 837 : iterator& operator++() noexcept
72 : {
73 837 : if( '~' == *base_ )
74 2 : base_ += 2;
75 : else
76 835 : ++base_;
77 837 : return *this;
78 : }
79 :
80 30 : iterator operator++(int) noexcept
81 : {
82 30 : iterator result = *this;
83 30 : ++(*this);
84 30 : return result;
85 : }
86 :
87 1710 : char const* base() const noexcept
88 : {
89 1710 : return base_;
90 : }
91 :
92 : private:
93 : char const* base_;
94 : };
95 :
96 633 : bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
97 : {
98 633 : return l.base() == r.base();
99 : }
100 :
101 222 : bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
102 : {
103 222 : return l.base() != r.base();
104 : }
105 :
106 294 : pointer_token::iterator pointer_token::begin() const noexcept
107 : {
108 294 : return iterator(b_);
109 : }
110 :
111 294 : pointer_token::iterator pointer_token::end() const noexcept
112 : {
113 294 : return iterator(e_);
114 : }
115 :
116 254 : bool operator==(pointer_token token, string_view sv) noexcept
117 : {
118 254 : auto t_b = token.begin();
119 254 : auto const t_e = token.end();
120 254 : auto s_b = sv.begin();
121 254 : auto const s_e = sv.end();
122 633 : while( s_b != s_e )
123 : {
124 483 : if( t_e == t_b )
125 4 : return false;
126 479 : if( *t_b != *s_b )
127 100 : return false;
128 379 : ++t_b;
129 379 : ++s_b;
130 : }
131 150 : return t_b == t_e;
132 : }
133 :
134 77 : 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 77 : if( *b != '0' )
140 61 : 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 74 : bool is_past_the_end_token(
153 : char const* b,
154 : char const* e) noexcept
155 : {
156 74 : if( *b != '-' )
157 62 : 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 80 : parse_number_token(
166 : string_view sv,
167 : system::error_code& ec) noexcept
168 : {
169 80 : BOOST_ASSERT( !sv.empty() );
170 :
171 80 : char const* b = sv.begin();
172 80 : BOOST_ASSERT( *b == '/' );
173 :
174 80 : ++b;
175 80 : char const* const e = sv.end();
176 80 : if( ( b == e )
177 80 : || is_invalid_zero(b, e) )
178 : {
179 6 : BOOST_JSON_FAIL(ec, error::token_not_number);
180 6 : return {};
181 : }
182 :
183 74 : 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 64 : std::size_t result = 0;
191 218 : for( ; b != e; ++b )
192 : {
193 187 : char const c = *b;
194 187 : BOOST_ASSERT( c != '/' );
195 :
196 187 : unsigned d = c - '0';
197 187 : if( d > 9 )
198 : {
199 29 : BOOST_JSON_FAIL(ec, error::token_not_number);
200 29 : return {};
201 : }
202 :
203 158 : if( result > std::size_t(-1) / 10 )
204 : {
205 2 : BOOST_JSON_FAIL(ec, error::token_overflow);
206 2 : return {};
207 : }
208 156 : result *= 10;
209 :
210 156 : if( result > std::size_t(-1) - d )
211 : {
212 2 : BOOST_JSON_FAIL(ec, error::token_overflow);
213 2 : return {};
214 : }
215 154 : result += d;
216 : }
217 31 : return result;
218 : }
219 :
220 : string_view
221 360 : next_segment(
222 : string_view& sv,
223 : system::error_code& ec) noexcept
224 : {
225 360 : if( sv.empty() )
226 114 : return sv;
227 :
228 246 : char const* const start = sv.begin();
229 246 : char const* b = start;
230 246 : if( *b++ != '/' )
231 : {
232 5 : BOOST_JSON_FAIL( ec, error::missing_slash );
233 5 : return {};
234 : }
235 :
236 241 : char const* e = sv.end();
237 870 : for( ; b < e; ++b )
238 : {
239 751 : char const c = *b;
240 751 : if( '/' == c )
241 116 : break;
242 :
243 635 : if( '~' == c )
244 : {
245 8 : if( ++b == e )
246 : {
247 3 : BOOST_JSON_FAIL( ec, error::invalid_escape );
248 3 : break;
249 : }
250 :
251 5 : switch (*b)
252 : {
253 2 : case '0': // fall through
254 : case '1':
255 : // valid escape sequence
256 2 : continue;
257 3 : default: {
258 3 : BOOST_JSON_FAIL( ec, error::invalid_escape );
259 3 : break;
260 : }
261 2 : }
262 3 : break;
263 : }
264 : }
265 :
266 241 : sv.remove_prefix( b - start );
267 241 : return string_view( start, b );
268 : }
269 :
270 : value*
271 129 : if_contains_token(object const& obj, pointer_token token)
272 : {
273 129 : if( obj.empty() )
274 2 : return nullptr;
275 :
276 127 : auto const it = detail::find_in_object(obj, token).first;
277 127 : if( !it )
278 5 : return nullptr;
279 :
280 122 : return &it->value();
281 : }
282 :
283 : template<
284 : class Value,
285 : class OnObject,
286 : class OnArray,
287 : class OnScalar >
288 : Value*
289 135 : walk_pointer(
290 : Value& jv,
291 : string_view sv,
292 : system::error_code& ec,
293 : OnObject on_object,
294 : OnArray on_array,
295 : OnScalar on_scalar)
296 : {
297 135 : ec.clear();
298 :
299 135 : string_view segment = detail::next_segment( sv, ec );
300 :
301 135 : Value* result = &jv;
302 255 : while( true )
303 : {
304 390 : if( ec.failed() )
305 47 : return nullptr;
306 :
307 343 : if( !result )
308 : {
309 12 : BOOST_JSON_FAIL(ec, error::not_found);
310 12 : return nullptr;
311 : }
312 :
313 331 : if( segment.empty() )
314 76 : break;
315 :
316 255 : switch( result->kind() )
317 : {
318 164 : case kind::object: {
319 164 : auto& obj = result->get_object();
320 :
321 164 : detail::pointer_token const token( segment );
322 164 : segment = detail::next_segment( sv, ec );
323 :
324 164 : result = on_object( obj, token );
325 164 : break;
326 : }
327 61 : case kind::array: {
328 61 : auto const index = detail::parse_number_token( segment, ec );
329 61 : segment = detail::next_segment( sv, ec );
330 :
331 61 : auto& arr = result->get_array();
332 61 : result = on_array( arr, index, ec );
333 61 : break;
334 : }
335 30 : default: {
336 30 : if( on_scalar( *result, segment ) )
337 22 : break;
338 8 : BOOST_JSON_FAIL( ec, error::value_is_scalar );
339 : }}
340 : }
341 :
342 76 : BOOST_ASSERT( result );
343 76 : return result;
344 : }
345 :
346 : } // namespace detail
347 :
348 : value const&
349 57 : value::at_pointer(string_view ptr, source_location const& loc) const&
350 : {
351 57 : return try_at_pointer(ptr).value(loc);
352 : }
353 :
354 : system::result<value const&>
355 59 : value::try_at_pointer(string_view ptr) const noexcept
356 : {
357 59 : system::error_code ec;
358 59 : auto const found = find_pointer(ptr, ec);
359 59 : if( !found )
360 10 : return ec;
361 49 : return *found;
362 : }
363 :
364 : system::result<value&>
365 2 : value::try_at_pointer(string_view ptr) noexcept
366 : {
367 2 : system::error_code ec;
368 2 : auto const found = find_pointer(ptr, ec);
369 2 : if( !found )
370 1 : return ec;
371 1 : return *found;
372 : }
373 :
374 : value const*
375 104 : value::find_pointer( string_view sv, system::error_code& ec ) const noexcept
376 : {
377 104 : return detail::walk_pointer(
378 : *this,
379 : sv,
380 : ec,
381 129 : []( object const& obj, detail::pointer_token token )
382 : {
383 129 : return detail::if_contains_token(obj, token);
384 : },
385 39 : []( array const& arr, std::size_t index, system::error_code& ec )
386 : -> value const*
387 : {
388 39 : if( ec )
389 24 : return nullptr;
390 :
391 15 : return arr.if_contains(index);
392 : },
393 5 : []( value const&, string_view)
394 : {
395 5 : return std::false_type();
396 104 : });
397 : }
398 :
399 : value*
400 23 : value::find_pointer(string_view ptr, system::error_code& ec) noexcept
401 : {
402 23 : value const& self = *this;
403 23 : return const_cast<value*>(self.find_pointer(ptr, ec));
404 : }
405 :
406 : value const*
407 21 : value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
408 : {
409 21 : system::error_code jec;
410 21 : value const* result = find_pointer(ptr, jec);
411 21 : ec = jec;
412 21 : return result;
413 : }
414 :
415 : value*
416 20 : value::find_pointer(string_view ptr, std::error_code& ec) noexcept
417 : {
418 20 : value const& self = *this;
419 20 : return const_cast<value*>(self.find_pointer(ptr, ec));
420 : }
421 :
422 : value*
423 31 : value::set_at_pointer(
424 : string_view sv,
425 : value_ref ref,
426 : system::error_code& ec,
427 : set_pointer_options const& opts )
428 : {
429 : // ref can refer to an element of this document, which the walk below
430 : // can replace or relocate while it creates missing elements, so the new
431 : // value is created before the document is modified
432 31 : value new_value = ref.make_value( storage() );
433 :
434 31 : value* result = detail::walk_pointer(
435 : *this,
436 : sv,
437 : ec,
438 35 : []( object& obj, detail::pointer_token token)
439 : {
440 35 : if( !obj.empty() )
441 : {
442 14 : key_value_pair* kv = detail::find_in_object( obj, token ).first;
443 14 : if( kv )
444 13 : return &kv->value();
445 : }
446 :
447 22 : string key( token.begin(), token.end(), obj.storage() );
448 22 : return &obj.emplace( std::move(key), nullptr ).first->value();
449 22 : },
450 22 : [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
451 : {
452 22 : if( ec == error::past_the_end )
453 6 : index = arr.size();
454 16 : else if( ec.failed() )
455 2 : return nullptr;
456 :
457 20 : if( index >= arr.size() )
458 : {
459 15 : std::size_t const n = index - arr.size();
460 15 : if( n >= opts.max_created_elements )
461 3 : return nullptr;
462 :
463 : // the resulting size, index + 1, would either exceed the
464 : // array's size limit or wrap to 0 when index == size_t(-1)
465 12 : if( index >= array::max_size() )
466 : {
467 2 : BOOST_JSON_FAIL( ec, error::array_too_large );
468 2 : return nullptr;
469 : }
470 :
471 10 : arr.resize( arr.size() + n + 1 );
472 : }
473 :
474 15 : ec.clear();
475 15 : return arr.data() + index;
476 : },
477 25 : [ &opts ]( value& jv, string_view segment )
478 : {
479 25 : if( jv.is_null() || opts.replace_any_scalar )
480 : {
481 22 : if( opts.create_arrays )
482 : {
483 19 : system::error_code ec;
484 19 : detail::parse_number_token( segment, ec );
485 19 : if( !ec.failed() || ec == error::past_the_end )
486 : {
487 2 : jv = array( jv.storage() );
488 2 : return true;
489 : }
490 : }
491 :
492 20 : if( opts.create_objects )
493 : {
494 20 : jv = object( jv.storage() );
495 20 : return true;
496 : }
497 : }
498 :
499 3 : return false;
500 : });
501 :
502 31 : if( result )
503 21 : *result = std::move(new_value);
504 31 : return result;
505 31 : }
506 :
507 : value*
508 5 : value::set_at_pointer(
509 : string_view sv,
510 : value_ref ref,
511 : std::error_code& ec,
512 : set_pointer_options const& opts )
513 : {
514 5 : system::error_code jec;
515 5 : value* result = set_at_pointer( sv, ref, jec, opts );
516 5 : ec = jec;
517 5 : return result;
518 : }
519 :
520 : system::result<value&>
521 20 : value::try_set_at_pointer(
522 : string_view sv,
523 : value_ref ref,
524 : set_pointer_options const& opts )
525 : {
526 20 : system::error_code ec;
527 20 : value* result = set_at_pointer( sv, ref, ec, opts );
528 20 : if( result )
529 17 : return *result;
530 3 : return ec;
531 : }
532 :
533 : value&
534 19 : value::set_at_pointer(
535 : string_view sv, value_ref ref, set_pointer_options const& opts )
536 : {
537 19 : return try_set_at_pointer(sv, ref, opts).value();
538 : }
539 :
540 : } // namespace json
541 : } // namespace boost
542 :
543 : #endif // BOOST_JSON_IMPL_POINTER_IPP
|