Coverage Report

Created: 2025-08-24 06:54

/src/jsoncons/include/jsoncons/source.hpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2013-2025 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_SOURCE_HPP
8
#define JSONCONS_SOURCE_HPP
9
10
#include <cstdint>
11
#include <cstring> // std::memcpy
12
#include <exception>
13
#include <functional>
14
#include <istream>
15
#include <iterator>
16
#include <memory> // std::addressof
17
#include <string>
18
#include <type_traits> // std::enable_if
19
#include <vector>
20
21
#include <jsoncons/config/compiler_support.hpp>
22
#include <jsoncons/utility/byte_string.hpp> // jsoncons::byte_traits
23
#include <jsoncons/config/jsoncons_config.hpp>
24
#include <jsoncons/utility/more_type_traits.hpp>
25
26
namespace jsoncons { 
27
28
    template <typename CharT>
29
    class basic_null_istream : public std::basic_istream<CharT>
30
    {
31
        class null_buffer : public std::basic_streambuf<CharT>
32
        {
33
        public:
34
            using typename std::basic_streambuf<CharT>::int_type;
35
            using typename std::basic_streambuf<CharT>::traits_type;
36
37
416
            null_buffer() = default;
38
            null_buffer(const null_buffer&) = delete;
39
            null_buffer(null_buffer&&) = default;
40
41
            null_buffer& operator=(const null_buffer&) = delete;
42
            null_buffer& operator=(null_buffer&&) = default;
43
44
            int_type overflow( int_type ch = typename std::basic_streambuf<CharT>::traits_type::eof()) override
45
0
            {
46
0
                return ch;
47
0
            }
48
        } nb_;
49
    public:
50
        basic_null_istream()
51
416
          : std::basic_istream<CharT>(&nb_)
52
416
        {
53
416
        }
54
55
        basic_null_istream(const null_buffer&) = delete;
56
        basic_null_istream& operator=(const null_buffer&) = delete;
57
        basic_null_istream(basic_null_istream&&) noexcept
58
            : std::basic_istream<CharT>(&nb_)
59
        {
60
        }
61
        basic_null_istream& operator=(basic_null_istream&&) noexcept
62
        {
63
            return *this;
64
        }
65
    };
66
67
    template <typename CharT>
68
    struct char_result
69
    {
70
        CharT value;
71
        bool eof;
72
    };
73
74
    // text sources
75
76
    template <typename CharT>
77
    class stream_source 
78
    {
79
    public:
80
        using value_type = CharT;
81
        static constexpr std::size_t default_max_buffer_size = 16384;
82
    private:
83
        using char_type = typename std::conditional<sizeof(CharT) == sizeof(char),char,CharT>::type;
84
        basic_null_istream<char_type> null_is_;
85
        std::basic_istream<char_type>* stream_ptr_;
86
        std::basic_streambuf<char_type>* sbuf_;
87
        std::size_t position_{0};
88
        std::vector<value_type> buffer_;
89
        const value_type* buffer_data_;
90
        std::size_t buffer_length_{0};
91
    public:
92
93
        stream_source()
94
            : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()),
95
              buffer_(1), buffer_data_(buffer_.data())
96
        {
97
        }
98
99
        // Noncopyable 
100
        stream_source(const stream_source&) = delete;
101
102
        stream_source(stream_source&& other) noexcept
103
            : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()),
104
              buffer_(), buffer_data_(buffer_.data())
105
        {
106
            if (other.stream_ptr_ != &other.null_is_)
107
            {
108
                stream_ptr_ = other.stream_ptr_;
109
                sbuf_ = other.sbuf_;
110
                position_ = other.position_;
111
                buffer_ = std::move(other.buffer_);
112
                buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data());
113
                buffer_length_ =  other.buffer_length_;
114
                other = stream_source();
115
            }
116
        }
117
118
        stream_source(std::basic_istream<char_type>& is, std::size_t buf_size = default_max_buffer_size)
119
416
            : stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()),
120
416
              buffer_(buf_size), buffer_data_(buffer_.data())
121
416
        {
122
416
        }
123
124
416
        ~stream_source() = default;
125
126
        stream_source& operator=(const stream_source&) = delete;
127
128
        stream_source& operator=(stream_source&& other) noexcept
129
        {
130
            if (other.stream_ptr_ != &other.null_is_)
131
            {
132
                stream_ptr_ = other.stream_ptr_;
133
                sbuf_ = other.sbuf_;
134
                position_ = other.position_;
135
                buffer_ = std::move(other.buffer_);
136
                buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data());
137
                buffer_length_ =  other.buffer_length_;
138
                other = stream_source();
139
            }
140
            else
141
            {
142
                stream_ptr_ = &null_is_;
143
                sbuf_ = null_is_.rdbuf();
144
                position_ = 0;
145
                buffer_data_ = buffer_.data();
146
                buffer_length_ =  0;
147
            }
148
            return *this;
149
        }
150
151
        bool eof() const
152
3.41M
        {
153
3.41M
            return buffer_length_ == 0 && stream_ptr_->eof();
154
3.41M
        }
155
156
        bool is_error() const
157
13.7M
        {
158
13.7M
            return stream_ptr_->bad();  
159
13.7M
        }
160
161
        std::size_t position() const
162
312
        {
163
312
            return position_;
164
312
        }
165
166
        void ignore(std::size_t length)
167
11.4M
        {
168
11.4M
            std::size_t len = 0;
169
11.4M
            if (buffer_length_ > 0)
170
11.4M
            {
171
11.4M
                len = (std::min)(buffer_length_, length);
172
11.4M
                position_ += len;
173
11.4M
                buffer_data_ += len;
174
11.4M
                buffer_length_ -= len;
175
11.4M
            }
176
11.4M
            while (len < length)
177
0
            {
178
0
                fill_buffer();
179
0
                if (buffer_length_ == 0)
180
0
                {
181
0
                    break;
182
0
                }
183
0
                std::size_t len2 = (std::min)(buffer_length_, length-len);
184
0
                position_ += len2;
185
0
                buffer_data_ += len2;
186
0
                buffer_length_ -= len2;
187
0
                len += len2;
188
0
            }
189
11.4M
        }
190
191
        char_result<value_type> peek() 
192
41.1M
        {
193
41.1M
            if (buffer_length_ == 0)
194
1.53k
            {
195
1.53k
                fill_buffer();
196
1.53k
            }
197
41.1M
            if (buffer_length_ > 0)
198
41.1M
            {
199
41.1M
                value_type c = *buffer_data_;
200
41.1M
                return char_result<value_type>{c, false};
201
41.1M
            }
202
88
            else
203
88
            {
204
88
                return char_result<value_type>{0, true};
205
88
            }
206
41.1M
        }
207
208
        span<const value_type> read_buffer() 
209
        {
210
            if (buffer_length_ == 0)
211
            {
212
                fill_buffer();
213
            }
214
            const value_type* data = buffer_data_;
215
            std::size_t length = buffer_length_;
216
            buffer_data_ += buffer_length_;
217
            position_ += buffer_length_;
218
            buffer_length_ = 0;
219
220
            return span<const value_type>(data, length);
221
        }
222
223
        std::size_t read(value_type* p, std::size_t length)
224
50.5M
        {
225
50.5M
            std::size_t len = 0;
226
50.5M
            if (buffer_length_ > 0)
227
50.5M
            {
228
50.5M
                len = (std::min)(buffer_length_, length);
229
50.5M
                std::memcpy(p, buffer_data_, len*sizeof(value_type));
230
50.5M
                buffer_data_ += len;
231
50.5M
                buffer_length_ -= len;
232
50.5M
                position_ += len;
233
50.5M
            }
234
50.5M
            if (length - len == 0)
235
50.5M
            {
236
50.5M
                return len;
237
50.5M
            }
238
2.73k
            else if (length - len < buffer_.size())
239
2.73k
            {
240
2.73k
                fill_buffer();
241
2.73k
                if (buffer_length_ > 0)
242
2.62k
                {
243
2.62k
                    std::size_t len2 = (std::min)(buffer_length_, length-len);
244
2.62k
                    std::memcpy(p+len, buffer_data_, len2*sizeof(value_type));
245
2.62k
                    buffer_data_ += len2;
246
2.62k
                    buffer_length_ -= len2;
247
2.62k
                    position_ += len2;
248
2.62k
                    len += len2;
249
2.62k
                }
250
2.73k
                return len;
251
2.73k
            }
252
0
            else
253
0
            {
254
0
                if (stream_ptr_->eof())
255
0
                {
256
0
                    buffer_length_ = 0;
257
0
                    return 0;
258
0
                }
259
0
                JSONCONS_TRY
260
0
                {
261
0
                    std::streamsize count = sbuf_->sgetn(reinterpret_cast<char_type*>(p+len), length-len);
262
0
                    std::size_t len2 = static_cast<std::size_t>(count);
263
0
                    if (len2 < length-len)
264
0
                    {
265
0
                        stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit);
266
0
                    }
267
0
                    len += len2;
268
0
                    position_ += len2;
269
0
                    return len;
270
0
                }
271
0
                JSONCONS_CATCH(const std::exception&)     
272
0
                {
273
0
                    stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit);
274
0
                    return 0;
275
0
                }
276
0
            }
277
50.5M
        }
278
    private:
279
        void fill_buffer()
280
4.26k
        {
281
4.26k
            if (stream_ptr_->eof())
282
175
            {
283
175
                buffer_length_ = 0;
284
175
                return;
285
175
            }
286
287
4.09k
            buffer_data_ = buffer_.data();
288
4.09k
            JSONCONS_TRY
289
4.09k
            {
290
4.09k
                std::streamsize count = sbuf_->sgetn(reinterpret_cast<char_type*>(buffer_.data()), buffer_.size());
291
4.09k
                buffer_length_ = static_cast<std::size_t>(count);
292
293
4.09k
                if (buffer_length_ < buffer_.size())
294
392
                {
295
392
                    stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit);
296
392
                }
297
4.09k
            }
298
4.09k
            JSONCONS_CATCH(const std::exception&)     
299
4.09k
            {
300
0
                stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit);
301
0
                buffer_length_ = 0;
302
0
            }
303
4.09k
        }
304
    };
305
306
    // string_source
307
308
    template <typename CharT>
309
    class string_source 
310
    {
311
    public:
312
        using value_type = CharT;
313
        using string_view_type = jsoncons::basic_string_view<value_type>;
314
    private:
315
        const value_type* data_{nullptr};
316
        const value_type* current_{nullptr};
317
        const value_type* end_{nullptr};
318
    public:
319
        string_source() noexcept = default;
320
321
        // Noncopyable 
322
        string_source(const string_source&) = delete;
323
324
        string_source(string_source&& other) = default;
325
326
        template <typename Sourceable>
327
        string_source(const Sourceable& s,
328
                      typename std::enable_if<ext_traits::is_sequence_of<Sourceable,value_type>::value>::type* = 0)
329
            : data_(s.data()), current_(s.data()), end_(s.data()+s.size())
330
        {
331
        }
332
333
        string_source(const value_type* data)
334
            : data_(data), current_(data), end_(data+std::char_traits<value_type>::length(data))
335
        {
336
        }
337
338
        string_source& operator=(const string_source&) = delete;
339
        string_source& operator=(string_source&& other) = default;
340
341
        bool eof() const
342
        {
343
            return current_ == end_;  
344
        }
345
346
        bool is_error() const
347
        {
348
            return false;  
349
        }
350
351
        std::size_t position() const
352
        {
353
            return (current_ - data_)/sizeof(value_type);
354
        }
355
356
        void ignore(std::size_t count)
357
        {
358
            std::size_t len;
359
            if (std::size_t(end_ - current_) < count)
360
            {
361
                len = end_ - current_;
362
            }
363
            else
364
            {
365
                len = count;
366
            }
367
            current_ += len;
368
        }
369
370
        char_result<value_type> peek() 
371
        {
372
            return current_ < end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
373
        }
374
375
        span<const value_type> read_buffer() 
376
        {
377
            const value_type* data = current_;
378
            std::size_t length = end_ - current_;
379
            current_ = end_;
380
381
            return span<const value_type>(data, length);
382
        }
383
384
        std::size_t read(value_type* p, std::size_t length)
385
        {
386
            std::size_t len;
387
            if (std::size_t(end_ - current_) < length)
388
            {
389
                len = end_ - current_;
390
            }
391
            else
392
            {
393
                len = length;
394
            }
395
            std::memcpy(p, current_, len*sizeof(value_type));
396
            current_  += len;
397
            return len;
398
        }
399
    };
400
401
    // iterator source
402
403
    template <typename IteratorT>
404
    class iterator_source
405
    {
406
    public:
407
        using value_type = typename std::iterator_traits<IteratorT>::value_type;
408
    private:
409
        static constexpr std::size_t default_max_buffer_size = 16384;
410
411
        IteratorT current_;
412
        IteratorT end_;
413
        std::size_t position_{0};
414
        std::vector<value_type> buffer_;
415
        std::size_t buffer_length_{0};
416
417
        using difference_type = typename std::iterator_traits<IteratorT>::difference_type;
418
        using iterator_category = typename std::iterator_traits<IteratorT>::iterator_category;
419
    public:
420
421
        // Noncopyable 
422
        iterator_source(const iterator_source&) = delete;
423
424
        iterator_source(iterator_source&& other) = default;
425
426
        iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size)
427
            : current_(first), end_(last), buffer_(buf_size)
428
        {
429
        }
430
        
431
        ~iterator_source() = default;
432
433
        iterator_source& operator=(const iterator_source&) = delete;
434
        iterator_source& operator=(iterator_source&& other) = default;
435
436
        bool eof() const
437
        {
438
            return !(current_ != end_);  
439
        }
440
441
        bool is_error() const
442
        {
443
            return false;  
444
        }
445
446
        std::size_t position() const
447
        {
448
            return position_;
449
        }
450
451
        void ignore(std::size_t count)
452
        {
453
            while (count-- > 0 && current_ != end_)
454
            {
455
                ++position_;
456
                ++current_;
457
            }
458
        }
459
460
        char_result<value_type> peek() 
461
        {
462
            return current_ != end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
463
        }
464
465
        span<const value_type> read_buffer() 
466
        {
467
            if (buffer_length_ == 0)
468
            {
469
                buffer_length_ = read(buffer_.data(), buffer_.size());
470
            }
471
            std::size_t length = buffer_length_;
472
            buffer_length_ = 0;
473
474
            return span<const value_type>(buffer_.data(), length);
475
        }
476
477
        template <typename Category = iterator_category>
478
        typename std::enable_if<std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
479
        read(value_type* data, std::size_t length)
480
        {
481
            std::size_t count = (std::min)(length, static_cast<std::size_t>(std::distance(current_, end_)));
482
483
            //JSONCONS_COPY(current_, current_ + count, data);
484
485
            auto end = current_ + count;
486
            value_type* p = data;
487
            while (current_ != end)
488
            {
489
                *p++ = *current_++;
490
            }
491
492
            //current_ += count;
493
            position_ += count;
494
495
            return count;
496
        }
497
498
        template <typename Category = iterator_category>
499
        typename std::enable_if<!std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
500
        read(value_type* data, std::size_t length)
501
        {
502
            value_type* p = data;
503
            value_type* pend = data + length;
504
505
            while (p < pend && current_ != end_)
506
            {
507
                *p = static_cast<value_type>(*current_);
508
                ++p;
509
                ++current_;
510
            }
511
512
            position_ += (p - data);
513
514
            return p - data;
515
        }
516
    };
517
518
    // binary sources
519
520
    using binary_stream_source = stream_source<uint8_t>;
521
522
    class bytes_source 
523
    {
524
    public:
525
        typedef uint8_t value_type;
526
    private:
527
        const value_type* data_{nullptr};
528
        const value_type* current_{nullptr};
529
        const value_type* end_{nullptr};
530
    public:
531
        bytes_source() noexcept = default;
532
533
        // Noncopyable 
534
        bytes_source(const bytes_source&) = delete;
535
536
        bytes_source(bytes_source&&) = default;
537
538
        template <typename Sourceable>
539
        bytes_source(const Sourceable& source,
540
                     typename std::enable_if<ext_traits::is_byte_sequence<Sourceable>::value,int>::type = 0)
541
            : data_(reinterpret_cast<const value_type*>(source.data())), 
542
              current_(data_), 
543
              end_(data_+source.size())
544
        {
545
        }
546
547
        bytes_source& operator=(const bytes_source&) = delete;
548
        bytes_source& operator=(bytes_source&&) = default;
549
550
        bool eof() const
551
0
        {
552
0
            return current_ == end_;  
553
0
        }
554
555
        bool is_error() const
556
0
        {
557
0
            return false;  
558
0
        }
559
560
        std::size_t position() const
561
0
        {
562
0
            return current_ - data_;
563
0
        }
564
565
        void ignore(std::size_t count)
566
0
        {
567
0
            std::size_t len;
568
0
            if (std::size_t(end_ - current_) < count)
569
0
            {
570
0
                len = end_ - current_;
571
0
            }
572
0
            else
573
0
            {
574
0
                len = count;
575
0
            }
576
0
            current_ += len;
577
0
        }
578
579
        char_result<value_type> peek() 
580
0
        {
581
0
            return current_ < end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
582
0
        }
583
584
        span<const value_type> read_buffer() 
585
0
        {
586
0
            const value_type* data = current_;
587
0
            std::size_t length = end_ - current_;
588
0
            current_ = end_;
589
0
590
0
            return span<const value_type>(data, length);
591
0
        }
592
593
        std::size_t read(value_type* p, std::size_t length)
594
0
        {
595
0
            std::size_t len;
596
0
            if (std::size_t(end_ - current_) < length)
597
0
            {
598
0
                len = end_ - current_;
599
0
            }
600
0
            else
601
0
            {
602
0
                len = length;
603
0
            }
604
0
            std::memcpy(p, current_, len*sizeof(value_type));
605
0
            current_  += len;
606
0
            return len;
607
0
        }
608
    };
609
610
    // binary_iterator source
611
612
    template <typename IteratorT>
613
    class binary_iterator_source
614
    {
615
    public:
616
        using value_type = uint8_t;
617
    private:
618
        static constexpr std::size_t default_max_buffer_size = 16384;
619
620
        IteratorT current_;
621
        IteratorT end_;
622
        std::size_t position_{0};
623
        std::vector<value_type> buffer_;
624
        std::size_t buffer_length_{0};
625
626
        using difference_type = typename std::iterator_traits<IteratorT>::difference_type;
627
        using iterator_category = typename std::iterator_traits<IteratorT>::iterator_category;
628
    public:
629
630
        // Noncopyable 
631
        binary_iterator_source(const binary_iterator_source&) = delete;
632
633
        binary_iterator_source(binary_iterator_source&& other) = default;
634
635
        binary_iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size)
636
            : current_(first), end_(last), buffer_(buf_size)
637
        {
638
        }
639
640
        binary_iterator_source& operator=(const binary_iterator_source&) = delete;
641
        binary_iterator_source& operator=(binary_iterator_source&& other) = default;
642
643
        bool eof() const
644
        {
645
            return !(current_ != end_);  
646
        }
647
648
        bool is_error() const
649
        {
650
            return false;  
651
        }
652
653
        std::size_t position() const
654
        {
655
            return position_;
656
        }
657
658
        void ignore(std::size_t count)
659
        {
660
            while (count-- > 0 && current_ != end_)
661
            {
662
                ++position_;
663
                ++current_;
664
            }
665
        }
666
667
        char_result<value_type> peek() 
668
        {
669
            return current_ != end_ ? char_result<value_type>{static_cast<value_type>(*current_), false} : char_result<value_type>{0, true};
670
        }
671
672
        span<const value_type> read_buffer() 
673
        {
674
            if (buffer_length_ == 0)
675
            {
676
                buffer_length_ = read(buffer_.data(), buffer_.size());
677
            }
678
            std::size_t length = buffer_length_;
679
            buffer_length_ = 0;
680
681
            return span<const value_type>(buffer_.data(), length);
682
        }
683
684
        template <typename Category = iterator_category>
685
        typename std::enable_if<std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
686
        read(value_type* data, std::size_t length)
687
        {
688
            std::size_t count = (std::min)(length, static_cast<std::size_t>(std::distance(current_, end_)));
689
            //JSONCONS_COPY(current_, current_ + count, data);
690
691
            auto end = current_ + count;
692
            value_type* p = data;
693
            while (current_ != end)
694
            {
695
                *p++ = *current_++;
696
            }
697
698
            //current_ += count;
699
            position_ += count;
700
701
            return count;
702
        }
703
704
        template <typename Category = iterator_category>
705
        typename std::enable_if<!std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
706
        read(value_type* data, std::size_t length)
707
        {
708
            value_type* p = data;
709
            value_type* pend = data + length;
710
711
            while (p < pend && current_ != end_)
712
            {
713
                *p = static_cast<value_type>(*current_);
714
                ++p;
715
                ++current_;
716
            }
717
718
            position_ += (p - data);
719
720
            return p - data;
721
        }
722
    };
723
724
    template <typename Source>
725
    struct source_reader
726
    {
727
        using value_type = typename Source::value_type;
728
        static constexpr std::size_t max_buffer_length = 16384;
729
730
        template <typename Container>
731
        static
732
        typename std::enable_if<std::is_convertible<value_type,typename Container::value_type>::value &&
733
                                ext_traits::has_reserve<Container>::value &&
734
                                ext_traits::has_data_exact<value_type*,Container>::value 
735
     , std::size_t>::type
736
        read(Source& source, Container& v, std::size_t length)
737
        {
738
            std::size_t unread = length;
739
740
            std::size_t n = (std::min)(max_buffer_length, unread);
741
            while (n > 0 && !source.eof())
742
            {
743
                std::size_t offset = v.size();
744
                v.resize(v.size()+n);
745
                std::size_t actual = source.read(v.data()+offset, n);
746
                unread -= actual;
747
                n = (std::min)(max_buffer_length, unread);
748
            }
749
750
            return length - unread;
751
        }
752
753
        template <typename Container>
754
        static
755
        typename std::enable_if<std::is_convertible<value_type,typename Container::value_type>::value &&
756
                                ext_traits::has_reserve<Container>::value &&
757
                                !ext_traits::has_data_exact<value_type*, Container>::value 
758
     , std::size_t>::type
759
        read(Source& source, Container& v, std::size_t length)
760
7.02M
        {
761
7.02M
            std::size_t unread = length;
762
763
7.02M
            std::size_t n = (std::min)(max_buffer_length, unread);
764
10.4M
            while (n > 0 && !source.eof())
765
3.41M
            {
766
3.41M
                v.reserve(v.size()+n);
767
3.41M
                std::size_t actual = 0;
768
27.4M
                while (actual < n)
769
24.0M
                {
770
24.0M
                    typename Source::value_type c{};
771
24.0M
                    if (source.read(&c,1) != 1)
772
34
                    {
773
34
                        break;
774
34
                    }
775
24.0M
                    v.push_back(c);
776
24.0M
                    ++actual;
777
24.0M
                }
778
3.41M
                unread -= actual;
779
3.41M
                n = (std::min)(max_buffer_length, unread);
780
3.41M
            }
781
782
7.02M
            return length - unread;
783
7.02M
        }
784
    };
785
#if __cplusplus >= 201703L
786
// not needed for C++17
787
#else
788
    template <typename Source>
789
    constexpr std::size_t source_reader<Source>::max_buffer_length;
790
#endif
791
792
} // namespace jsoncons
793
794
#endif // JSONCONS_SOURCE_HPP