Line data Source code
1 : /***************************************************************************\
2 : * Name : serialize library for json *
3 : * Description : all json serialization functions *
4 : * Author : antonin.kriz@gmail.com *
5 : * ------------------------------------------------------------------------- *
6 : * This is free software; you can redistribute it and/or modify it under the *
7 : * terms of the MIT license. A copy of the license can be found in the file *
8 : * "LICENSE" at the root of this distribution. *
9 : \***************************************************************************/
10 :
11 : #pragma once
12 :
13 : #include "../concepts.h"
14 :
15 : #include "../to_from_chars.h"
16 : #include "base64.h"
17 : #include "field.hpp"
18 : #include "spb/json/deserialize.hpp"
19 : #include "spb/utf8.h"
20 : #include <algorithm>
21 : #include <array>
22 : #include <cctype>
23 : #include <charconv>
24 : #include <cinttypes>
25 : #include <cstddef>
26 : #include <cstring>
27 : #include <map>
28 : #include <memory>
29 : #include <spb/io/io.hpp>
30 : #include <stdexcept>
31 : #include <string>
32 : #include <string_view>
33 : #include <sys/types.h>
34 : #include <type_traits>
35 :
36 : namespace spb::json::detail
37 : {
38 : struct ostream
39 : {
40 : private:
41 : size_t bytes_written = 0;
42 : spb::io::writer on_write;
43 :
44 : public:
45 : //- flag if put ',' before value
46 : bool put_comma = false;
47 :
48 : /**
49 : * @brief Construct a new ostream object
50 : *
51 : * @param writer if null, stream will skip all writes but will still count number of written
52 : * chars
53 : */
54 7251 : explicit ostream(spb::io::writer writer) noexcept : on_write(writer)
55 : {
56 7251 : }
57 :
58 8760015 : void write(char c) noexcept
59 : {
60 8760015 : if (on_write)
61 4379015 : on_write(&c, sizeof(c));
62 :
63 8760015 : bytes_written += sizeof(c);
64 8760015 : }
65 :
66 20 : void write_unicode(uint32_t codepoint)
67 : {
68 20 : if (codepoint <= 0xffff)
69 : {
70 15 : char buffer[8] = {};
71 15 : auto size = snprintf(buffer, sizeof(buffer), "\\u%04" PRIx32, codepoint);
72 15 : return write(std::string_view(buffer, size));
73 : }
74 5 : if (codepoint <= 0x10FFFF)
75 : {
76 5 : codepoint -= 0x10000;
77 :
78 5 : auto high = static_cast<uint16_t>((codepoint >> 10) + 0xD800);
79 5 : auto low = static_cast<uint16_t>((codepoint & 0x3FF) + 0xDC00);
80 5 : char buffer[16] = {};
81 5 : auto size = snprintf(buffer, sizeof(buffer), "\\u%04" PRIx16 "\\u%04" PRIx16, high, low);
82 5 : return write(std::string_view(buffer, size));
83 : }
84 0 : throw std::invalid_argument("invalid utf8");
85 : }
86 :
87 5805 : void write(std::string_view str)
88 : {
89 5805 : if (on_write)
90 2383 : on_write(str.data(), str.size());
91 :
92 5805 : bytes_written += str.size();
93 5805 : }
94 :
95 2363 : void write_escaped(std::string_view str)
96 : {
97 2363 : if (!has_escape_chars(str))
98 : {
99 2319 : write(str);
100 2319 : return;
101 : }
102 :
103 : using namespace std::literals;
104 44 : uint32_t codepoint = 0;
105 44 : uint32_t state = spb::detail::utf8::ok;
106 44 : bool decoding_utf8 = false;
107 285 : for (uint8_t c : str)
108 : {
109 241 : if (decoding_utf8)
110 : {
111 30 : if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
112 : {
113 15 : write_unicode(codepoint);
114 15 : decoding_utf8 = false;
115 : }
116 30 : continue;
117 : }
118 211 : if (is_escape(c))
119 : {
120 128 : switch (c)
121 : {
122 21 : case '"':
123 21 : write(R"(\")"sv);
124 21 : break;
125 14 : case '\\':
126 14 : write(R"(\\)"sv);
127 14 : break;
128 9 : case '\b':
129 9 : write(R"(\b)"sv);
130 9 : break;
131 9 : case '\f':
132 9 : write(R"(\f)"sv);
133 9 : break;
134 14 : case '\n':
135 14 : write(R"(\n)"sv);
136 14 : break;
137 14 : case '\r':
138 14 : write(R"(\r)"sv);
139 14 : break;
140 26 : case '\t':
141 26 : write(R"(\t)"sv);
142 26 : break;
143 21 : default:
144 21 : decoding_utf8 = true;
145 21 : if (spb::detail::utf8::decode_point(&state, &codepoint, c) == spb::detail::utf8::ok)
146 : {
147 5 : write_unicode(codepoint);
148 5 : decoding_utf8 = false;
149 : }
150 : }
151 : }
152 : else
153 : {
154 83 : write(c);
155 : }
156 : }
157 44 : if (state != spb::detail::utf8::ok)
158 : {
159 1 : throw std::runtime_error("invalid utf8");
160 : }
161 : }
162 :
163 : void serialize(const auto &value, const field_attributes &field = {});
164 : void serialize(std::string_view value);
165 :
166 4656 : [[nodiscard]] auto size() const noexcept -> size_t
167 : {
168 4656 : return bytes_written;
169 : }
170 :
171 : private:
172 11618 : static auto is_escape(uint8_t c) -> bool
173 : {
174 : static constexpr std::string_view escape_chars = "\\\"\b\f\n\r\t<>";
175 11618 : return c <= 0x1f || c >= 0x7f || escape_chars.find(c) != std::string_view::npos;
176 : }
177 :
178 2363 : static auto has_escape_chars(std::string_view str) -> bool
179 : {
180 2363 : return std::any_of(str.begin(), str.end(), is_escape);
181 : }
182 : };
183 :
184 : using namespace std::literals;
185 :
186 5118 : static inline void serialize_key(ostream &stream, std::string_view key)
187 : {
188 5118 : if (stream.put_comma)
189 690 : stream.write(',');
190 :
191 5118 : stream.put_comma = true;
192 :
193 5118 : if (key.empty())
194 3322 : return;
195 :
196 1796 : stream.write('"');
197 1796 : stream.write_escaped(key);
198 1796 : stream.write(R"(":)"sv);
199 : }
200 :
201 : static inline void serialize(ostream &stream, const bool &value, const field_attributes &field);
202 : static inline void serialize(ostream &stream, const spb::detail::proto_field_int_or_float auto &value,
203 : const field_attributes &field);
204 : static inline void serialize(ostream &stream, const spb::detail::proto_message auto &value,
205 : const field_attributes &field);
206 : static inline void serialize(ostream &stream, const spb::detail::proto_enum auto &value,
207 : const field_attributes &field);
208 : static inline void serialize(ostream &stream, const spb::detail::proto_field_string auto &value,
209 : const field_attributes &field);
210 : static inline void serialize(ostream &stream, const spb::detail::proto_field_bytes auto &value,
211 : const field_attributes &field);
212 : static inline void serialize(ostream &stream, const spb::detail::proto_label_repeated auto &value,
213 : const field_attributes &field);
214 : static inline void serialize(ostream &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
215 : const field_attributes &field);
216 : template <typename keyT, typename valueT>
217 : static inline void serialize(ostream &stream, const std::map<keyT, valueT> &map,
218 : const field_attributes &field);
219 :
220 : static inline void serialize(ostream &stream, bool value);
221 : static inline void serialize(ostream &stream, spb::detail::proto_field_int_or_float auto value);
222 : static inline void serialize(ostream &stream, const spb::detail::proto_field_string auto &value);
223 :
224 76 : static inline void serialize(ostream &stream, bool value)
225 : {
226 76 : stream.write(value ? "true"sv : "false"sv);
227 76 : }
228 :
229 567 : static inline void serialize(ostream &stream, const std::string_view &value)
230 : {
231 567 : stream.write('"');
232 567 : stream.write_escaped(value);
233 566 : stream.write('"');
234 566 : }
235 :
236 463 : static inline void serialize(ostream &stream, const spb::detail::proto_field_string auto &value)
237 : {
238 463 : serialize(stream, std::string_view(value.data(), value.size()));
239 462 : }
240 :
241 1479 : static inline void serialize(ostream &stream, spb::detail::proto_field_int_or_float auto value)
242 : {
243 1479 : auto buffer = std::array<char, 32>();
244 :
245 3448 : auto result = spb_std_emu::to_chars(buffer.data(), buffer.data() + sizeof(buffer), value);
246 4437 : stream.write(std::string_view(buffer.data(), static_cast<size_t>(result.ptr - buffer.data())));
247 1479 : }
248 :
249 76 : static inline void serialize(ostream &stream, const bool &value, const field_attributes &field)
250 : {
251 76 : serialize_key(stream, field.name);
252 76 : serialize(stream, value);
253 76 : }
254 :
255 1471 : static inline void serialize(ostream &stream, const spb::detail::proto_field_int_or_float auto &value,
256 : const field_attributes &field)
257 : {
258 1471 : serialize_key(stream, field.name);
259 1471 : serialize(stream, value);
260 1471 : }
261 :
262 476 : static inline void serialize(ostream &stream, const spb::detail::proto_field_string auto &value,
263 : const field_attributes &field)
264 : {
265 476 : if (value.empty())
266 5 : return;
267 :
268 471 : if (field.max_size && value.size() > field.max_size) [[unlikely]]
269 8 : throw std::length_error("string is too large");
270 :
271 463 : serialize_key(stream, field.name);
272 463 : serialize(stream, value);
273 : }
274 :
275 324 : static inline void serialize(ostream &stream, const spb::detail::proto_field_bytes auto &value,
276 : const field_attributes &field)
277 : {
278 324 : if (value.empty())
279 15 : return;
280 :
281 309 : if (field.max_size && value.size() > field.max_size) [[unlikely]]
282 8 : throw std::length_error("bytes is too large");
283 :
284 301 : serialize_key(stream, field.name);
285 301 : stream.write('"');
286 301 : base64_encode(stream, value);
287 301 : stream.write('"');
288 : }
289 839 : static inline void serialize(ostream &stream, const spb::detail::proto_label_repeated auto &value,
290 : const field_attributes &field)
291 : {
292 839 : if (value.empty())
293 270 : return;
294 :
295 569 : if (field.max_count && value.size() > field.max_count) [[unlikely]]
296 17 : throw std::length_error("repeated is too large");
297 :
298 552 : serialize_key(stream, field.name);
299 552 : stream.write('[');
300 552 : stream.put_comma = false;
301 1731 : for (const auto &v : value)
302 : {
303 : if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
304 : {
305 34 : serialize(stream, bool(v), {});
306 : }
307 : else
308 : {
309 1145 : serialize(stream, v, {});
310 : }
311 : }
312 552 : stream.write(']');
313 552 : stream.put_comma = true;
314 : }
315 :
316 10 : static inline void serialize(ostream &stream, const spb::detail::proto_label_repeated_fixed_size auto &value,
317 : const field_attributes &field)
318 : {
319 10 : serialize_key(stream, field.name);
320 10 : stream.write('[');
321 10 : stream.put_comma = false;
322 60 : for (size_t i = 0; i < value.size(); i++)
323 : {
324 : if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
325 : {
326 : serialize(stream, bool(value[i]));
327 : }
328 : else
329 : {
330 40 : serialize(stream, value[i], {});
331 : }
332 : }
333 10 : stream.write(']');
334 10 : stream.put_comma = true;
335 10 : }
336 :
337 : static constexpr field_attributes no_name = {};
338 :
339 : template <typename keyT, typename valueT>
340 20 : static inline void serialize(ostream &stream, const std::map<keyT, valueT> &map,
341 : const field_attributes &field)
342 : {
343 20 : if (map.empty())
344 8 : return;
345 :
346 12 : serialize_key(stream, field.name);
347 12 : stream.write('{');
348 12 : stream.put_comma = false;
349 26 : for (auto &[map_key, map_value] : map)
350 : {
351 : if constexpr (std::is_same_v<keyT, std::string_view> || std::is_same_v<keyT, std::string>)
352 : {
353 6 : serialize_key(stream, map_key);
354 : }
355 : else
356 : {
357 8 : if (stream.put_comma)
358 2 : stream.write(',');
359 :
360 8 : stream.write('"');
361 8 : serialize(stream, map_key);
362 8 : stream.write(R"(":)"sv);
363 : }
364 14 : stream.put_comma = false;
365 14 : serialize(stream, map_value, no_name);
366 14 : stream.put_comma = true;
367 : }
368 12 : stream.write('}');
369 12 : stream.put_comma = true;
370 : }
371 :
372 615 : static inline void serialize(ostream &stream, const spb::detail::proto_label_optional auto &p_value,
373 : const field_attributes &field)
374 : {
375 615 : if (p_value.has_value())
376 513 : return serialize(stream, *p_value, field);
377 : }
378 :
379 : template <typename T>
380 : static inline void serialize(ostream &stream, const std::unique_ptr<T> &p_value,
381 : const field_attributes &field)
382 : {
383 : if (p_value)
384 : return serialize(stream, *p_value, field);
385 : }
386 :
387 2123 : static inline void serialize(ostream &stream, const spb::detail::proto_message auto &value,
388 : const field_attributes &field)
389 : {
390 2123 : serialize_key(stream, field.name);
391 2123 : stream.write('{');
392 2123 : stream.put_comma = false;
393 :
394 : //
395 : //- serialize_value is generated by the spb-protoc
396 : //
397 2123 : serialize_value(stream, value);
398 2090 : stream.write('}');
399 2090 : stream.put_comma = true;
400 2090 : }
401 :
402 104 : static inline void serialize(ostream &stream, const spb::detail::proto_enum auto &value,
403 : const field_attributes &field)
404 : {
405 104 : serialize_key(stream, field.name);
406 :
407 : //
408 : //- serialize_value is generated by the spb-protoc
409 : //
410 104 : serialize_value(stream, value);
411 104 : }
412 :
413 2129 : static inline auto serialize(const auto &value, spb::io::writer on_write) -> size_t
414 : {
415 2129 : auto stream = ostream(on_write);
416 2129 : serialize(stream, value, no_name);
417 4190 : return stream.size();
418 : }
419 :
420 2183 : void ostream::serialize(const auto &value, const field_attributes &field)
421 : {
422 2183 : detail::serialize(*this, value, field);
423 2150 : }
424 :
425 104 : inline void ostream::serialize(std::string_view value)
426 : {
427 104 : detail::serialize(*this, value);
428 104 : }
429 :
430 : } // namespace spb::json::detail
|