diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml
index e3be5eb1..078402ae 100644
--- a/.github/workflows/clang-format.yml
+++ b/.github/workflows/clang-format.yml
@@ -2,9 +2,9 @@ name: Clang Format Diff
on:
push:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
pull_request:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
jobs:
build:
diff --git a/.github/workflows/linux-clang.yml b/.github/workflows/linux-clang.yml
index e2e7f01c..401c8570 100644
--- a/.github/workflows/linux-clang.yml
+++ b/.github/workflows/linux-clang.yml
@@ -2,9 +2,9 @@ name: Ubuntu (clang)
on:
push:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
pull_request:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
jobs:
build:
diff --git a/.github/workflows/linux-gcc.yml b/.github/workflows/linux-gcc.yml
index caaba1ef..1e0596f8 100644
--- a/.github/workflows/linux-gcc.yml
+++ b/.github/workflows/linux-gcc.yml
@@ -2,9 +2,9 @@ name: Ubuntu (gcc)
on:
push:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
pull_request:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
jobs:
build:
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index 035f72de..c334ff00 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -2,9 +2,9 @@ name: macOS Monterey 12
on:
push:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
pull_request:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
jobs:
build:
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index a93d8f62..6a9583d8 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -2,9 +2,9 @@ name: Windows Server 2022
on:
push:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
pull_request:
- branches: [ master, fullsupport_xml]
+ branches: [ master, improve_xml]
jobs:
build:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3822a92c..b463ae1b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,7 @@ set(TEST_YAML test/test_yaml.cpp test/test_yaml_bech.cpp)
set(YAMLBENCH benchmark/yaml_benchmark.cpp)
set(TEST_NOTHROW test/test_yaml_nothrow.cpp)
set(TEST_UTIL test/test_util.cpp)
+set(TEST_XMLNOTHROW test/test_xml_nothrow.cpp)
add_executable(json_example ${JSON_EXAMPLE})
add_executable(xml_example ${XML_EXAMPLE})
@@ -67,6 +68,7 @@ add_executable(test_yaml ${TEST_YAML})
add_executable(yaml_benchmark ${YAMLBENCH})
add_executable(test_nothrow ${TEST_NOTHROW})
add_executable(test_util ${TEST_UTIL})
+add_executable(test_xml_nothrow ${TEST_XMLNOTHROW})
# unit test
option(BUILD_UNIT_TESTS "Build unit tests" ON)
@@ -91,3 +93,4 @@ add_test(NAME test_xml COMMAND test_xml)
add_test(NAME test_yaml COMMAND test_yaml)
add_test(NAME test_nothrow COMMAND test_nothrow)
add_test(NAME test_util COMMAND test_util)
+add_test(NAME test_xml_nothrow COMMAND test_xml_nothrow)
diff --git a/README.md b/README.md
index e80960b1..7b3cd433 100644
--- a/README.md
+++ b/README.md
@@ -96,14 +96,10 @@ person p = {"admin", 20};
iguana::string_stream ss; // here use std::string is also ok
iguana::to_xml(ss, p);
std::cout << ss.str() << std::endl;
-// you can also call iguana::to_xml_pretty to get pretty string
-iguana::string_stream s;
-iguana::to_xml_pretty(s, p);
-std::cout << s.str() << std::endl;
// deserialization the structure from the string
std::string xml = " buke 30";
-iguana::from_xml(p, xml.data());
+iguana::from_xml(p, xml);
```
#### Serialization of yaml
@@ -212,7 +208,7 @@ std::string str = R"(
)";
library_t lib;
-iguana::from_xml(lib, str.data());
+iguana::from_xml(lib, str);
```
#### yaml
diff --git a/benchmark/xml_bench.hpp b/benchmark/xml_bench.hpp
index 16827dbe..95774951 100644
--- a/benchmark/xml_bench.hpp
+++ b/benchmark/xml_bench.hpp
@@ -96,13 +96,13 @@ REFLECTION(rss_t, channel);
// ************ struct for bench_num.xml ****************
struct goods_t {
- int id;
- int sales;
- int inventory;
- double weight;
- double price;
- double rating;
- double discount;
+ std::string_view id;
+ std::string_view sales;
+ std::string_view inventory;
+ std::string_view weight;
+ std::string_view price;
+ std::string_view rating;
+ std::string_view discount;
};
REFLECTION(goods_t, id, sales, inventory, weight, price, rating, discount);
struct storeowner_t {
diff --git a/benchmark/xml_benchmark.cpp b/benchmark/xml_benchmark.cpp
index 5c378b3a..e5dd563a 100644
--- a/benchmark/xml_benchmark.cpp
+++ b/benchmark/xml_benchmark.cpp
@@ -1,5 +1,6 @@
#include "../rapidxml/rapidxml.hpp"
#include "xml_bench.hpp"
+#include
class ScopedTimer {
public:
@@ -44,16 +45,18 @@ const int iterations = 1000;
void bench_de_sample_filelists() {
std::string xmlfilelist = xml_file_content("../data/rpm_filelists.xml");
+ rapidxml::xml_document<> doc;
{
ScopedTimer timer("rapidxml fastest parse rpm_filelists.xml");
for (int i = 0; i < iterations; ++i) {
- rapidxml::xml_document<> doc;
doc.parse(xmlfilelist.data());
+ doc.clear();
}
}
+
+ filelists_t filelist;
{
ScopedTimer timer("iguana_xml deserialize rpm_filelists.xml");
- filelists_t filelist;
for (int i = 0; i < iterations; ++i) {
iguana::from_xml(filelist, xmlfilelist.begin(), xmlfilelist.end());
filelist.package.clear();
@@ -63,16 +66,18 @@ void bench_de_sample_filelists() {
void bench_de_sample_rss() {
std::string xmlrss = xml_file_content("../data/sample_rss.xml");
+ rapidxml::xml_document<> doc;
{
ScopedTimer timer("rapidxml fastest parse sample_rss.xml");
for (int i = 0; i < iterations; ++i) {
- rapidxml::xml_document<> doc;
doc.parse(xmlrss.data());
+ doc.clear();
}
}
+
+ rss_t rss;
{
ScopedTimer timer("iguana_xml deserialize sample_rss.xml");
- rss_t rss;
for (int i = 0; i < iterations; ++i) {
iguana::from_xml(rss, xmlrss.begin(), xmlrss.end());
rss.channel.item.clear();
@@ -82,28 +87,46 @@ void bench_de_sample_rss() {
void bench_num() {
std::string xmlnum = xml_file_content("../data/bench_num.xml");
+
+ store_t s;
+ iguana::from_xml(s, xmlnum);
+ auto good = s.goods[1];
+ std::cout << iguana::get_number(good.sales) << ", "
+ << iguana::get_number(good.weight) << "\n";
+ s.goods.clear();
+
{
ScopedTimer timer("iguana_xml deserialize bench_num.xml");
for (int i = 0; i < iterations; ++i) {
- store_t s;
iguana::from_xml(s, xmlnum);
+ s.goods.clear();
}
}
- store_t store;
- iguana::from_xml(store, xmlnum);
- std::string ss;
- ss.reserve(xmlnum.size());
+
+ rapidxml::xml_document<> doc;
{
- ScopedTimer timer("iguana_xml serialize bench_num.xml");
+ ScopedTimer timer("rapidxml fastest parse bench_num.xml");
for (int i = 0; i < iterations; ++i) {
- iguana::to_xml(store, ss);
- ss.clear();
+ doc.parse(xmlnum.data());
+ doc.clear();
}
}
+
+ // store_t store;
+ // iguana::from_xml(store, xmlnum);
+ // std::string ss;
+ // ss.reserve(xmlnum.size());
+ // {
+ // ScopedTimer timer("iguana_xml serialize bench_num.xml");
+ // for (int i = 0; i < iterations; ++i) {
+ // iguana::to_xml(store, ss);
+ // ss.clear();
+ // }
+ // }
}
int main() {
bench_de_sample_filelists();
bench_de_sample_rss();
- // bench_num();
+ bench_num();
}
diff --git a/frozen/string.h b/frozen/string.h
index ccdef751..cad92bbe 100644
--- a/frozen/string.h
+++ b/frozen/string.h
@@ -28,6 +28,7 @@
#include "frozen/bits/hash_string.h"
#include "frozen/bits/version.h"
+#include
#include
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
diff --git a/iguana/json_writer.hpp b/iguana/json_writer.hpp
index 7dd5c770..4134f4f8 100644
--- a/iguana/json_writer.hpp
+++ b/iguana/json_writer.hpp
@@ -174,12 +174,12 @@ template
IGUANA_INLINE void render_json_value(Stream &s, T &&t) {
using U = typename std::decay_t;
s.push_back('[');
- const size_t size = std::tuple_size_v;
+ constexpr size_t size = std::tuple_size_v;
for_each(std::forward(t),
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
render_json_value(s, v);
- if (i != size - 1)
+ if (i != size - 1) [[likely]]
s.push_back(',');
});
s.push_back(']');
diff --git a/iguana/xml_reader.hpp b/iguana/xml_reader.hpp
index a3aabe37..0f8b1384 100644
--- a/iguana/xml_reader.hpp
+++ b/iguana/xml_reader.hpp
@@ -134,7 +134,6 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end,
skip_sapces_and_newline(it, end);
auto value_begin = it;
auto value_end = skip_pass_smaller(it, end);
- ;
if (value_begin == value_end) {
match_close_tag(it, end, name);
return;
@@ -160,6 +159,30 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end,
parse_item(value.value(), it, end, name);
}
+// /> or skip >、 and until the
+// loose inspection here
+template
+IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) {
+ skip_till_greater(it, end);
+ ++it;
+ if (*(it - 2) == '/') {
+ // .../>
+ return;
+ }
+ //
+ size_t size = name.size();
+ while (it != end) {
+ skip_till_greater(it, end);
+ if (*(it - size - 2) == '<' && *(it - size - 1) == '/' &&
+ (std::string_view{&*(it - size), size} == name)) {
+ ++it;
+ return;
+ }
+ ++it;
+ }
+ throw std::runtime_error("unclosed tag: " + std::string(name));
+}
+
template
IGUANA_INLINE void parse_item(T &value, It &&it, It &&end,
std::string_view name) {
@@ -242,12 +265,16 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end,
"type must be memberptr");
using V = std::remove_reference_t;
if constexpr (!cdata_t) {
- detail::parse_item(value.*member_ptr, it, end, key);
+ parse_item(value.*member_ptr, it, end, key);
}
},
member_it->second);
} else [[unlikely]] {
+#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
+#else
+ skip_object_value(it, end, key);
+#endif
}
skip_sapces_and_newline(it, end);
}
@@ -299,4 +326,10 @@ IGUANA_INLINE void from_xml(U &value, const View &view) {
from_xml(value, std::begin(view), std::end(view));
}
+template IGUANA_INLINE Num get_number(std::string_view str) {
+ Num num;
+ detail::parse_value(num, str.begin(), str.end());
+ return num;
+}
+
} // namespace iguana
\ No newline at end of file
diff --git a/iguana/xml_util.hpp b/iguana/xml_util.hpp
index eda70fec..8f73f27e 100644
--- a/iguana/xml_util.hpp
+++ b/iguana/xml_util.hpp
@@ -165,6 +165,35 @@ template struct string_literal {
constexpr const std::string_view sv() const noexcept { return {value, size}; }
};
+inline constexpr auto has_zero = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
+ return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
+};
+
+inline constexpr auto has_greater = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
+ return has_zero(
+ chunk ^
+ 0b0011111000111110001111100011111000111110001111100011111000111110);
+};
+
+inline constexpr auto has_space = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
+ return has_zero(
+ chunk ^
+ 0b0010000000100000001000000010000000100000001000000010000000100000);
+};
+
+inline constexpr auto has_smaller = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
+ return has_zero(
+ chunk ^
+ 0b0011110000111100001111000011110000111100001111000011110000111100);
+};
+
+inline constexpr auto has_square_bracket =
+ [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
+ return has_zero(
+ chunk ^
+ 0b0101110101011101010111010101110101011101010111010101110101011101);
+ };
+
IGUANA_INLINE void skip_sapces_and_newline(auto &&it, auto &&end) {
while (it != end && (*it < 33)) {
++it;
@@ -227,9 +256,9 @@ IGUANA_INLINE void match_close_tag(auto &&it, auto &&end,
if (it == end || (*it++) != '/') [[unlikely]] {
throw std::runtime_error("unclosed tag: " + std::string(key));
}
- auto size = key.size();
- if ((std::distance(it, end) <= size) || (std::string_view{&*it, size} != key))
- [[unlikely]] {
+ size_t size = key.size();
+ if (static_cast(std::distance(it, end)) <= size ||
+ std::string_view{&*it, size} != key) [[unlikely]] {
throw std::runtime_error("unclosed tag: " + std::string(key));
}
it += size;
@@ -243,15 +272,6 @@ IGUANA_INLINE void match_close_tag(auto &&it, auto &&end,
IGUANA_INLINE void skip_till_greater(auto &&it, auto &&end) {
static_assert(std::contiguous_iterator>);
- auto has_zero = [](uint64_t chunk) {
- return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
- };
- auto has_greater = [&](uint64_t chunk) {
- return has_zero(
- chunk ^
- 0b0011111000111110001111100011111000111110001111100011111000111110);
- };
-
if (std::distance(it, end) >= 7) [[likely]] {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
@@ -279,19 +299,6 @@ IGUANA_INLINE void skip_till_greater(auto &&it, auto &&end) {
IGUANA_INLINE void skip_till_greater_or_space(auto &&it, auto &&end) {
static_assert(std::contiguous_iterator>);
- auto has_zero = [](uint64_t chunk) {
- return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
- };
- auto has_greater = [&](uint64_t chunk) {
- return has_zero(
- chunk ^
- 0b0011111000111110001111100011111000111110001111100011111000111110);
- };
- auto has_space = [&](uint64_t chunk) {
- return has_zero(
- chunk ^
- 0b0010000000100000001000000010000000100000001000000010000000100000);
- };
if (std::distance(it, end) >= 7) [[likely]] {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
@@ -320,15 +327,6 @@ IGUANA_INLINE void skip_till_greater_or_space(auto &&it, auto &&end) {
IGUANA_INLINE void skip_till_smaller(auto &&it, auto &&end) {
static_assert(std::contiguous_iterator>);
- auto has_zero = [](uint64_t chunk) {
- return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
- };
- auto has_smaller = [&](uint64_t chunk) {
- return has_zero(
- chunk ^
- 0b0011110000111100001111000011110000111100001111000011110000111100);
- };
-
if (std::distance(it, end) >= 7) [[likely]] {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
@@ -356,14 +354,6 @@ IGUANA_INLINE void skip_till_smaller(auto &&it, auto &&end) {
IGUANA_INLINE void skip_till_square_bracket(auto &&it, auto &&end) {
static_assert(std::contiguous_iterator>);
- auto has_zero = [](uint64_t chunk) {
- return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
- };
- auto has_square_bracket = [&](uint64_t chunk) {
- return has_zero(
- chunk ^
- 0b0101110101011101010111010101110101011101010111010101110101011101);
- };
if (std::distance(it, end) >= 7) [[likely]] {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
diff --git a/test/test_xml_nothrow.cpp b/test/test_xml_nothrow.cpp
new file mode 100644
index 00000000..c5941136
--- /dev/null
+++ b/test/test_xml_nothrow.cpp
@@ -0,0 +1,77 @@
+#define DOCTEST_CONFIG_IMPLEMENT
+#include "doctest.h"
+#undef THROW_UNKNOWN_KEY
+#include "iguana/xml_reader.hpp"
+#include "iguana/xml_writer.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+
+enum class enum_status {
+ paid,
+ unpaid,
+};
+struct order_t {
+ enum_status status;
+ float total;
+};
+REFLECTION(order_t, status, total);
+
+TEST_CASE("test unkonwn key") {
+ auto validator = [](order_t od) {
+ CHECK(od.status == enum_status::unpaid);
+ CHECK(od.total == 65.0f);
+ };
+ std::string str = R"(
+
+ 12345
+ 1
+
+
+ John
+ ]]>
+ Doe
+
+ 123 Main St
+ Anytown
+ CA
+ 12345
+
+
+
+ -
+ 67890
+ Widget A
+ 2
+ 10.00
+
+ -
+ 98765
+ Widget B
+ 3
+ 15.00
+
+
+ ]]>
+ 65.00
+
+ )";
+ order_t od;
+ iguana::from_xml(od, str);
+ validator(od);
+
+ std::string ss;
+ iguana::to_xml(od, ss);
+ order_t od1;
+ iguana::from_xml(od1, ss);
+ validator(od1);
+}
+
+// doctest comments
+// 'function' : must be 'attribute' - see issue #182
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)
+int main(int argc, char **argv) { return doctest::Context(argc, argv).run(); }
+DOCTEST_MSVC_SUPPRESS_WARNING_POP