impl/pointer.ipp

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