/src/glaze/include/glaze/json/json_format.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Glaze Library |
2 | | // For the license information refer to glaze.hpp |
3 | | |
4 | | #pragma once |
5 | | |
6 | | #include "glaze/core/common.hpp" |
7 | | #include "glaze/core/opts.hpp" |
8 | | #include "glaze/util/dump.hpp" |
9 | | #include "glaze/util/parse.hpp" |
10 | | |
11 | | namespace glz::detail |
12 | | { |
13 | | enum struct json_type : char { |
14 | | Unset = 'x', |
15 | | String = '"', |
16 | | Comma = ',', |
17 | | Number = '-', |
18 | | Colon = ':', |
19 | | Array_Start = '[', |
20 | | Array_End = ']', |
21 | | Null = 'n', |
22 | | Bool = 't', |
23 | | Object_Start = '{', |
24 | | Object_End = '}', |
25 | | Comment = '/' |
26 | | }; |
27 | | |
28 | | constexpr std::array<json_type, 256> json_types = [] { |
29 | | std::array<json_type, 256> t{}; |
30 | | using enum json_type; |
31 | | t['"'] = String; |
32 | | t[','] = Comma; |
33 | | t['0'] = Number; |
34 | | t['1'] = Number; |
35 | | t['2'] = Number; |
36 | | t['3'] = Number; |
37 | | t['4'] = Number; |
38 | | t['5'] = Number; |
39 | | t['6'] = Number; |
40 | | t['7'] = Number; |
41 | | t['8'] = Number; |
42 | | t['9'] = Number; |
43 | | t['-'] = Number; |
44 | | t[':'] = Colon; |
45 | | t['['] = Array_Start; |
46 | | t[']'] = Array_End; |
47 | | t['n'] = Null; |
48 | | t['t'] = Bool; |
49 | | t['f'] = Bool; |
50 | | t['{'] = Object_Start; |
51 | | t['}'] = Object_End; |
52 | | t['/'] = Comment; |
53 | | return t; |
54 | | }(); |
55 | | |
56 | | template <bool use_tabs, uint8_t indentation_width> |
57 | | inline void append_new_line(auto&& b, auto&& ix, const int64_t indent) |
58 | 203k | { |
59 | 203k | dump<'\n'>(b, ix); |
60 | | if constexpr (use_tabs) { |
61 | | dumpn<'\t'>(indent, b, ix); |
62 | | } |
63 | 203k | else { |
64 | 203k | dumpn<' '>(indent * indentation_width, b, ix); |
65 | 203k | } |
66 | 203k | }; |
67 | | |
68 | | template <opts Opts> |
69 | | requires(has_is_padded(Opts)) |
70 | | sv read_json_string(auto&& it, auto&& end) noexcept |
71 | 1.15k | { |
72 | 1.15k | auto start = it; |
73 | 1.15k | ++it; // skip quote |
74 | 605k | while (it < end) [[likely]] { |
75 | 605k | uint64_t chunk; |
76 | 605k | std::memcpy(&chunk, it, 8); |
77 | 605k | const uint64_t quote = has_quote(chunk); |
78 | 605k | if (quote) { |
79 | 1.63k | it += (countr_zero(quote) >> 3); |
80 | | |
81 | 1.63k | auto* prev = it - 1; |
82 | 2.38k | while (*prev == '\\') { |
83 | 757 | --prev; |
84 | 757 | } |
85 | 1.63k | if (size_t(it - prev) % 2) { |
86 | 1.10k | ++it; // add quote |
87 | 1.10k | return {start, size_t(it - start)}; |
88 | 1.10k | } |
89 | 527 | ++it; // skip escaped quote and continue |
90 | 527 | } |
91 | 604k | else { |
92 | 604k | it += 8; |
93 | 604k | } |
94 | 605k | } |
95 | | |
96 | 48 | return {}; |
97 | 1.15k | } |
98 | | |
99 | | template <opts Opts> |
100 | | requires(!has_is_padded(Opts)) |
101 | | sv read_json_string(auto&& it, auto&& end) noexcept |
102 | 27.0k | { |
103 | 27.0k | auto start = it; |
104 | 27.0k | ++it; // skip quote |
105 | 1.02M | for (const auto end_m7 = end - 7; it < end_m7;) { |
106 | 1.02M | uint64_t chunk; |
107 | 1.02M | std::memcpy(&chunk, it, 8); |
108 | 1.02M | const uint64_t quote = has_quote(chunk); |
109 | 1.02M | if (quote) { |
110 | 27.0k | it += (countr_zero(quote) >> 3); |
111 | | |
112 | 27.0k | auto* prev = it - 1; |
113 | 27.6k | while (*prev == '\\') { |
114 | 600 | --prev; |
115 | 600 | } |
116 | 27.0k | if (size_t(it - prev) % 2) { |
117 | 27.0k | ++it; // add quote |
118 | 27.0k | return {start, size_t(it - start)}; |
119 | 27.0k | } |
120 | 40 | ++it; // skip escaped quote and continue |
121 | 40 | } |
122 | 1.00M | else { |
123 | 1.00M | it += 8; |
124 | 1.00M | } |
125 | 1.02M | } |
126 | | |
127 | | // Tail end of buffer. Should be rare we even get here |
128 | 26 | while (it < end) { |
129 | 21 | if (*it == '"') { |
130 | 1 | auto* prev = it - 1; |
131 | 1 | while (*prev == '\\') { |
132 | 0 | --prev; |
133 | 0 | } |
134 | 1 | if (size_t(it - prev) % 2) { |
135 | 1 | ++it; // add quote |
136 | 1 | return {start, size_t(it - start)}; |
137 | 1 | } |
138 | 1 | } |
139 | 20 | ++it; |
140 | 20 | } |
141 | | |
142 | 5 | return {}; |
143 | 6 | } |
144 | | |
145 | | // Reads /* my comment */ style comments |
146 | | inline sv read_jsonc_comment(auto&& it, auto&& end) noexcept |
147 | | { |
148 | | auto start = it; |
149 | | it += 2; // skip /* |
150 | | for (const auto end_m7 = end - 7; it < end_m7;) { |
151 | | uint64_t chunk; |
152 | | std::memcpy(&chunk, it, 8); |
153 | | const uint64_t slash = has_char<'/'>(chunk); |
154 | | if (slash) { |
155 | | it += (countr_zero(slash) >> 3); |
156 | | |
157 | | if (it[-1] == '*') { |
158 | | ++it; // add slash |
159 | | return {start, size_t(it - start)}; |
160 | | } |
161 | | // skip slash and continue |
162 | | ++it; |
163 | | } |
164 | | else { |
165 | | it += 8; |
166 | | } |
167 | | } |
168 | | |
169 | | // Tail end of buffer. Should be rare we even get here |
170 | | while (it < end) { |
171 | | if (it[-1] == '*' && *it == '/') { |
172 | | ++it; // add slash |
173 | | return {start, size_t(it - start)}; |
174 | | } |
175 | | ++it; |
176 | | } |
177 | | |
178 | | return {}; |
179 | | } |
180 | | |
181 | | inline sv read_json_number(auto&& it) noexcept |
182 | 41.7k | { |
183 | 41.7k | auto start = it; |
184 | 470k | while (numeric_table[uint8_t(*it)]) { |
185 | 429k | ++it; |
186 | 429k | } |
187 | 41.7k | return {start, size_t(it - start)}; |
188 | 41.7k | } |
189 | | } |