GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/pointer.ipp
Date: 2026-01-06 03:55:48
Exec Total Coverage
Lines: 244 304 80.3%
Functions: 35 36 97.2%
Branches: 123 157 78.3%

Line Branch Exec Source
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
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
142 BOOST_ASSERT( !sv.empty() );
31
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
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 520 explicit iterator(char const* base) noexcept
52 520 : base_(base)
53 {
54 520 }
55
56 389 char operator*() const noexcept
57 {
58
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 387 times.
389 switch( char c = *base_ )
59 {
60 2 case '~':
61 2 c = base_[1];
62
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if( '0' == c )
63 1 return '~';
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT('1' == c);
65 1 return '/';
66 387 default:
67 387 return c;
68 }
69 }
70
71 285 iterator& operator++() noexcept
72 {
73
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 283 times.
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 1090 char const* base() const noexcept
88 {
89 1090 return base_;
90 }
91
92 private:
93 char const* base_;
94 };
95
96 487 bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
97 {
98 487 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 260 pointer_token::iterator pointer_token::begin() const noexcept
107 {
108 260 return iterator(b_);
109 }
110
111 260 pointer_token::iterator pointer_token::end() const noexcept
112 {
113 260 return iterator(e_);
114 }
115
116 237 bool operator==(pointer_token token, string_view sv) noexcept
117 {
118 237 auto t_b = token.begin();
119 237 auto const t_e = token.end();
120 237 auto s_b = sv.begin();
121 237 auto const s_e = sv.end();
122
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 129 times.
487 while( s_b != s_e )
123 {
124
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 354 times.
358 if( t_e == t_b )
125 4 return false;
126
2/2
✓ Branch 1 taken 104 times.
✓ Branch 2 taken 250 times.
354 if( *t_b != *s_b )
127 104 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
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 16 times.
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
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
16 if( b == e )
146 13 return false;
147
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
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
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 12 times.
69 if( *b != '-' )
157 57 return false;
158
159 12 ++b;
160
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
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
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
75 BOOST_ASSERT( !sv.empty() );
170
171 75 char const* b = sv.begin();
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 BOOST_ASSERT( *b == '/' );
173
174 75 ++b;
175 75 char const* const e = sv.end();
176 75 if( ( b == e )
177
6/6
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 69 times.
75 || is_invalid_zero(b, e) )
178 {
179 6 BOOST_JSON_FAIL(ec, error::token_not_number);
180 6 return {};
181 }
182
183
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 59 times.
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
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 29 times.
133 for( ; b != e; ++b )
192 {
193 104 char const c = *b;
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
104 BOOST_ASSERT( c != '/' );
195
196 104 unsigned d = c - '0';
197
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 76 times.
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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 74 times.
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
2/2
✓ Branch 1 taken 92 times.
✓ Branch 2 taken 220 times.
312 if( sv.empty() )
222 92 return sv;
223
224 220 char const* const start = sv.begin();
225 220 char const* b = start;
226
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 215 times.
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
2/2
✓ Branch 0 taken 535 times.
✓ Branch 1 taken 97 times.
632 for( ; b < e; ++b )
234 {
235 535 char const c = *b;
236
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 423 times.
535 if( '/' == c )
237 112 break;
238
239
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 415 times.
423 if( '~' == c )
240 {
241
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if( ++b == e )
242 {
243 3 BOOST_JSON_FAIL( ec, error::invalid_escape );
244 3 break;
245 }
246
247
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
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
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 107 times.
109 if( obj.empty() )
270 2 return nullptr;
271
272 107 auto const it = detail::find_in_object(obj, token).first;
273
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 102 times.
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 226 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 226 ec.clear();
294
295 226 string_view segment = detail::next_segment( sv, ec );
296
297 226 Value* result = &jv;
298 456 while( true )
299 {
300
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 298 times.
682 if( ec.failed() )
301 86 return nullptr;
302
303
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 286 times.
596 if( !result )
304 {
305 24 BOOST_JSON_FAIL(ec, error::not_found);
306 24 return nullptr;
307 }
308
309
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 228 times.
572 if( segment.empty() )
310 116 break;
311
312
3/3
✓ Branch 1 taken 142 times.
✓ Branch 2 taken 57 times.
✓ Branch 3 taken 29 times.
456 switch( result->kind() )
313 {
314 284 case kind::object: {
315 284 auto& obj = result->get_object();
316
317 284 detail::pointer_token const token( segment );
318 284 segment = detail::next_segment( sv, ec );
319
320
1/1
✓ Branch 1 taken 33 times.
284 result = on_object( obj, token );
321 284 break;
322 }
323 114 case kind::array: {
324 114 auto const index = detail::parse_number_token( segment, ec );
325 114 segment = detail::next_segment( sv, ec );
326
327 114 auto& arr = result->get_array();
328
1/1
✓ Branch 1 taken 20 times.
114 result = on_array( arr, index, ec );
329 114 break;
330 }
331 58 default: {
332
3/4
✓ Branch 1 taken 24 times.
✓ Branch 3 taken 26 times.
✓ Branch 4 taken 3 times.
✗ Branch 2 not taken.
58 if( on_scalar( *result, segment ) )
333 42 break;
334 16 BOOST_JSON_FAIL( ec, error::value_is_scalar );
335 }}
336 }
337
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
116 BOOST_ASSERT( result );
339 116 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
1/1
✓ Branch 2 taken 31 times.
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
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 32 times.
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/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
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
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 15 times.
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 bool
419 value::erase_at_pointer(
420 string_view sv,
421 system::error_code& ec)
422 {
423 ec.clear();
424 if(sv.empty()){
425 BOOST_JSON_FAIL(ec, error::syntax);
426 return false;
427 }
428
429 string_view previous_segment;
430
431 string_view segment = detail::next_segment(sv, ec);
432
433 auto result = this;
434 auto previous_result = this;
435
436 while (true)
437 {
438 if (ec.failed())
439 return false;
440
441 if (!result)
442 {
443 BOOST_JSON_FAIL(ec, error::not_found);
444 return false;
445 }
446
447 if( segment.empty() )
448 break;
449
450 previous_segment = segment;
451 previous_result = result;
452
453 switch (result->kind())
454 {
455 case boost::json::kind::object: {
456 auto& obj = result->get_object();
457
458 detail::pointer_token const token(segment);
459 segment = detail::next_segment(sv, ec);
460
461 result = detail::if_contains_token(obj, token);
462 if( !result )
463 {
464 BOOST_JSON_FAIL(ec, error::not_found);
465 return false;
466 }
467 break;
468 }
469 case boost::json::kind::array: {
470 auto const index = detail::parse_number_token(segment, ec);
471 segment = detail::next_segment(sv, ec);
472
473 auto& arr = result->get_array();
474 result = arr.if_contains(index);
475 if( !result )
476 {
477 BOOST_JSON_FAIL(ec, error::past_the_end);
478 return false;
479 }
480 break;
481 }
482 default: {
483 BOOST_JSON_FAIL(ec, error::value_is_scalar);
484 return false;
485 }
486 }
487 }
488
489 switch (previous_result->kind())
490 {
491 case boost::json::kind::object: {
492 auto& obj = previous_result->get_object();
493 detail::pointer_token const token(previous_segment);
494 key_value_pair* kv = detail::find_in_object(obj, token).first;
495 if (kv) {
496 obj.erase(kv);
497 return true;
498 }
499 return false;
500 }
501 case boost::json::kind::array: {
502 auto const index = detail::parse_number_token(previous_segment, ec);
503 auto& arr = previous_result->get_array();
504 if (arr.if_contains(index)){
505 arr.erase(arr.begin() + index);
506 return true;
507 }
508 return false;
509 }
510 default: {
511 BOOST_JSON_FAIL(ec, error::value_is_scalar);
512 return false;
513 }
514 }
515 }
516
517 value*
518 28 value::set_at_pointer(
519 string_view sv,
520 value_ref ref,
521 system::error_code& ec,
522 set_pointer_options const& opts )
523 {
524 28 value* result = detail::walk_pointer(
525 *this,
526 sv,
527 ec,
528 33 []( object& obj, detail::pointer_token token)
529 {
530
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 20 times.
33 if( !obj.empty() )
531 {
532 13 key_value_pair* kv = detail::find_in_object( obj, token ).first;
533
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 if( kv )
534 12 return &kv->value();
535 }
536
537
1/1
✓ Branch 5 taken 21 times.
21 string key( token.begin(), token.end(), obj.storage() );
538
1/1
✓ Branch 3 taken 21 times.
21 return &obj.emplace( std::move(key), nullptr ).first->value();
539 21 },
540 20 [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
541 {
542
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if( ec == error::past_the_end )
543 6 index = arr.size();
544
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 else if( ec.failed() )
545 2 return nullptr;
546
547
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 5 times.
18 if( index >= arr.size() )
548 {
549 13 std::size_t const n = index - arr.size();
550
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if( n >= opts.max_created_elements )
551 3 return nullptr;
552
553 10 arr.resize( arr.size() + n + 1 );
554 }
555
556 15 ec.clear();
557 15 return arr.data() + index;
558 },
559 24 [ &opts ]( value& jv, string_view segment )
560 {
561
6/6
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 3 times.
24 if( jv.is_null() || opts.replace_any_scalar )
562 {
563
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
21 if( opts.create_arrays )
564 {
565 18 system::error_code ec;
566 18 detail::parse_number_token( segment, ec );
567
5/6
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 16 times.
18 if( !ec.failed() || ec == error::past_the_end )
568 {
569
1/1
✓ Branch 4 taken 2 times.
2 jv = array( jv.storage() );
570 2 return true;
571 }
572 }
573
574
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if( opts.create_objects )
575 {
576
1/1
✓ Branch 4 taken 19 times.
19 jv = object( jv.storage() );
577 19 return true;
578 }
579 }
580
581 3 return false;
582 });
583
584
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
28 if( result )
585
2/2
✓ Branch 3 taken 20 times.
✓ Branch 6 taken 20 times.
20 *result = ref.make_value( storage() );
586 28 return result;
587 }
588
589 value*
590 5 value::set_at_pointer(
591 string_view sv,
592 value_ref ref,
593 std::error_code& ec,
594 set_pointer_options const& opts )
595 {
596 5 system::error_code jec;
597
1/1
✓ Branch 1 taken 5 times.
5 value* result = set_at_pointer( sv, ref, jec, opts );
598
1/1
✓ Branch 1 taken 5 times.
5 ec = jec;
599 5 return result;
600 }
601
602 system::result<value&>
603 18 value::try_set_at_pointer(
604 string_view sv,
605 value_ref ref,
606 set_pointer_options const& opts )
607 {
608 18 system::error_code ec;
609
1/1
✓ Branch 1 taken 18 times.
18 value* result = set_at_pointer( sv, ref, ec, opts );
610
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 if( result )
611 16 return *result;
612 2 return ec;
613 }
614
615 value&
616 17 value::set_at_pointer(
617 string_view sv, value_ref ref, set_pointer_options const& opts )
618 {
619
2/2
✓ Branch 1 taken 17 times.
✓ Branch 5 taken 15 times.
17 return try_set_at_pointer(sv, ref, opts).value();
620 }
621
622 } // namespace json
623 } // namespace boost
624
625 #endif // BOOST_JSON_IMPL_POINTER_IPP
626