Coverage Report

Created: 2023-06-07 06:25

/src/boost/boost/json/impl/pointer.ipp
Line
Count
Source (jump to first uncovered line)
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
        : b_( sv.begin() + 1 )
28
        , 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
        : 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
    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
    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
    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::$_4, boost::json::value::find_pointer(boost::core::basic_string_view<char>, boost::system::error_code&) const::$_5, boost::json::value::find_pointer(boost::core::basic_string_view<char>, boost::system::error_code&) const::$_6>(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::$_4, boost::json::value::find_pointer(boost::core::basic_string_view<char>, boost::system::error_code&) const::$_5, boost::json::value::find_pointer(boost::core::basic_string_view<char>, boost::system::error_code&) const::$_6)
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&)::$_7, 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&)::$_8, 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&)::$_9>(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&)::$_7, 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&)::$_8, 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&)::$_9)
341
342
} // namespace detail
343
344
value const&
345
value::at_pointer(string_view ptr) const&
346
0
{
347
0
    error_code ec;
348
0
    auto const found = find_pointer(ptr, ec);
349
0
    if( !found )
350
0
        detail::throw_system_error( ec );
351
0
    return *found;
352
0
}
353
354
value const*
355
value::find_pointer( string_view sv, error_code& ec ) const noexcept
356
0
{
357
0
    return detail::walk_pointer(
358
0
        *this,
359
0
        sv,
360
0
        ec,
361
0
        []( object const& obj, detail::pointer_token token )
362
0
        {
363
0
            return detail::if_contains_token(obj, token);
364
0
        },
365
0
        []( array const& arr, std::size_t index, error_code& ec )
366
0
            -> value const*
367
0
        {
368
0
            if( ec )
369
0
                return nullptr;
370
371
0
            return arr.if_contains(index);
372
0
        },
373
0
        []( value const&, string_view)
374
0
        {
375
0
            return std::false_type();
376
0
        });
377
0
}
378
379
value*
380
value::find_pointer(string_view ptr, error_code& ec) noexcept
381
0
{
382
0
    value const& self = *this;
383
0
    return const_cast<value*>(self.find_pointer(ptr, ec));
384
0
}
385
386
value const*
387
value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
388
0
{
389
0
    error_code jec;
390
0
    value const* result = find_pointer(ptr, jec);
391
0
    ec = jec;
392
0
    return result;
393
0
}
394
395
value*
396
value::find_pointer(string_view ptr, std::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*
403
value::set_at_pointer(
404
    string_view sv,
405
    value_ref ref,
406
    error_code& ec,
407
    set_pointer_options const& opts )
408
0
{
409
0
    value* result = detail::walk_pointer(
410
0
        *this,
411
0
        sv,
412
0
        ec,
413
0
        []( object& obj, detail::pointer_token token)
414
0
        {
415
0
            if( !obj.empty() )
416
0
            {
417
0
                key_value_pair* kv = detail::find_in_object( obj, token ).first;
418
0
                if( kv )
419
0
                    return &kv->value();
420
0
            }
421
422
0
            string key( token.begin(), token.end(), obj.storage() );
423
0
            return &obj.emplace( std::move(key), nullptr ).first->value();
424
0
        },
425
0
        [ &opts ]( array& arr, std::size_t index, error_code& ec ) -> value*
426
0
        {
427
0
            if( ec == error::past_the_end )
428
0
                index = arr.size();
429
0
            else if( ec.failed() )
430
0
                return nullptr;
431
432
0
            if( index >= arr.size() )
433
0
            {
434
0
                std::size_t const n = index - arr.size();
435
0
                if( n >= opts.max_created_elements )
436
0
                    return nullptr;
437
438
0
                arr.resize( arr.size() + n + 1 );
439
0
            }
440
441
0
            ec.clear();
442
0
            return arr.data() + index;
443
0
        },
444
0
        [ &opts ]( value& jv, string_view segment )
445
0
        {
446
0
            if( jv.is_null() || opts.replace_any_scalar )
447
0
            {
448
0
                if( opts.create_arrays )
449
0
                {
450
0
                    error_code ec;
451
0
                    detail::parse_number_token( segment, ec );
452
0
                    if( !ec.failed() || ec == error::past_the_end )
453
0
                    {
454
0
                        jv = array( jv.storage() );
455
0
                        return true;
456
0
                    }
457
0
                }
458
459
0
                if( opts.create_objects )
460
0
                {
461
0
                    jv = object( jv.storage() );
462
0
                    return true;
463
0
                }
464
0
            }
465
466
0
            return false;
467
0
        });
468
469
0
    if( result )
470
0
        *result = ref.make_value( storage() );
471
0
    return result;
472
0
}
473
474
value*
475
value::set_at_pointer(
476
    string_view sv,
477
    value_ref ref,
478
    std::error_code& ec,
479
    set_pointer_options const& opts )
480
0
{
481
0
    error_code jec;
482
0
    value* result = set_at_pointer( sv, ref, jec, opts );
483
0
    ec = jec;
484
0
    return result;
485
0
}
486
487
value&
488
value::set_at_pointer(
489
    string_view sv, value_ref ref, set_pointer_options const& opts )
490
0
{
491
0
    error_code ec;
492
0
    value* result = set_at_pointer( sv, ref, ec, opts );
493
0
    if( !result )
494
0
        detail::throw_system_error( ec );
495
0
    return *result;
496
0
}
497
498
} // namespace json
499
} // namespace boost
500
501
#endif // BOOST_JSON_IMPL_POINTER_IPP