Coverage Report

Created: 2026-05-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jsoncons/include/jsoncons/json_cursor.hpp
Line
Count
Source
1
// Copyright 2013-2026 Daniel Parker
2
// Distributed under the Boost license, Version 1.0.
3
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4
5
// See https://github.com/danielaparker/jsoncons for latest version
6
7
#ifndef JSONCONS_JSON_CURSOR_HPP
8
#define JSONCONS_JSON_CURSOR_HPP
9
10
#include <cstddef>
11
#include <functional>
12
#include <ios>
13
#include <memory> // std::allocator
14
#include <system_error>
15
16
#include <jsoncons/config/compiler_support.hpp>
17
#include <jsoncons/utility/byte_string.hpp>
18
#include <jsoncons/config/jsoncons_config.hpp>
19
#include <jsoncons/conv_error.hpp>
20
#include <jsoncons/json_exception.hpp>
21
#include <jsoncons/json_parser.hpp>
22
#include <jsoncons/json_visitor.hpp>
23
#include <jsoncons/ser_utils.hpp>
24
#include <jsoncons/source.hpp>
25
#include <jsoncons/source_adaptor.hpp>
26
#include <jsoncons/staj_cursor.hpp>
27
#include <jsoncons/utility/unicode_traits.hpp>
28
29
namespace jsoncons {
30
31
template <typename CharT,typename Source=jsoncons::stream_source<CharT>,typename Allocator=std::allocator<char>>
32
class basic_json_cursor : public basic_staj_cursor<CharT>, private virtual ser_context
33
{
34
public:
35
    using source_type = Source;
36
    using char_type = CharT;
37
    using allocator_type = Allocator;
38
    using string_view_type = jsoncons::basic_string_view<CharT>;
39
private:
40
    using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT>;
41
    static constexpr size_t default_max_buffer_size = 16384;
42
43
    json_source_adaptor<Source> source_;
44
    basic_json_parser<CharT,Allocator> parser_;
45
    basic_staj_visitor<CharT> cursor_visitor_;
46
    bool done_{false};
47
48
public:
49
50
    // Constructors that throw parse exceptions
51
    template <typename Sourceable>
52
    basic_json_cursor(Sourceable&& source, 
53
        const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(),
54
        const Allocator& alloc = Allocator(),
55
        typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
56
       : source_(std::forward<Sourceable>(source)),
57
         parser_(options, alloc)
58
    {
59
        parser_.cursor_mode(true);
60
        if (!read_done())
61
        {
62
            std::error_code local_ec;
63
            read_next(local_ec);
64
            if (local_ec)
65
            {
66
                if (local_ec == json_errc::unexpected_eof)
67
                {
68
                    done_ = true;
69
                }
70
                else
71
                {
72
                    JSONCONS_THROW(ser_error(local_ec, 1, 1));
73
                }
74
            }
75
        }
76
    }
77
    template <typename Sourceable>
78
    basic_json_cursor(Sourceable&& source, 
79
        const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(),
80
        const Allocator& alloc = Allocator(),
81
        typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
82
       : source_(),
83
         parser_(options, alloc)
84
    {
85
        parser_.cursor_mode(true);
86
        initialize_with_string_view(std::forward<Sourceable>(source));
87
    }
88
89
#if !defined(JSONCONS_NO_DEPRECATED)
90
    template <typename Sourceable>
91
    basic_json_cursor(Sourceable&& source, 
92
        const basic_json_decode_options<CharT>& options,
93
        std::function<bool(json_errc,const ser_context&)> err_handler,
94
        const Allocator& alloc = Allocator(),
95
        typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
96
       : source_(std::forward<Sourceable>(source)),
97
         parser_(options,err_handler,alloc)
98
    {
99
        parser_.cursor_mode(true);
100
        if (!read_done())
101
        {
102
            std::error_code local_ec;
103
            read_next(local_ec);
104
            if (local_ec)
105
            {
106
                if (local_ec == json_errc::unexpected_eof)
107
                {
108
                    done_ = true;
109
                }
110
                else
111
                {
112
                    JSONCONS_THROW(ser_error(local_ec, 1, 1));
113
                }
114
            }
115
        }
116
    }
117
    template <typename Sourceable>
118
    basic_json_cursor(Sourceable&& source, 
119
        const basic_json_decode_options<CharT>& options,
120
        std::function<bool(json_errc,const ser_context&)> err_handler,
121
        const Allocator& alloc = Allocator(),
122
        typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
123
       : source_(),
124
         parser_(options, err_handler, alloc)
125
    {
126
        parser_.cursor_mode(true);
127
        initialize_with_string_view(std::forward<Sourceable>(source));
128
    }
129
#endif
130
131
    // Constructors that set parse error codes
132
    template <typename Sourceable>
133
    basic_json_cursor(Sourceable&& source, std::error_code& ec)
134
3.35k
        : basic_json_cursor(std::allocator_arg, Allocator(), 
135
3.35k
              std::forward<Sourceable>(source),
136
3.35k
              basic_json_decode_options<CharT>(),
137
3.35k
              ec)
138
3.35k
    {
139
3.35k
    }
140
141
    template <typename Sourceable>
142
    basic_json_cursor(Sourceable&& source, 
143
        const basic_json_decode_options<CharT>& options,
144
        std::error_code& ec)
145
        : basic_json_cursor(std::allocator_arg, Allocator(), 
146
              std::forward<Sourceable>(source),
147
              options,
148
              ec)
149
    {
150
    }
151
152
#if !defined(JSONCONS_NO_DEPRECATED)
153
    template <typename Sourceable>
154
    basic_json_cursor(Sourceable&& source, 
155
        const basic_json_decode_options<CharT>& options,
156
        std::function<bool(json_errc,const ser_context&)> err_handler,
157
        std::error_code& ec)
158
        : basic_json_cursor(std::allocator_arg, Allocator(), 
159
              std::forward<Sourceable>(source),
160
              options,
161
              err_handler,
162
              ec)
163
    {
164
    }
165
166
    template <typename Sourceable>
167
    basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
168
        Sourceable&& source, 
169
        const basic_json_decode_options<CharT>& options,
170
        std::function<bool(json_errc,const ser_context&)> err_handler,
171
        std::error_code& ec,
172
        typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
173
       : source_(std::forward<Sourceable>(source)),
174
         parser_(options,err_handler,alloc)
175
    {
176
        parser_.cursor_mode(true);
177
178
        if (!read_done())
179
        {
180
            std::error_code local_ec;
181
            read_next(local_ec);
182
            if (local_ec)
183
            {
184
                if (local_ec == json_errc::unexpected_eof)
185
                {
186
                    done_ = true;
187
                }
188
                else
189
                {
190
                    ec = local_ec;
191
                }
192
            }
193
        }
194
    }
195
#endif
196
197
    template <typename Sourceable>
198
    basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
199
        Sourceable&& source, 
200
        const basic_json_decode_options<CharT>& options,
201
        std::error_code& ec,
202
        typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
203
3.35k
       : source_(std::forward<Sourceable>(source)),
204
3.35k
         parser_(options, alloc)
205
3.35k
    {
206
3.35k
        parser_.cursor_mode(true);
207
208
3.35k
        if (!read_done())
209
3.35k
        {
210
3.35k
            std::error_code local_ec;
211
3.35k
            read_next(local_ec);
212
3.35k
            if (local_ec)
213
1.34k
            {
214
1.34k
                if (local_ec == json_errc::unexpected_eof)
215
644
                {
216
644
                    done_ = true;
217
644
                }
218
704
                else
219
704
                {
220
704
                    ec = local_ec;
221
704
                }
222
1.34k
            }
223
3.35k
        }
224
3.35k
    }
225
#if !defined(JSONCONS_NO_DEPRECATED)
226
227
    template <typename Sourceable>
228
    basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
229
        Sourceable&& source, 
230
        const basic_json_decode_options<CharT>& options,
231
        std::function<bool(json_errc,const ser_context&)> err_handler,
232
        std::error_code& ec,
233
        typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
234
       : source_(),
235
         parser_(options, err_handler, alloc)
236
    {
237
        parser_.cursor_mode(true);
238
        initialize_with_string_view(std::forward<Sourceable>(source), ec);
239
    }
240
#endif
241
    template <typename Sourceable>
242
    basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
243
        Sourceable&& source, 
244
        const basic_json_decode_options<CharT>& options,
245
        std::error_code& ec,
246
        typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
247
       : source_(),
248
         parser_(options, alloc)
249
    {
250
        parser_.cursor_mode(true);
251
        initialize_with_string_view(std::forward<Sourceable>(source), ec);
252
    }
253
    
254
    basic_json_cursor(const basic_json_cursor&) = delete;
255
    basic_json_cursor(basic_json_cursor&&) = default;
256
    
257
3.35k
    ~basic_json_cursor() = default;
258
259
    // Noncopyable and nonmoveable
260
261
    basic_json_cursor& operator=(const basic_json_cursor&) = delete;
262
    basic_json_cursor& operator=(basic_json_cursor&&) = default;
263
264
    void reset()
265
    {
266
        parser_.reset();
267
        cursor_visitor_.reset();
268
        done_ = false;
269
        if (!read_done())
270
        {
271
            read_next();
272
        }
273
    }
274
275
    template <typename Sourceable>
276
    typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
277
    reset(Sourceable&& source)
278
    {
279
        source_ = std::forward<Sourceable>(source);
280
        parser_.reinitialize();
281
        cursor_visitor_.reset();
282
        done_ = false;
283
        if (!read_done())
284
        {
285
            read_next();
286
        }
287
    }
288
289
    template <typename Sourceable>
290
    typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
291
    reset(Sourceable&& source)
292
    {
293
        source_ = {};
294
        parser_.reinitialize();
295
        cursor_visitor_.reset();
296
        done_ = false;
297
        initialize_with_string_view(std::forward<Sourceable>(source));
298
    }
299
300
    void reset(std::error_code& ec)
301
    {
302
        parser_.reset();
303
        cursor_visitor_.reset();
304
        done_ = false;
305
        if (!read_done())
306
        {
307
            read_next(ec);
308
        }
309
    }
310
311
    template <typename Sourceable>
312
    typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
313
    reset(Sourceable&& source, std::error_code& ec)
314
    {
315
        source_ = std::forward<Sourceable>(source);
316
        parser_.reinitialize();
317
        cursor_visitor_.reset();
318
        done_ = false;
319
        if (!read_done())
320
        {
321
            read_next(ec);
322
        }
323
    }
324
325
    template <typename Sourceable>
326
    typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
327
    reset(Sourceable&& source, std::error_code& ec)
328
    {
329
        source_ = {};
330
        parser_.reinitialize();
331
        done_ = false;
332
        initialize_with_string_view(std::forward<Sourceable>(source), ec);
333
    }
334
335
    bool done() const override
336
5.34k
    {
337
5.34k
        return parser_.done() || done_;
338
5.34k
    }
339
340
    const basic_staj_event<CharT>& current() const override
341
1.98k
    {
342
1.98k
        return cursor_visitor_.event();
343
1.98k
    }
344
345
    void read_to(basic_json_visitor<CharT>& visitor) override
346
0
    {
347
0
        std::error_code ec;
348
0
        read_to(visitor, ec);
349
0
        if (JSONCONS_UNLIKELY(ec))
350
0
        {
351
0
            JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
352
0
        }
353
0
    }
354
355
    void read_to(basic_json_visitor<CharT>& visitor,
356
        std::error_code& ec) override
357
0
    {
358
0
        if (is_begin_container(current().event_type()))
359
0
        {
360
0
            parser_.cursor_mode(false);
361
0
            parser_.mark_level(parser_.level());
362
0
            cursor_visitor_.event().send_json_event(visitor, *this, ec);
363
0
            if (JSONCONS_UNLIKELY(ec))
364
0
            {
365
0
                return;
366
0
            }
367
0
            read_next(visitor, ec);
368
0
            parser_.cursor_mode(true);
369
0
            parser_.mark_level(0);
370
0
            if (current().event_type() == staj_events::begin_object)
371
0
            {
372
0
                cursor_visitor_.end_object(*this);
373
0
            }
374
0
            else
375
0
            {
376
0
                cursor_visitor_.end_array(*this);
377
0
            }
378
0
        }
379
0
        else
380
0
        {
381
0
            cursor_visitor_.event().send_json_event(visitor, *this, ec);
382
0
        }
383
0
    }
384
385
    void next() override
386
0
    {
387
0
        read_next();
388
0
    }
389
390
    void next(std::error_code& ec) override
391
1.98k
    {
392
1.98k
        read_next(ec);
393
1.98k
    }
394
395
    void check_done()
396
    {
397
        std::error_code ec;
398
        check_done(ec);
399
        if (JSONCONS_UNLIKELY(ec))
400
        {
401
            JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
402
        }
403
    }
404
405
    const ser_context& context() const override
406
0
    {
407
0
        return *this;
408
0
    }
409
410
    void check_done(std::error_code& ec)
411
    {
412
        if (source_.is_error())
413
        {
414
            ec = json_errc::source_error;
415
            return;
416
        }   
417
        if (source_.eof())
418
        {
419
            parser_.check_done(ec);
420
            if (JSONCONS_UNLIKELY(ec)) {return;}
421
        }
422
        else
423
        {
424
            do
425
            {
426
                if (parser_.source_exhausted())
427
                {
428
                    auto s = source_.read_buffer(ec);
429
                    if (JSONCONS_UNLIKELY(ec)) {return;}
430
                    if (s.size() > 0)
431
                    {
432
                        parser_.update(s.data(),s.size());
433
                    }
434
                }
435
                if (!parser_.source_exhausted())
436
                {
437
                    parser_.check_done(ec);
438
                    if (JSONCONS_UNLIKELY(ec)) {return;}
439
                }
440
            }
441
            while (!eof());
442
        }
443
    }
444
445
    bool eof() const
446
    {
447
        return parser_.source_exhausted() && source_.eof();
448
    }
449
450
    std::size_t line() const override
451
0
    {
452
0
        return parser_.line();
453
0
    }
454
455
    std::size_t column() const override
456
0
    {
457
0
        return parser_.column();
458
0
    }
459
460
    friend
461
    basic_staj_filter_view<CharT> operator|(basic_json_cursor& cursor, 
462
        std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
463
    {
464
        return basic_staj_filter_view<CharT>(cursor, pred);
465
    }
466
467
private:
468
469
    bool read_done() const 
470
3.35k
    {
471
3.35k
        return parser_.done() || done_;
472
3.35k
    }
473
474
    void initialize_with_string_view(string_view_type sv)
475
    {
476
        std::error_code local_ec;
477
        initialize_with_string_view(sv, local_ec);
478
        if (local_ec)
479
        {
480
            JSONCONS_THROW(ser_error(local_ec, 1, 1));
481
        }
482
    }
483
484
    void initialize_with_string_view(string_view_type sv, std::error_code& ec)
485
    {
486
        auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
487
        if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
488
        {
489
            ec = json_errc::illegal_unicode_character;
490
            return;
491
        }
492
        std::size_t offset = (r.ptr - sv.data());
493
        parser_.update(sv.data()+offset,sv.size()-offset);
494
        bool read_done = parser_.done() || done_;
495
        if (!read_done)
496
        {
497
            std::error_code local_ec;
498
            read_next(local_ec);
499
            if (local_ec)
500
            {
501
                if (local_ec == json_errc::unexpected_eof)
502
                {
503
                    done_ = true;
504
                }
505
                else
506
                {
507
                    ec = local_ec;
508
                }
509
            }
510
        }
511
    }
512
513
    void read_next()
514
0
    {
515
0
        std::error_code ec;
516
0
        read_next(cursor_visitor_, ec);
517
0
        if (JSONCONS_UNLIKELY(ec))
518
0
        {
519
0
            JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
520
0
        }
521
0
    }
522
523
    void read_next(std::error_code& ec)
524
5.34k
    {
525
5.34k
        read_next(cursor_visitor_, ec);
526
5.34k
    }
527
528
    void read_next(basic_json_visitor<CharT>& visitor, std::error_code& ec)
529
5.34k
    {
530
5.34k
        parser_.restart();
531
15.4k
        while (!parser_.stopped())
532
11.4k
        {
533
11.4k
            if (parser_.source_exhausted())
534
11.3k
            {
535
11.3k
                auto s = source_.read_buffer(ec);
536
11.3k
                if (JSONCONS_UNLIKELY(ec)) {return;}
537
11.3k
                if (s.size() > 0)
538
6.99k
                {
539
6.99k
                    parser_.update(s.data(),s.size());
540
6.99k
                    if (JSONCONS_UNLIKELY(ec)) {return;}
541
6.99k
                }
542
11.3k
            }
543
11.4k
            bool eof = parser_.source_exhausted() && source_.eof();
544
11.4k
            parser_.parse_some(visitor, ec);
545
11.4k
            if (JSONCONS_UNLIKELY(ec)) {return;}
546
10.1k
            if (eof)
547
3.74k
            {
548
3.74k
                if (parser_.enter())
549
25
                {
550
25
                    done_ = true;
551
25
                    break;
552
25
                }
553
3.72k
                else if (!parser_.accept())
554
9
                {
555
9
                    ec = json_errc::unexpected_eof;
556
9
                    return;
557
9
                }
558
3.74k
            }
559
10.1k
        }
560
5.34k
    }
561
};
562
563
using json_stream_cursor = basic_json_cursor<char,jsoncons::stream_source<char>>;
564
using json_string_cursor = basic_json_cursor<char,jsoncons::string_source<char>>;
565
using wjson_stream_cursor = basic_json_cursor<wchar_t,jsoncons::stream_source<wchar_t>>;
566
using wjson_string_cursor = basic_json_cursor<wchar_t,jsoncons::string_source<wchar_t>>;
567
568
} // namespace jsoncons
569
570
#endif // JSONCONS_JSON_CURSOR_HPP
571