/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 |