From 6766f35338fafca445faf3c8a271ef558d6ec963 Mon Sep 17 00:00:00 2001 From: halex2005 Date: Tue, 14 Apr 2015 00:56:23 +0500 Subject: add align each attribute on new line support with format_indent_attribute --- src/pugixml.cpp | 33 ++++++++++++++++++++++----------- src/pugixml.hpp | 3 +++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 7c463cc..bf5835f 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -3480,13 +3480,26 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN void node_output_attributes(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); + bool eachAttributeOnNewLine = PUGI__NODETYPE(node) != node_declaration + && (flags & format_indent) + && (flags & format_each_attribute_on_new_line) + && (flags & format_raw) == 0; + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { - writer.write(' '); + if (eachAttributeOnNewLine) + { + writer.write('\n'); + text_output_indent(writer, indent, indent_length, depth + 1); + } + else + { + writer.write(' '); + } writer.write_string(a->name ? a->name : default_name); writer.write('=', '"'); @@ -3497,7 +3510,7 @@ PUGI__NS_BEGIN } } - PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN bool node_output_start(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name : default_name; @@ -3506,18 +3519,16 @@ PUGI__NS_BEGIN writer.write_string(name); if (node->first_attribute) - node_output_attributes(writer, node, flags); + node_output_attributes(writer, node, indent, indent_length, flags, depth); if (!node->first_child) { writer.write(' ', '/', '>'); - return false; } else { writer.write('>'); - return true; } } @@ -3532,7 +3543,7 @@ PUGI__NS_BEGIN writer.write('>'); } - PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN void node_output_simple(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); @@ -3566,7 +3577,7 @@ PUGI__NS_BEGIN case node_declaration: writer.write('<', '?'); writer.write_string(node->name ? node->name : default_name); - node_output_attributes(writer, node, flags); + node_output_attributes(writer, node, indent, indent_length, flags, depth); writer.write('?', '>'); break; @@ -3608,7 +3619,7 @@ PUGI__NS_BEGIN // begin writing current node if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) { - node_output_simple(writer, node, flags); + node_output_simple(writer, node, indent, indent_length, flags, depth); indent_flags = 0; } @@ -3624,7 +3635,7 @@ PUGI__NS_BEGIN { indent_flags = indent_newline | indent_indent; - if (node_output_start(writer, node, flags)) + if (node_output_start(writer, node, indent, indent_length, flags, depth)) { node = node->first_child; depth++; @@ -3643,7 +3654,7 @@ PUGI__NS_BEGIN } else { - node_output_simple(writer, node, flags); + node_output_simple(writer, node, indent, indent_length, flags, depth); indent_flags = indent_newline | indent_indent; } diff --git a/src/pugixml.hpp b/src/pugixml.hpp index d59f864..36ed955 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -203,9 +203,12 @@ namespace pugi // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default. const unsigned int format_save_file_text = 0x20; + // Set each attribute to new line + const unsigned int format_each_attribute_on_new_line = 0x40; // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; + const unsigned int format_indent_attributes = format_indent | format_each_attribute_on_new_line; // Forward declarations struct xml_attribute_struct; -- cgit v1.2.3 From 5d66ae9fb952a0954df5ae35df93191e7c73feac Mon Sep 17 00:00:00 2001 From: halex2005 Date: Tue, 14 Apr 2015 00:56:42 +0500 Subject: add tests for aligning each attribute on next line --- tests/test_write.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_write.cpp b/tests/test_write.cpp index af4acf4..d280840 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -21,6 +21,21 @@ TEST_XML(write_indent, "text") CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\t\n\n"), STR("\t"), format_indent); } +TEST_XML(write_indent_attribute, "text") +{ + CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\t\n\n"), STR("\t"), format_indent_attributes); +} + +TEST_XML(write_indent_attribute_empty_tag, "") +{ + CHECK_NODE_EX(doc, STR("\n"), STR("\t"), format_indent_attributes); +} + +TEST_XML_FLAGS(write_indent_attribute_on_declaration, "", pugi::parse_full) +{ + CHECK_NODE_EX(doc, STR("\n\n"), STR("\t"), format_indent_attributes); +} + TEST_XML(write_pcdata, "text") { CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\n"), STR("\t"), format_indent); @@ -360,7 +375,7 @@ TEST(write_encoding_huge_invalid) TEST(write_unicode_escape) { char s_utf8[] = "<\xE2\x82\xAC \xC2\xA2='\"\xF0\xA4\xAD\xA2 \"'>&\x14\xF0\xA4\xAD\xA2<"; - + xml_document doc; CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8)); -- cgit v1.2.3 From f241318f9c4054beb31199b79919e2e7a6fbce86 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 13 Apr 2015 20:38:52 -0700 Subject: Add branch name to AppVeyor version --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 33ed76b..641b835 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: "{build}" +version: "{branch}-{build}" build_script: - ps: .\tests\autotest-appveyor.ps1 \ No newline at end of file -- cgit v1.2.3 From 2a3435274f0d7d0e3ccb4417e143e5957e820332 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 13 Apr 2015 21:21:26 -0700 Subject: Refactor format_indent_attributes implementation Fix code style and revert redundant parameters/whitespace changes. Also remove format_each_attribute_on_new_line - we're only introducing one extra formatting flag. The flag implies format_indent but does not include its bitmask. Also add a few more tests. Fixes #14. --- src/pugixml.cpp | 25 ++++++++++++------------- src/pugixml.hpp | 8 ++++---- tests/test_write.cpp | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index bf5835f..590d07f 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -3480,26 +3480,23 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_output_attributes(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - bool eachAttributeOnNewLine = PUGI__NODETYPE(node) != node_declaration - && (flags & format_indent) - && (flags & format_each_attribute_on_new_line) - && (flags & format_raw) == 0; - for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { - if (eachAttributeOnNewLine) + if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) { writer.write('\n'); + text_output_indent(writer, indent, indent_length, depth + 1); } else { writer.write(' '); } + writer.write_string(a->name ? a->name : default_name); writer.write('=', '"'); @@ -3510,7 +3507,7 @@ PUGI__NS_BEGIN } } - PUGI__FN bool node_output_start(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name : default_name; @@ -3524,11 +3521,13 @@ PUGI__NS_BEGIN if (!node->first_child) { writer.write(' ', '/', '>'); + return false; } else { writer.write('>'); + return true; } } @@ -3543,7 +3542,7 @@ PUGI__NS_BEGIN writer.write('>'); } - PUGI__FN void node_output_simple(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth) + PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); @@ -3577,7 +3576,7 @@ PUGI__NS_BEGIN case node_declaration: writer.write('<', '?'); writer.write_string(node->name ? node->name : default_name); - node_output_attributes(writer, node, indent, indent_length, flags, depth); + node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); writer.write('?', '>'); break; @@ -3607,7 +3606,7 @@ PUGI__NS_BEGIN PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { - size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0; + size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; unsigned int indent_flags = indent_indent; xml_node_struct* node = root; @@ -3619,7 +3618,7 @@ PUGI__NS_BEGIN // begin writing current node if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) { - node_output_simple(writer, node, indent, indent_length, flags, depth); + node_output_simple(writer, node, flags); indent_flags = 0; } @@ -3654,7 +3653,7 @@ PUGI__NS_BEGIN } else { - node_output_simple(writer, node, indent, indent_length, flags, depth); + node_output_simple(writer, node, flags); indent_flags = indent_newline | indent_indent; } diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 36ed955..02ca86b 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -203,13 +203,13 @@ namespace pugi // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default. const unsigned int format_save_file_text = 0x20; - // Set each attribute to new line - const unsigned int format_each_attribute_on_new_line = 0x40; + // Write every attribute on a new line with appropriate indentation. This flag is off by default. + const unsigned int format_indent_attributes = 0x40; + // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; - const unsigned int format_indent_attributes = format_indent | format_each_attribute_on_new_line; - + // Forward declarations struct xml_attribute_struct; struct xml_node_struct; diff --git a/tests/test_write.cpp b/tests/test_write.cpp index d280840..df7b0b1 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -21,21 +21,31 @@ TEST_XML(write_indent, "text") CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\t\n\n"), STR("\t"), format_indent); } -TEST_XML(write_indent_attribute, "text") +TEST_XML(write_indent_attributes, "text") { CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\t\n\n"), STR("\t"), format_indent_attributes); } -TEST_XML(write_indent_attribute_empty_tag, "") +TEST_XML(write_indent_attributes_empty_element, "") { CHECK_NODE_EX(doc, STR("\n"), STR("\t"), format_indent_attributes); } -TEST_XML_FLAGS(write_indent_attribute_on_declaration, "", pugi::parse_full) +TEST_XML_FLAGS(write_indent_attributes_declaration, "", parse_full) { CHECK_NODE_EX(doc, STR("\n\n"), STR("\t"), format_indent_attributes); } +TEST_XML(write_indent_attributes_raw, "text") +{ + CHECK_NODE_EX(doc, STR("text"), STR("\t"), format_indent_attributes | format_raw); +} + +TEST_XML(write_indent_attributes_empty_indent, "text") +{ + CHECK_NODE_EX(doc, STR("\n\ntext\n\n\n"), STR(""), format_indent_attributes); +} + TEST_XML(write_pcdata, "text") { CHECK_NODE_EX(doc, STR("\n\t\n\t\ttext\n\n"), STR("\t"), format_indent); -- cgit v1.2.3 From e977f04fe221d56bac2f0f18d81a1934f6754cb5 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 13 Apr 2015 21:50:24 -0700 Subject: docs: Add format_indent_attributes documentation Slightly reword format_indent description. --- docs/manual.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/manual.adoc b/docs/manual.adoc index 9d75ea1..0d31934 100644 --- a/docs/manual.adoc +++ b/docs/manual.adoc @@ -1668,7 +1668,9 @@ NOTE: You should use the usual bitwise arithmetics to manipulate the bitmask: to These flags control the resulting tree contents: -* [[format_indent]]`format_indent` determines if all nodes should be indented with the indentation string (this is an additional parameter for all saving functions, and is `"\t"` by default). If this flag is on, before every node the indentation string is output several times, where the amount of indentation depends on the node's depth relative to the output subtree. This flag has no effect if <> is enabled. This flag is *on* by default. +* [[format_indent]]`format_indent` determines if all nodes should be indented with the indentation string (this is an additional parameter for all saving functions, and is `"\t"` by default). If this flag is on, the indentations string is printed several times before every node, where the amount of indentation depends on the node's depth relative to the output subtree. This flag has no effect if <> is enabled. This flag is *on* by default. + +* [[format_indent_attributes]]`format_indent_attributes` determines if all attributes should be printed on a new line, indented with the indentation string according to the attribute's depth. This flag implies <>. This flag has no effect if <> is enabled. This flag is *off* by default. * [[format_raw]]`format_raw` switches between formatted and raw output. If this flag is on, the nodes are not indented in any way, and also no newlines that are not part of document text are printed. Raw mode can be used for serialization where the result is not intended to be read by humans; also it can be useful if the document was parsed with <> flag, to preserve the original document formatting as much as possible. This flag is *off* by default. @@ -2546,6 +2548,7 @@ enum +++xpath_value_type+++ // Formatting options bit flags: const unsigned int +++format_default+++ const unsigned int +++format_indent+++ +const unsigned int +++format_indent_attributes+++ const unsigned int +++format_no_declaration+++ const unsigned int +++format_no_escapes+++ const unsigned int +++format_raw+++ -- cgit v1.2.3 From 2badcbb6743ff803c9cd82db77a1d8a50802058f Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 14 Apr 2015 19:11:26 -0700 Subject: Explicitly call xml_buffered_writer::flush() If xml_writer::write throws an exception while being called from flush(), the exception is thrown from destructor. Clang in C++11 mode calls std::terminate in this case. --- src/pugixml.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 590d07f..1c1b539 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -3115,11 +3115,6 @@ PUGI__NS_BEGIN PUGI__STATIC_ASSERT(bufcapacity >= 8); } - ~xml_buffered_writer() - { - flush(); - } - size_t flush() { flush(buffer, bufsize); @@ -5496,6 +5491,8 @@ namespace pugi impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, _root, indent, flags, depth); + + buffered_writer.flush(); } #ifndef PUGIXML_NO_STL @@ -6205,6 +6202,8 @@ namespace pugi } impl::node_output(buffered_writer, _root, indent, flags, 0); + + buffered_writer.flush(); } #ifndef PUGIXML_NO_STL -- cgit v1.2.3 From 5158ee903be463c189a80715f92235ceb04871a7 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 14 Apr 2015 19:19:13 -0700 Subject: Fix xpath_node_set assignment to provide strong exception guarantee Since the type of the set was updated before assignment, assigning in out-of-memory condition could change the type to not match the content. --- src/pugixml.cpp | 17 +++++++++-------- src/pugixml.hpp | 2 +- tests/test_xpath_api.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 1c1b539..fbe13f6 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -11025,7 +11025,7 @@ namespace pugi } #endif - PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) + PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) { assert(begin_ <= end_); @@ -11041,6 +11041,7 @@ namespace pugi _begin = &_storage; _end = &_storage + size_; + _type = type_; } else { @@ -11064,6 +11065,7 @@ namespace pugi // finalize _begin = storage; _end = storage + size_; + _type = type_; } } @@ -11071,9 +11073,9 @@ namespace pugi { } - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) { - _assign(begin_, end_); + _assign(begin_, end_, type_); } PUGI__FN xpath_node_set::~xpath_node_set() @@ -11081,17 +11083,16 @@ namespace pugi if (_begin != &_storage) impl::xml_memory::deallocate(_begin); } - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) { - _assign(ns._begin, ns._end); + _assign(ns._begin, ns._end, ns._type); } PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; - - _type = ns._type; - _assign(ns._begin, ns._end); + + _assign(ns._begin, ns._end, ns._type); return *this; } diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 02ca86b..b56f66a 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1286,7 +1286,7 @@ namespace pugi xpath_node* _begin; xpath_node* _end; - void _assign(const_iterator begin, const_iterator end); + void _assign(const_iterator begin, const_iterator end, type_t type); }; #endif diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index df078a0..4fc5be3 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -381,15 +381,20 @@ TEST_XML(xpath_api_node_set_assign_out_of_memory_preserve, "") -- cgit v1.2.3 From 8c8940430ac0d0f82add6fb028ffe9712c9669e8 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 15 Apr 2015 08:32:22 -0700 Subject: Minor xpath_variable refactoring The type of the variable is now initialized correctly in the ctor, so that there is no interim invalid state. --- src/pugixml.cpp | 19 ++++++++++++------- src/pugixml.hpp | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index fbe13f6..078a39c 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -7626,7 +7626,7 @@ PUGI__NS_BEGIN struct xpath_variable_boolean: xpath_variable { - xpath_variable_boolean(): value(false) + xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) { } @@ -7636,7 +7636,7 @@ PUGI__NS_BEGIN struct xpath_variable_number: xpath_variable { - xpath_variable_number(): value(0) + xpath_variable_number(): xpath_variable(xpath_type_number), value(0) { } @@ -7646,7 +7646,7 @@ PUGI__NS_BEGIN struct xpath_variable_string: xpath_variable { - xpath_variable_string(): value(0) + xpath_variable_string(): xpath_variable(xpath_type_string), value(0) { } @@ -7661,6 +7661,10 @@ PUGI__NS_BEGIN struct xpath_variable_node_set: xpath_variable { + xpath_variable_node_set(): xpath_variable(xpath_type_node_set) + { + } + xpath_node_set value; char_t name[1]; }; @@ -11080,7 +11084,8 @@ namespace pugi PUGI__FN xpath_node_set::~xpath_node_set() { - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); } PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) @@ -11152,7 +11157,7 @@ namespace pugi return error ? error : "No error"; } - PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0) + PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } @@ -11251,7 +11256,8 @@ namespace pugi PUGI__FN xpath_variable_set::xpath_variable_set() { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() @@ -11299,7 +11305,6 @@ namespace pugi if (result) { - result->_type = type; result->_next = _data[hash]; _data[hash] = result; diff --git a/src/pugixml.hpp b/src/pugixml.hpp index b56f66a..a7154f1 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1043,7 +1043,7 @@ namespace pugi xpath_value_type _type; xpath_variable* _next; - xpath_variable(); + xpath_variable(xpath_value_type type); // Non-copyable semantics xpath_variable(const xpath_variable&); -- cgit v1.2.3 From bb3aee447b698c7dcec6428a17c61e66dd43dfd6 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 15 Apr 2015 21:44:52 -0700 Subject: tests: Use malloc for OSX/Linux page heap Switch to malloc and manually aligning the pointer to the page boundary. mmap is much slower than malloc; this change makes tests ~4x faster. --- tests/allocator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/allocator.cpp b/tests/allocator.cpp index 8ca0963..a43e14a 100644 --- a/tests/allocator.cpp +++ b/tests/allocator.cpp @@ -80,7 +80,9 @@ namespace void* allocate_page_aligned(size_t size) { - return mmap(0, size + page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + void* result = malloc(size + page_size); + + return reinterpret_cast(align_to_page(reinterpret_cast(result))); } void* allocate(size_t size) -- cgit v1.2.3 From 70a78b2fa5553931cb8e90457440f671ca1afc06 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 15 Apr 2015 22:11:13 -0700 Subject: tests: Fix Linux build --- tests/allocator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/allocator.cpp b/tests/allocator.cpp index a43e14a..e1d99d5 100644 --- a/tests/allocator.cpp +++ b/tests/allocator.cpp @@ -2,6 +2,7 @@ #include #include +#include // Low-level allocation functions #if defined(_WIN32) || defined(_WIN64) @@ -113,8 +114,6 @@ namespace } } #else -# include - namespace { void* allocate(size_t size) -- cgit v1.2.3 From cbf3807ad4e93441d4a4324d1c21099ca644fac7 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 15 Apr 2015 23:22:31 -0700 Subject: Implement copy ctor/assignment for xpath_variable_set xpath_variable_set is essentially an associative container; it's about time it became copyable. Implementation is slightly tricky due to out of memory handling. Both copy ctor and assignment operator have strong exception guarantee (even if exceptions are disabled! which translates to "roll back on allocation errors"). --- src/pugixml.cpp | 94 ++++++++++++++++++++++++++++++++++++++++-- src/pugixml.hpp | 13 ++++-- tests/test_xpath_variables.cpp | 60 +++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 078a39c..ce47fce 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -7758,6 +7758,28 @@ PUGI__NS_BEGIN } } + PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + { + switch (rhs->type()) + { + case xpath_type_node_set: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_number: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_string: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_boolean: + return lhs->set(static_cast(rhs)->value); + + default: + assert(!"Invalid variable type"); + return false; + } + } + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); @@ -11277,7 +11299,46 @@ namespace pugi } } - PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const + PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + + _assign(rhs); + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + { + if (this == &rhs) return *this; + + _assign(rhs); + + return *this; + } + + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + { + xpath_variable_set temp; + + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) + return; + + _swap(temp); + } + + PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* chain = _data[i]; + + _data[i] = rhs._data[i]; + rhs._data[i] = chain; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; @@ -11290,6 +11351,33 @@ namespace pugi return 0; } + PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + { + xpath_variable* last = 0; + + while (var) + { + // allocate storage for new variable + xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); + if (!nvar) return false; + + // link the variable to the result immediately to handle failures gracefully + if (last) + last->_next = nvar; + else + *out_result = nvar; + + last = nvar; + + // copy the value; this can fail due to out-of-memory conditions + if (!impl::copy_xpath_variable(nvar, var)) return false; + + var = var->_next; + } + + return true; + } + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); @@ -11339,12 +11427,12 @@ namespace pugi PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) { - return find(name); + return _find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { - return find(name); + return _find(name); } PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) diff --git a/src/pugixml.hpp b/src/pugixml.hpp index a7154f1..b8b8946 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1075,17 +1075,22 @@ namespace pugi private: xpath_variable* _data[64]; - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set&); - xpath_variable_set& operator=(const xpath_variable_set&); + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); + + xpath_variable* _find(const char_t* name) const; - xpath_variable* find(const char_t* name) const; + static bool _clone(xpath_variable* var, xpath_variable** out_result); public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 0d33312..b2ebc47 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -413,4 +413,64 @@ TEST_XML(xpath_variables_count_sum, "122334") +{ + xpath_variable_set set1; + set1.set(STR("a"), true); + set1.set(STR("b"), 2.0); + set1.set(STR("c"), STR("string")); + set1.set(STR("d"), doc.select_nodes(STR("//*"))); + + CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set1, STR("ring")); + + xpath_variable_set set2 = set1; + + CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring")); + + xpath_variable_set set3; + + CHECK(!set3.get(STR("a"))); + + set3 = set1; + + CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring")); + + set3 = set3; + + CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring")); + + set3 = xpath_variable_set(); + + CHECK(!set3.get(STR("a"))); +} + +TEST_XML(xpath_variables_copy_out_of_memory, "") +{ + xpath_variable_set set1; + set1.set(STR("a"), true); + set1.set(STR("b"), 2.0); + set1.set(STR("c"), STR("string")); + set1.set(STR("d"), doc.select_nodes(STR("//*"))); + + xpath_variable_set set2 = set1; + + test_runner::_memory_fail_threshold = 32768 + 75 * sizeof(void*); + + CHECK_ALLOC_FAIL(xpath_variable_set set3 = set1); + + xpath_variable_set set4; + + CHECK_ALLOC_FAIL(set4 = set1); + CHECK(!set4.get(STR("a")) && !set4.get(STR("b")) && !set4.get(STR("c")) && !set4.get(STR("d"))); + + CHECK_ALLOC_FAIL(set2 = set1); + + CHECK(set2.get(STR("a")) && set2.get(STR("b")) && set2.get(STR("c")) && set2.get(STR("d"))); + + CHECK(set2.get(STR("a"))->get_boolean() == true); + CHECK(set2.get(STR("b"))->get_number() == 2.0); + CHECK_STRING(set2.get(STR("c"))->get_string(), STR("string")); + CHECK(set2.get(STR("d"))->get_node_set().size() == 1); +} #endif -- cgit v1.2.3 From a414c5c52d50714d5e88f743760a5d83cd37c1a1 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 10:02:26 -0700 Subject: Fix compilation warning in some configurations --- src/pugixml.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index ce47fce..d98df3e 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1691,6 +1691,7 @@ PUGI__NS_BEGIN assert(begin + size == end); (void)!end; + (void)!size; } #ifndef PUGIXML_NO_STL -- cgit v1.2.3 From 83b894b8f17e8c09eb21675983be0656301d2d99 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 19:42:31 -0700 Subject: XPath: Implement move semantics support xpath_query, xpath_node_set and xpath_variable_set are now moveable. This is a nice performance optimization for variable/node sets, and enables storing xpath_query in containers without using pointers (it's only possible now since the query is not copyable). --- src/pugixml.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++------- src/pugixml.hpp | 25 ++++++++++++- 2 files changed, 119 insertions(+), 13 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index d98df3e..37c61bb 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -11096,6 +11096,20 @@ namespace pugi } } +#if __cplusplus >= 201103 + PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) + { + _type = rhs._type; + _storage = rhs._storage; + _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _end = _begin + (rhs._end - rhs._begin); + + rhs._type = type_unsorted; + rhs._begin = &rhs._storage; + rhs._end = rhs._begin; + } +#endif + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) { } @@ -11125,6 +11139,25 @@ namespace pugi return *this; } +#if __cplusplus >= 201103 + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _move(rhs); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) + { + if (this == &rhs) return *this; + + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + + _move(rhs); + + return *this; + } +#endif + PUGI__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; @@ -11286,18 +11319,7 @@ namespace pugi PUGI__FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) - { - xpath_variable* var = _data[i]; - - while (var) - { - xpath_variable* next = var->_next; - - impl::delete_xpath_variable(var->_type, var); - - var = next; - } - } + _destroy(_data[i]); } PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) @@ -11317,6 +11339,30 @@ namespace pugi return *this; } +#if __cplusplus >= 201103 + PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _destroy(_data[i]); + + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + + return *this; + } +#endif + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) { xpath_variable_set temp; @@ -11379,6 +11425,18 @@ namespace pugi return true; } + PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + { + while (var) + { + xpath_variable* next = var->_next; + + impl::delete_xpath_variable(var->_type, var); + + var = next; + } + } + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); @@ -11464,12 +11522,37 @@ namespace pugi } } + PUGI__FN xpath_query::xpath_query(): _impl(0) + { + } + PUGI__FN xpath_query::~xpath_query() { if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); } +#if __cplusplus >= 201103 + PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) + { + _impl = rhs._impl; + rhs._impl = 0; + } + + xpath_query& xpath_query::operator=(xpath_query&& rhs) + { + if (this == &rhs) return *this; + + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + + _impl = rhs._impl; + rhs._impl = 0; + + return *this; + } +#endif + PUGI__FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; diff --git a/src/pugixml.hpp b/src/pugixml.hpp index b8b8946..0f79fb7 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -65,7 +65,7 @@ // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG -# if defined(__cplusplus) && __cplusplus >= 201103 +# if __cplusplus >= 201103 # define PUGIXML_HAS_LONG_LONG # elif defined(_MSC_VER) && _MSC_VER >= 1400 # define PUGIXML_HAS_LONG_LONG @@ -1081,6 +1081,7 @@ namespace pugi xpath_variable* _find(const char_t* name) const; static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); public: // Default constructor/destructor @@ -1091,6 +1092,12 @@ namespace pugi xpath_variable_set(const xpath_variable_set& rhs); xpath_variable_set& operator=(const xpath_variable_set& rhs); + #if __cplusplus >= 201103 + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs); + xpath_variable_set& operator=(xpath_variable_set&& rhs); + #endif + // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); @@ -1123,9 +1130,18 @@ namespace pugi // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + // Constructor + xpath_query(); + // Destructor ~xpath_query(); + #if __cplusplus >= 201103 + // Move semantics support + xpath_query(xpath_query&& rhs); + xpath_query& operator=(xpath_query&& rhs); + #endif + // Get query expression return type xpath_value_type return_type() const; @@ -1261,6 +1277,12 @@ namespace pugi xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); + #if __cplusplus >= 201103 + // Move semantics support + xpath_node_set(xpath_node_set&& rhs); + xpath_node_set& operator=(xpath_node_set&& rhs); + #endif + // Get collection type type_t type() const; @@ -1292,6 +1314,7 @@ namespace pugi xpath_node* _end; void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs); }; #endif -- cgit v1.2.3 From 4eadece45f559825b236709ffb92037c6af5e962 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 19:44:19 -0700 Subject: tests: Add move semantics tests Also test ranged for and copying big xpath_variable_set objects (to make sure we actually handle hash collisions properly) --- tests/test_dom_traverse.cpp | 24 +++++ tests/test_xpath_api.cpp | 221 +++++++++++++++++++++++++++++++++++++++++ tests/test_xpath_variables.cpp | 106 ++++++++++++++++++++ 3 files changed, 351 insertions(+) diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index 4423dbe..e4b8c44 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -1106,3 +1106,27 @@ TEST_XML(dom_unspecified_bool_coverage, "text") static_cast(qn)(0); #endif } + +#if __cplusplus >= 201103 +TEST_XML(dom_ranged_for, "354") +{ + int index = 1; + + for (xml_node n: doc.children()) + { + for (xml_attribute a: n.attributes()) + { + CHECK(a.as_int() == index); + index++; + } + + for (xml_node c: n.children(STR("test"))) + { + CHECK(c.text().as_int() == index); + index++; + } + } + + CHECK(index == 5); +} +#endif diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index 4fc5be3..c070ed0 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -7,6 +7,7 @@ #include "helpers.hpp" #include +#include TEST_XML(xpath_api_select_nodes, "") { @@ -407,4 +408,224 @@ TEST_XML(xpath_api_deprecated_select_single_node, "= 201103 +TEST_XML(xpath_api_nodeset_move_ctor, "") +{ + xpath_node_set set = doc.select_nodes(STR("node/bar/preceding::*")); + + CHECK(set.size() == 2); + CHECK(set.type() == xpath_node_set::type_sorted_reverse); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 2); + CHECK(move.type() == xpath_node_set::type_sorted_reverse); + CHECK(move[1] == doc.first_child().first_child()); +} + + +TEST_XML(xpath_api_nodeset_move_ctor_single, "") +{ + xpath_node_set set = doc.select_nodes(STR("node/bar")); + + CHECK(set.size() == 1); + CHECK(set.type() == xpath_node_set::type_sorted); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 1); + CHECK(move.type() == xpath_node_set::type_sorted); + CHECK(move[0] == doc.first_child().last_child()); +} + +TEST(xpath_api_nodeset_move_ctor_empty) +{ + xpath_node_set set; + set.sort(); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_sorted); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 0); + CHECK(move.type() == xpath_node_set::type_sorted); +} + +TEST_XML(xpath_api_nodeset_move_assign, "") +{ + xpath_node_set set = doc.select_nodes(STR("node/bar/preceding::*")); + + CHECK(set.size() == 2); + CHECK(set.type() == xpath_node_set::type_sorted_reverse); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move; + + CHECK(move.size() == 0); + CHECK(move.type() == xpath_node_set::type_unsorted); + + move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 2); + CHECK(move.type() == xpath_node_set::type_sorted_reverse); + CHECK(move[1] == doc.first_child().first_child()); +} + +TEST_XML(xpath_api_nodeset_move_assign_destroy, "") +{ + xpath_node_set set = doc.select_nodes(STR("node/bar/preceding::*")); + + CHECK(set.size() == 2); + CHECK(set.type() == xpath_node_set::type_sorted_reverse); + + xpath_node_set all = doc.select_nodes(STR("//*")); + + CHECK(all.size() == 4); + + test_runner::_memory_fail_threshold = 1; + + all = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(all.size() == 2); + CHECK(all.type() == xpath_node_set::type_sorted_reverse); + CHECK(all[1] == doc.first_child().first_child()); +} + +TEST_XML(xpath_api_nodeset_move_assign_single, "") +{ + xpath_node_set set = doc.select_nodes(STR("node/bar")); + + CHECK(set.size() == 1); + CHECK(set.type() == xpath_node_set::type_sorted); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move; + + CHECK(move.size() == 0); + CHECK(move.type() == xpath_node_set::type_unsorted); + + move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 1); + CHECK(move.type() == xpath_node_set::type_sorted); + CHECK(move[0] == doc.first_child().last_child()); +} + +TEST(xpath_api_nodeset_move_assign_empty) +{ + xpath_node_set set; + set.sort(); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_sorted); + + test_runner::_memory_fail_threshold = 1; + + xpath_node_set move; + + CHECK(move.size() == 0); + CHECK(move.type() == xpath_node_set::type_unsorted); + + move = std::move(set); + + CHECK(set.size() == 0); + CHECK(set.type() == xpath_node_set::type_unsorted); + + CHECK(move.size() == 0); + CHECK(move.type() == xpath_node_set::type_sorted); +} + +TEST(xpath_api_query_move) +{ + xml_node c; + + xpath_query q1(STR("true()")); + xpath_query q4(STR("true() and false()")); + + test_runner::_memory_fail_threshold = 1; + + CHECK(q1); + CHECK(q1.evaluate_boolean(c)); + + xpath_query q2 = std::move(q1); + CHECK(!q1); + CHECK(!q1.evaluate_boolean(c)); + CHECK(q2); + CHECK(q2.evaluate_boolean(c)); + + xpath_query q3; + CHECK(!q3); + CHECK(!q3.evaluate_boolean(c)); + + q3 = std::move(q2); + CHECK(!q2); + CHECK(!q2.evaluate_boolean(c)); + CHECK(q3); + CHECK(q3.evaluate_boolean(c)); + + CHECK(q4); + CHECK(!q4.evaluate_boolean(c)); + + q4 = std::move(q3); + + CHECK(!q3); + CHECK(!q3.evaluate_boolean(c)); + CHECK(q4); + CHECK(q4.evaluate_boolean(c)); + + q4 = std::move(q4); + + CHECK(q4); + CHECK(q4.evaluate_boolean(c)); +} + +TEST(xpath_api_query_vector) +{ + std::vector qv; + + for (int i = 0; i < 10; ++i) + { + char_t expr[2]; + expr[0] = '0' + i; + expr[1] = 0; + + qv.push_back(xpath_query(expr)); + } + + double result = 0; + + for (auto& q: qv) + result += q.evaluate_number(xml_node()); + + CHECK(result == 45); +} +#endif #endif diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index b2ebc47..1fe0b6e 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -473,4 +473,110 @@ TEST_XML(xpath_variables_copy_out_of_memory, "") CHECK_STRING(set2.get(STR("c"))->get_string(), STR("string")); CHECK(set2.get(STR("d"))->get_node_set().size() == 1); } + +#if __cplusplus >= 201103 +TEST_XML(xpath_variables_move, "") +{ + xpath_variable_set set; + set.set(STR("a"), true); + set.set(STR("b"), 2.0); + set.set(STR("c"), STR("string")); + set.set(STR("d"), doc.select_nodes(STR("//*"))); + + xpath_variable_set copy = set; + copy.set(STR("e"), 42.0); + + test_runner::_memory_fail_threshold = 1; + + xpath_variable_set move1 = std::move(set); + + CHECK(!set.get(STR("a")) && !set.get(STR("b")) && !set.get(STR("c")) && !set.get(STR("d"))); + CHECK(move1.get(STR("a")) && move1.get(STR("b")) && move1.get(STR("c")) && move1.get(STR("d"))); + + CHECK(move1.get(STR("a"))->get_boolean() == true); + CHECK(move1.get(STR("b"))->get_number() == 2.0); + CHECK_STRING(move1.get(STR("c"))->get_string(), STR("string")); + CHECK(move1.get(STR("d"))->get_node_set().size() == 1); + + xpath_variable_set move2; + move2 = std::move(move1); + + CHECK(!move1.get(STR("a")) && !move1.get(STR("b")) && !move1.get(STR("c")) && !move1.get(STR("d"))); + CHECK(move2.get(STR("a")) && move2.get(STR("b")) && move2.get(STR("c")) && move2.get(STR("d"))); + + CHECK(copy.get(STR("e"))); + + copy = std::move(move2); + + CHECK(!move2.get(STR("a")) && !move2.get(STR("b")) && !move2.get(STR("c")) && !move2.get(STR("d"))); + CHECK(copy.get(STR("a")) && copy.get(STR("b")) && copy.get(STR("c")) && copy.get(STR("d"))); + CHECK(!copy.get(STR("e"))); + + CHECK(copy.get(STR("a"))->get_boolean() == true); + CHECK(copy.get(STR("b"))->get_number() == 2.0); + CHECK_STRING(copy.get(STR("c"))->get_string(), STR("string")); + CHECK(copy.get(STR("d"))->get_node_set().size() == 1); +} +#endif + +TEST(xpath_variables_copy_big) +{ + xpath_variable_set set; + + for (int i = 0; i < 100; ++i) + { + char_t name[4]; + name[0] = 'a'; + name[1] = '0' + (i / 10); + name[2] = '0' + (i % 10); + name[3] = 0; + + set.set(name, double(i)); + } + + xpath_variable_set copy = set; + + for (int i = 0; i < 100; ++i) + { + char_t name[4]; + name[0] = 'a'; + name[1] = '0' + (i / 10); + name[2] = '0' + (i % 10); + name[3] = 0; + + CHECK(copy.get(name) && copy.get(name)->get_number() == i); + } +} + +TEST(xpath_variables_copy_big_out_of_memory) +{ + xpath_variable_set set; + + for (int i = 0; i < 100; ++i) + { + char_t name[4]; + name[0] = 'a'; + name[1] = '0' + (i / 10); + name[2] = '0' + (i % 10); + name[3] = 0; + + set.set(name, double(i)); + } + + test_runner::_memory_fail_threshold = 1; + + xpath_variable_set copy; + CHECK_ALLOC_FAIL(copy = set); + + for (int i = 0; i < 100; ++i) + { + char_t name[4]; + name[0] = 'a'; + name[1] = '0' + (i / 10); + name[2] = '0' + (i % 10); + name[3] = 0; + + CHECK(!copy.get(name)); + } +} #endif -- cgit v1.2.3 From 8d4544f2e1f95ffbf83bc2233850fcafdf0195ea Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 20:32:40 -0700 Subject: Enable C++11 in Makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 59f055b..8bb0a19 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,10 @@ ifneq ($(findstring PUGIXML_NO_EXCEPTIONS,$(defines)),) CXXFLAGS+=-fno-exceptions endif +ifeq ($(findstring PUGIXML_NO_CXX11,$(defines)),) + CXXFLAGS+=-std=c++11 +endif + OBJECTS=$(SOURCES:%=$(BUILD)/%.o) all: $(EXECUTABLE) -- cgit v1.2.3 From 250d020e9b121f537bbcbf8439033ba4a6f225ef Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 20:46:33 -0700 Subject: Use -std=c++0x instead of -std=c++11 --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8bb0a19..ead0ddd 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ ifneq ($(findstring PUGIXML_NO_EXCEPTIONS,$(defines)),) endif ifeq ($(findstring PUGIXML_NO_CXX11,$(defines)),) - CXXFLAGS+=-std=c++11 + # Can't use std=c++11 since Travis-CI has gcc 4.6.3 + CXXFLAGS+=-std=c++0x endif OBJECTS=$(SOURCES:%=$(BUILD)/%.o) -- cgit v1.2.3 From a6cc636a6b0d531686311b5666ea77225b10903e Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 21 Apr 2015 21:07:58 -0700 Subject: tests: Fix MSVC warnings --- tests/test_xpath_api.cpp | 2 +- tests/test_xpath_variables.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index c070ed0..295bd95 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -614,7 +614,7 @@ TEST(xpath_api_query_vector) for (int i = 0; i < 10; ++i) { char_t expr[2]; - expr[0] = '0' + i; + expr[0] = '0' + char_t(i); expr[1] = 0; qv.push_back(xpath_query(expr)); diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp index 1fe0b6e..f72d6ff 100644 --- a/tests/test_xpath_variables.cpp +++ b/tests/test_xpath_variables.cpp @@ -527,8 +527,8 @@ TEST(xpath_variables_copy_big) { char_t name[4]; name[0] = 'a'; - name[1] = '0' + (i / 10); - name[2] = '0' + (i % 10); + name[1] = '0' + char_t(i / 10); + name[2] = '0' + char_t(i % 10); name[3] = 0; set.set(name, double(i)); @@ -540,8 +540,8 @@ TEST(xpath_variables_copy_big) { char_t name[4]; name[0] = 'a'; - name[1] = '0' + (i / 10); - name[2] = '0' + (i % 10); + name[1] = '0' + char_t(i / 10); + name[2] = '0' + char_t(i % 10); name[3] = 0; CHECK(copy.get(name) && copy.get(name)->get_number() == i); @@ -556,8 +556,8 @@ TEST(xpath_variables_copy_big_out_of_memory) { char_t name[4]; name[0] = 'a'; - name[1] = '0' + (i / 10); - name[2] = '0' + (i % 10); + name[1] = '0' + char_t(i / 10); + name[2] = '0' + char_t(i % 10); name[3] = 0; set.set(name, double(i)); @@ -572,8 +572,8 @@ TEST(xpath_variables_copy_big_out_of_memory) { char_t name[4]; name[0] = 'a'; - name[1] = '0' + (i / 10); - name[2] = '0' + (i % 10); + name[1] = '0' + char_t(i / 10); + name[2] = '0' + char_t(i % 10); name[3] = 0; CHECK(!copy.get(name)); -- cgit v1.2.3