GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/pointer.ipp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 244 244 100.0%
Functions: 35 35 100.0%
Branches: 123 135 91.1%

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 value*
419 28 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 28 value* result = detail::walk_pointer(
426 *this,
427 sv,
428 ec,
429 33 []( object& obj, detail::pointer_token token)
430 {
431
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 20 times.
33 if( !obj.empty() )
432 {
433 13 key_value_pair* kv = detail::find_in_object( obj, token ).first;
434
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 if( kv )
435 12 return &kv->value();
436 }
437
438
1/1
✓ Branch 5 taken 21 times.
21 string key( token.begin(), token.end(), obj.storage() );
439
1/1
✓ Branch 3 taken 21 times.
21 return &obj.emplace( std::move(key), nullptr ).first->value();
440 21 },
441 20 [ &opts ]( array& arr, std::size_t index, system::error_code& ec ) -> value*
442 {
443
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if( ec == error::past_the_end )
444 6 index = arr.size();
445
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 else if( ec.failed() )
446 2 return nullptr;
447
448
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 5 times.
18 if( index >= arr.size() )
449 {
450 13 std::size_t const n = index - arr.size();
451
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if( n >= opts.max_created_elements )
452 3 return nullptr;
453
454 10 arr.resize( arr.size() + n + 1 );
455 }
456
457 15 ec.clear();
458 15 return arr.data() + index;
459 },
460 24 [ &opts ]( value& jv, string_view segment )
461 {
462
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 )
463 {
464
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
21 if( opts.create_arrays )
465 {
466 18 system::error_code ec;
467 18 detail::parse_number_token( segment, ec );
468
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 )
469 {
470
1/1
✓ Branch 4 taken 2 times.
2 jv = array( jv.storage() );
471 2 return true;
472 }
473 }
474
475
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if( opts.create_objects )
476 {
477
1/1
✓ Branch 4 taken 19 times.
19 jv = object( jv.storage() );
478 19 return true;
479 }
480 }
481
482 3 return false;
483 });
484
485
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
28 if( result )
486
2/2
✓ Branch 3 taken 20 times.
✓ Branch 6 taken 20 times.
20 *result = ref.make_value( storage() );
487 28 return result;
488 }
489
490 value*
491 5 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 5 system::error_code jec;
498
1/1
✓ Branch 1 taken 5 times.
5 value* result = set_at_pointer( sv, ref, jec, opts );
499
1/1
✓ Branch 1 taken 5 times.
5 ec = jec;
500 5 return result;
501 }
502
503 system::result<value&>
504 18 value::try_set_at_pointer(
505 string_view sv,
506 value_ref ref,
507 set_pointer_options const& opts )
508 {
509 18 system::error_code ec;
510
1/1
✓ Branch 1 taken 18 times.
18 value* result = set_at_pointer( sv, ref, ec, opts );
511
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 if( result )
512 16 return *result;
513 2 return ec;
514 }
515
516 value&
517 17 value::set_at_pointer(
518 string_view sv, value_ref ref, set_pointer_options const& opts )
519 {
520
2/2
✓ Branch 1 taken 17 times.
✓ Branch 5 taken 15 times.
17 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