impl/pointer.ipp

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