Coverage Report

Created: 2025-02-07 06:28

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