GMP 0.4.0
Generative Metaprogramming library for C++
Loading...
Searching...
No Matches
xml.hpp
Go to the documentation of this file.
1// ___ __ __ ___
2// / __| \/ | _ \ GMP(Generative Metaprogramming)
3// | (_ | |\/| | _/ version 0.4.0
4// \___|_| |_|_| https://github.com/lkimuk/gmp
5//
6// SPDX-FileCopyrightText: 2023-2026 Miles Li <https://www.cppmore.com/>
7// SPDX-License-Identifier: MIT
8//
9// This file is part of the GMP (Generative Metaprogramming) library.
10// Full project source: https://github.com/lkimuk/gmp
11
12#ifndef GMP_SERIALIZATION_XML_HPP_
13#define GMP_SERIALIZATION_XML_HPP_
14
15#include <charconv>
16#include <cmath>
17#include <cstdint>
18#include <limits>
19#include <string>
20#include <string_view>
21#include <utility>
22#include <vector>
23
24#include <gmp/serialization/detail/text.hpp>
25#include <gmp/serialization/detail/write_value.hpp>
27
28namespace gmp::detail {
29
30inline bool is_xml_code_point(std::uint32_t value) noexcept {
31 return value == 0x9 || value == 0xa || value == 0xd || (value >= 0x20 && value <= 0xd7ff) ||
32 (value >= 0xe000 && value <= 0xfffd) || (value >= 0x10000 && value <= 0x10ffff);
33}
34
35inline bool has_only_xml_characters(std::string_view text) noexcept {
36 // Keep this helper safe even when called independently of the parser or
37 // xml_escape. In particular, never read continuation bytes past the end of
38 // a malformed UTF-8 sequence.
39 if (!is_valid_utf8(text)) {
40 return false;
41 }
42 for (std::size_t i = 0; i < text.size();) {
43 const auto first = static_cast<unsigned char>(text[i]);
44 std::uint32_t code_point = first;
45 std::size_t width = 1;
46 if (first >= 0xc2 && first <= 0xdf) {
47 code_point = first & 0x1f;
48 width = 2;
49 } else if (first >= 0xe0 && first <= 0xef) {
50 code_point = first & 0x0f;
51 width = 3;
52 } else if (first >= 0xf0 && first <= 0xf4) {
53 code_point = first & 0x07;
54 width = 4;
55 }
56 for (std::size_t j = 1; j < width; ++j) {
58 }
59 if (!is_xml_code_point(code_point)) {
60 return false;
61 }
62 i += width;
63 }
64 return true;
65}
66
67inline serialization_result<void> xml_escape(std::string &out, std::string_view text,
68 std::size_t limit, bool attribute = false) {
69 if (!is_valid_utf8(text)) {
70 return make_serialization_error(serialization_errc::invalid_utf8, "string is not valid UTF-8");
71 }
72 if (!has_only_xml_characters(text)) {
74 "string contains a character forbidden by XML 1.0");
75 }
76 for (const char c : text) {
77 std::string_view replacement;
78 switch (c) {
79 case '&':
80 replacement = "&amp;";
81 break;
82 case '<':
83 replacement = "&lt;";
84 break;
85 case '>':
86 replacement = "&gt;";
87 break;
88 case '"':
89 if (attribute) {
90 replacement = "&quot;";
91 }
92 break;
93 case '\'':
94 if (attribute) {
95 replacement = "&apos;";
96 }
97 break;
98 case '\t':
99 if (attribute) {
100 replacement = "&#x9;";
101 }
102 break;
103 case '\n':
104 if (attribute) {
105 replacement = "&#xA;";
106 }
107 break;
108 case '\r':
109 replacement = "&#xD;";
110 break;
111 default:
112 break;
113 }
114 if (replacement.empty()) {
115 if (out.size() >= limit) {
117 "XML output exceeds configured limit");
118 }
119 out.push_back(c);
120 } else {
121 if (out.size() > limit || replacement.size() > limit - out.size()) {
123 "XML output exceeds configured limit");
124 }
125 out += replacement;
126 }
127 }
128 return {};
129}
130
131class xml_writer {
132public:
133 explicit xml_writer(xml_write_options options = {}) : options_(options) {}
134
135 serialization_result<void> write_null() {
136 auto status = before_value();
137 if (!status) {
138 return status;
139 }
140 if (!(status = append("<value kind=\"null\"/>"))) {
141 return status;
142 }
143 return complete_value();
144 }
145
146 serialization_result<void> write_bool(bool value) {
147 return scalar(value ? "true" : "false", "boolean");
148 }
149
150 serialization_result<void> write_signed(std::int64_t value) {
151 return number(value, "signed");
152 }
153
154 serialization_result<void> write_unsigned(std::uint64_t value) {
155 return number(value, "unsigned");
156 }
157
158 serialization_result<void> write_floating(double value) {
159 if (!std::isfinite(value)) {
161 "non-finite number cannot be represented");
162 }
163 char buffer[64];
164 const auto result =
165 std::to_chars(buffer, buffer + sizeof(buffer), value, std::chars_format::general,
166 std::numeric_limits<double>::max_digits10);
167 if (result.ec != std::errc{}) {
169 "failed to format floating point value");
170 }
171 return scalar(std::string_view(buffer, result.ptr - buffer), "floating");
172 }
173
174 serialization_result<void> write_string(std::string_view value) {
175 return scalar(value, "string", true);
176 }
177
178 serialization_result<void> begin_array(std::size_t) {
179 return begin("array");
180 }
181
182 serialization_result<void> end_array() {
183 return end("array");
184 }
185
186 serialization_result<void> begin_object(std::size_t) {
187 return begin("object");
188 }
189
190 serialization_result<void> write_key(std::string_view key) {
191 if (stack_.empty() || stack_.back().kind != "object" || stack_.back().expecting_value) {
193 "object key in invalid archive state");
194 }
195 serialization_result<void> status;
196 if (options_.pretty && !(status = newline_and_indent(stack_.back().value_depth + 1))) {
197 return status;
198 }
199 status = append("<member name=\"");
200 if (!status) {
201 return status;
202 }
203 if (!(status = xml_escape(output_, key, options_.max_output_size, true))) {
204 return status;
205 }
206 if (!(status = append("\">"))) {
207 return status;
208 }
209 stack_.back().expecting_value = true;
210 return {};
211 }
212
213 serialization_result<void> end_object() {
214 return end("object");
215 }
216
218 if (finished_) {
220 "XML archive is already finished");
221 }
222 if (!stack_.empty() || !has_root_) {
223 return make_serialization_error(serialization_errc::custom_error, "incomplete XML archive");
224 }
225 serialization_result<void> status;
226 if (options_.pretty) {
227 status = newline_and_indent(0);
228 if (!status) {
229 return status.error();
230 }
231 status = append("</gmp>");
232 } else {
233 status = append("</gmp>");
234 }
235 if (!status) {
236 return status.error();
237 }
238 finished_ = true;
239 return std::move(output_);
240 }
241
242private:
243 struct frame {
244 std::string_view kind;
245 bool expecting_value = false;
246 std::size_t count = 0;
247 std::size_t value_depth = 0;
248 };
249
250 serialization_result<void> append(std::string_view text) {
251 if (output_.size() > options_.max_output_size ||
252 text.size() > options_.max_output_size - output_.size()) {
254 "XML output exceeds configured limit");
255 }
256 output_ += text;
257 return {};
258 }
259
260 serialization_result<void> before_value() {
261 if (finished_) {
263 "XML archive is already finished");
264 }
265 if (stack_.empty()) {
266 if (has_root_) {
267 return make_serialization_error(serialization_errc::custom_error, "multiple root values");
268 }
269 if (!document_open_) {
270 auto status = append("<gmp version=\"1\">");
271 if (!status) {
272 return status;
273 }
274 document_open_ = true;
275 }
276 if (options_.pretty) {
277 auto status = newline_and_indent(1);
278 if (!status) {
279 return status;
280 }
281 }
282 return {};
283 }
284 auto &parent = stack_.back();
285 if (parent.kind == "object" && !parent.expecting_value) {
286 return make_serialization_error(serialization_errc::custom_error, "object value without key");
287 }
288 if (options_.pretty && parent.kind == "array") {
289 return newline_and_indent(parent.value_depth + 1);
290 }
291 return {};
292 }
293
294 serialization_result<void> complete_value() {
295 if (stack_.empty()) {
296 has_root_ = true;
297 return {};
298 }
299 auto &parent = stack_.back();
300 if (parent.kind == "object") {
301 auto status = append("</member>");
302 if (!status) {
303 return status;
304 }
305 parent.expecting_value = false;
306 }
307 ++parent.count;
308 return {};
309 }
310
311 serialization_result<void> newline_and_indent(std::size_t depth) {
312 auto status = append("\n");
313 if (!status) {
314 return status;
315 }
316 if (output_.size() > options_.max_output_size) {
318 "XML output exceeds configured limit");
319 }
320 const auto remaining = options_.max_output_size - output_.size();
321 if (depth != 0 && options_.indent_width > remaining / depth) {
323 "XML output exceeds configured limit");
324 }
325 output_.append(depth * options_.indent_width, ' ');
326 return {};
327 }
328
329 std::size_t next_value_depth() const noexcept {
330 if (stack_.empty()) {
331 return 1;
332 }
333 const auto &parent = stack_.back();
334 return parent.value_depth + (parent.kind == "object" ? 2 : 1);
335 }
336
337 serialization_result<void> scalar(std::string_view value, std::string_view kind,
338 bool escape_text = false) {
339 auto status = before_value();
340 if (!status) {
341 return status;
342 }
343 status = append("<value kind=\"");
344 if (!status) {
345 return status;
346 }
347 status = append(kind);
348 if (!status) {
349 return status;
350 }
351 status = append("\">");
352 if (!status) {
353 return status;
354 }
355 if (escape_text) {
356 status = xml_escape(output_, value, options_.max_output_size);
357 } else {
358 status = append(value);
359 }
360 if (!status) {
361 return status;
362 }
363 status = append("</value>");
364 if (!status) {
365 return status;
366 }
367 return complete_value();
368 }
369
370 template <typename I> serialization_result<void> number(I value, std::string_view kind) {
371 char buffer[32];
372 const auto result = std::to_chars(buffer, buffer + sizeof(buffer), value);
373 if (result.ec != std::errc{}) {
375 "failed to format integer");
376 }
377 return scalar(std::string_view(buffer, result.ptr - buffer), kind);
378 }
379
380 serialization_result<void> begin(std::string_view kind) {
381 if (stack_.size() >= options_.max_depth) {
383 "maximum XML output depth exceeded");
384 }
385 const auto value_depth = next_value_depth();
386 auto status = before_value();
387 if (!status) {
388 return status;
389 }
390 if (!(status = append("<value kind=\"")) || !(status = append(kind)) ||
391 !(status = append("\">"))) {
392 return status;
393 }
394 stack_.push_back({kind, false, 0, value_depth});
395 return {};
396 }
397
398 serialization_result<void> end(std::string_view kind) {
399 if (stack_.empty() || stack_.back().kind != kind || stack_.back().expecting_value) {
401 "mismatched XML archive end");
402 }
403 const auto frame = stack_.back();
404 stack_.pop_back();
405 serialization_result<void> status;
406 if (options_.pretty && frame.count && !(status = newline_and_indent(frame.value_depth))) {
407 return status;
408 }
409 status = append("</value>");
410 if (!status) {
411 return status;
412 }
413 return complete_value();
414 }
415
416 xml_write_options options_;
417 std::string output_;
418 std::vector<frame> stack_;
419 bool has_root_ = false;
420 bool document_open_ = false;
421 bool finished_ = false;
422};
423
424class xml_parser {
425public:
426 xml_parser(std::string_view input, xml_read_options options) : input_(input), options_(options) {}
427
429 if (input_.size() > options_.max_input_size) {
431 "XML input exceeds the configured size limit");
432 }
433 if (!is_valid_utf8(input_)) {
434 return error(serialization_errc::invalid_utf8, "XML input is not valid UTF-8");
435 }
436 if (starts("\xef\xbb\xbf")) {
437 position_ += 3;
438 }
439 if (starts("<?xml")) {
440 auto declaration = parse_declaration();
441 if (!declaration) {
442 return declaration.error();
443 }
444 }
445 skip_space();
446 if (starts("<!DOCTYPE")) {
448 "XML DTDs and external entities are not supported");
449 }
450 if (!consume("<gmp")) {
451 return error(serialization_errc::invalid_syntax, "expected GMP XML document element");
452 }
453 if (position_ >= input_.size() || (input_[position_] != ' ' && input_[position_] != '\t' &&
454 input_[position_] != '\r' && input_[position_] != '\n')) {
456 "expected whitespace after GMP XML document name");
457 }
458 auto version = attribute("version");
459 if (!version) {
460 return version.error();
461 }
462 if (*version != "1") {
463 return error(serialization_errc::unsupported_type, "unsupported GMP XML format version");
464 }
465 skip_space();
466 if (!consume(">")) {
467 return error(serialization_errc::invalid_syntax, "expected GMP XML document close");
468 }
469 skip_space();
470 auto value = parse_node(0);
471 if (!value) {
472 return value.error();
473 }
474 skip_space();
475 if (!consume("</gmp>")) {
476 return error(serialization_errc::invalid_syntax, "expected GMP XML document end");
477 }
478 skip_space();
479 if (position_ != input_.size()) {
480 return error(serialization_errc::invalid_syntax, "unexpected characters after XML value");
481 }
482 return value;
483 }
484
485private:
486 serialization_error error(serialization_errc code, std::string message) const {
487 return make_serialization_error(code, std::move(message), {}, position_);
488 }
489
490 bool starts(std::string_view text) const {
491 return input_.substr(position_, text.size()) == text;
492 }
493
494 static bool is_space(char value) noexcept {
495 return value == ' ' || value == '\t' || value == '\r' || value == '\n';
496 }
497
498 void skip_space() {
499 while (position_ < input_.size() && is_space(input_[position_])) {
500 ++position_;
501 }
502 }
503
504 bool consume(std::string_view text) {
505 if (!starts(text)) {
506 return false;
507 }
508 position_ += text.size();
509 return true;
510 }
511
512 serialization_result<std::string> declaration_attribute(std::string_view expected) {
513 if (!consume(expected)) {
514 return error(serialization_errc::invalid_syntax, "invalid XML declaration attribute order");
515 }
516 skip_space();
517 if (!consume("=")) {
518 return error(serialization_errc::invalid_syntax, "expected '=' in XML declaration");
519 }
520 skip_space();
521 if (position_ >= input_.size() || (input_[position_] != '"' && input_[position_] != '\'')) {
522 return error(serialization_errc::invalid_syntax, "expected quoted XML declaration value");
523 }
524 const char quote = input_[position_++];
525 const auto start = position_;
526 while (position_ < input_.size() && input_[position_] != quote) {
527 const auto c = static_cast<unsigned char>(input_[position_]);
528 if (c >= 0x80 || !is_xml_code_point(c) || input_[position_] == '<' ||
529 input_[position_] == '&') {
530 return error(serialization_errc::invalid_syntax, "invalid XML declaration value");
531 }
532 ++position_;
533 }
534 if (position_ >= input_.size()) {
535 return error(serialization_errc::unexpected_end, "unterminated XML declaration value");
536 }
537 std::string value(input_.substr(start, position_ - start));
538 ++position_;
539 return value;
540 }
541
542 static bool ascii_iequal(std::string_view left, std::string_view right) noexcept {
543 if (left.size() != right.size()) {
544 return false;
545 }
546 for (std::size_t i = 0; i < left.size(); ++i) {
547 auto a = left[i];
548 auto b = right[i];
549 if (a >= 'A' && a <= 'Z') {
550 a = static_cast<char>(a - 'A' + 'a');
551 }
552 if (b >= 'A' && b <= 'Z') {
553 b = static_cast<char>(b - 'A' + 'a');
554 }
555 if (a != b) {
556 return false;
557 }
558 }
559 return true;
560 }
561
562 serialization_result<void> parse_declaration() {
563 consume("<?xml");
564 if (position_ >= input_.size() || !is_space(input_[position_])) {
565 return error(serialization_errc::invalid_syntax, "XML declaration requires a version");
566 }
567 skip_space();
568 auto version = declaration_attribute("version");
569 if (!version) {
570 return version.error();
571 }
572 if (*version != "1.0") {
573 return error(serialization_errc::unsupported_type, "only XML 1.0 is supported");
574 }
575 bool has_encoding = false;
576 bool has_standalone = false;
577 for (;;) {
578 const auto before_space = position_;
579 skip_space();
580 const bool separated = position_ != before_space;
581 if (consume("?>")) {
582 return {};
583 }
584 if (!separated) {
586 "XML declaration attributes must be separated");
587 }
588 if (starts("encoding")) {
591 "invalid XML declaration attribute order");
592 }
593 auto encoding = declaration_attribute("encoding");
594 if (!encoding) {
595 return encoding.error();
596 }
597 if (!ascii_iequal(*encoding, "UTF-8")) {
598 return error(serialization_errc::unsupported_type, "only UTF-8 XML input is supported");
599 }
600 has_encoding = true;
601 } else if (starts("standalone")) {
602 if (has_standalone) {
603 return error(serialization_errc::invalid_syntax, "duplicate XML standalone declaration");
604 }
605 auto standalone = declaration_attribute("standalone");
606 if (!standalone) {
607 return standalone.error();
608 }
609 if (*standalone != "yes" && *standalone != "no") {
610 return error(serialization_errc::invalid_syntax, "invalid XML standalone declaration");
611 }
612 has_standalone = true;
613 } else {
614 return error(serialization_errc::invalid_syntax, "unknown XML declaration attribute");
615 }
616 }
617 }
618
620 const auto start = position_;
621 const auto end = input_.find(';', position_);
622 if (end == std::string_view::npos) {
623 return error(serialization_errc::unexpected_end, "unterminated XML entity");
624 }
625 position_ = end + 1;
626 const auto name = input_.substr(start, end - start);
627 if (name == "amp") {
628 return std::string("&");
629 }
630 if (name == "lt") {
631 return std::string("<");
632 }
633 if (name == "gt") {
634 return std::string(">");
635 }
636 if (name == "quot") {
637 return std::string("\"");
638 }
639 if (name == "apos") {
640 return std::string("'");
641 }
642 if (!name.empty() && name.front() == '#') {
643 std::uint32_t code_point = 0;
644 std::string_view digits = name.substr(1);
645 int base = 10;
646 if (!digits.empty() && digits.front() == 'x') {
647 base = 16;
648 digits.remove_prefix(1);
649 }
650 const auto parsed =
651 std::from_chars(digits.data(), digits.data() + digits.size(), code_point, base);
652 if (digits.empty() || parsed.ec != std::errc{} ||
653 parsed.ptr != digits.data() + digits.size() || !is_xml_code_point(code_point)) {
654 return error(serialization_errc::invalid_escape, "invalid XML character reference");
655 }
656 std::string result;
658 return result;
659 }
660 return error(serialization_errc::invalid_escape, "unknown XML entity");
661 }
662
663 serialization_result<std::string> text_until_tag() {
664 std::string text;
665 while (position_ < input_.size() && input_[position_] != '<') {
666 if (starts("]]>")) {
668 "']]>' is not allowed in XML character data");
669 }
670 if (input_[position_] == '&') {
671 ++position_;
672 auto part = entity();
673 if (!part) {
674 return part.error();
675 }
676 text += *part;
677 } else {
678 if (input_[position_] == '\r') {
679 ++position_;
680 if (position_ < input_.size() && input_[position_] == '\n') {
681 ++position_;
682 }
683 text.push_back('\n');
684 } else {
685 text.push_back(input_[position_++]);
686 }
687 }
688 if (text.size() > options_.max_string_size) {
690 "XML text exceeds the configured size limit");
691 }
692 }
693 if (!is_valid_utf8(text)) {
694 return error(serialization_errc::invalid_utf8, "XML text is not valid UTF-8");
695 }
696 if (!has_only_xml_characters(text)) {
697 return error(serialization_errc::invalid_syntax, "XML text contains a forbidden character");
698 }
699 return text;
700 }
701
702 serialization_result<std::string> attribute(std::string_view expected) {
703 skip_space();
704 if (!consume(expected)) {
705 return error(serialization_errc::invalid_syntax, "expected XML attribute");
706 }
707 skip_space();
708 if (!consume("=")) {
709 return error(serialization_errc::invalid_syntax, "expected quoted XML attribute");
710 }
711 skip_space();
712 if (position_ >= input_.size() || (input_[position_] != '\"' && input_[position_] != '\'')) {
713 return error(serialization_errc::invalid_syntax, "expected quoted XML attribute");
714 }
715 const char quote = input_[position_++];
716 std::string value;
717 while (position_ < input_.size() && input_[position_] != quote) {
718 if (input_[position_] == '&') {
719 ++position_;
720 auto part = entity();
721 if (!part) {
722 return part.error();
723 }
724 value += *part;
725 } else {
726 if (input_[position_] == '<') {
728 "'<' is not allowed in an XML attribute");
729 }
730 if (input_[position_] == '\r') {
731 ++position_;
732 if (position_ < input_.size() && input_[position_] == '\n') {
733 ++position_;
734 }
735 value.push_back(' ');
736 } else if (input_[position_] == '\n' || input_[position_] == '\t') {
737 ++position_;
738 value.push_back(' ');
739 } else {
740 value.push_back(input_[position_++]);
741 }
742 }
743 if (value.size() > options_.max_string_size) {
745 "XML attribute exceeds the configured size limit");
746 }
747 }
748 if (position_ >= input_.size() || input_[position_] != quote) {
749 return error(serialization_errc::unexpected_end, "unterminated XML attribute");
750 }
751 ++position_;
752 if (!is_valid_utf8(value)) {
753 return error(serialization_errc::invalid_utf8, "XML attribute is not valid UTF-8");
754 }
755 if (!has_only_xml_characters(value)) {
757 "XML attribute contains a forbidden character");
758 }
759 return value;
760 }
761
762 serialization_result<serialization_value> parse_node(std::size_t depth) {
763 const auto source_offset = position_;
764 if (!consume("<value")) {
765 return error(serialization_errc::invalid_syntax, "expected XML value element");
766 }
767 if (position_ >= input_.size() || (input_[position_] != ' ' && input_[position_] != '\t' &&
768 input_[position_] != '\r' && input_[position_] != '\n')) {
770 "expected whitespace after XML element name");
771 }
772 auto kind = attribute("kind");
773 if (!kind) {
774 return kind.error();
775 }
776 skip_space();
777 if (consume("/>")) {
778 if (*kind != "null") {
780 "only null XML values may be self-closing");
781 }
782 serialization_value result(nullptr);
783 result.source_offset(source_offset);
784 return result;
785 }
786 if (!consume(">")) {
787 return error(serialization_errc::invalid_syntax, "expected XML value close");
788 }
789 serialization_value result;
790 if (*kind == "array") {
791 if (depth >= options_.max_depth) {
793 "maximum XML nesting depth exceeded");
794 }
796 skip_space();
797 while (!starts("</value>")) {
798 if (values.size() >= options_.max_container_size) {
800 "XML array exceeds the configured size limit");
801 }
802 auto item = parse_node(depth + 1);
803 if (!item) {
804 return item.error();
805 }
806 values.push_back(std::move(*item));
807 skip_space();
808 }
809 consume("</value>");
810 result = serialization_value(std::move(values));
811 } else if (*kind == "object") {
812 if (depth >= options_.max_depth) {
814 "maximum XML nesting depth exceeded");
815 }
817 skip_space();
818 while (!starts("</value>")) {
819 if (values.size() >= options_.max_container_size) {
821 "XML object exceeds the configured size limit");
822 }
823 if (!consume("<member")) {
824 return error(serialization_errc::invalid_syntax, "expected XML object member");
825 }
826 if (position_ >= input_.size() ||
827 (input_[position_] != ' ' && input_[position_] != '\t' && input_[position_] != '\r' &&
828 input_[position_] != '\n')) {
830 "expected whitespace after XML member name");
831 }
832 auto name = attribute("name");
833 if (!name) {
834 return name.error();
835 }
836 skip_space();
837 if (!consume(">")) {
838 return error(serialization_errc::invalid_syntax, "expected XML member close");
839 }
840 skip_space();
841 auto item = parse_node(depth + 1);
842 if (!item) {
843 return item.error();
844 }
845 skip_space();
846 if (!consume("</member>")) {
847 return error(serialization_errc::invalid_syntax, "expected XML member end");
848 }
849 values.emplace_back(std::move(*name), std::move(*item));
850 skip_space();
851 }
852 consume("</value>");
853 result = serialization_value(std::move(values));
854 } else {
855 auto text = text_until_tag();
856 if (!text) {
857 return text.error();
858 }
859 if (!consume("</value>")) {
860 return error(serialization_errc::invalid_syntax, "expected XML value end");
861 }
862 if (*kind == "string") {
863 result = serialization_value(std::move(*text));
864 } else if (*kind == "boolean") {
865 if (*text != "true" && *text != "false") {
866 return error(serialization_errc::invalid_syntax, "invalid XML boolean");
867 }
868 result = serialization_value(*text == "true");
869 } else if (*kind == "signed") {
870 std::int64_t number = 0;
871 const auto parsed = std::from_chars(text->data(), text->data() + text->size(), number);
872 if (parsed.ec != std::errc{} || parsed.ptr != text->data() + text->size()) {
873 return error(serialization_errc::invalid_number, "invalid XML signed integer");
874 }
875 result = serialization_value(number);
876 } else if (*kind == "unsigned") {
877 std::uint64_t number = 0;
878 const auto parsed = std::from_chars(text->data(), text->data() + text->size(), number);
879 if (parsed.ec != std::errc{} || parsed.ptr != text->data() + text->size()) {
880 return error(serialization_errc::invalid_number, "invalid XML unsigned integer");
881 }
882 result = serialization_value(number);
883 } else if (*kind == "floating") {
884 double number = 0;
885 const auto parsed = std::from_chars(text->data(), text->data() + text->size(), number,
886 std::chars_format::general);
887 if (parsed.ec != std::errc{} || parsed.ptr != text->data() + text->size() ||
888 !std::isfinite(number)) {
889 return error(serialization_errc::invalid_number, "invalid XML floating point value");
890 }
891 result = serialization_value(number);
892 } else {
893 return error(serialization_errc::invalid_syntax, "unknown XML value kind");
894 }
895 }
896 result.source_offset(source_offset);
897 return result;
898 }
899
900 std::string_view input_;
901 xml_read_options options_;
902 std::size_t position_ = 0;
903};
904
905} // namespace gmp::detail
906
907namespace gmp {
908
909inline serialization_result<serialization_value> parse_xml(std::string_view input,
910 xml_read_options options = {}) {
911 return detail::xml_parser(input, options).parse();
912}
913
914inline serialization_result<std::string> write_xml(const serialization_value &value,
915 xml_write_options options = {}) {
916 detail::xml_writer writer(options);
917 auto status = detail::write_serialization_value(writer, value);
918 if (!status) {
919 return status.error();
920 }
921 return writer.finish();
922}
923
924template <typename T>
925serialization_result<std::string> to_xml(const T &value, serialization_options serialization = {},
926 xml_write_options xml = {}) {
927 detail::xml_writer writer(xml);
928 basic_serializer archive(writer, serialization);
929 auto status = archive.encode(value);
930 if (!status) {
931 return status.error();
932 }
933 return writer.finish();
934}
935
936template <typename T>
937serialization_result<T> from_xml(std::string_view input,
938 deserialization_options deserialization = {},
939 xml_read_options xml = {}) {
940 auto value = parse_xml(input, xml);
941 if (!value) {
942 return value.error();
943 }
945}
946
947template <typename T>
948std::string to_xml_or_throw(const T &value, serialization_options serialization = {},
949 xml_write_options xml = {}) {
950 auto result = to_xml(value, serialization, xml);
951 if (!result) {
952 throw serialization_exception(result.error());
953 }
954 return std::move(*result);
955}
956
957template <typename T>
958T from_xml_or_throw(std::string_view input, deserialization_options deserialization = {},
959 xml_read_options xml = {}) {
961 if (!result) {
962 throw serialization_exception(result.error());
963 }
964 return std::move(*result);
965}
966
967} // namespace gmp
968
969#endif // GMP_SERIALIZATION_XML_HPP_
std::vector< std::pair< std::string, serialization_value > > object
Definition value.hpp:32
std::vector< serialization_value > array
Definition value.hpp:31
consteval auto enum_values()
Get all enumerator values of an enumeration type at compile-time.
Definition meta.hpp:193
Definition lock.hpp:21
serialization_error make_serialization_error(serialization_errc code, std::string message, std::string path={}, std::size_t offset=serialization_error::unknown_offset)
Definition error.hpp:160
serialization_errc
Definition error.hpp:26