Coverage Report

Created: 2025-08-24 06:53

/src/jsoncons/include/jsoncons/json_encoder.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_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
            switch (tag)
697
            {
698
                case semantic_tag::bigint:
699
                    write_bigint_value(sv);
700
                    break;
701
                case semantic_tag::bigdec:
702
                {
703
                    // output lossless number
704
                    if (options_.bignum_format() == bignum_format_kind::raw)
705
                    {
706
                        write_bigint_value(sv);
707
                        break;
708
                    }
709
                    JSONCONS_FALLTHROUGH;
710
                }
711
                default:
712
                {
713
                    sink_.push_back('\"');
714
                    std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
715
                    sink_.push_back('\"');
716
                    column_ += (length+2);
717
                    break;
718
                }
719
            }
720
        }
721
722
        JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, 
723
                                  semantic_tag tag,
724
                                  const ser_context&,
725
                                  std::error_code&) final
726
        {
727
            if (!stack_.empty()) 
728
            {
729
                if (stack_.back().is_array())
730
                {
731
                    begin_scalar_value();
732
                }
733
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
734
                {
735
                    break_line();
736
                }
737
            }
738
739
            byte_string_chars_format encoding_hint;
740
            switch (tag)
741
            {
742
                case semantic_tag::base16:
743
                    encoding_hint = byte_string_chars_format::base16;
744
                    break;
745
                case semantic_tag::base64:
746
                    encoding_hint = byte_string_chars_format::base64;
747
                    break;
748
                case semantic_tag::base64url:
749
                    encoding_hint = byte_string_chars_format::base64url;
750
                    break;
751
                default:
752
                    encoding_hint = byte_string_chars_format::none;
753
                    break;
754
            }
755
756
            byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), 
757
                                                                                                 encoding_hint, 
758
                                                                                                 byte_string_chars_format::base64url);
759
            switch (format)
760
            {
761
                case byte_string_chars_format::base16:
762
                {
763
                    sink_.push_back('\"');
764
                    std::size_t length = bytes_to_base16(b.begin(),b.end(),sink_);
765
                    sink_.push_back('\"');
766
                    column_ += (length + 2);
767
                    break;
768
                }
769
                case byte_string_chars_format::base64:
770
                {
771
                    sink_.push_back('\"');
772
                    std::size_t length = bytes_to_base64(b.begin(), b.end(), sink_);
773
                    sink_.push_back('\"');
774
                    column_ += (length + 2);
775
                    break;
776
                }
777
                case byte_string_chars_format::base64url:
778
                {
779
                    sink_.push_back('\"');
780
                    std::size_t length = bytes_to_base64url(b.begin(),b.end(),sink_);
781
                    sink_.push_back('\"');
782
                    column_ += (length + 2);
783
                    break;
784
                }
785
                default:
786
                {
787
                    JSONCONS_UNREACHABLE();
788
                }
789
            }
790
791
            end_value();
792
            JSONCONS_VISITOR_RETURN;
793
        }
794
795
        JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, 
796
                             semantic_tag,
797
                             const ser_context& context,
798
                             std::error_code& ec) final
799
        {
800
            if (!stack_.empty()) 
801
            {
802
                if (stack_.back().is_array())
803
                {
804
                    begin_scalar_value();
805
                }
806
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
807
                {
808
                    break_line();
809
                }
810
            }
811
812
            if (!std::isfinite(value))
813
            {
814
                if ((std::isnan)(value))
815
                {
816
                    if (options_.enable_nan_to_num())
817
                    {
818
                        sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
819
                        column_ += options_.nan_to_num().length();
820
                    }
821
                    else if (options_.enable_nan_to_str())
822
                    {
823
                        write_string(options_.nan_to_str(), semantic_tag::none, context, ec);
824
                    }
825
                    else
826
                    {
827
                        sink_.append(null_constant().data(), null_constant().size());
828
                        column_ += null_constant().size();
829
                    }
830
                }
831
                else if (value == std::numeric_limits<double>::infinity())
832
                {
833
                    if (options_.enable_inf_to_num())
834
                    {
835
                        sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
836
                        column_ += options_.inf_to_num().length();
837
                    }
838
                    else if (options_.enable_inf_to_str())
839
                    {
840
                        write_string(options_.inf_to_str(), semantic_tag::none, context, ec);
841
                    }
842
                    else
843
                    {
844
                        sink_.append(null_constant().data(), null_constant().size());
845
                        column_ += null_constant().size();
846
                    }
847
                }
848
                else
849
                {
850
                    if (options_.enable_neginf_to_num())
851
                    {
852
                        sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
853
                        column_ += options_.neginf_to_num().length();
854
                    }
855
                    else if (options_.enable_neginf_to_str())
856
                    {
857
                        write_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
858
                    }
859
                    else
860
                    {
861
                        sink_.append(null_constant().data(), null_constant().size());
862
                        column_ += null_constant().size();
863
                    }
864
                }
865
            }
866
            else
867
            {
868
                std::size_t length = fp_(value, sink_);
869
                column_ += length;
870
            }
871
872
            end_value();
873
            JSONCONS_VISITOR_RETURN;
874
        }
875
876
        JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, 
877
                            semantic_tag,
878
                            const ser_context&,
879
                            std::error_code&) final
880
        {
881
            if (!stack_.empty()) 
882
            {
883
                if (stack_.back().is_array())
884
                {
885
                    begin_scalar_value();
886
                }
887
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
888
                {
889
                    break_line();
890
                }
891
            }
892
            std::size_t length = jsoncons::utility::from_integer(value, sink_);
893
            column_ += length;
894
            end_value();
895
            JSONCONS_VISITOR_RETURN;
896
        }
897
898
        JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, 
899
                             semantic_tag, 
900
                             const ser_context&,
901
                             std::error_code&) final
902
        {
903
            if (!stack_.empty()) 
904
            {
905
                if (stack_.back().is_array())
906
                {
907
                    begin_scalar_value();
908
                }
909
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
910
                {
911
                    break_line();
912
                }
913
            }
914
            std::size_t length = jsoncons::utility::from_integer(value, sink_);
915
            column_ += length;
916
            end_value();
917
            JSONCONS_VISITOR_RETURN;
918
        }
919
920
        JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final
921
        {
922
            if (!stack_.empty()) 
923
            {
924
                if (stack_.back().is_array())
925
                {
926
                    begin_scalar_value();
927
                }
928
                if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
929
                {
930
                    break_line();
931
                }
932
            }
933
934
            if (value)
935
            {
936
                sink_.append(true_constant().data(), true_constant().size());
937
                column_ += true_constant().size();
938
            }
939
            else
940
            {
941
                sink_.append(false_constant().data(), false_constant().size());
942
                column_ += false_constant().size();
943
            }
944
945
            end_value();
946
            JSONCONS_VISITOR_RETURN;
947
        }
948
949
        void begin_scalar_value()
950
        {
951
            if (!stack_.empty())
952
            {
953
                if (stack_.back().count() > 0)
954
                {
955
                    sink_.append(comma_str_.data(),comma_str_.length());
956
                    column_ += comma_str_.length();
957
                }
958
                if (stack_.back().is_multi_line() || stack_.back().is_indent_once())
959
                {
960
                    stack_.back().new_line_after(true);
961
                    new_line();
962
                }
963
            }
964
        }
965
966
        void write_bigint_value(const string_view_type& sv)
967
        {
968
            switch (options_.bignum_format())
969
            {
970
                case bignum_format_kind::raw:
971
                {
972
                    sink_.append(sv.data(),sv.size());
973
                    column_ += sv.size();
974
                    break;
975
                }
976
                case bignum_format_kind::base64:
977
                {
978
                    bigint n = bigint::parse(sv.data(), sv.length());
979
                    bool is_neg = n < 0;
980
                    if (is_neg)
981
                    {
982
                        n = - n -1;
983
                    }
984
                    int signum;
985
                    std::vector<uint8_t> v;
986
                    n.write_bytes_be(signum, v);
987
988
                    sink_.push_back('\"');
989
                    if (is_neg)
990
                    {
991
                        sink_.push_back('~');
992
                        ++column_;
993
                    }
994
                    std::size_t length = bytes_to_base64(v.begin(), v.end(), sink_);
995
                    sink_.push_back('\"');
996
                    column_ += (length+2);
997
                    break;
998
                }
999
                case bignum_format_kind::base64url:
1000
                {
1001
                    bigint n = bigint::parse(sv.data(), sv.length());
1002
                    bool is_neg = n < 0;
1003
                    if (is_neg)
1004
                    {
1005
                        n = - n -1;
1006
                    }
1007
                    int signum;
1008
                    std::vector<uint8_t> v;
1009
                    n.write_bytes_be(signum, v);
1010
1011
                    sink_.push_back('\"');
1012
                    if (is_neg)
1013
                    {
1014
                        sink_.push_back('~');
1015
                        ++column_;
1016
                    }
1017
                    std::size_t length = bytes_to_base64url(v.begin(), v.end(), sink_);
1018
                    sink_.push_back('\"');
1019
                    column_ += (length+2);
1020
                    break;
1021
                }
1022
                default:
1023
                {
1024
                    sink_.push_back('\"');
1025
                    sink_.append(sv.data(),sv.size());
1026
                    sink_.push_back('\"');
1027
                    column_ += (sv.size() + 2);
1028
                    break;
1029
                }
1030
            }
1031
        }
1032
1033
        void end_value()
1034
        {
1035
            if (!stack_.empty())
1036
            {
1037
                stack_.back().increment_count();
1038
            }
1039
        }
1040
1041
        void indent()
1042
        {
1043
            indent_amount_ += static_cast<int>(options_.indent_size());
1044
        }
1045
1046
        void unindent()
1047
        {
1048
            indent_amount_ -= static_cast<int>(options_.indent_size());
1049
        }
1050
1051
        void new_line()
1052
        {
1053
            sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
1054
            for (int i = 0; i < indent_amount_; ++i)
1055
            {
1056
                sink_.push_back(' ');
1057
            }
1058
            column_ = indent_amount_;
1059
        }
1060
1061
        void new_line(std::size_t len)
1062
        {
1063
            sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
1064
            for (std::size_t i = 0; i < len; ++i)
1065
            {
1066
                sink_.push_back(' ');
1067
            }
1068
            column_ = len;
1069
        }
1070
1071
        void break_line()
1072
        {
1073
            stack_.back().new_line_after(true);
1074
            new_line();
1075
        }
1076
    };
1077
1078
    template <typename CharT,typename Sink=jsoncons::stream_sink<CharT>,typename Allocator=std::allocator<char>>
1079
    class basic_compact_json_encoder final : public basic_json_visitor<CharT>
1080
    {
1081
        static const std::array<CharT, 4>& null_constant()
1082
        {
1083
            static constexpr std::array<CharT,4> k{{'n','u','l','l'}};
1084
            return k;
1085
        }
1086
        static const std::array<CharT, 4>& true_constant()
1087
        {
1088
            static constexpr std::array<CharT,4> k{{'t','r','u','e'}};
1089
            return k;
1090
        }
1091
        static const std::array<CharT, 5>& false_constant()
1092
        {
1093
            static constexpr std::array<CharT,5> k{{'f','a','l','s','e'}};
1094
            return k;
1095
        }
1096
    public:
1097
        using allocator_type = Allocator;
1098
        using char_type = CharT;
1099
        using typename basic_json_visitor<CharT>::string_view_type;
1100
        using sink_type = Sink;
1101
        using string_type = typename basic_json_encode_options<CharT>::string_type;
1102
1103
    private:
1104
        enum class container_type {object, array};
1105
1106
        class encoding_context
1107
        {
1108
            container_type type_;
1109
            std::size_t count_{0};
1110
        public:
1111
            encoding_context(container_type type) noexcept
1112
               : type_(type)
1113
            {
1114
            }
1115
1116
            std::size_t count() const
1117
            {
1118
                return count_;
1119
            }
1120
1121
            void increment_count()
1122
            {
1123
                ++count_;
1124
            }
1125
1126
            bool is_array() const
1127
            {
1128
                return type_ == container_type::array;
1129
            }
1130
        };
1131
        using encoding_context_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context>;
1132
1133
        Sink sink_;
1134
        basic_json_encode_options<CharT> options_;
1135
        jsoncons::utility::write_double fp_;
1136
        std::vector<encoding_context,encoding_context_allocator_type> stack_;
1137
        int nesting_depth_;
1138
    public:
1139
1140
        // Noncopyable and nonmoveable
1141
        basic_compact_json_encoder(const basic_compact_json_encoder&) = delete;
1142
        basic_compact_json_encoder(basic_compact_json_encoder&&) = delete;
1143
1144
        basic_compact_json_encoder(Sink&& sink, 
1145
            const Allocator& alloc = Allocator())
1146
            : basic_compact_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
1147
        {
1148
        }
1149
1150
        basic_compact_json_encoder(Sink&& sink, 
1151
            const basic_json_encode_options<CharT>& options, 
1152
            const Allocator& alloc = Allocator())
1153
           : sink_(std::forward<Sink>(sink)),
1154
             options_(options),
1155
             fp_(options.float_format(), options.precision()),
1156
             stack_(alloc),
1157
             nesting_depth_(0)          
1158
        {
1159
        }
1160
1161
        ~basic_compact_json_encoder() noexcept
1162
        {
1163
            JSONCONS_TRY
1164
            {
1165
                sink_.flush();
1166
            }
1167
            JSONCONS_CATCH(...)
1168
            {
1169
            }
1170
        }
1171
1172
        basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete;
1173
        basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = delete;
1174
1175
        void reset()
1176
        {
1177
            stack_.clear();
1178
            nesting_depth_ = 0;
1179
        }
1180
1181
        void reset(Sink&& sink)
1182
        {
1183
            sink_ = std::move(sink);
1184
            reset();
1185
        }
1186
1187
    private:
1188
        // Implementing methods
1189
        void visit_flush() final
1190
        {
1191
            sink_.flush();
1192
        }
1193
1194
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) final
1195
        {
1196
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
1197
            {
1198
                ec = json_errc::max_nesting_depth_exceeded;
1199
                JSONCONS_VISITOR_RETURN;
1200
            } 
1201
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1202
            {
1203
                sink_.push_back(',');
1204
            }
1205
1206
            stack_.emplace_back(container_type::object);
1207
            sink_.push_back('{');
1208
            JSONCONS_VISITOR_RETURN;
1209
        }
1210
1211
        JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) final
1212
        {
1213
            JSONCONS_ASSERT(!stack_.empty());
1214
            --nesting_depth_;
1215
1216
            stack_.pop_back();
1217
            sink_.push_back('}');
1218
1219
            if (!stack_.empty())
1220
            {
1221
                stack_.back().increment_count();
1222
            }
1223
            JSONCONS_VISITOR_RETURN;
1224
        }
1225
1226
1227
        JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) final
1228
        {
1229
            if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
1230
            {
1231
                ec = json_errc::max_nesting_depth_exceeded;
1232
                JSONCONS_VISITOR_RETURN;
1233
            } 
1234
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1235
            {
1236
                sink_.push_back(',');
1237
            }
1238
            stack_.emplace_back(container_type::array);
1239
            sink_.push_back('[');
1240
            JSONCONS_VISITOR_RETURN;
1241
        }
1242
1243
        JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) final
1244
        {
1245
            JSONCONS_ASSERT(!stack_.empty());
1246
            --nesting_depth_;
1247
1248
            stack_.pop_back();
1249
            sink_.push_back(']');
1250
            if (!stack_.empty())
1251
            {
1252
                stack_.back().increment_count();
1253
            }
1254
            JSONCONS_VISITOR_RETURN;
1255
        }
1256
1257
        JSONCONS_VISITOR_RETURN_TYPE visit_key(const string_view_type& name, const ser_context&, std::error_code&) final
1258
        {
1259
            if (!stack_.empty() && stack_.back().count() > 0)
1260
            {
1261
                sink_.push_back(',');
1262
            }
1263
1264
            sink_.push_back('\"');
1265
            jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
1266
            sink_.push_back('\"');
1267
            sink_.push_back(':');
1268
            JSONCONS_VISITOR_RETURN;
1269
        }
1270
1271
        JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) final
1272
        {
1273
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1274
            {
1275
                sink_.push_back(',');
1276
            }
1277
1278
            sink_.append(null_constant().data(), null_constant().size());
1279
1280
            if (!stack_.empty())
1281
            {
1282
                stack_.back().increment_count();
1283
            }
1284
            JSONCONS_VISITOR_RETURN;
1285
        }
1286
1287
        void write_bigint_value(const string_view_type& sv)
1288
        {
1289
            switch (options_.bignum_format())
1290
            {
1291
                case bignum_format_kind::raw:
1292
                {
1293
                    sink_.append(sv.data(),sv.size());
1294
                    break;
1295
                }
1296
                case bignum_format_kind::base64:
1297
                {
1298
                    bigint n = bigint::parse(sv.data(), sv.length());
1299
                    bool is_neg = n < 0;
1300
                    if (is_neg)
1301
                    {
1302
                        n = - n -1;
1303
                    }
1304
                    int signum;
1305
                    std::vector<uint8_t> v;
1306
                    n.write_bytes_be(signum, v);
1307
1308
                    sink_.push_back('\"');
1309
                    if (is_neg)
1310
                    {
1311
                        sink_.push_back('~');
1312
                    }
1313
                    bytes_to_base64(v.begin(), v.end(), sink_);
1314
                    sink_.push_back('\"');
1315
                    break;
1316
                }
1317
                case bignum_format_kind::base64url:
1318
                {
1319
                    bigint n = bigint::parse(sv.data(), sv.length());
1320
                    bool is_neg = n < 0;
1321
                    if (is_neg)
1322
                    {
1323
                        n = - n -1;
1324
                    }
1325
                    int signum;
1326
                    std::vector<uint8_t> v;
1327
                    n.write_bytes_be(signum, v);
1328
1329
                    sink_.push_back('\"');
1330
                    if (is_neg)
1331
                    {
1332
                        sink_.push_back('~');
1333
                    }
1334
                    bytes_to_base64url(v.begin(), v.end(), sink_);
1335
                    sink_.push_back('\"');
1336
                    break;
1337
                }
1338
                default:
1339
                {
1340
                    sink_.push_back('\"');
1341
                    sink_.append(sv.data(),sv.size());
1342
                    sink_.push_back('\"');
1343
                    break;
1344
                }
1345
            }
1346
        }
1347
1348
        JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) final
1349
        {
1350
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1351
            {
1352
                sink_.push_back(',');
1353
            }
1354
1355
            switch (tag)
1356
            {
1357
                case semantic_tag::bigint:
1358
                    write_bigint_value(sv);
1359
                    break;
1360
                case semantic_tag::bigdec:
1361
                {
1362
                    // output lossless number
1363
                    if (options_.bignum_format() == bignum_format_kind::raw)
1364
                    {
1365
                        write_bigint_value(sv);
1366
                        break;
1367
                    }
1368
                    JSONCONS_FALLTHROUGH;
1369
                }
1370
                default:
1371
                {
1372
                    sink_.push_back('\"');
1373
                    jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
1374
                    sink_.push_back('\"');
1375
                    break;
1376
                }
1377
            }
1378
1379
            if (!stack_.empty())
1380
            {
1381
                stack_.back().increment_count();
1382
            }
1383
            JSONCONS_VISITOR_RETURN;
1384
        }
1385
1386
        void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) 
1387
        {
1388
            switch (tag)
1389
            {
1390
                case semantic_tag::bigint:
1391
                    write_bigint_value(sv);
1392
                    break;
1393
                case semantic_tag::bigdec:
1394
                {
1395
                    // output lossless number
1396
                    if (options_.bignum_format() == bignum_format_kind::raw)
1397
                    {
1398
                        write_bigint_value(sv);
1399
                        break;
1400
                    }
1401
                    JSONCONS_FALLTHROUGH;
1402
                }
1403
                default:
1404
                {
1405
                    sink_.push_back('\"');
1406
                    jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
1407
                    sink_.push_back('\"');
1408
                    break;
1409
                }
1410
            }
1411
        }
1412
1413
        JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, 
1414
                                  semantic_tag tag,
1415
                                  const ser_context&,
1416
                                  std::error_code&) final
1417
        {
1418
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1419
            {
1420
                sink_.push_back(',');
1421
            }
1422
1423
            byte_string_chars_format encoding_hint;
1424
            switch (tag)
1425
            {
1426
                case semantic_tag::base16:
1427
                    encoding_hint = byte_string_chars_format::base16;
1428
                    break;
1429
                case semantic_tag::base64:
1430
                    encoding_hint = byte_string_chars_format::base64;
1431
                    break;
1432
                case semantic_tag::base64url:
1433
                    encoding_hint = byte_string_chars_format::base64url;
1434
                    break;
1435
                default:
1436
                    encoding_hint = byte_string_chars_format::none;
1437
                    break;
1438
            }
1439
1440
            byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(), 
1441
                                                                                       encoding_hint, 
1442
                                                                                       byte_string_chars_format::base64url);
1443
            switch (format)
1444
            {
1445
                case byte_string_chars_format::base16:
1446
                {
1447
                    sink_.push_back('\"');
1448
                    bytes_to_base16(b.begin(),b.end(),sink_);
1449
                    sink_.push_back('\"');
1450
                    break;
1451
                }
1452
                case byte_string_chars_format::base64:
1453
                {
1454
                    sink_.push_back('\"');
1455
                    bytes_to_base64(b.begin(), b.end(), sink_);
1456
                    sink_.push_back('\"');
1457
                    break;
1458
                }
1459
                case byte_string_chars_format::base64url:
1460
                {
1461
                    sink_.push_back('\"');
1462
                    bytes_to_base64url(b.begin(),b.end(),sink_);
1463
                    sink_.push_back('\"');
1464
                    break;
1465
                }
1466
                default:
1467
                {
1468
                    JSONCONS_UNREACHABLE();
1469
                }
1470
            }
1471
1472
            if (!stack_.empty())
1473
            {
1474
                stack_.back().increment_count();
1475
            }
1476
            JSONCONS_VISITOR_RETURN;
1477
        }
1478
1479
        JSONCONS_VISITOR_RETURN_TYPE visit_double(double value, 
1480
                             semantic_tag,
1481
                             const ser_context& context,
1482
                             std::error_code& ec) final
1483
        {
1484
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1485
            {
1486
                sink_.push_back(',');
1487
            }
1488
1489
            if (JSONCONS_UNLIKELY(!std::isfinite(value)))
1490
            {
1491
                if ((std::isnan)(value))
1492
                {
1493
                    if (options_.enable_nan_to_num())
1494
                    {
1495
                        sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
1496
                    }
1497
                    else if (options_.enable_nan_to_str())
1498
                    {
1499
                        write_string(options_.nan_to_str(), semantic_tag::none, context, ec);
1500
                    }
1501
                    else
1502
                    {
1503
                        sink_.append(null_constant().data(), null_constant().size());
1504
                    }
1505
                }
1506
                else if (value == std::numeric_limits<double>::infinity())
1507
                {
1508
                    if (options_.enable_inf_to_num())
1509
                    {
1510
                        sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
1511
                    }
1512
                    else if (options_.enable_inf_to_str())
1513
                    {
1514
                        write_string(options_.inf_to_str(), semantic_tag::none, context, ec);
1515
                    }
1516
                    else
1517
                    {
1518
                        sink_.append(null_constant().data(), null_constant().size());
1519
                    }
1520
                }
1521
                else 
1522
                {
1523
                    if (options_.enable_neginf_to_num())
1524
                    {
1525
                        sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
1526
                    }
1527
                    else if (options_.enable_neginf_to_str())
1528
                    {
1529
                        write_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
1530
                    }
1531
                    else
1532
                    {
1533
                        sink_.append(null_constant().data(), null_constant().size());
1534
                    }
1535
                }
1536
            }
1537
            else
1538
            {
1539
                fp_(value, sink_);
1540
            }
1541
1542
            if (!stack_.empty())
1543
            {
1544
                stack_.back().increment_count();
1545
            }
1546
            JSONCONS_VISITOR_RETURN;
1547
        }
1548
1549
        JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t value, 
1550
                            semantic_tag,
1551
                            const ser_context&,
1552
                            std::error_code&) final
1553
        {
1554
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1555
            {
1556
                sink_.push_back(',');
1557
            }
1558
            jsoncons::utility::from_integer(value, sink_);
1559
            if (!stack_.empty())
1560
            {
1561
                stack_.back().increment_count();
1562
            }
1563
            JSONCONS_VISITOR_RETURN;
1564
        }
1565
1566
        JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t value, 
1567
                             semantic_tag, 
1568
                             const ser_context&,
1569
                             std::error_code&) final
1570
        {
1571
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1572
            {
1573
                sink_.push_back(',');
1574
            }
1575
            jsoncons::utility::from_integer(value, sink_);
1576
            if (!stack_.empty())
1577
            {
1578
                stack_.back().increment_count();
1579
            }
1580
            JSONCONS_VISITOR_RETURN;
1581
        }
1582
1583
        JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) final
1584
        {
1585
            if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
1586
            {
1587
                sink_.push_back(',');
1588
            }
1589
1590
            if (value)
1591
            {
1592
                sink_.append(true_constant().data(), true_constant().size());
1593
            }
1594
            else
1595
            {
1596
                sink_.append(false_constant().data(), false_constant().size());
1597
            }
1598
1599
            if (!stack_.empty())
1600
            {
1601
                stack_.back().increment_count();
1602
            }
1603
            JSONCONS_VISITOR_RETURN;
1604
        }
1605
    };
1606
1607
    using json_stream_encoder = basic_json_encoder<char,jsoncons::stream_sink<char>>;
1608
    using wjson_stream_encoder = basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
1609
    using compact_json_stream_encoder = basic_compact_json_encoder<char,jsoncons::stream_sink<char>>;
1610
    using compact_wjson_stream_encoder = basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
1611
1612
    using json_string_encoder = basic_json_encoder<char,jsoncons::string_sink<std::string>>;
1613
    using wjson_string_encoder = basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
1614
    using compact_json_string_encoder = basic_compact_json_encoder<char,jsoncons::string_sink<std::string>>;
1615
    using compact_wjson_string_encoder = basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
1616
1617
} // namespace jsoncons
1618
1619
#endif // JSONCONS_JSON_ENCODER_HPP