Coverage Report

Created: 2025-11-11 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jsoncons/include/jsoncons/json_encoder.hpp
Line
Count
Source
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_JSON_ENCODER_HPP
8
#define JSONCONS_JSON_ENCODER_HPP
9
10
#include <array> // std::array
11
#include <cstddef>
12
#include <cstdint>
13
#include <cmath> // std::isfinite, std::isnan
14
#include <limits> // std::numeric_limits
15
#include <memory>
16
#include <string>
17
#include <utility> // std::move
18
#include <vector>
19
20
#include <jsoncons/config/compiler_support.hpp>
21
#include <jsoncons/config/jsoncons_config.hpp>
22
#include <jsoncons/utility/write_number.hpp>
23
#include <jsoncons/json_error.hpp>
24
#include <jsoncons/json_exception.hpp>
25
#include <jsoncons/json_options.hpp>
26
#include <jsoncons/json_type.hpp>
27
#include <jsoncons/json_visitor.hpp>
28
#include <jsoncons/semantic_tag.hpp>
29
#include <jsoncons/ser_util.hpp>
30
#include <jsoncons/sink.hpp>
31
#include <jsoncons/utility/bigint.hpp>
32
#include <jsoncons/utility/byte_string.hpp>
33
#include <jsoncons/utility/unicode_traits.hpp>
34
35
namespace jsoncons { 
36
namespace detail {
37
38
    inline
39
    bool is_control_character(uint32_t c)
40
0
    {
41
0
        return c <= 0x1F || c == 0x7f;
42
0
    }
43
44
    inline
45
    bool is_non_ascii_codepoint(uint32_t cp)
46
0
    {
47
0
        return cp >= 0x80;
48
0
    }
49
50
    template <typename CharT,typename Sink>
51
    std::size_t escape_string(const CharT* s, std::size_t length,
52
                         bool escape_all_non_ascii, bool escape_solidus,
53
                         Sink& sink)
54
    {
55
        std::size_t count = 0;
56
        const CharT* begin = s;
57
        const CharT* end = s + length;
58
        for (const CharT* it = begin; it != end; ++it)
59
        {
60
            CharT c = *it;
61
            switch (c)
62
            {
63
                case '\\':
64
                    sink.push_back('\\');
65
                    sink.push_back('\\');
66
                    count += 2;
67
                    break;
68
                case '"':
69
                    sink.push_back('\\');
70
                    sink.push_back('\"');
71
                    count += 2;
72
                    break;
73
                case '\b':
74
                    sink.push_back('\\');
75
                    sink.push_back('b');
76
                    count += 2;
77
                    break;
78
                case '\f':
79
                    sink.push_back('\\');
80
                    sink.push_back('f');
81
                    count += 2;
82
                    break;
83
                case '\n':
84
                    sink.push_back('\\');
85
                    sink.push_back('n');
86
                    count += 2;
87
                    break;
88
                case '\r':
89
                    sink.push_back('\\');
90
                    sink.push_back('r');
91
                    count += 2;
92
                    break;
93
                case '\t':
94
                    sink.push_back('\\');
95
                    sink.push_back('t');
96
                    count += 2;
97
                    break;
98
                default:
99
                    if (escape_solidus && c == '/')
100
                    {
101
                        sink.push_back('\\');
102
                        sink.push_back('/');
103
                        count += 2;
104
                    }
105
                    else if (is_control_character(c) || escape_all_non_ascii)
106
                    {
107
                        // convert to codepoint
108
                        uint32_t cp;
109
                        auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict);
110
                        if (r.ec != unicode_traits::conv_errc())
111
                        {
112
                            JSONCONS_THROW(ser_error(json_errc::illegal_codepoint));
113
                        }
114
                        it = r.ptr - 1;
115
                        if (is_non_ascii_codepoint(cp) || is_control_character(c))
116
                        {
117
                            if (cp > 0xFFFF)
118
                            {
119
                                cp -= 0x10000;
120
                                uint32_t first = (cp >> 10) + 0xD800;
121
                                uint32_t second = ((cp & 0x03FF) + 0xDC00);
122
123
                                sink.push_back('\\');
124
                                sink.push_back('u');
125
                                sink.push_back(jsoncons::utility::to_hex_character(first >> 12 & 0x000F));
126
                                sink.push_back(jsoncons::utility::to_hex_character(first >> 8 & 0x000F));
127
                                sink.push_back(jsoncons::utility::to_hex_character(first >> 4 & 0x000F));
128
                                sink.push_back(jsoncons::utility::to_hex_character(first & 0x000F));
129
                                sink.push_back('\\');
130
                                sink.push_back('u');
131
                                sink.push_back(jsoncons::utility::to_hex_character(second >> 12 & 0x000F));
132
                                sink.push_back(jsoncons::utility::to_hex_character(second >> 8 & 0x000F));
133
                                sink.push_back(jsoncons::utility::to_hex_character(second >> 4 & 0x000F));
134
                                sink.push_back(jsoncons::utility::to_hex_character(second & 0x000F));
135
                                count += 12;
136
                            }
137
                            else
138
                            {
139
                                sink.push_back('\\');
140
                                sink.push_back('u');
141
                                sink.push_back(jsoncons::utility::to_hex_character(cp >> 12 & 0x000F));
142
                                sink.push_back(jsoncons::utility::to_hex_character(cp >> 8 & 0x000F));
143
                                sink.push_back(jsoncons::utility::to_hex_character(cp >> 4 & 0x000F));
144
                                sink.push_back(jsoncons::utility::to_hex_character(cp & 0x000F));
145
                                count += 6;
146
                            }
147
                        }
148
                        else
149
                        {
150
                            sink.push_back(c);
151
                            ++count;
152
                        }
153
                    }
154
                    else
155
                    {
156
                        sink.push_back(c);
157
                        ++count;
158
                    }
159
                    break;
160
            }
161
        }
162
        return count;
163
    }
164
165
    inline
166
    byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1,
167
                                                              byte_string_chars_format format2,
168
                                                              byte_string_chars_format default_format = byte_string_chars_format::base64url)
169
0
    {
170
0
        byte_string_chars_format sink;
171
0
        switch (format1)
172
0
        {
173
0
            case byte_string_chars_format::base16:
174
0
            case byte_string_chars_format::base64:
175
0
            case byte_string_chars_format::base64url:
176
0
                sink = format1;
177
0
                break;
178
0
            default:
179
0
                switch (format2)
180
0
                {
181
0
                    case byte_string_chars_format::base64url:
182
0
                    case byte_string_chars_format::base64:
183
0
                    case byte_string_chars_format::base16:
184
0
                        sink = format2;
185
0
                        break;
186
0
                    default: // base64url
187
0
                    {
188
0
                        sink = default_format;
189
0
                        break;
190
0
                    }
191
0
                }
192
0
                break;
193
0
        }
194
0
        return sink;
195
0
    }
196
197
} // namespace detail
198
199
    template <typename CharT,typename Sink=jsoncons::stream_sink<CharT>,typename Allocator=std::allocator<char>>
200
    class basic_json_encoder final : public basic_json_visitor<CharT>
201
    {
202
        static const jsoncons::basic_string_view<CharT> null_constant()
203
        {
204
            static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null");
205
            return k;
206
        }
207
        static const jsoncons::basic_string_view<CharT> true_constant()
208
        {
209
            static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true");
210
            return k;
211
        }
212
        static const jsoncons::basic_string_view<CharT> false_constant()
213
        {
214
            static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false");
215
            return k;
216
        }
217
    public:
218
        using allocator_type = Allocator;
219
        using char_type = CharT;
220
        using typename basic_json_visitor<CharT>::string_view_type;
221
        using sink_type = Sink;
222
        using string_type = typename basic_json_encode_options<CharT>::string_type;
223
224
    private:
225
        enum class container_type {object, array};
226
227
        class encoding_context
228
        {
229
            container_type type_;
230
            line_split_kind line_splits_;
231
            bool indent_before_;
232
            bool new_line_after_;
233
            std::size_t begin_pos_{0};
234
            std::size_t data_pos_{0};
235
            std::size_t count_{0};
236
        public:
237
            encoding_context(container_type type, line_split_kind split_lines, bool indent_once,
238
                             std::size_t begin_pos, std::size_t data_pos) noexcept
239
               : type_(type), line_splits_(split_lines), indent_before_(indent_once), new_line_after_(false),
240
                 begin_pos_(begin_pos), data_pos_(data_pos)
241
            {
242
            }
243
244
            encoding_context(const encoding_context&) = default;
245
            
246
            ~encoding_context() = default;
247
            
248
            encoding_context& operator=(const encoding_context&) = default;
249
250
            void set_position(std::size_t pos)
251
            {
252
                data_pos_ = pos;
253
            }
254
255
            std::size_t begin_pos() const
256
            {
257
                return begin_pos_;
258
            }
259
260
            std::size_t data_pos() const
261
            {
262
                return data_pos_;
263
            }
264
265
            std::size_t count() const
266
            {
267
                return count_;
268
            }
269
270
            void increment_count()
271
            {
272
                ++count_;
273
            }
274
275
            bool new_line_after() const
276
            {
277
                return new_line_after_;
278
            }
279
280
            void new_line_after(bool value) 
281
            {
282
                new_line_after_ = value;
283
            }
284
285
            bool is_object() const
286
            {
287
                return type_ == container_type::object;
288
            }
289
290
            bool is_array() const
291
            {
292
                return type_ == container_type::array;
293
            }
294
295
            bool is_same_line() const
296
            {
297
                return line_splits_ == line_split_kind::same_line;
298
            }
299
300
            bool is_new_line() const
301
            {
302
                return line_splits_ == line_split_kind::new_line;
303
            }
304
305
            bool is_multi_line() const
306
            {
307
                return line_splits_ == line_split_kind::multi_line;
308
            }
309
310
            bool is_indent_once() const
311
            {
312
                return count_ == 0 ? indent_before_ : false;
313
            }
314
315
        };
316
        using encoding_context_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context>;
317
318
        Sink sink_;
319
        basic_json_encode_options<CharT> options_;
320
        jsoncons::utility::write_double fp_;
321
322
        std::vector<encoding_context,encoding_context_allocator_type> stack_;
323
        int indent_amount_{0};
324
        std::size_t column_{0};
325
        std::basic_string<CharT> colon_str_;
326
        std::basic_string<CharT> comma_str_;
327
        std::basic_string<CharT> open_object_brace_str_;
328
        std::basic_string<CharT> close_object_brace_str_;
329
        std::basic_string<CharT> open_array_bracket_str_;
330
        std::basic_string<CharT> close_array_bracket_str_;
331
        int nesting_depth_{0};
332
    public:
333
334
        // Noncopyable and nonmoveable
335
        basic_json_encoder(const basic_json_encoder&) = delete;
336
        basic_json_encoder(basic_json_encoder&&) = delete;
337
338
        basic_json_encoder(Sink&& sink, 
339
                           const Allocator& alloc = Allocator())
340
            : basic_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
341
        {
342
        }
343
344
        basic_json_encoder(Sink&& sink, 
345
                           const basic_json_encode_options<CharT>& options, 
346
                           const Allocator& alloc = Allocator())
347
           : sink_(std::forward<Sink>(sink)), 
348
             options_(options),
349
             fp_(options.float_format(), options.precision()),
350
             stack_(alloc)
351
        {
352
            switch (options.spaces_around_colon())
353
            {
354
                case spaces_option::space_after:
355
                    colon_str_ = std::basic_string<CharT>({':',' '});
356
                    break;
357
                case spaces_option::space_before:
358
                    colon_str_ = std::basic_string<CharT>({' ',':'});
359
                    break;
360
                case spaces_option::space_before_and_after:
361
                    colon_str_ = std::basic_string<CharT>({' ',':',' '});
362
                    break;
363
                default:
364
                    colon_str_.push_back(':');
365
                    break;
366
            }
367
            switch (options.spaces_around_comma())
368
            {
369
                case spaces_option::space_after:
370
                    comma_str_ = std::basic_string<CharT>({',',' '});
371
                    break;
372
                case spaces_option::space_before:
373
                    comma_str_ = std::basic_string<CharT>({' ',','});
374
                    break;
375
                case spaces_option::space_before_and_after:
376
                    comma_str_ = std::basic_string<CharT>({' ',',',' '});
377
                    break;
378
                default:
379
                    comma_str_.push_back(',');
380
                    break;
381
            }
382
            if (options.pad_inside_object_braces())
383
            {
384
                open_object_brace_str_ = std::basic_string<CharT>({'{', ' '});
385
                close_object_brace_str_ = std::basic_string<CharT>({' ', '}'});
386
            }
387
            else
388
            {
389
                open_object_brace_str_.push_back('{');
390
                close_object_brace_str_.push_back('}');
391
            }
392
            if (options.pad_inside_array_brackets())
393
            {
394
                open_array_bracket_str_ = std::basic_string<CharT>({'[', ' '});
395
                close_array_bracket_str_ = std::basic_string<CharT>({' ', ']'});
396
            }
397
            else
398
            {
399
                open_array_bracket_str_.push_back('[');
400
                close_array_bracket_str_.push_back(']');
401
            }
402
        }
403
404
        ~basic_json_encoder() noexcept
405
        {
406
            JSONCONS_TRY
407
            {
408
                sink_.flush();
409
            }
410
            JSONCONS_CATCH(...)
411
            {
412
            }
413
        }
414
415
        basic_json_encoder& operator=(const basic_json_encoder&) = delete;
416
        basic_json_encoder& operator=(basic_json_encoder&&) = delete;
417
418
        void reset()
419
        {
420
            stack_.clear();
421
            indent_amount_ = 0;
422
            column_ = 0;
423
            nesting_depth_ = 0;
424
        }
425
426
        void reset(Sink&& sink)
427
        {
428
            sink_ = std::move(sink);
429
            reset();
430
        }
431
432
    private:
433
        // Implementing methods
434
        void visit_flush() final
435
        {
436
            sink_.flush();
437
        }
438
439
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) final
440
        {
441
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
442
            {
443
                ec = json_errc::max_nesting_depth_exceeded;
444
                JSONCONS_VISITOR_RETURN;
445
            } 
446
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
447
            {
448
                sink_.append(comma_str_.data(),comma_str_.length());
449
                column_ += comma_str_.length();
450
            }
451
452
            if (!stack_.empty()) // object or array
453
            {
454
                if (stack_.back().is_object())
455
                {
456
                    switch (options_.object_object_line_splits())
457
                    {
458
                        case line_split_kind::same_line:
459
                        case line_split_kind::new_line:
460
                            if (column_ >= options_.line_length_limit())
461
                            {
462
                                break_line();
463
                            }
464
                            break;
465
                        default: // multi_line
466
                            break;
467
                    }
468
                    stack_.emplace_back(container_type::object,options_.object_object_line_splits(), false,
469
                                        column_, column_+open_object_brace_str_.length());
470
                }
471
                else // array
472
                {
473
                    switch (options_.array_object_line_splits())
474
                    {
475
                        case line_split_kind::same_line:
476
                            if (column_ >= options_.line_length_limit())
477
                            {
478
                                //stack_.back().new_line_after(true);
479
                                new_line();
480
                            }
481
                            else
482
                            {
483
                                stack_.back().new_line_after(true);
484
                                new_line();
485
                            }
486
                            break;
487
                        case line_split_kind::new_line:
488
                            stack_.back().new_line_after(true);
489
                            new_line();
490
                            break;
491
                        default: // multi_line
492
                            stack_.back().new_line_after(true);
493
                            new_line();
494
                            break;
495
                    }
496
                    stack_.emplace_back(container_type::object,options_.array_object_line_splits(), false,
497
                                        column_, column_+open_object_brace_str_.length());
498
                }
499
            }
500
            else 
501
            {
502
                stack_.emplace_back(container_type::object, options_.line_splits(), false,
503
                                    column_, column_+open_object_brace_str_.length());
504
            }
505
            indent();
506
            
507
            sink_.append(open_object_brace_str_.data(), open_object_brace_str_.length());
508
            column_ += open_object_brace_str_.length();
509
            JSONCONS_VISITOR_RETURN;
510
        }
511
512
        JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) final
513
        {
514
            JSONCONS_ASSERT(!stack_.empty());
515
            --nesting_depth_;
516
517
            unindent();
518
            if (stack_.back().new_line_after())
519
            {
520
                new_line();
521
            }
522
            stack_.pop_back();
523
            sink_.append(close_object_brace_str_.data(), close_object_brace_str_.length());
524
            column_ += close_object_brace_str_.length();
525
526
            end_value();
527
            JSONCONS_VISITOR_RETURN;
528
        }
529
530
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) final
531
        {
532
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
533
            {
534
                ec = json_errc::max_nesting_depth_exceeded;
535
                JSONCONS_VISITOR_RETURN;
536
            } 
537
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
538
            {
539
                sink_.append(comma_str_.data(),comma_str_.length());
540
                column_ += comma_str_.length();
541
            }
542
            if (!stack_.empty())
543
            {
544
                if (stack_.back().is_object())
545
                {
546
                    switch (options_.object_array_line_splits())
547
                    {
548
                        case line_split_kind::same_line:
549
                            stack_.emplace_back(container_type::array,options_.object_array_line_splits(),false,
550
                                                column_, column_ + open_array_bracket_str_.length());
551
                            break;
552
                        case line_split_kind::new_line:
553
                        {
554
                            stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true,
555
                                                column_, column_+open_array_bracket_str_.length());
556
                            break;
557
                        }
558
                        default: // multi_line
559
                            stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true,
560
                                                column_, column_+open_array_bracket_str_.length());
561
                            break;
562
                    }
563
                }
564
                else // array
565
                {
566
                    switch (options_.array_array_line_splits())
567
                    {
568
                        case line_split_kind::same_line:
569
                            if (stack_.back().is_multi_line())
570
                            {
571
                                stack_.back().new_line_after(true);
572
                                new_line();
573
                            }
574
                            stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
575
                                                column_, column_+open_array_bracket_str_.length());
576
                            break;
577
                        case line_split_kind::new_line:
578
                            stack_.back().new_line_after(true);
579
                            new_line();
580
                            stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
581
                                                column_, column_+open_array_bracket_str_.length());
582
                            break;
583
                        default: // multi_line
584
                            stack_.back().new_line_after(true);
585
                            new_line();
586
                            stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
587
                                                column_, column_+open_array_bracket_str_.length());
588
                            //new_line();
589
                            break;
590
                    }
591
                }
592
            }
593
            else 
594
            {
595
                stack_.emplace_back(container_type::array, options_.line_splits(), false,
596
                                    column_, column_+open_array_bracket_str_.length());
597
            }
598
            indent();
599
            sink_.append(open_array_bracket_str_.data(), open_array_bracket_str_.length());
600
            column_ += open_array_bracket_str_.length();
601
            JSONCONS_VISITOR_RETURN;
602
        }
603
604
        JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) final
605
        {
606
            JSONCONS_ASSERT(!stack_.empty());
607
            --nesting_depth_;
608
609
            unindent();
610
            if (stack_.back().new_line_after())
611
            {
612
                new_line();
613
            }
614
            stack_.pop_back();
615
            sink_.append(close_array_bracket_str_.data(), close_array_bracket_str_.length());
616
            column_ += close_array_bracket_str_.length();
617
            end_value();
618
            JSONCONS_VISITOR_RETURN;
619
        }
620
621
        JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code&) final
622
        {
623
            JSONCONS_ASSERT(!stack_.empty());
624
            if (stack_.back().count() > 0)
625
            {
626
                sink_.append(comma_str_.data(),comma_str_.length());
627
                column_ += comma_str_.length();
628
            }
629
630
            if (stack_.back().is_multi_line())
631
            {
632
                stack_.back().new_line_after(true);
633
                new_line();
634
            }
635
            else if (stack_.back().count() > 0 && column_ >= options_.line_length_limit())
636
            {
637
                //stack_.back().new_line_after(true);
638
                new_line(stack_.back().data_pos());
639
            }
640
641
            if (stack_.back().count() == 0)
642
            {
643
                stack_.back().set_position(column_);
644
            }
645
            sink_.push_back('\"');
646
            std::size_t length = jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
647
            sink_.push_back('\"');
648
            sink_.append(colon_str_.data(),colon_str_.length());
649
            column_ += (length+2+colon_str_.length());
650
            JSONCONS_VISITOR_RETURN;
651
        }
652
653
        JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) final
654
        {
655
            if (!stack_.empty()) 
656
            {
657
                if (stack_.back().is_array())
658
                {
659
                    begin_scalar_value();
660
                }
661
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
662
                {
663
                    break_line();
664
                }
665
            }
666
667
            sink_.append(null_constant().data(), null_constant().size());
668
            column_ += null_constant().size();
669
670
            end_value();
671
            JSONCONS_VISITOR_RETURN;
672
        }
673
674
        JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) final
675
        {
676
            if (!stack_.empty()) 
677
            {
678
                if (stack_.back().is_array())
679
                {
680
                    begin_scalar_value();
681
                }
682
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
683
                {
684
                    break_line();
685
                }
686
            }
687
            
688
            write_string(sv, tag, context, ec);
689
690
            end_value();
691
            JSONCONS_VISITOR_RETURN;
692
        }
693
694
        void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) 
695
        {
696
            if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus()))
697
            {
698
                //std::cout << "noesc\n";
699
                sink_.push_back('\"');
700
                const CharT* begin = sv.data();
701
                const CharT* end = begin + sv.length();
702
                for (const CharT* it = begin; it != end; ++it)
703
                {
704
                    sink_.push_back(*it);
705
                }
706
                sink_.push_back('\"');
707
                column_ += (sv.length()+2);
708
            }
709
            else if (tag == semantic_tag::bigint)
710
            {
711
                write_bignum_value(sv);
712
            }
713
            else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw)
714
            {
715
                write_bignum_value(sv);
716
            }
717
            else
718
            {
719
                //if (tag != semantic_tag::bigdec)
720
                //    std::cout << "esc\n";
721
                sink_.push_back('\"');
722
                std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
723
                sink_.push_back('\"');
724
                column_ += (length+2);
725
            }           
726
        }
727
728
        JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, 
729
                                  semantic_tag tag,
730
                                  const ser_context&,
731
                                  std::error_code&) final
732
        {
733
            if (!stack_.empty()) 
734
            {
735
                if (stack_.back().is_array())
736
                {
737
                    begin_scalar_value();
738
                }
739
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
740
                {
741
                    break_line();
742
                }
743
            }
744
745
            byte_string_chars_format encoding_hint;
746
            switch (tag)
747
            {
748
                case semantic_tag::base16:
749
                    encoding_hint = byte_string_chars_format::base16;
750
                    break;
751
                case semantic_tag::base64:
752
                    encoding_hint = byte_string_chars_format::base64;
753
                    break;
754
                case semantic_tag::base64url:
755
                    encoding_hint = byte_string_chars_format::base64url;
756
                    break;
757
                default:
758
                    encoding_hint = byte_string_chars_format::none;
759
                    break;
760
            }
761
762
            byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), 
763
                                                                                                 encoding_hint, 
764
                                                                                                 byte_string_chars_format::base64url);
765
            switch (format)
766
            {
767
                case byte_string_chars_format::base16:
768
                {
769
                    sink_.push_back('\"');
770
                    std::size_t length = bytes_to_base16(b.begin(),b.end(),sink_);
771
                    sink_.push_back('\"');
772
                    column_ += (length + 2);
773
                    break;
774
                }
775
                case byte_string_chars_format::base64:
776
                {
777
                    sink_.push_back('\"');
778
                    std::size_t length = bytes_to_base64(b.begin(), b.end(), sink_);
779
                    sink_.push_back('\"');
780
                    column_ += (length + 2);
781
                    break;
782
                }
783
                case byte_string_chars_format::base64url:
784
                {
785
                    sink_.push_back('\"');
786
                    std::size_t length = bytes_to_base64url(b.begin(),b.end(),sink_);
787
                    sink_.push_back('\"');
788
                    column_ += (length + 2);
789
                    break;
790
                }
791
                default:
792
                {
793
                    JSONCONS_UNREACHABLE();
794
                }
795
            }
796
797
            end_value();
798
            JSONCONS_VISITOR_RETURN;
799
        }
800
801
        JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, 
802
                             semantic_tag,
803
                             const ser_context& context,
804
                             std::error_code& ec) final
805
        {
806
            if (!stack_.empty()) 
807
            {
808
                if (stack_.back().is_array())
809
                {
810
                    begin_scalar_value();
811
                }
812
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
813
                {
814
                    break_line();
815
                }
816
            }
817
818
            if (!std::isfinite(value))
819
            {
820
                if ((std::isnan)(value))
821
                {
822
                    if (options_.enable_nan_to_num())
823
                    {
824
                        sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
825
                        column_ += options_.nan_to_num().length();
826
                    }
827
                    else if (options_.enable_nan_to_str())
828
                    {
829
                        write_string(options_.nan_to_str(), semantic_tag::none, context, ec);
830
                    }
831
                    else
832
                    {
833
                        sink_.append(null_constant().data(), null_constant().size());
834
                        column_ += null_constant().size();
835
                    }
836
                }
837
                else if (value == std::numeric_limits<double>::infinity())
838
                {
839
                    if (options_.enable_inf_to_num())
840
                    {
841
                        sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
842
                        column_ += options_.inf_to_num().length();
843
                    }
844
                    else if (options_.enable_inf_to_str())
845
                    {
846
                        write_string(options_.inf_to_str(), semantic_tag::none, context, ec);
847
                    }
848
                    else
849
                    {
850
                        sink_.append(null_constant().data(), null_constant().size());
851
                        column_ += null_constant().size();
852
                    }
853
                }
854
                else
855
                {
856
                    if (options_.enable_neginf_to_num())
857
                    {
858
                        sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
859
                        column_ += options_.neginf_to_num().length();
860
                    }
861
                    else if (options_.enable_neginf_to_str())
862
                    {
863
                        write_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
864
                    }
865
                    else
866
                    {
867
                        sink_.append(null_constant().data(), null_constant().size());
868
                        column_ += null_constant().size();
869
                    }
870
                }
871
            }
872
            else
873
            {
874
                std::size_t length = fp_(value, sink_);
875
                column_ += length;
876
            }
877
878
            end_value();
879
            JSONCONS_VISITOR_RETURN;
880
        }
881
882
        JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, 
883
                            semantic_tag,
884
                            const ser_context&,
885
                            std::error_code&) final
886
        {
887
            if (!stack_.empty()) 
888
            {
889
                if (stack_.back().is_array())
890
                {
891
                    begin_scalar_value();
892
                }
893
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
894
                {
895
                    break_line();
896
                }
897
            }
898
            std::size_t length = jsoncons::utility::from_integer(value, sink_);
899
            column_ += length;
900
            end_value();
901
            JSONCONS_VISITOR_RETURN;
902
        }
903
904
        JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, 
905
                             semantic_tag, 
906
                             const ser_context&,
907
                             std::error_code&) final
908
        {
909
            if (!stack_.empty()) 
910
            {
911
                if (stack_.back().is_array())
912
                {
913
                    begin_scalar_value();
914
                }
915
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
916
                {
917
                    break_line();
918
                }
919
            }
920
            std::size_t length = jsoncons::utility::from_integer(value, sink_);
921
            column_ += length;
922
            end_value();
923
            JSONCONS_VISITOR_RETURN;
924
        }
925
926
        JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final
927
        {
928
            if (!stack_.empty()) 
929
            {
930
                if (stack_.back().is_array())
931
                {
932
                    begin_scalar_value();
933
                }
934
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
935
                {
936
                    break_line();
937
                }
938
            }
939
940
            if (value)
941
            {
942
                sink_.append(true_constant().data(), true_constant().size());
943
                column_ += true_constant().size();
944
            }
945
            else
946
            {
947
                sink_.append(false_constant().data(), false_constant().size());
948
                column_ += false_constant().size();
949
            }
950
951
            end_value();
952
            JSONCONS_VISITOR_RETURN;
953
        }
954
955
        void begin_scalar_value()
956
        {
957
            if (!stack_.empty())
958
            {
959
                if (stack_.back().count() > 0)
960
                {
961
                    sink_.append(comma_str_.data(),comma_str_.length());
962
                    column_ += comma_str_.length();
963
                }
964
                if (stack_.back().is_multi_line() || stack_.back().is_indent_once())
965
                {
966
                    stack_.back().new_line_after(true);
967
                    new_line();
968
                }
969
            }
970
        }
971
972
        void write_bignum_value(const string_view_type& sv)
973
        {
974
            switch (options_.bignum_format())
975
            {
976
                case bignum_format_kind::raw:
977
                {
978
                    sink_.append(sv.data(),sv.size());
979
                    column_ += sv.size();
980
                    break;
981
                }
982
                case bignum_format_kind::base64:
983
                {
984
                    bigint n = bigint::parse(sv.data(), sv.length());
985
                    bool is_neg = n < 0;
986
                    if (is_neg)
987
                    {
988
                        n = - n -1;
989
                    }
990
                    int signum;
991
                    std::vector<uint8_t> v;
992
                    n.write_bytes_be(signum, v);
993
994
                    sink_.push_back('\"');
995
                    if (is_neg)
996
                    {
997
                        sink_.push_back('~');
998
                        ++column_;
999
                    }
1000
                    std::size_t length = bytes_to_base64(v.begin(), v.end(), sink_);
1001
                    sink_.push_back('\"');
1002
                    column_ += (length+2);
1003
                    break;
1004
                }
1005
                case bignum_format_kind::base64url:
1006
                {
1007
                    bigint n = bigint::parse(sv.data(), sv.length());
1008
                    bool is_neg = n < 0;
1009
                    if (is_neg)
1010
                    {
1011
                        n = - n -1;
1012
                    }
1013
                    int signum;
1014
                    std::vector<uint8_t> v;
1015
                    n.write_bytes_be(signum, v);
1016
1017
                    sink_.push_back('\"');
1018
                    if (is_neg)
1019
                    {
1020
                        sink_.push_back('~');
1021
                        ++column_;
1022
                    }
1023
                    std::size_t length = bytes_to_base64url(v.begin(), v.end(), sink_);
1024
                    sink_.push_back('\"');
1025
                    column_ += (length+2);
1026
                    break;
1027
                }
1028
                default:
1029
                {
1030
                    sink_.push_back('\"');
1031
                    sink_.append(sv.data(),sv.size());
1032
                    sink_.push_back('\"');
1033
                    column_ += (sv.size() + 2);
1034
                    break;
1035
                }
1036
            }
1037
        }
1038
1039
        void end_value()
1040
        {
1041
            if (!stack_.empty())
1042
            {
1043
                stack_.back().increment_count();
1044
            }
1045
        }
1046
1047
        void indent()
1048
        {
1049
            indent_amount_ += static_cast<int>(options_.indent_size());
1050
        }
1051
1052
        void unindent()
1053
        {
1054
            indent_amount_ -= static_cast<int>(options_.indent_size());
1055
        }
1056
1057
        void new_line()
1058
        {
1059
            sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
1060
            for (int i = 0; i < indent_amount_; ++i)
1061
            {
1062
                sink_.push_back(' ');
1063
            }
1064
            column_ = indent_amount_;
1065
        }
1066
1067
        void new_line(std::size_t len)
1068
        {
1069
            sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
1070
            for (std::size_t i = 0; i < len; ++i)
1071
            {
1072
                sink_.push_back(' ');
1073
            }
1074
            column_ = len;
1075
        }
1076
1077
        void break_line()
1078
        {
1079
            stack_.back().new_line_after(true);
1080
            new_line();
1081
        }
1082
    };
1083
1084
    template <typename CharT,typename Sink=jsoncons::stream_sink<CharT>,typename Allocator=std::allocator<char>>
1085
    class basic_compact_json_encoder final : public basic_json_visitor<CharT>
1086
    {
1087
        static const std::array<CharT, 4>& null_constant()
1088
        {
1089
            static constexpr std::array<CharT,4> k{{'n','u','l','l'}};
1090
            return k;
1091
        }
1092
        static const std::array<CharT, 4>& true_constant()
1093
        {
1094
            static constexpr std::array<CharT,4> k{{'t','r','u','e'}};
1095
            return k;
1096
        }
1097
        static const std::array<CharT, 5>& false_constant()
1098
        {
1099
            static constexpr std::array<CharT,5> k{{'f','a','l','s','e'}};
1100
            return k;
1101
        }
1102
    public:
1103
        using allocator_type = Allocator;
1104
        using char_type = CharT;
1105
        using typename basic_json_visitor<CharT>::string_view_type;
1106
        using sink_type = Sink;
1107
        using string_type = typename basic_json_encode_options<CharT>::string_type;
1108
1109
    private:
1110
        enum class container_type {object, array};
1111
1112
        class encoding_context
1113
        {
1114
            container_type type_;
1115
            std::size_t count_{0};
1116
        public:
1117
            encoding_context(container_type type) noexcept
1118
               : type_(type)
1119
            {
1120
            }
1121
1122
            std::size_t count() const
1123
            {
1124
                return count_;
1125
            }
1126
1127
            void increment_count()
1128
            {
1129
                ++count_;
1130
            }
1131
1132
            bool is_array() const
1133
            {
1134
                return type_ == container_type::array;
1135
            }
1136
        };
1137
        using encoding_context_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context>;
1138
1139
        Sink sink_;
1140
        basic_json_encode_options<CharT> options_;
1141
        jsoncons::utility::write_double fp_;
1142
        std::vector<encoding_context,encoding_context_allocator_type> stack_;
1143
        int nesting_depth_;
1144
    public:
1145
1146
        // Noncopyable and nonmoveable
1147
        basic_compact_json_encoder(const basic_compact_json_encoder&) = delete;
1148
        basic_compact_json_encoder(basic_compact_json_encoder&&) = delete;
1149
1150
        basic_compact_json_encoder(Sink&& sink, 
1151
            const Allocator& alloc = Allocator())
1152
            : basic_compact_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
1153
        {
1154
        }
1155
1156
        basic_compact_json_encoder(Sink&& sink, 
1157
            const basic_json_encode_options<CharT>& options, 
1158
            const Allocator& alloc = Allocator())
1159
           : sink_(std::forward<Sink>(sink)),
1160
             options_(options),
1161
             fp_(options.float_format(), options.precision()),
1162
             stack_(alloc),
1163
             nesting_depth_(0)          
1164
        {
1165
        }
1166
1167
        ~basic_compact_json_encoder() noexcept
1168
        {
1169
            JSONCONS_TRY
1170
            {
1171
                sink_.flush();
1172
            }
1173
            JSONCONS_CATCH(...)
1174
            {
1175
            }
1176
        }
1177
1178
        basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete;
1179
        basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = delete;
1180
1181
        void reset()
1182
        {
1183
            stack_.clear();
1184
            nesting_depth_ = 0;
1185
        }
1186
1187
        void reset(Sink&& sink)
1188
        {
1189
            sink_ = std::move(sink);
1190
            reset();
1191
        }
1192
1193
    private:
1194
        // Implementing methods
1195
        void visit_flush() final
1196
        {
1197
            sink_.flush();
1198
        }
1199
1200
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) final
1201
        {
1202
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
1203
            {
1204
                ec = json_errc::max_nesting_depth_exceeded;
1205
                JSONCONS_VISITOR_RETURN;
1206
            } 
1207
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1208
            {
1209
                sink_.push_back(',');
1210
            }
1211
1212
            stack_.emplace_back(container_type::object);
1213
            sink_.push_back('{');
1214
            JSONCONS_VISITOR_RETURN;
1215
        }
1216
1217
        JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) final
1218
        {
1219
            JSONCONS_ASSERT(!stack_.empty());
1220
            --nesting_depth_;
1221
1222
            stack_.pop_back();
1223
            sink_.push_back('}');
1224
1225
            if (!stack_.empty())
1226
            {
1227
                stack_.back().increment_count();
1228
            }
1229
            JSONCONS_VISITOR_RETURN;
1230
        }
1231
1232
1233
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) final
1234
        {
1235
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
1236
            {
1237
                ec = json_errc::max_nesting_depth_exceeded;
1238
                JSONCONS_VISITOR_RETURN;
1239
            } 
1240
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1241
            {
1242
                sink_.push_back(',');
1243
            }
1244
            stack_.emplace_back(container_type::array);
1245
            sink_.push_back('[');
1246
            JSONCONS_VISITOR_RETURN;
1247
        }
1248
1249
        JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) final
1250
        {
1251
            JSONCONS_ASSERT(!stack_.empty());
1252
            --nesting_depth_;
1253
1254
            stack_.pop_back();
1255
            sink_.push_back(']');
1256
            if (!stack_.empty())
1257
            {
1258
                stack_.back().increment_count();
1259
            }
1260
            JSONCONS_VISITOR_RETURN;
1261
        }
1262
1263
        JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code&) final
1264
        {
1265
            if (!stack_.empty() && stack_.back().count() > 0)
1266
            {
1267
                sink_.push_back(',');
1268
            }
1269
1270
            sink_.push_back('\"');
1271
            jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
1272
            sink_.push_back('\"');
1273
            sink_.push_back(':');
1274
            JSONCONS_VISITOR_RETURN;
1275
        }
1276
1277
        JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) final
1278
        {
1279
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1280
            {
1281
                sink_.push_back(',');
1282
            }
1283
1284
            sink_.append(null_constant().data(), null_constant().size());
1285
1286
            if (!stack_.empty())
1287
            {
1288
                stack_.back().increment_count();
1289
            }
1290
            JSONCONS_VISITOR_RETURN;
1291
        }
1292
1293
        void write_bignum_value(const string_view_type& sv)
1294
        {
1295
            switch (options_.bignum_format())
1296
            {
1297
                case bignum_format_kind::raw:
1298
                {
1299
                    sink_.append(sv.data(),sv.size());
1300
                    break;
1301
                }
1302
                case bignum_format_kind::base64:
1303
                {
1304
                    bigint n = bigint::parse(sv.data(), sv.length());
1305
                    bool is_neg = n < 0;
1306
                    if (is_neg)
1307
                    {
1308
                        n = - n -1;
1309
                    }
1310
                    int signum;
1311
                    std::vector<uint8_t> v;
1312
                    n.write_bytes_be(signum, v);
1313
1314
                    sink_.push_back('\"');
1315
                    if (is_neg)
1316
                    {
1317
                        sink_.push_back('~');
1318
                    }
1319
                    bytes_to_base64(v.begin(), v.end(), sink_);
1320
                    sink_.push_back('\"');
1321
                    break;
1322
                }
1323
                case bignum_format_kind::base64url:
1324
                {
1325
                    bigint n = bigint::parse(sv.data(), sv.length());
1326
                    bool is_neg = n < 0;
1327
                    if (is_neg)
1328
                    {
1329
                        n = - n -1;
1330
                    }
1331
                    int signum;
1332
                    std::vector<uint8_t> v;
1333
                    n.write_bytes_be(signum, v);
1334
1335
                    sink_.push_back('\"');
1336
                    if (is_neg)
1337
                    {
1338
                        sink_.push_back('~');
1339
                    }
1340
                    bytes_to_base64url(v.begin(), v.end(), sink_);
1341
                    sink_.push_back('\"');
1342
                    break;
1343
                }
1344
                default:
1345
                {
1346
                    sink_.push_back('\"');
1347
                    sink_.append(sv.data(),sv.size());
1348
                    sink_.push_back('\"');
1349
                    break;
1350
                }
1351
            }
1352
        }
1353
1354
        JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) final
1355
        {
1356
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1357
            {
1358
                sink_.push_back(',');
1359
            }
1360
1361
            write_string(sv, tag, context, ec);
1362
1363
            if (!stack_.empty())
1364
            {
1365
                stack_.back().increment_count();
1366
            }
1367
            JSONCONS_VISITOR_RETURN;
1368
        }
1369
1370
        void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) 
1371
        {
1372
            if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus()))
1373
            {
1374
                //std::cout << "noesc\n";
1375
                sink_.push_back('\"');
1376
                const CharT* begin = sv.data();
1377
                const CharT* end = begin + sv.length();
1378
                for (const CharT* it = begin; it != end; ++it)
1379
                {
1380
                    sink_.push_back(*it);
1381
                }
1382
                sink_.push_back('\"');
1383
            }
1384
            else if (tag == semantic_tag::bigint)
1385
            {
1386
                write_bignum_value(sv);
1387
            }
1388
            else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw)
1389
            {
1390
                write_bignum_value(sv);
1391
            }
1392
            else
1393
            {
1394
                //if (tag != semantic_tag::bigdec)
1395
                //    std::cout << "esc\n";
1396
                sink_.push_back('\"');
1397
                jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
1398
                sink_.push_back('\"');
1399
            }           
1400
        }
1401
1402
        JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, 
1403
            semantic_tag tag,
1404
            const ser_context&,
1405
            std::error_code&) final
1406
        {
1407
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1408
            {
1409
                sink_.push_back(',');
1410
            }
1411
1412
            byte_string_chars_format encoding_hint;
1413
            switch (tag)
1414
            {
1415
                case semantic_tag::base16:
1416
                    encoding_hint = byte_string_chars_format::base16;
1417
                    break;
1418
                case semantic_tag::base64:
1419
                    encoding_hint = byte_string_chars_format::base64;
1420
                    break;
1421
                case semantic_tag::base64url:
1422
                    encoding_hint = byte_string_chars_format::base64url;
1423
                    break;
1424
                default:
1425
                    encoding_hint = byte_string_chars_format::none;
1426
                    break;
1427
            }
1428
1429
            byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), 
1430
                                                                                       encoding_hint, 
1431
                                                                                       byte_string_chars_format::base64url);
1432
            switch (format)
1433
            {
1434
                case byte_string_chars_format::base16:
1435
                {
1436
                    sink_.push_back('\"');
1437
                    bytes_to_base16(b.begin(),b.end(),sink_);
1438
                    sink_.push_back('\"');
1439
                    break;
1440
                }
1441
                case byte_string_chars_format::base64:
1442
                {
1443
                    sink_.push_back('\"');
1444
                    bytes_to_base64(b.begin(), b.end(), sink_);
1445
                    sink_.push_back('\"');
1446
                    break;
1447
                }
1448
                case byte_string_chars_format::base64url:
1449
                {
1450
                    sink_.push_back('\"');
1451
                    bytes_to_base64url(b.begin(),b.end(),sink_);
1452
                    sink_.push_back('\"');
1453
                    break;
1454
                }
1455
                default:
1456
                {
1457
                    JSONCONS_UNREACHABLE();
1458
                }
1459
            }
1460
1461
            if (!stack_.empty())
1462
            {
1463
                stack_.back().increment_count();
1464
            }
1465
            JSONCONS_VISITOR_RETURN;
1466
        }
1467
1468
        JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, 
1469
                             semantic_tag,
1470
                             const ser_context& context,
1471
                             std::error_code& ec) final
1472
        {
1473
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1474
            {
1475
                sink_.push_back(',');
1476
            }
1477
1478
            if (JSONCONS_UNLIKELY(!std::isfinite(value)))
1479
            {
1480
                if ((std::isnan)(value))
1481
                {
1482
                    if (options_.enable_nan_to_num())
1483
                    {
1484
                        sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
1485
                    }
1486
                    else if (options_.enable_nan_to_str())
1487
                    {
1488
                        write_string(options_.nan_to_str(), semantic_tag::none, context, ec);
1489
                    }
1490
                    else
1491
                    {
1492
                        sink_.append(null_constant().data(), null_constant().size());
1493
                    }
1494
                }
1495
                else if (value == std::numeric_limits<double>::infinity())
1496
                {
1497
                    if (options_.enable_inf_to_num())
1498
                    {
1499
                        sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
1500
                    }
1501
                    else if (options_.enable_inf_to_str())
1502
                    {
1503
                        write_string(options_.inf_to_str(), semantic_tag::none, context, ec);
1504
                    }
1505
                    else
1506
                    {
1507
                        sink_.append(null_constant().data(), null_constant().size());
1508
                    }
1509
                }
1510
                else 
1511
                {
1512
                    if (options_.enable_neginf_to_num())
1513
                    {
1514
                        sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
1515
                    }
1516
                    else if (options_.enable_neginf_to_str())
1517
                    {
1518
                        write_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
1519
                    }
1520
                    else
1521
                    {
1522
                        sink_.append(null_constant().data(), null_constant().size());
1523
                    }
1524
                }
1525
            }
1526
            else
1527
            {
1528
                fp_(value, sink_);
1529
            }
1530
1531
            if (!stack_.empty())
1532
            {
1533
                stack_.back().increment_count();
1534
            }
1535
            JSONCONS_VISITOR_RETURN;
1536
        }
1537
1538
        JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, 
1539
                            semantic_tag,
1540
                            const ser_context&,
1541
                            std::error_code&) final
1542
        {
1543
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1544
            {
1545
                sink_.push_back(',');
1546
            }
1547
            jsoncons::utility::from_integer(value, sink_);
1548
            if (!stack_.empty())
1549
            {
1550
                stack_.back().increment_count();
1551
            }
1552
            JSONCONS_VISITOR_RETURN;
1553
        }
1554
1555
        JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, 
1556
                             semantic_tag, 
1557
                             const ser_context&,
1558
                             std::error_code&) final
1559
        {
1560
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1561
            {
1562
                sink_.push_back(',');
1563
            }
1564
            jsoncons::utility::from_integer(value, sink_);
1565
            if (!stack_.empty())
1566
            {
1567
                stack_.back().increment_count();
1568
            }
1569
            JSONCONS_VISITOR_RETURN;
1570
        }
1571
1572
        JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final
1573
        {
1574
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1575
            {
1576
                sink_.push_back(',');
1577
            }
1578
1579
            if (value)
1580
            {
1581
                sink_.append(true_constant().data(), true_constant().size());
1582
            }
1583
            else
1584
            {
1585
                sink_.append(false_constant().data(), false_constant().size());
1586
            }
1587
1588
            if (!stack_.empty())
1589
            {
1590
                stack_.back().increment_count();
1591
            }
1592
            JSONCONS_VISITOR_RETURN;
1593
        }
1594
    };
1595
1596
    using json_stream_encoder = basic_json_encoder<char,jsoncons::stream_sink<char>>;
1597
    using wjson_stream_encoder = basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
1598
    using compact_json_stream_encoder = basic_compact_json_encoder<char,jsoncons::stream_sink<char>>;
1599
    using compact_wjson_stream_encoder = basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
1600
1601
    using json_string_encoder = basic_json_encoder<char,jsoncons::string_sink<std::string>>;
1602
    using wjson_string_encoder = basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
1603
    using compact_json_string_encoder = basic_compact_json_encoder<char,jsoncons::string_sink<std::string>>;
1604
    using compact_wjson_string_encoder = basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
1605
1606
} // namespace jsoncons
1607
1608
#endif // JSONCONS_JSON_ENCODER_HPP