/src/glaze/include/glaze/json/minify.hpp
Line | Count | Source |
1 | | // Glaze Library |
2 | | // For the license information refer to glaze.hpp |
3 | | |
4 | | #pragma once |
5 | | |
6 | | // Minifying JSONC handles both comment styles. A block comment is carried through as written; a |
7 | | // line comment is dropped, because minifying is what removes the newline that ends it and without |
8 | | // that newline the comment would swallow everything written after it. |
9 | | |
10 | | #include "glaze/json/json_format.hpp" |
11 | | |
12 | | namespace glz |
13 | | { |
14 | | namespace detail |
15 | | { |
16 | | template <auto Opts> |
17 | | inline void minify_json(is_context auto&& ctx, auto&& it, auto&& end, auto&& b, auto& ix) noexcept |
18 | 26.4k | { |
19 | 26.4k | using enum json_type; |
20 | | |
21 | | // Every write below goes through this, and the dump it makes is unchecked: the caller's |
22 | | // one-shot sizing covers a resizable destination, because minifying only ever removes |
23 | | // bytes, so the input's length is room enough. |
24 | | // |
25 | | // A bounded destination cannot be grown, and asking it for the input's length up front |
26 | | // refuses a buffer that fits the result -- sizing the output to the minified length is the |
27 | | // obvious thing to do, and `{ "a" : 1 }` into a 7 byte buffer has to work. So each write |
28 | | // asks for exactly what it stores instead; see emit_bytes for where that count comes from. |
29 | | // None of it survives for a resizable destination, which keeps the unchecked dumps and |
30 | | // nothing more. |
31 | 14.1M | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); };_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clINS8_17basic_string_viewIcSB_EEEEDaSS_ Line | Count | Source | 31 | 5.56k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIcEEDaSS_ Line | Count | Source | 31 | 48.0k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA5_cEEDaSS_ Line | Count | Source | 31 | 3.11k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA6_cEEDaSS_ Line | Count | Source | 31 | 1.28k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clINS8_17basic_string_viewIcSB_EEEEDaSS_ Line | Count | Source | 31 | 4.18k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIcEEDaSS_ Line | Count | Source | 31 | 24.5k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA5_cEEDaSS_ Line | Count | Source | 31 | 1.58k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA6_cEEDaSS_ Line | Count | Source | 31 | 641 | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clINS8_17basic_string_viewIcSB_EEEEDaSS_ Line | Count | Source | 31 | 2.39M | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIcEEDaSS_ Line | Count | Source | 31 | 10.9M | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA5_cEEDaSS_ Line | Count | Source | 31 | 130k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA6_cEEDaSS_ Line | Count | Source | 31 | 553k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clINS8_17basic_string_viewIcNS8_11char_traitsIcEEEEEEDaSO_ Line | Count | Source | 31 | 2.16k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIcEEDaSO_ Line | Count | Source | 31 | 17.9k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA5_cEEDaSO_ Line | Count | Source | 31 | 672 | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlRKT_E_clIA6_cEEDaSO_ Line | Count | Source | 31 | 220 | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; |
|
32 | | |
33 | 26.4k | auto ws_start = it; |
34 | 26.4k | uint64_t ws_size{}; |
35 | | |
36 | 7.21M | auto skip_expected_whitespace = [&] { |
37 | 7.21M | auto new_ws_start = it; |
38 | 7.21M | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { |
39 | 2.67M | skip_matching_ws(ws_start, it, ws_size); |
40 | 2.67M | } |
41 | | |
42 | 7.21M | if constexpr (Opts.null_terminated) { |
43 | 35.5M | while (whitespace_table[uint8_t(*it)]) { |
44 | 28.3M | ++it; |
45 | 28.3M | } |
46 | | } |
47 | 76.2k | else { |
48 | 77.3M | while ((it < end) && whitespace_table[uint8_t(*it)]) { |
49 | 77.2M | ++it; |
50 | 77.2M | } |
51 | 76.2k | } |
52 | 7.21M | ws_start = new_ws_start; |
53 | 7.21M | ws_size = size_t(it - new_ws_start); |
54 | 7.21M | }; _ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE_clEv Line | Count | Source | 36 | 40.4k | auto skip_expected_whitespace = [&] { | 37 | 40.4k | auto new_ws_start = it; | 38 | 40.4k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 27.5k | skip_matching_ws(ws_start, it, ws_size); | 40 | 27.5k | } | 41 | | | 42 | | if constexpr (Opts.null_terminated) { | 43 | | while (whitespace_table[uint8_t(*it)]) { | 44 | | ++it; | 45 | | } | 46 | | } | 47 | 40.4k | else { | 48 | 38.7M | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 38.6M | ++it; | 50 | 38.6M | } | 51 | 40.4k | } | 52 | 40.4k | ws_start = new_ws_start; | 53 | 40.4k | ws_size = size_t(it - new_ws_start); | 54 | 40.4k | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE_clEv Line | Count | Source | 36 | 20.6k | auto skip_expected_whitespace = [&] { | 37 | 20.6k | auto new_ws_start = it; | 38 | 20.6k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 13.4k | skip_matching_ws(ws_start, it, ws_size); | 40 | 13.4k | } | 41 | | | 42 | | if constexpr (Opts.null_terminated) { | 43 | | while (whitespace_table[uint8_t(*it)]) { | 44 | | ++it; | 45 | | } | 46 | | } | 47 | 20.6k | else { | 48 | 19.3M | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 19.3M | ++it; | 50 | 19.3M | } | 51 | 20.6k | } | 52 | 20.6k | ws_start = new_ws_start; | 53 | 20.6k | ws_size = size_t(it - new_ws_start); | 54 | 20.6k | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE_clEv Line | Count | Source | 36 | 7.14M | auto skip_expected_whitespace = [&] { | 37 | 7.14M | auto new_ws_start = it; | 38 | 7.14M | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 2.62M | skip_matching_ws(ws_start, it, ws_size); | 40 | 2.62M | } | 41 | | | 42 | 7.14M | if constexpr (Opts.null_terminated) { | 43 | 35.5M | while (whitespace_table[uint8_t(*it)]) { | 44 | 28.3M | ++it; | 45 | 28.3M | } | 46 | | } | 47 | | else { | 48 | | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | | ++it; | 50 | | } | 51 | | } | 52 | 7.14M | ws_start = new_ws_start; | 53 | 7.14M | ws_size = size_t(it - new_ws_start); | 54 | 7.14M | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE_clEv Line | Count | Source | 36 | 15.1k | auto skip_expected_whitespace = [&] { | 37 | 15.1k | auto new_ws_start = it; | 38 | 15.1k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 9.19k | skip_matching_ws(ws_start, it, ws_size); | 40 | 9.19k | } | 41 | | | 42 | | if constexpr (Opts.null_terminated) { | 43 | | while (whitespace_table[uint8_t(*it)]) { | 44 | | ++it; | 45 | | } | 46 | | } | 47 | 15.1k | else { | 48 | 19.2M | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 19.2M | ++it; | 50 | 19.2M | } | 51 | 15.1k | } | 52 | 15.1k | ws_start = new_ws_start; | 53 | 15.1k | ws_size = size_t(it - new_ws_start); | 54 | 15.1k | }; |
|
55 | | |
56 | 7.63M | auto skip_whitespace = [&] { |
57 | 7.63M | if constexpr (Opts.null_terminated) { |
58 | 11.6M | while (whitespace_table[uint8_t(*it)]) { |
59 | 4.04M | ++it; |
60 | 4.04M | } |
61 | | } |
62 | 49.4k | else { |
63 | 459k | while ((it < end) && whitespace_table[uint8_t(*it)]) { |
64 | 410k | ++it; |
65 | 410k | } |
66 | 49.4k | } |
67 | 7.63M | }; _ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE0_clEv Line | Count | Source | 56 | 24.4k | auto skip_whitespace = [&] { | 57 | | if constexpr (Opts.null_terminated) { | 58 | | while (whitespace_table[uint8_t(*it)]) { | 59 | | ++it; | 60 | | } | 61 | | } | 62 | 24.4k | else { | 63 | 231k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 206k | ++it; | 65 | 206k | } | 66 | 24.4k | } | 67 | 24.4k | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE0_clEv Line | Count | Source | 56 | 14.9k | auto skip_whitespace = [&] { | 57 | | if constexpr (Opts.null_terminated) { | 58 | | while (whitespace_table[uint8_t(*it)]) { | 59 | | ++it; | 60 | | } | 61 | | } | 62 | 14.9k | else { | 63 | 122k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 107k | ++it; | 65 | 107k | } | 66 | 14.9k | } | 67 | 14.9k | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE0_clEv Line | Count | Source | 56 | 7.58M | auto skip_whitespace = [&] { | 57 | 7.58M | if constexpr (Opts.null_terminated) { | 58 | 11.6M | while (whitespace_table[uint8_t(*it)]) { | 59 | 4.04M | ++it; | 60 | 4.04M | } | 61 | | } | 62 | | else { | 63 | | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | | ++it; | 65 | | } | 66 | | } | 67 | 7.58M | }; |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE0_clEv Line | Count | Source | 56 | 9.97k | auto skip_whitespace = [&] { | 57 | | if constexpr (Opts.null_terminated) { | 58 | | while (whitespace_table[uint8_t(*it)]) { | 59 | | ++it; | 60 | | } | 61 | | } | 62 | 9.97k | else { | 63 | 105k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 95.5k | ++it; | 65 | 95.5k | } | 66 | 9.97k | } | 67 | 9.97k | }; |
|
68 | | |
69 | 26.4k | skip_whitespace(); |
70 | | |
71 | 14.8M | while ([&]() -> bool { |
72 | 14.8M | if constexpr (Opts.null_terminated) { |
73 | 14.7M | return true; |
74 | | } |
75 | 125k | else { |
76 | 125k | return it < end; |
77 | 125k | } |
78 | 14.8M | }()) {_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE1_clEv Line | Count | Source | 71 | 64.9k | while ([&]() -> bool { | 72 | | if constexpr (Opts.null_terminated) { | 73 | | return true; | 74 | | } | 75 | 64.9k | else { | 76 | 64.9k | return it < end; | 77 | 64.9k | } | 78 | 64.9k | }()) { |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE1_clEv Line | Count | Source | 71 | 35.6k | while ([&]() -> bool { | 72 | | if constexpr (Opts.null_terminated) { | 73 | | return true; | 74 | | } | 75 | 35.6k | else { | 76 | 35.6k | return it < end; | 77 | 35.6k | } | 78 | 35.6k | }()) { |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE1_clEv Line | Count | Source | 71 | 14.7M | while ([&]() -> bool { | 72 | 14.7M | if constexpr (Opts.null_terminated) { | 73 | 14.7M | return true; | 74 | | } | 75 | | else { | 76 | | return it < end; | 77 | | } | 78 | 14.7M | }()) { |
_ZZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ENKUlvE1_clEv Line | Count | Source | 71 | 25.1k | while ([&]() -> bool { | 72 | | if constexpr (Opts.null_terminated) { | 73 | | return true; | 74 | | } | 75 | 25.1k | else { | 76 | 25.1k | return it < end; | 77 | 25.1k | } | 78 | 25.1k | }()) { |
|
79 | 14.8M | switch (json_types[uint8_t(*it)]) { |
80 | 1.40M | case String: { |
81 | 1.40M | const auto value = read_json_string(ctx, it, end); |
82 | 1.40M | if (bool(ctx.error)) [[unlikely]] { |
83 | 515 | return; |
84 | 515 | } |
85 | | // non-empty: the scanner reports an empty view as an error |
86 | 1.40M | if (not put(value)) [[unlikely]] { |
87 | 60 | return; |
88 | 60 | } |
89 | 1.40M | skip_whitespace(); |
90 | 1.40M | break; |
91 | 1.40M | } |
92 | 4.42M | case Comma: { |
93 | 4.42M | if (not put(',')) [[unlikely]] { |
94 | 34 | return; |
95 | 34 | } |
96 | 4.42M | ++it; |
97 | 4.42M | skip_expected_whitespace(); |
98 | 4.42M | break; |
99 | 4.42M | } |
100 | 991k | case Number: { |
101 | 991k | const auto value = read_json_number<Opts.null_terminated>(it, end); |
102 | | // non-empty: a Number match is one valid character |
103 | 991k | if (not put(value)) [[unlikely]] { |
104 | 32 | return; |
105 | 32 | } |
106 | 991k | skip_whitespace(); |
107 | 991k | break; |
108 | 991k | } |
109 | 1.10M | case Colon: { |
110 | 1.10M | if (not put(':')) [[unlikely]] { |
111 | 5 | return; |
112 | 5 | } |
113 | 1.10M | ++it; |
114 | 1.10M | skip_whitespace(); |
115 | 1.10M | break; |
116 | 1.10M | } |
117 | 600k | case Array_Start: { |
118 | 600k | if (not put('[')) [[unlikely]] { |
119 | 32 | return; |
120 | 32 | } |
121 | 600k | ++it; |
122 | 600k | skip_expected_whitespace(); |
123 | 600k | break; |
124 | 600k | } |
125 | 576k | case Array_End: { |
126 | 576k | if (not put(']')) [[unlikely]] { |
127 | 9 | return; |
128 | 9 | } |
129 | 576k | ++it; |
130 | 576k | skip_whitespace(); |
131 | 576k | break; |
132 | 576k | } |
133 | 51.2k | case Null: { |
134 | | // The type table matched on the first byte alone; see match_literal for why writing |
135 | | // the literal from the writer's own spelling needs the rest of it checked. Stepping |
136 | | // over it unchecked also puts `it` past `end`, and from there the loop goes on |
137 | | // minifying whatever follows the document into an output sized for the document. |
138 | 51.2k | if (not match_literal<"null">(ctx, it, end)) [[unlikely]] { |
139 | 580 | return; |
140 | 580 | } |
141 | 50.6k | if (not put("null")) [[unlikely]] { |
142 | 11 | return; |
143 | 11 | } |
144 | 50.6k | skip_whitespace(); |
145 | 50.6k | break; |
146 | 50.6k | } |
147 | 642k | case Bool: { |
148 | 642k | if (*it == 't') { |
149 | 86.1k | if (not match_literal<"true">(ctx, it, end)) [[unlikely]] { |
150 | 513 | return; |
151 | 513 | } |
152 | 85.6k | if (not put("true")) [[unlikely]] { |
153 | 11 | return; |
154 | 11 | } |
155 | 85.6k | skip_whitespace(); |
156 | 85.6k | break; |
157 | 85.6k | } |
158 | 556k | else { |
159 | 556k | if (not match_literal<"false">(ctx, it, end)) [[unlikely]] { |
160 | 611 | return; |
161 | 611 | } |
162 | 555k | if (not put("false")) [[unlikely]] { |
163 | 11 | return; |
164 | 11 | } |
165 | 555k | skip_whitespace(); |
166 | 555k | break; |
167 | 555k | } |
168 | 642k | } |
169 | 2.18M | case Object_Start: { |
170 | 2.18M | if (not put('{')) [[unlikely]] { |
171 | 30 | return; |
172 | 30 | } |
173 | 2.18M | ++it; |
174 | 2.18M | skip_expected_whitespace(); |
175 | 2.18M | break; |
176 | 2.18M | } |
177 | 2.16M | case Object_End: { |
178 | 2.16M | if (not put('}')) [[unlikely]] { |
179 | 8 | return; |
180 | 8 | } |
181 | 2.16M | ++it; |
182 | 2.16M | skip_whitespace(); |
183 | 2.16M | break; |
184 | 2.16M | } |
185 | 678k | case Comment: { |
186 | 678k | if constexpr (Opts.comments) { |
187 | 678k | const auto comment = read_jsonc_comment(ctx, it, end); |
188 | 678k | if (bool(ctx.error)) [[unlikely]] { |
189 | 409 | return; |
190 | 409 | } |
191 | | // A line comment is dropped rather than written out. Minifying is what removes |
192 | | // the newline that ends it, and a line comment with no line break behind it |
193 | | // comments out the whole rest of the output. |
194 | 677k | if (not comment.line) { |
195 | | // non-empty: an empty view comes with an error |
196 | 11.4k | if (not put(comment.text)) [[unlikely]] { |
197 | 52 | return; |
198 | 52 | } |
199 | 11.4k | } |
200 | 677k | skip_whitespace(); |
201 | 677k | break; |
202 | | } |
203 | 570 | else { |
204 | 570 | [[fallthrough]]; |
205 | 570 | } |
206 | 678k | } |
207 | 698k | [[unlikely]] default: { |
208 | | // A null terminated run carries no bound in its loop condition, so it ends here, on |
209 | | // the terminator, which is not a JSON token. Reaching it is the end of the document |
210 | | // rather than an error -- unclassified is how the type table reports both, and |
211 | | // without this the minifier left syntax_error behind on every input it was ever |
212 | | // given, valid or not, which is what made its error not worth returning. |
213 | 698k | if constexpr (Opts.null_terminated) { |
214 | 686k | if (it >= end) { |
215 | 10.4k | return; |
216 | 10.4k | } |
217 | 686k | } |
218 | 675k | ctx.error = error_code::syntax_error; |
219 | 698k | return; |
220 | 678k | } |
221 | 14.8M | } |
222 | 14.8M | } |
223 | 26.4k | } _ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb0ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ Line | Count | Source | 18 | 6.86k | { | 19 | 6.86k | using enum json_type; | 20 | | | 21 | | // Every write below goes through this, and the dump it makes is unchecked: the caller's | 22 | | // one-shot sizing covers a resizable destination, because minifying only ever removes | 23 | | // bytes, so the input's length is room enough. | 24 | | // | 25 | | // A bounded destination cannot be grown, and asking it for the input's length up front | 26 | | // refuses a buffer that fits the result -- sizing the output to the minified length is the | 27 | | // obvious thing to do, and `{ "a" : 1 }` into a 7 byte buffer has to work. So each write | 28 | | // asks for exactly what it stores instead; see emit_bytes for where that count comes from. | 29 | | // None of it survives for a resizable destination, which keeps the unchecked dumps and | 30 | | // nothing more. | 31 | 6.86k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; | 32 | | | 33 | 6.86k | auto ws_start = it; | 34 | 6.86k | uint64_t ws_size{}; | 35 | | | 36 | 6.86k | auto skip_expected_whitespace = [&] { | 37 | 6.86k | auto new_ws_start = it; | 38 | 6.86k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 6.86k | skip_matching_ws(ws_start, it, ws_size); | 40 | 6.86k | } | 41 | | | 42 | 6.86k | if constexpr (Opts.null_terminated) { | 43 | 6.86k | while (whitespace_table[uint8_t(*it)]) { | 44 | 6.86k | ++it; | 45 | 6.86k | } | 46 | 6.86k | } | 47 | 6.86k | else { | 48 | 6.86k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 6.86k | ++it; | 50 | 6.86k | } | 51 | 6.86k | } | 52 | 6.86k | ws_start = new_ws_start; | 53 | 6.86k | ws_size = size_t(it - new_ws_start); | 54 | 6.86k | }; | 55 | | | 56 | 6.86k | auto skip_whitespace = [&] { | 57 | 6.86k | if constexpr (Opts.null_terminated) { | 58 | 6.86k | while (whitespace_table[uint8_t(*it)]) { | 59 | 6.86k | ++it; | 60 | 6.86k | } | 61 | 6.86k | } | 62 | 6.86k | else { | 63 | 6.86k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 6.86k | ++it; | 65 | 6.86k | } | 66 | 6.86k | } | 67 | 6.86k | }; | 68 | | | 69 | 6.86k | skip_whitespace(); | 70 | | | 71 | 64.9k | while ([&]() -> bool { | 72 | 64.9k | if constexpr (Opts.null_terminated) { | 73 | 64.9k | return true; | 74 | 64.9k | } | 75 | 64.9k | else { | 76 | 64.9k | return it < end; | 77 | 64.9k | } | 78 | 64.9k | }()) { | 79 | 63.8k | switch (json_types[uint8_t(*it)]) { | 80 | 2.95k | case String: { | 81 | 2.95k | const auto value = read_json_string(ctx, it, end); | 82 | 2.95k | if (bool(ctx.error)) [[unlikely]] { | 83 | 198 | return; | 84 | 198 | } | 85 | | // non-empty: the scanner reports an empty view as an error | 86 | 2.75k | if (not put(value)) [[unlikely]] { | 87 | 0 | return; | 88 | 0 | } | 89 | 2.75k | skip_whitespace(); | 90 | 2.75k | break; | 91 | 2.75k | } | 92 | 14.4k | case Comma: { | 93 | 14.4k | if (not put(',')) [[unlikely]] { | 94 | 0 | return; | 95 | 0 | } | 96 | 14.4k | ++it; | 97 | 14.4k | skip_expected_whitespace(); | 98 | 14.4k | break; | 99 | 14.4k | } | 100 | 2.80k | case Number: { | 101 | 2.80k | const auto value = read_json_number<Opts.null_terminated>(it, end); | 102 | | // non-empty: a Number match is one valid character | 103 | 2.80k | if (not put(value)) [[unlikely]] { | 104 | 0 | return; | 105 | 0 | } | 106 | 2.80k | skip_whitespace(); | 107 | 2.80k | break; | 108 | 2.80k | } | 109 | 2.59k | case Colon: { | 110 | 2.59k | if (not put(':')) [[unlikely]] { | 111 | 0 | return; | 112 | 0 | } | 113 | 2.59k | ++it; | 114 | 2.59k | skip_whitespace(); | 115 | 2.59k | break; | 116 | 2.59k | } | 117 | 12.1k | case Array_Start: { | 118 | 12.1k | if (not put('[')) [[unlikely]] { | 119 | 0 | return; | 120 | 0 | } | 121 | 12.1k | ++it; | 122 | 12.1k | skip_expected_whitespace(); | 123 | 12.1k | break; | 124 | 12.1k | } | 125 | 2.50k | case Array_End: { | 126 | 2.50k | if (not put(']')) [[unlikely]] { | 127 | 0 | return; | 128 | 0 | } | 129 | 2.50k | ++it; | 130 | 2.50k | skip_whitespace(); | 131 | 2.50k | break; | 132 | 2.50k | } | 133 | 1.55k | case Null: { | 134 | | // The type table matched on the first byte alone; see match_literal for why writing | 135 | | // the literal from the writer's own spelling needs the rest of it checked. Stepping | 136 | | // over it unchecked also puts `it` past `end`, and from there the loop goes on | 137 | | // minifying whatever follows the document into an output sized for the document. | 138 | 1.55k | if (not match_literal<"null">(ctx, it, end)) [[unlikely]] { | 139 | 234 | return; | 140 | 234 | } | 141 | 1.31k | if (not put("null")) [[unlikely]] { | 142 | 0 | return; | 143 | 0 | } | 144 | 1.31k | skip_whitespace(); | 145 | 1.31k | break; | 146 | 1.31k | } | 147 | 3.53k | case Bool: { | 148 | 3.53k | if (*it == 't') { | 149 | 2.01k | if (not match_literal<"true">(ctx, it, end)) [[unlikely]] { | 150 | 210 | return; | 151 | 210 | } | 152 | 1.80k | if (not put("true")) [[unlikely]] { | 153 | 0 | return; | 154 | 0 | } | 155 | 1.80k | skip_whitespace(); | 156 | 1.80k | break; | 157 | 1.80k | } | 158 | 1.52k | else { | 159 | 1.52k | if (not match_literal<"false">(ctx, it, end)) [[unlikely]] { | 160 | 246 | return; | 161 | 246 | } | 162 | 1.28k | if (not put("false")) [[unlikely]] { | 163 | 0 | return; | 164 | 0 | } | 165 | 1.28k | skip_whitespace(); | 166 | 1.28k | break; | 167 | 1.28k | } | 168 | 3.53k | } | 169 | 13.7k | case Object_Start: { | 170 | 13.7k | if (not put('{')) [[unlikely]] { | 171 | 0 | return; | 172 | 0 | } | 173 | 13.7k | ++it; | 174 | 13.7k | skip_expected_whitespace(); | 175 | 13.7k | break; | 176 | 13.7k | } | 177 | 2.57k | case Object_End: { | 178 | 2.57k | if (not put('}')) [[unlikely]] { | 179 | 0 | return; | 180 | 0 | } | 181 | 2.57k | ++it; | 182 | 2.57k | skip_whitespace(); | 183 | 2.57k | break; | 184 | 2.57k | } | 185 | 570 | case Comment: { | 186 | | if constexpr (Opts.comments) { | 187 | | const auto comment = read_jsonc_comment(ctx, it, end); | 188 | | if (bool(ctx.error)) [[unlikely]] { | 189 | | return; | 190 | | } | 191 | | // A line comment is dropped rather than written out. Minifying is what removes | 192 | | // the newline that ends it, and a line comment with no line break behind it | 193 | | // comments out the whole rest of the output. | 194 | | if (not comment.line) { | 195 | | // non-empty: an empty view comes with an error | 196 | | if (not put(comment.text)) [[unlikely]] { | 197 | | return; | 198 | | } | 199 | | } | 200 | | skip_whitespace(); | 201 | | break; | 202 | | } | 203 | 570 | else { | 204 | 570 | [[fallthrough]]; | 205 | 570 | } | 206 | 570 | } | 207 | 4.92k | [[unlikely]] default: { | 208 | | // A null terminated run carries no bound in its loop condition, so it ends here, on | 209 | | // the terminator, which is not a JSON token. Reaching it is the end of the document | 210 | | // rather than an error -- unclassified is how the type table reports both, and | 211 | | // without this the minifier left syntax_error behind on every input it was ever | 212 | | // given, valid or not, which is what made its error not worth returning. | 213 | | if constexpr (Opts.null_terminated) { | 214 | | if (it >= end) { | 215 | | return; | 216 | | } | 217 | | } | 218 | 4.92k | ctx.error = error_code::syntax_error; | 219 | 4.92k | return; | 220 | 570 | } | 221 | 63.8k | } | 222 | 63.8k | } | 223 | 6.86k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ Line | Count | Source | 18 | 3.43k | { | 19 | 3.43k | using enum json_type; | 20 | | | 21 | | // Every write below goes through this, and the dump it makes is unchecked: the caller's | 22 | | // one-shot sizing covers a resizable destination, because minifying only ever removes | 23 | | // bytes, so the input's length is room enough. | 24 | | // | 25 | | // A bounded destination cannot be grown, and asking it for the input's length up front | 26 | | // refuses a buffer that fits the result -- sizing the output to the minified length is the | 27 | | // obvious thing to do, and `{ "a" : 1 }` into a 7 byte buffer has to work. So each write | 28 | | // asks for exactly what it stores instead; see emit_bytes for where that count comes from. | 29 | | // None of it survives for a resizable destination, which keeps the unchecked dumps and | 30 | | // nothing more. | 31 | 3.43k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; | 32 | | | 33 | 3.43k | auto ws_start = it; | 34 | 3.43k | uint64_t ws_size{}; | 35 | | | 36 | 3.43k | auto skip_expected_whitespace = [&] { | 37 | 3.43k | auto new_ws_start = it; | 38 | 3.43k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 3.43k | skip_matching_ws(ws_start, it, ws_size); | 40 | 3.43k | } | 41 | | | 42 | 3.43k | if constexpr (Opts.null_terminated) { | 43 | 3.43k | while (whitespace_table[uint8_t(*it)]) { | 44 | 3.43k | ++it; | 45 | 3.43k | } | 46 | 3.43k | } | 47 | 3.43k | else { | 48 | 3.43k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 3.43k | ++it; | 50 | 3.43k | } | 51 | 3.43k | } | 52 | 3.43k | ws_start = new_ws_start; | 53 | 3.43k | ws_size = size_t(it - new_ws_start); | 54 | 3.43k | }; | 55 | | | 56 | 3.43k | auto skip_whitespace = [&] { | 57 | 3.43k | if constexpr (Opts.null_terminated) { | 58 | 3.43k | while (whitespace_table[uint8_t(*it)]) { | 59 | 3.43k | ++it; | 60 | 3.43k | } | 61 | 3.43k | } | 62 | 3.43k | else { | 63 | 3.43k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 3.43k | ++it; | 65 | 3.43k | } | 66 | 3.43k | } | 67 | 3.43k | }; | 68 | | | 69 | 3.43k | skip_whitespace(); | 70 | | | 71 | 35.6k | while ([&]() -> bool { | 72 | 35.6k | if constexpr (Opts.null_terminated) { | 73 | 35.6k | return true; | 74 | 35.6k | } | 75 | 35.6k | else { | 76 | 35.6k | return it < end; | 77 | 35.6k | } | 78 | 35.6k | }()) { | 79 | 34.4k | switch (json_types[uint8_t(*it)]) { | 80 | 1.53k | case String: { | 81 | 1.53k | const auto value = read_json_string(ctx, it, end); | 82 | 1.53k | if (bool(ctx.error)) [[unlikely]] { | 83 | 110 | return; | 84 | 110 | } | 85 | | // non-empty: the scanner reports an empty view as an error | 86 | 1.42k | if (not put(value)) [[unlikely]] { | 87 | 0 | return; | 88 | 0 | } | 89 | 1.42k | skip_whitespace(); | 90 | 1.42k | break; | 91 | 1.42k | } | 92 | 7.30k | case Comma: { | 93 | 7.30k | if (not put(',')) [[unlikely]] { | 94 | 0 | return; | 95 | 0 | } | 96 | 7.30k | ++it; | 97 | 7.30k | skip_expected_whitespace(); | 98 | 7.30k | break; | 99 | 7.30k | } | 100 | 1.44k | case Number: { | 101 | 1.44k | const auto value = read_json_number<Opts.null_terminated>(it, end); | 102 | | // non-empty: a Number match is one valid character | 103 | 1.44k | if (not put(value)) [[unlikely]] { | 104 | 0 | return; | 105 | 0 | } | 106 | 1.44k | skip_whitespace(); | 107 | 1.44k | break; | 108 | 1.44k | } | 109 | 1.33k | case Colon: { | 110 | 1.33k | if (not put(':')) [[unlikely]] { | 111 | 0 | return; | 112 | 0 | } | 113 | 1.33k | ++it; | 114 | 1.33k | skip_whitespace(); | 115 | 1.33k | break; | 116 | 1.33k | } | 117 | 6.34k | case Array_Start: { | 118 | 6.34k | if (not put('[')) [[unlikely]] { | 119 | 0 | return; | 120 | 0 | } | 121 | 6.34k | ++it; | 122 | 6.34k | skip_expected_whitespace(); | 123 | 6.34k | break; | 124 | 6.34k | } | 125 | 1.25k | case Array_End: { | 126 | 1.25k | if (not put(']')) [[unlikely]] { | 127 | 0 | return; | 128 | 0 | } | 129 | 1.25k | ++it; | 130 | 1.25k | skip_whitespace(); | 131 | 1.25k | break; | 132 | 1.25k | } | 133 | 777 | case Null: { | 134 | | // The type table matched on the first byte alone; see match_literal for why writing | 135 | | // the literal from the writer's own spelling needs the rest of it checked. Stepping | 136 | | // over it unchecked also puts `it` past `end`, and from there the loop goes on | 137 | | // minifying whatever follows the document into an output sized for the document. | 138 | 777 | if (not match_literal<"null">(ctx, it, end)) [[unlikely]] { | 139 | 118 | return; | 140 | 118 | } | 141 | 659 | if (not put("null")) [[unlikely]] { | 142 | 0 | return; | 143 | 0 | } | 144 | 659 | skip_whitespace(); | 145 | 659 | break; | 146 | 659 | } | 147 | 1.79k | case Bool: { | 148 | 1.79k | if (*it == 't') { | 149 | 1.03k | if (not match_literal<"true">(ctx, it, end)) [[unlikely]] { | 150 | 105 | return; | 151 | 105 | } | 152 | 926 | if (not put("true")) [[unlikely]] { | 153 | 0 | return; | 154 | 0 | } | 155 | 926 | skip_whitespace(); | 156 | 926 | break; | 157 | 926 | } | 158 | 765 | else { | 159 | 765 | if (not match_literal<"false">(ctx, it, end)) [[unlikely]] { | 160 | 124 | return; | 161 | 124 | } | 162 | 641 | if (not put("false")) [[unlikely]] { | 163 | 0 | return; | 164 | 0 | } | 165 | 641 | skip_whitespace(); | 166 | 641 | break; | 167 | 641 | } | 168 | 1.79k | } | 169 | 6.99k | case Object_Start: { | 170 | 6.99k | if (not put('{')) [[unlikely]] { | 171 | 0 | return; | 172 | 0 | } | 173 | 6.99k | ++it; | 174 | 6.99k | skip_expected_whitespace(); | 175 | 6.99k | break; | 176 | 6.99k | } | 177 | 1.30k | case Object_End: { | 178 | 1.30k | if (not put('}')) [[unlikely]] { | 179 | 0 | return; | 180 | 0 | } | 181 | 1.30k | ++it; | 182 | 1.30k | skip_whitespace(); | 183 | 1.30k | break; | 184 | 1.30k | } | 185 | 2.69k | case Comment: { | 186 | 2.69k | if constexpr (Opts.comments) { | 187 | 2.69k | const auto comment = read_jsonc_comment(ctx, it, end); | 188 | 2.69k | if (bool(ctx.error)) [[unlikely]] { | 189 | 141 | return; | 190 | 141 | } | 191 | | // A line comment is dropped rather than written out. Minifying is what removes | 192 | | // the newline that ends it, and a line comment with no line break behind it | 193 | | // comments out the whole rest of the output. | 194 | 2.55k | if (not comment.line) { | 195 | | // non-empty: an empty view comes with an error | 196 | 1.31k | if (not put(comment.text)) [[unlikely]] { | 197 | 0 | return; | 198 | 0 | } | 199 | 1.31k | } | 200 | 2.55k | skip_whitespace(); | 201 | 2.55k | break; | 202 | | } | 203 | | else { | 204 | | [[fallthrough]]; | 205 | | } | 206 | 2.69k | } | 207 | 4.38k | [[unlikely]] default: { | 208 | | // A null terminated run carries no bound in its loop condition, so it ends here, on | 209 | | // the terminator, which is not a JSON token. Reaching it is the end of the document | 210 | | // rather than an error -- unclassified is how the type table reports both, and | 211 | | // without this the minifier left syntax_error behind on every input it was ever | 212 | | // given, valid or not, which is what made its error not worth returning. | 213 | | if constexpr (Opts.null_terminated) { | 214 | | if (it >= end) { | 215 | | return; | 216 | | } | 217 | | } | 218 | 4.38k | ctx.error = error_code::syntax_error; | 219 | 4.38k | return; | 220 | 2.69k | } | 221 | 34.4k | } | 222 | 34.4k | } | 223 | 3.43k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__112basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEEmEEvOT0_OT1_OT2_OT3_RT4_ Line | Count | Source | 18 | 12.7k | { | 19 | 12.7k | using enum json_type; | 20 | | | 21 | | // Every write below goes through this, and the dump it makes is unchecked: the caller's | 22 | | // one-shot sizing covers a resizable destination, because minifying only ever removes | 23 | | // bytes, so the input's length is room enough. | 24 | | // | 25 | | // A bounded destination cannot be grown, and asking it for the input's length up front | 26 | | // refuses a buffer that fits the result -- sizing the output to the minified length is the | 27 | | // obvious thing to do, and `{ "a" : 1 }` into a 7 byte buffer has to work. So each write | 28 | | // asks for exactly what it stores instead; see emit_bytes for where that count comes from. | 29 | | // None of it survives for a resizable destination, which keeps the unchecked dumps and | 30 | | // nothing more. | 31 | 12.7k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; | 32 | | | 33 | 12.7k | auto ws_start = it; | 34 | 12.7k | uint64_t ws_size{}; | 35 | | | 36 | 12.7k | auto skip_expected_whitespace = [&] { | 37 | 12.7k | auto new_ws_start = it; | 38 | 12.7k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 12.7k | skip_matching_ws(ws_start, it, ws_size); | 40 | 12.7k | } | 41 | | | 42 | 12.7k | if constexpr (Opts.null_terminated) { | 43 | 12.7k | while (whitespace_table[uint8_t(*it)]) { | 44 | 12.7k | ++it; | 45 | 12.7k | } | 46 | 12.7k | } | 47 | 12.7k | else { | 48 | 12.7k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 12.7k | ++it; | 50 | 12.7k | } | 51 | 12.7k | } | 52 | 12.7k | ws_start = new_ws_start; | 53 | 12.7k | ws_size = size_t(it - new_ws_start); | 54 | 12.7k | }; | 55 | | | 56 | 12.7k | auto skip_whitespace = [&] { | 57 | 12.7k | if constexpr (Opts.null_terminated) { | 58 | 12.7k | while (whitespace_table[uint8_t(*it)]) { | 59 | 12.7k | ++it; | 60 | 12.7k | } | 61 | 12.7k | } | 62 | 12.7k | else { | 63 | 12.7k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 12.7k | ++it; | 65 | 12.7k | } | 66 | 12.7k | } | 67 | 12.7k | }; | 68 | | | 69 | 12.7k | skip_whitespace(); | 70 | | | 71 | 14.7M | while ([&]() -> bool { | 72 | 14.7M | if constexpr (Opts.null_terminated) { | 73 | 14.7M | return true; | 74 | 14.7M | } | 75 | 14.7M | else { | 76 | 14.7M | return it < end; | 77 | 14.7M | } | 78 | 14.7M | }()) { | 79 | 14.7M | switch (json_types[uint8_t(*it)]) { | 80 | 1.39M | case String: { | 81 | 1.39M | const auto value = read_json_string(ctx, it, end); | 82 | 1.39M | if (bool(ctx.error)) [[unlikely]] { | 83 | 110 | return; | 84 | 110 | } | 85 | | // non-empty: the scanner reports an empty view as an error | 86 | 1.39M | if (not put(value)) [[unlikely]] { | 87 | 0 | return; | 88 | 0 | } | 89 | 1.39M | skip_whitespace(); | 90 | 1.39M | break; | 91 | 1.39M | } | 92 | 4.40M | case Comma: { | 93 | 4.40M | if (not put(',')) [[unlikely]] { | 94 | 0 | return; | 95 | 0 | } | 96 | 4.40M | ++it; | 97 | 4.40M | skip_expected_whitespace(); | 98 | 4.40M | break; | 99 | 4.40M | } | 100 | 986k | case Number: { | 101 | 986k | const auto value = read_json_number<Opts.null_terminated>(it, end); | 102 | | // non-empty: a Number match is one valid character | 103 | 986k | if (not put(value)) [[unlikely]] { | 104 | 0 | return; | 105 | 0 | } | 106 | 986k | skip_whitespace(); | 107 | 986k | break; | 108 | 986k | } | 109 | 1.09M | case Colon: { | 110 | 1.09M | if (not put(':')) [[unlikely]] { | 111 | 0 | return; | 112 | 0 | } | 113 | 1.09M | ++it; | 114 | 1.09M | skip_whitespace(); | 115 | 1.09M | break; | 116 | 1.09M | } | 117 | 576k | case Array_Start: { | 118 | 576k | if (not put('[')) [[unlikely]] { | 119 | 0 | return; | 120 | 0 | } | 121 | 576k | ++it; | 122 | 576k | skip_expected_whitespace(); | 123 | 576k | break; | 124 | 576k | } | 125 | 571k | case Array_End: { | 126 | 571k | if (not put(']')) [[unlikely]] { | 127 | 0 | return; | 128 | 0 | } | 129 | 571k | ++it; | 130 | 571k | skip_whitespace(); | 131 | 571k | break; | 132 | 571k | } | 133 | 48.5k | case Null: { | 134 | | // The type table matched on the first byte alone; see match_literal for why writing | 135 | | // the literal from the writer's own spelling needs the rest of it checked. Stepping | 136 | | // over it unchecked also puts `it` past `end`, and from there the loop goes on | 137 | | // minifying whatever follows the document into an output sized for the document. | 138 | 48.5k | if (not match_literal<"null">(ctx, it, end)) [[unlikely]] { | 139 | 118 | return; | 140 | 118 | } | 141 | 48.4k | if (not put("null")) [[unlikely]] { | 142 | 0 | return; | 143 | 0 | } | 144 | 48.4k | skip_whitespace(); | 145 | 48.4k | break; | 146 | 48.4k | } | 147 | 636k | case Bool: { | 148 | 636k | if (*it == 't') { | 149 | 82.6k | if (not match_literal<"true">(ctx, it, end)) [[unlikely]] { | 150 | 105 | return; | 151 | 105 | } | 152 | 82.5k | if (not put("true")) [[unlikely]] { | 153 | 0 | return; | 154 | 0 | } | 155 | 82.5k | skip_whitespace(); | 156 | 82.5k | break; | 157 | 82.5k | } | 158 | 553k | else { | 159 | 553k | if (not match_literal<"false">(ctx, it, end)) [[unlikely]] { | 160 | 124 | return; | 161 | 124 | } | 162 | 553k | if (not put("false")) [[unlikely]] { | 163 | 0 | return; | 164 | 0 | } | 165 | 553k | skip_whitespace(); | 166 | 553k | break; | 167 | 553k | } | 168 | 636k | } | 169 | 2.16M | case Object_Start: { | 170 | 2.16M | if (not put('{')) [[unlikely]] { | 171 | 0 | return; | 172 | 0 | } | 173 | 2.16M | ++it; | 174 | 2.16M | skip_expected_whitespace(); | 175 | 2.16M | break; | 176 | 2.16M | } | 177 | 2.15M | case Object_End: { | 178 | 2.15M | if (not put('}')) [[unlikely]] { | 179 | 0 | return; | 180 | 0 | } | 181 | 2.15M | ++it; | 182 | 2.15M | skip_whitespace(); | 183 | 2.15M | break; | 184 | 2.15M | } | 185 | 674k | case Comment: { | 186 | 674k | if constexpr (Opts.comments) { | 187 | 674k | const auto comment = read_jsonc_comment(ctx, it, end); | 188 | 674k | if (bool(ctx.error)) [[unlikely]] { | 189 | 141 | return; | 190 | 141 | } | 191 | | // A line comment is dropped rather than written out. Minifying is what removes | 192 | | // the newline that ends it, and a line comment with no line break behind it | 193 | | // comments out the whole rest of the output. | 194 | 673k | if (not comment.line) { | 195 | | // non-empty: an empty view comes with an error | 196 | 9.63k | if (not put(comment.text)) [[unlikely]] { | 197 | 0 | return; | 198 | 0 | } | 199 | 9.63k | } | 200 | 673k | skip_whitespace(); | 201 | 673k | break; | 202 | | } | 203 | | else { | 204 | | [[fallthrough]]; | 205 | | } | 206 | 674k | } | 207 | 686k | [[unlikely]] default: { | 208 | | // A null terminated run carries no bound in its loop condition, so it ends here, on | 209 | | // the terminator, which is not a JSON token. Reaching it is the end of the document | 210 | | // rather than an error -- unclassified is how the type table reports both, and | 211 | | // without this the minifier left syntax_error behind on every input it was ever | 212 | | // given, valid or not, which is what made its error not worth returning. | 213 | 686k | if constexpr (Opts.null_terminated) { | 214 | 686k | if (it >= end) { | 215 | 10.4k | return; | 216 | 10.4k | } | 217 | 686k | } | 218 | 675k | ctx.error = error_code::syntax_error; | 219 | 686k | return; | 220 | 674k | } | 221 | 14.7M | } | 222 | 14.7M | } | 223 | 12.7k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb0ELb1ELb1ELb1EEETkNS_10is_contextERNS_7contextERPKcS7_RNSt3__15arrayIcLm64EEEmEEvOT0_OT1_OT2_OT3_RT4_ Line | Count | Source | 18 | 3.43k | { | 19 | 3.43k | using enum json_type; | 20 | | | 21 | | // Every write below goes through this, and the dump it makes is unchecked: the caller's | 22 | | // one-shot sizing covers a resizable destination, because minifying only ever removes | 23 | | // bytes, so the input's length is room enough. | 24 | | // | 25 | | // A bounded destination cannot be grown, and asking it for the input's length up front | 26 | | // refuses a buffer that fits the result -- sizing the output to the minified length is the | 27 | | // obvious thing to do, and `{ "a" : 1 }` into a 7 byte buffer has to work. So each write | 28 | | // asks for exactly what it stores instead; see emit_bytes for where that count comes from. | 29 | | // None of it survives for a resizable destination, which keeps the unchecked dumps and | 30 | | // nothing more. | 31 | 3.43k | const auto put = [&](const auto& x) { return emit_bytes<false>(ctx, b, ix, x); }; | 32 | | | 33 | 3.43k | auto ws_start = it; | 34 | 3.43k | uint64_t ws_size{}; | 35 | | | 36 | 3.43k | auto skip_expected_whitespace = [&] { | 37 | 3.43k | auto new_ws_start = it; | 38 | 3.43k | if (ws_size && std::ptrdiff_t(ws_size) < end - it) [[likely]] { | 39 | 3.43k | skip_matching_ws(ws_start, it, ws_size); | 40 | 3.43k | } | 41 | | | 42 | 3.43k | if constexpr (Opts.null_terminated) { | 43 | 3.43k | while (whitespace_table[uint8_t(*it)]) { | 44 | 3.43k | ++it; | 45 | 3.43k | } | 46 | 3.43k | } | 47 | 3.43k | else { | 48 | 3.43k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 49 | 3.43k | ++it; | 50 | 3.43k | } | 51 | 3.43k | } | 52 | 3.43k | ws_start = new_ws_start; | 53 | 3.43k | ws_size = size_t(it - new_ws_start); | 54 | 3.43k | }; | 55 | | | 56 | 3.43k | auto skip_whitespace = [&] { | 57 | 3.43k | if constexpr (Opts.null_terminated) { | 58 | 3.43k | while (whitespace_table[uint8_t(*it)]) { | 59 | 3.43k | ++it; | 60 | 3.43k | } | 61 | 3.43k | } | 62 | 3.43k | else { | 63 | 3.43k | while ((it < end) && whitespace_table[uint8_t(*it)]) { | 64 | 3.43k | ++it; | 65 | 3.43k | } | 66 | 3.43k | } | 67 | 3.43k | }; | 68 | | | 69 | 3.43k | skip_whitespace(); | 70 | | | 71 | 25.1k | while ([&]() -> bool { | 72 | 25.1k | if constexpr (Opts.null_terminated) { | 73 | 25.1k | return true; | 74 | 25.1k | } | 75 | 25.1k | else { | 76 | 25.1k | return it < end; | 77 | 25.1k | } | 78 | 25.1k | }()) { | 79 | 24.1k | switch (json_types[uint8_t(*it)]) { | 80 | 929 | case String: { | 81 | 929 | const auto value = read_json_string(ctx, it, end); | 82 | 929 | if (bool(ctx.error)) [[unlikely]] { | 83 | 97 | return; | 84 | 97 | } | 85 | | // non-empty: the scanner reports an empty view as an error | 86 | 832 | if (not put(value)) [[unlikely]] { | 87 | 60 | return; | 88 | 60 | } | 89 | 772 | skip_whitespace(); | 90 | 772 | break; | 91 | 832 | } | 92 | 5.38k | case Comma: { | 93 | 5.38k | if (not put(',')) [[unlikely]] { | 94 | 34 | return; | 95 | 34 | } | 96 | 5.34k | ++it; | 97 | 5.34k | skip_expected_whitespace(); | 98 | 5.34k | break; | 99 | 5.38k | } | 100 | 879 | case Number: { | 101 | 879 | const auto value = read_json_number<Opts.null_terminated>(it, end); | 102 | | // non-empty: a Number match is one valid character | 103 | 879 | if (not put(value)) [[unlikely]] { | 104 | 32 | return; | 105 | 32 | } | 106 | 847 | skip_whitespace(); | 107 | 847 | break; | 108 | 879 | } | 109 | 1.06k | case Colon: { | 110 | 1.06k | if (not put(':')) [[unlikely]] { | 111 | 5 | return; | 112 | 5 | } | 113 | 1.05k | ++it; | 114 | 1.05k | skip_whitespace(); | 115 | 1.05k | break; | 116 | 1.06k | } | 117 | 4.74k | case Array_Start: { | 118 | 4.74k | if (not put('[')) [[unlikely]] { | 119 | 32 | return; | 120 | 32 | } | 121 | 4.70k | ++it; | 122 | 4.70k | skip_expected_whitespace(); | 123 | 4.70k | break; | 124 | 4.74k | } | 125 | 738 | case Array_End: { | 126 | 738 | if (not put(']')) [[unlikely]] { | 127 | 9 | return; | 128 | 9 | } | 129 | 729 | ++it; | 130 | 729 | skip_whitespace(); | 131 | 729 | break; | 132 | 738 | } | 133 | 409 | case Null: { | 134 | | // The type table matched on the first byte alone; see match_literal for why writing | 135 | | // the literal from the writer's own spelling needs the rest of it checked. Stepping | 136 | | // over it unchecked also puts `it` past `end`, and from there the loop goes on | 137 | | // minifying whatever follows the document into an output sized for the document. | 138 | 409 | if (not match_literal<"null">(ctx, it, end)) [[unlikely]] { | 139 | 110 | return; | 140 | 110 | } | 141 | 299 | if (not put("null")) [[unlikely]] { | 142 | 11 | return; | 143 | 11 | } | 144 | 288 | skip_whitespace(); | 145 | 288 | break; | 146 | 299 | } | 147 | 803 | case Bool: { | 148 | 803 | if (*it == 't') { | 149 | 466 | if (not match_literal<"true">(ctx, it, end)) [[unlikely]] { | 150 | 93 | return; | 151 | 93 | } | 152 | 373 | if (not put("true")) [[unlikely]] { | 153 | 11 | return; | 154 | 11 | } | 155 | 362 | skip_whitespace(); | 156 | 362 | break; | 157 | 373 | } | 158 | 337 | else { | 159 | 337 | if (not match_literal<"false">(ctx, it, end)) [[unlikely]] { | 160 | 117 | return; | 161 | 117 | } | 162 | 220 | if (not put("false")) [[unlikely]] { | 163 | 11 | return; | 164 | 11 | } | 165 | 209 | skip_whitespace(); | 166 | 209 | break; | 167 | 220 | } | 168 | 803 | } | 169 | 5.12k | case Object_Start: { | 170 | 5.12k | if (not put('{')) [[unlikely]] { | 171 | 30 | return; | 172 | 30 | } | 173 | 5.09k | ++it; | 174 | 5.09k | skip_expected_whitespace(); | 175 | 5.09k | break; | 176 | 5.12k | } | 177 | 867 | case Object_End: { | 178 | 867 | if (not put('}')) [[unlikely]] { | 179 | 8 | return; | 180 | 8 | } | 181 | 859 | ++it; | 182 | 859 | skip_whitespace(); | 183 | 859 | break; | 184 | 867 | } | 185 | 1.60k | case Comment: { | 186 | 1.60k | if constexpr (Opts.comments) { | 187 | 1.60k | const auto comment = read_jsonc_comment(ctx, it, end); | 188 | 1.60k | if (bool(ctx.error)) [[unlikely]] { | 189 | 127 | return; | 190 | 127 | } | 191 | | // A line comment is dropped rather than written out. Minifying is what removes | 192 | | // the newline that ends it, and a line comment with no line break behind it | 193 | | // comments out the whole rest of the output. | 194 | 1.47k | if (not comment.line) { | 195 | | // non-empty: an empty view comes with an error | 196 | 452 | if (not put(comment.text)) [[unlikely]] { | 197 | 52 | return; | 198 | 52 | } | 199 | 452 | } | 200 | 1.42k | skip_whitespace(); | 201 | 1.42k | break; | 202 | | } | 203 | | else { | 204 | | [[fallthrough]]; | 205 | | } | 206 | 1.60k | } | 207 | 3.20k | [[unlikely]] default: { | 208 | | // A null terminated run carries no bound in its loop condition, so it ends here, on | 209 | | // the terminator, which is not a JSON token. Reaching it is the end of the document | 210 | | // rather than an error -- unclassified is how the type table reports both, and | 211 | | // without this the minifier left syntax_error behind on every input it was ever | 212 | | // given, valid or not, which is what made its error not worth returning. | 213 | | if constexpr (Opts.null_terminated) { | 214 | | if (it >= end) { | 215 | | return; | 216 | | } | 217 | | } | 218 | 3.20k | ctx.error = error_code::syntax_error; | 219 | 3.20k | return; | 220 | 1.60k | } | 221 | 24.1k | } | 222 | 24.1k | } | 223 | 3.43k | } |
|
224 | | |
225 | | // Returns the number of bytes written, which is what a bounded output has no other way to |
226 | | // learn: it has no size to be shrunk to the result the way a resizable one does. |
227 | | template <auto Opts, class In, output_buffer Out> |
228 | | requires(contiguous<In> && resizable<In>) |
229 | | inline size_t minify_json(is_context auto&& ctx, In&& in, Out&& out) |
230 | 26.4k | { |
231 | 26.4k | if (in.size() == 0) { |
232 | 0 | return 0; |
233 | 0 | } |
234 | | |
235 | 26.4k | if constexpr (resizable<Out>) { |
236 | | // Minifying only ever removes bytes, so this is room enough for whatever comes out. |
237 | | // A bounded output is checked per write instead; see the scan above for why. |
238 | 23.0k | out.resize(in.size() + 2 * padding_bytes); |
239 | 23.0k | } |
240 | 26.4k | size_t ix = 0; |
241 | 26.4k | auto [it, end] = read_iterators<Opts>(in); |
242 | 26.4k | if (bool(ctx.error)) [[unlikely]] { |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | | // The input is no longer grown and shrunk around this: everything the scan below reads |
247 | | // through -- read_json_string, read_json_number, skip_matching_ws -- bounds its own loads |
248 | | // against `end`, so there is nothing left for the padding to protect. |
249 | 26.4k | if constexpr (self_terminating<In>) { |
250 | 12.7k | minify_json<opt_true<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); |
251 | | } |
252 | 13.7k | else { |
253 | 13.7k | minify_json<opt_false<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); |
254 | 13.7k | } |
255 | | |
256 | 26.4k | if constexpr (resizable<Out>) { |
257 | 23.0k | out.resize(ix); |
258 | 23.0k | } |
259 | 26.4k | return ix; |
260 | 26.4k | } _ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEERNSt3__16vectorIcNS3_9allocatorIcEEEETkNS_13output_bufferERNS3_12basic_stringIcNS3_11char_traitsIcEES6_EETkNS_10is_contextERNS_7contextEQaa10contiguousIT0_E9resizableISG_EEEmOT2_OSG_OT1_ Line | Count | Source | 230 | 6.86k | { | 231 | 6.86k | if (in.size() == 0) { | 232 | 0 | return 0; | 233 | 0 | } | 234 | | | 235 | 6.86k | if constexpr (resizable<Out>) { | 236 | | // Minifying only ever removes bytes, so this is room enough for whatever comes out. | 237 | | // A bounded output is checked per write instead; see the scan above for why. | 238 | 6.86k | out.resize(in.size() + 2 * padding_bytes); | 239 | 6.86k | } | 240 | 6.86k | size_t ix = 0; | 241 | 6.86k | auto [it, end] = read_iterators<Opts>(in); | 242 | 6.86k | if (bool(ctx.error)) [[unlikely]] { | 243 | 0 | return 0; | 244 | 0 | } | 245 | | | 246 | | // The input is no longer grown and shrunk around this: everything the scan below reads | 247 | | // through -- read_json_string, read_json_number, skip_matching_ws -- bounds its own loads | 248 | | // against `end`, so there is nothing left for the padding to protect. | 249 | | if constexpr (self_terminating<In>) { | 250 | | minify_json<opt_true<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 251 | | } | 252 | 6.86k | else { | 253 | 6.86k | minify_json<opt_false<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 254 | 6.86k | } | 255 | | | 256 | 6.86k | if constexpr (resizable<Out>) { | 257 | 6.86k | out.resize(ix); | 258 | 6.86k | } | 259 | 6.86k | return ix; | 260 | 6.86k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEERNSt3__16vectorIcNS3_9allocatorIcEEEETkNS_13output_bufferERNS3_12basic_stringIcNS3_11char_traitsIcEES6_EETkNS_10is_contextERNS_7contextEQaa10contiguousIT0_E9resizableISG_EEEmOT2_OSG_OT1_ Line | Count | Source | 230 | 3.43k | { | 231 | 3.43k | if (in.size() == 0) { | 232 | 0 | return 0; | 233 | 0 | } | 234 | | | 235 | 3.43k | if constexpr (resizable<Out>) { | 236 | | // Minifying only ever removes bytes, so this is room enough for whatever comes out. | 237 | | // A bounded output is checked per write instead; see the scan above for why. | 238 | 3.43k | out.resize(in.size() + 2 * padding_bytes); | 239 | 3.43k | } | 240 | 3.43k | size_t ix = 0; | 241 | 3.43k | auto [it, end] = read_iterators<Opts>(in); | 242 | 3.43k | if (bool(ctx.error)) [[unlikely]] { | 243 | 0 | return 0; | 244 | 0 | } | 245 | | | 246 | | // The input is no longer grown and shrunk around this: everything the scan below reads | 247 | | // through -- read_json_string, read_json_number, skip_matching_ws -- bounds its own loads | 248 | | // against `end`, so there is nothing left for the padding to protect. | 249 | | if constexpr (self_terminating<In>) { | 250 | | minify_json<opt_true<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 251 | | } | 252 | 3.43k | else { | 253 | 3.43k | minify_json<opt_false<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 254 | 3.43k | } | 255 | | | 256 | 3.43k | if constexpr (resizable<Out>) { | 257 | 3.43k | out.resize(ix); | 258 | 3.43k | } | 259 | 3.43k | return ix; | 260 | 3.43k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEERNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEETkNS_13output_bufferESA_TkNS_10is_contextERNS_7contextEQaa10contiguousIT0_E9resizableISD_EEEmOT2_OSD_OT1_ Line | Count | Source | 230 | 12.7k | { | 231 | 12.7k | if (in.size() == 0) { | 232 | 0 | return 0; | 233 | 0 | } | 234 | | | 235 | 12.7k | if constexpr (resizable<Out>) { | 236 | | // Minifying only ever removes bytes, so this is room enough for whatever comes out. | 237 | | // A bounded output is checked per write instead; see the scan above for why. | 238 | 12.7k | out.resize(in.size() + 2 * padding_bytes); | 239 | 12.7k | } | 240 | 12.7k | size_t ix = 0; | 241 | 12.7k | auto [it, end] = read_iterators<Opts>(in); | 242 | 12.7k | if (bool(ctx.error)) [[unlikely]] { | 243 | 0 | return 0; | 244 | 0 | } | 245 | | | 246 | | // The input is no longer grown and shrunk around this: everything the scan below reads | 247 | | // through -- read_json_string, read_json_number, skip_matching_ws -- bounds its own loads | 248 | | // against `end`, so there is nothing left for the padding to protect. | 249 | 12.7k | if constexpr (self_terminating<In>) { | 250 | 12.7k | minify_json<opt_true<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 251 | | } | 252 | | else { | 253 | | minify_json<opt_false<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 254 | | } | 255 | | | 256 | 12.7k | if constexpr (resizable<Out>) { | 257 | 12.7k | out.resize(ix); | 258 | 12.7k | } | 259 | 12.7k | return ix; | 260 | 12.7k | } |
_ZN3glz6detail11minify_jsonITnDaXtlNS_4optsELj10ELb1ELb1ELb1ELb1EEERNSt3__16vectorIcNS3_9allocatorIcEEEETkNS_13output_bufferERNS3_5arrayIcLm64EEETkNS_10is_contextERNS_7contextEQaa10contiguousIT0_E9resizableISE_EEEmOT2_OSE_OT1_ Line | Count | Source | 230 | 3.43k | { | 231 | 3.43k | if (in.size() == 0) { | 232 | 0 | return 0; | 233 | 0 | } | 234 | | | 235 | | if constexpr (resizable<Out>) { | 236 | | // Minifying only ever removes bytes, so this is room enough for whatever comes out. | 237 | | // A bounded output is checked per write instead; see the scan above for why. | 238 | | out.resize(in.size() + 2 * padding_bytes); | 239 | | } | 240 | 3.43k | size_t ix = 0; | 241 | 3.43k | auto [it, end] = read_iterators<Opts>(in); | 242 | 3.43k | if (bool(ctx.error)) [[unlikely]] { | 243 | 0 | return 0; | 244 | 0 | } | 245 | | | 246 | | // The input is no longer grown and shrunk around this: everything the scan below reads | 247 | | // through -- read_json_string, read_json_number, skip_matching_ws -- bounds its own loads | 248 | | // against `end`, so there is nothing left for the padding to protect. | 249 | | if constexpr (self_terminating<In>) { | 250 | | minify_json<opt_true<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 251 | | } | 252 | 3.43k | else { | 253 | 3.43k | minify_json<opt_false<Opts, &opts::null_terminated>>(ctx, it, end, out, ix); | 254 | 3.43k | } | 255 | | | 256 | | if constexpr (resizable<Out>) { | 257 | | out.resize(ix); | 258 | | } | 259 | 3.43k | return ix; | 260 | 3.43k | } |
|
261 | | } |
262 | | |
263 | | // The overloads that write into a caller's buffer report what went wrong, and how many bytes |
264 | | // they wrote, which is the only way a bounded output learns where its result ends. That count is |
265 | | // an offset into the output, not the input, so glz::format_error(ec, source) -- which reads it |
266 | | // as a position in the buffer it is handed -- does not point at the offending byte here. Not |
267 | | // [[nodiscard]]: |
268 | | // minifying auto-generated JSON does not fail, so the callers that have always ignored the |
269 | | // outcome are right to, and warning at all of them would say nothing useful. The overloads that |
270 | | // return the text have nowhere to put an error, so they come back empty rather than hand over |
271 | | // the prefix the scan managed before it stopped -- routinely an unterminated string or an |
272 | | // unclosed brace, which is not a document but a silent truncation of one. |
273 | | // |
274 | | // Minifying reports what it actually parses, which is strings, comments and literals. It does |
275 | | // not check that the document is structurally valid JSON -- `[1 2]` minifies to `[12]` -- so a |
276 | | // document that may not be well formed wants glz::validate_json or glz::validate_jsonc. |
277 | | |
278 | | template <auto Opts = opts{}> |
279 | | inline error_ctx minify_json(resizable auto& in, auto& out) |
280 | | { |
281 | | context ctx{}; |
282 | | const auto n = detail::minify_json<Opts>(ctx, in, out); |
283 | | return {n, ctx.error, ctx.custom_error_message}; |
284 | | } |
285 | | |
286 | | template <auto Opts = opts{}> |
287 | | inline std::string minify_json(resizable auto& in) |
288 | 6.86k | { |
289 | 6.86k | context ctx{}; |
290 | 6.86k | std::string out{}; |
291 | 6.86k | detail::minify_json<Opts>(ctx, in, out); |
292 | 6.86k | if (bool(ctx.error)) [[unlikely]] { |
293 | 5.81k | return {}; // an incomplete result is not a document; see above |
294 | 5.81k | } |
295 | 1.04k | return out; |
296 | 6.86k | } |
297 | | |
298 | | template <auto Opts = opts{}> |
299 | | inline error_ctx minify_jsonc(resizable auto& in, auto& out) |
300 | 12.7k | { |
301 | 12.7k | context ctx{}; |
302 | 12.7k | const auto n = detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); |
303 | 12.7k | return {n, ctx.error, ctx.custom_error_message}; |
304 | 12.7k | } _ZN3glz12minify_jsoncITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETkNS_9resizableENSt3__16vectorIcNS2_9allocatorIcEEEENS2_5arrayIcLm64EEEEENS_9error_ctxERT0_RT1_ Line | Count | Source | 300 | 3.43k | { | 301 | 3.43k | context ctx{}; | 302 | 3.43k | const auto n = detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); | 303 | 3.43k | return {n, ctx.error, ctx.custom_error_message}; | 304 | 3.43k | } |
_ZN3glz12minify_jsoncITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETkNS_9resizableENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEES8_EENS_9error_ctxERT0_RT1_ Line | Count | Source | 300 | 9.29k | { | 301 | 9.29k | context ctx{}; | 302 | 9.29k | const auto n = detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); | 303 | 9.29k | return {n, ctx.error, ctx.custom_error_message}; | 304 | 9.29k | } |
|
305 | | |
306 | | template <auto Opts = opts{}> |
307 | | inline std::string minify_jsonc(resizable auto& in) |
308 | 6.86k | { |
309 | 6.86k | context ctx{}; |
310 | 6.86k | std::string out{}; |
311 | 6.86k | detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); |
312 | 6.86k | if (bool(ctx.error)) [[unlikely]] { |
313 | 4.56k | return {}; // an incomplete result is not a document; see above |
314 | 4.56k | } |
315 | 2.29k | return out; |
316 | 6.86k | } _ZN3glz12minify_jsoncITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETkNS_9resizableENSt3__16vectorIcNS2_9allocatorIcEEEEEENS2_12basic_stringIcNS2_11char_traitsIcEES5_EERT0_ Line | Count | Source | 308 | 3.43k | { | 309 | 3.43k | context ctx{}; | 310 | 3.43k | std::string out{}; | 311 | 3.43k | detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); | 312 | 3.43k | if (bool(ctx.error)) [[unlikely]] { | 313 | 2.28k | return {}; // an incomplete result is not a document; see above | 314 | 2.28k | } | 315 | 1.14k | return out; | 316 | 3.43k | } |
_ZN3glz12minify_jsoncITnDaXtlNS_4optsELj10ELb1ELb0ELb1ELb1EEETkNS_9resizableENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_RT0_ Line | Count | Source | 308 | 3.43k | { | 309 | 3.43k | context ctx{}; | 310 | 3.43k | std::string out{}; | 311 | 3.43k | detail::minify_json<opt_true<Opts, &opts::comments>>(ctx, in, out); | 312 | 3.43k | if (bool(ctx.error)) [[unlikely]] { | 313 | 2.28k | return {}; // an incomplete result is not a document; see above | 314 | 2.28k | } | 315 | 1.14k | return out; | 316 | 3.43k | } |
|
317 | | } |