Coverage Report

Created: 2025-11-08 06:51

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