GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/pointer.ipp
Date: 2025-12-23 17:38:58
Exec Total Coverage
Lines: 244 307 79.5%
Functions: 35 36 97.2%
Branches: 123 156 78.8%

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 516 explicit iterator(char const* base) noexcept
52 516 : base_(base)
53 {
54 516 }
55
56 387 char operator*() const noexcept
57 {
58
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 385 times.
387 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 385 default:
67 385 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 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
2/2
✓ Branch 0 taken 356 times.
✓ Branch 1 taken 129 times.
485 while( s_b != s_e )
123 {
124
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 352 times.
356 if( t_e == t_b )
125 4 return false;
126
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 250 times.
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
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 std::pair<bool, string_view>
419 value::delete_at_pointer(
420 string_view sv,
421 system::error_code& ec)
422 {
423 ec.clear();
424
425
426 string_view previous_segment;
427 string_view sv_copy = sv;
428 string_view err_position;
429 string_view segment = detail::next_segment(sv, ec);
430 size_t shift = 0;
431
432 auto result = this;
433 auto previous_result = this;
434
435 while (true)
436 {
437 if (ec.failed())
438 return {false, err_position};
439
440 if (!result)
441 {
442 BOOST_JSON_FAIL(ec, error::not_found);
443 return {false, err_position};
444 }
445
446 if( segment.empty() )
447 break;
448
449 shift += segment.size();
450 err_position = sv_copy.substr(0, shift);
451
452 previous_segment = segment;
453 previous_result = result;
454
455 switch (result->kind())
456 {
457 case boost::json::kind::object: {
458 auto& obj = result->get_object();
459
460 detail::pointer_token const token(segment);
461 segment = detail::next_segment(sv, ec);
462
463 result = detail::if_contains_token(obj, token);
464 if( !result )
465 {
466 BOOST_JSON_FAIL(ec, error::not_found);
467 return {false, err_position};
468 }
469 break;
470 }
471 case boost::json::kind::array: {
472 auto const index = detail::parse_number_token(segment, ec);
473 segment = detail::next_segment(sv, ec);
474
475 auto& arr = result->get_array();
476 result = arr.if_contains(index);
477 if( !result )
478 {
479 BOOST_JSON_FAIL(ec, error::past_the_end);
480 return {false, err_position};
481 }
482 break;
483 }
484 default: {
485 BOOST_JSON_FAIL(ec, error::value_is_scalar);
486 return {false, err_position};
487 }
488 }
489 }
490
491 err_position = {};
492
493 switch (previous_result->kind())
494 {
495 case boost::json::kind::object: {
496 auto& obj = previous_result->get_object();
497 detail::pointer_token const token(previous_segment);
498 key_value_pair* kv = detail::find_in_object(obj, token).first;
499 if (kv) {
500 obj.erase(kv);
501 return {true, err_position};
502 }
503 return {false,err_position};
504 }
505 case boost::json::kind::array: {
506 auto const index = detail::parse_number_token(previous_segment, ec);
507 auto& arr = previous_result->get_array();
508 if (arr.if_contains(index)){
509 arr.erase(arr.begin() + index);
510 return {true, err_position};
511 }
512 return {false,err_position};
513 }
514 default: {
515 BOOST_JSON_FAIL(ec, error::value_is_scalar);
516 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
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 20 times.
33 if( !obj.empty() )
535 {
536 13 key_value_pair* kv = detail::find_in_object( obj, token ).first;
537
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 if( kv )
538 12 return &kv->value();
539 }
540
541
1/1
✓ Branch 5 taken 21 times.
21 string key( token.begin(), token.end(), obj.storage() );
542
1/1
✓ Branch 3 taken 21 times.
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
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if( ec == error::past_the_end )
547 6 index = arr.size();
548
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 else if( ec.failed() )
549 2 return nullptr;
550
551
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 5 times.
18 if( index >= arr.size() )
552 {
553 13 std::size_t const n = index - arr.size();
554
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
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
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 )
566 {
567
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
21 if( opts.create_arrays )
568 {
569 18 system::error_code ec;
570 18 detail::parse_number_token( segment, ec );
571
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 )
572 {
573
1/1
✓ Branch 4 taken 2 times.
2 jv = array( jv.storage() );
574 2 return true;
575 }
576 }
577
578
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if( opts.create_objects )
579 {
580
1/1
✓ Branch 4 taken 19 times.
19 jv = object( jv.storage() );
581 19 return true;
582 }
583 }
584
585 3 return false;
586 });
587
588
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
28 if( result )
589
2/2
✓ Branch 3 taken 20 times.
✓ Branch 6 taken 20 times.
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
1/1
✓ Branch 1 taken 5 times.
5 value* result = set_at_pointer( sv, ref, jec, opts );
602
1/1
✓ Branch 1 taken 5 times.
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
1/1
✓ Branch 1 taken 18 times.
18 value* result = set_at_pointer( sv, ref, ec, opts );
614
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
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
2/2
✓ Branch 1 taken 17 times.
✓ Branch 5 taken 15 times.
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
630