Skip to content

Commit

Permalink
add test[xml];add example[xml]
Browse files Browse the repository at this point in the history
  • Loading branch information
bbbgan committed Apr 29, 2024
1 parent 177b0ce commit 4cc6cfb
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 51 deletions.
47 changes: 41 additions & 6 deletions example/xml_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,47 @@ void province_example() {
std::cout << ss1;
}

struct text_t {
using escape_attr_t =
iguana::xml_attr_t<std::string, std::map<std::string_view, std::string>>;
escape_attr_t ID;
std::string DisplayName;
};
REFLECTION(text_t, ID, DisplayName);
void escape_example() {
{
std::string str = R"(
<text_t description="&quot;&lt;'&#x5c0f;&#24378;'&gt;&quot;">
<ID ID'msg='{"msg&apos;reply": "it&apos;s ok"}'>&amp;&lt;&gt;</ID>
<DisplayName>&#x5c0f;&#24378;</DisplayName>
</text_t>
)";
using text_attr_t =
iguana::xml_attr_t<text_t, std::map<std::string_view, std::string>>;
auto validator = [](const text_attr_t& text) {
auto v = text.value();
auto attr = text.attr();
assert(attr["description"] == R"("<'小强'>")");
assert(v.ID.value() == R"(&<>)");
assert(v.ID.attr()["ID'msg"] == R"({"msg'reply": "it's ok"})");
assert(v.DisplayName == "小强");
};
text_attr_t text;
iguana::from_xml(text, str);
validator(text);
std::string ss;
iguana::to_xml<true>(text, ss);
std::cout << ss << std::endl;
}
}

int main(void) {
some_type_example();
lib_example();
package_example();
derived_object();
cdata_example();
province_example();
// some_type_example();
// lib_example();
// package_example();
// derived_object();
// cdata_example();
// province_example();
escape_example();
return 0;
}
18 changes: 15 additions & 3 deletions iguana/xml_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) {
value = T(&*begin, static_cast<size_t>(std::distance(begin, end)));
}
else {
// TODO: When not parsing the value in the attribute, it is not necessary
// to unescape'and "
value.clear();
auto pre = begin;
while (advance_until_character<'&'>(begin, end)) {
Expand Down Expand Up @@ -99,9 +101,19 @@ IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) {
parse_value(key, key_begin, key_end);

skip_sapces_and_newline(it, end);
match<'"'>(it, end);
auto value_begin = it;
auto value_end = skip_pass<'"'>(it, end);
auto value_begin = it + 1;
auto value_end = value_begin;
if (*it == '"')
IGUANA_LIKELY {
++it;
value_end = skip_pass<'"'>(it, end);
}
else if (*it == '\'') {
++it;
value_end = skip_pass<'\''>(it, end);
}
else
IGUANA_UNLIKELY { throw std::runtime_error("expected quote or apos"); }
value_type v;
parse_value(v, value_begin, value_end);
value.emplace(std::move(key), std::move(v));
Expand Down
9 changes: 9 additions & 0 deletions iguana/xml_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ inline constexpr auto has_equal = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
0b0011110100111101001111010011110100111101001111010011110100111101);
};

inline constexpr auto has_apos = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0010011100100111001001110010011100100111001001110010011100100111);
};

template <typename It>
IGUANA_INLINE void skip_sapces_and_newline(It &&it, It &&end) {
while (it != end && (static_cast<uint8_t>(*it) < 33)) {
Expand Down Expand Up @@ -161,6 +167,8 @@ IGUANA_INLINE void skip_till(It &&it, It &&end) {
test = has_square_bracket(chunk);
else if constexpr (c == '=')
test = has_equal(chunk);
else if constexpr (c == '\'')
test = has_apos(chunk);
else
static_assert(!c, "not support this character");
if (test != 0) {
Expand Down Expand Up @@ -257,6 +265,7 @@ IGUANA_INLINE void parse_escape_xml(U &value, It &&it, It &&end) {
if (is_match<'m', 'p', ';'>(it + 2, end)) {
value.push_back('&');
it += 5;
return;
}
if (is_match<'p', 'o', 's', ';'>(it + 2, end)) {
value.push_back('\'');
Expand Down
44 changes: 34 additions & 10 deletions iguana/xml_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@

namespace iguana {

template <typename Ch, typename SizeType, typename Stream>
#ifdef XML_ATTR_USE_APOS
#define XML_ATTR_DELIMITER '\''
#else
#define XML_ATTR_DELIMITER '\"'
#endif

// TODO: improve by precaculate size
template <bool escape_quote_apos, typename Ch, typename SizeType,
typename Stream>
IGUANA_INLINE void render_string_with_escape_xml(const Ch *it, SizeType length,
Stream &ss) {
auto end = it;
Expand All @@ -19,10 +27,24 @@ IGUANA_INLINE void render_string_with_escape_xml(const Ch *it, SizeType length,
continue;
}
#endif
// if (*it == '\'')
// IGUANA_UNLIKELY { ss.append("&apos;"); }
// else if (*it == '"')
// IGUANA_UNLIKELY { ss.append("&quot;"); }
if constexpr (escape_quote_apos) {
if constexpr (XML_ATTR_DELIMITER == '\"') {
if (*it == '"')
IGUANA_UNLIKELY {
ss.append("&quot;");
++it;
continue;
}
}
else {
if (*it == '\'')
IGUANA_UNLIKELY {
ss.append("&apos;");
++it;
continue;
}
}
}
if (*it == '&')
IGUANA_UNLIKELY { ss.append("&amp;"); }
else if (*it == '>')
Expand Down Expand Up @@ -69,10 +91,12 @@ IGUANA_INLINE void render_head(Stream &ss, std::string_view str) {
ss.push_back('>');
}

template <typename Stream, typename T, std::enable_if_t<plain_v<T>, int> = 0>
template <bool escape_quote_apos = false, typename Stream, typename T,
std::enable_if_t<plain_v<T>, int> = 0>
IGUANA_INLINE void render_value(Stream &ss, const T &value) {
if constexpr (string_container_v<T>) {
render_string_with_escape_xml(value.data(), value.size(), ss);
render_string_with_escape_xml<escape_quote_apos>(value.data(), value.size(),
ss);
}
else if constexpr (num_v<T>) {
char temp[65];
Expand Down Expand Up @@ -121,9 +145,9 @@ inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) {
ss.push_back(' ');
render_value(ss, k);
ss.push_back('=');
ss.push_back('"');
render_value(ss, v);
ss.push_back('"');
ss.push_back(XML_ATTR_DELIMITER);
render_value<true>(ss, v);
ss.push_back(XML_ATTR_DELIMITER);
}
ss.push_back('>');
}
Expand Down
67 changes: 37 additions & 30 deletions test/test_xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,6 @@ struct Owner_t {
}
};
REFLECTION(Owner_t, ID, DisplayName);
TEST_CASE("test escape") {
{
std::string str = R"(
<Owner_t description="&lt;&#x5c0f;&#24378;&gt;">
<ID>&amp;&lt;&gt;</ID>
<DisplayName>&#x5c0f;&#24378;</DisplayName>
</Owner_t>
)";
using Owner_attr_t =
iguana::xml_attr_t<Owner_t, std::map<std::string_view, std::string>>;
auto validator = [](const Owner_attr_t &Owner) {
auto Ow = Owner.value();
auto attr = Owner.attr();
CHECK(attr["description"] == "<小强>");
CHECK(Ow.ID == R"(&<>)");
CHECK(Ow.DisplayName == "小强");
};
Owner_attr_t Owner;
iguana::from_xml(Owner, str);
validator(Owner);

// std::string ss;
// iguana::to_xml(Owner, ss);
// std::cout << ss << std::endl;
// Owner_attr_t Owner1;
// iguana::from_xml(Owner1, ss);
// validator(Owner1);
}
}

struct Contents {
std::string Key;
std::string LastModified;
Expand Down Expand Up @@ -786,6 +756,43 @@ TEST_CASE("test alias") {
CHECK(m1.obj.y == 42);
}

struct text_t {
using escape_attr_t =
iguana::xml_attr_t<std::string, std::map<std::string_view, std::string>>;
escape_attr_t ID;
std::string DisplayName;
};
REFLECTION(text_t, ID, DisplayName);
TEST_CASE("test escape") {
{
std::string str = R"(
<text_t description="&quot;&lt;'&#x5c0f;&#24378;'&gt;&quot;">
<ID ID'msg='{"msg&apos;reply": "it&apos;s ok"}'>&amp;&lt;&gt;</ID>
<DisplayName>&#x5c0f;&#24378;</DisplayName>
</text_t>
)";
using text_attr_t =
iguana::xml_attr_t<text_t, std::map<std::string_view, std::string>>;
auto validator = [](const text_attr_t &text) {
auto v = text.value();
auto attr = text.attr();
CHECK(attr["description"] == R"("<'小强'>")");
CHECK(v.ID.value() == R"(&<>)");
CHECK(v.ID.attr()["ID'msg"] == R"({"msg'reply": "it's ok"})");
CHECK(v.DisplayName == "小强");
};
text_attr_t text;
iguana::from_xml(text, str);
validator(text);
std::string ss;
iguana::to_xml<true>(text, ss);

text_attr_t text1;
iguana::from_xml(text1, ss);
validator(text1);
}
}

// doctest comments
// 'function' : must be 'attribute' - see issue #182
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)
Expand Down
45 changes: 43 additions & 2 deletions test/test_xml_nothrow.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#undef THROW_UNKNOWN_KEY
#include "iguana/xml_reader.hpp"
#include "iguana/xml_writer.hpp"
#define XML_ATTR_USE_APOS
#define XML_ESCAPE_UNICODE
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <optional>
#include <vector>

#include "iguana/xml_reader.hpp"
#include "iguana/xml_writer.hpp"

enum class enum_status {
paid,
unpaid,
Expand Down Expand Up @@ -82,6 +85,44 @@ TEST_CASE("test exception") {
CHECK_THROWS(iguana::from_xml(od, str));
}

struct text_t {
using escape_attr_t =
iguana::xml_attr_t<std::string, std::map<std::string_view, std::string>>;
escape_attr_t ID;
std::string DisplayName;
};
REFLECTION(text_t, ID, DisplayName);
TEST_CASE("test escape") {
{
std::string str = R"(
<text_t description="&quot;&lt;'&#x5c0f;&#24378;'&gt;&quot;">
<ID ID'msg='{"msg&apos;reply": "it&apos;s ok"}'>&amp;&lt;&gt;</ID>
<DisplayName>&#x5c0f;&#24378;</DisplayName>
</text_t>
)";
using text_attr_t =
iguana::xml_attr_t<text_t, std::map<std::string_view, std::string>>;
auto validator = [](const text_attr_t &text) {
auto v = text.value();
auto attr = text.attr();
CHECK(attr["description"] == R"("<'小强'>")");
CHECK(v.ID.value() == R"(&<>)");
CHECK(v.ID.attr()["ID'msg"] == R"({"msg'reply": "it's ok"})");
CHECK(v.DisplayName == "小强");
};
text_attr_t text;
iguana::from_xml(text, str);
validator(text);
std::string ss;
iguana::to_xml<true>(text, ss);
std::cout << ss << std::endl;

text_attr_t text1;
iguana::from_xml(text1, ss);
validator(text1);
}
}

// doctest comments
// 'function' : must be 'attribute' - see issue #182
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)
Expand Down

0 comments on commit 4cc6cfb

Please sign in to comment.