From f388c465ddff7bb0cac473a0573521295fa55736 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 8 Feb 2022 22:44:31 -0800 Subject: [PATCH 01/31] Optimize compact mode: reuse access in insert/remove --- src/pugixml.cpp | 66 ++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 60b55da..0627950 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1276,12 +1276,14 @@ PUGI__NS_BEGIN child->parent = parent; - if (node->next_sibling) - node->next_sibling->prev_sibling_c = child; + xml_node_struct* next = node->next_sibling; + + if (next) + next->prev_sibling_c = child; else parent->first_child->prev_sibling_c = child; - child->next_sibling = node->next_sibling; + child->next_sibling = next; child->prev_sibling_c = node; node->next_sibling = child; @@ -1293,12 +1295,14 @@ PUGI__NS_BEGIN child->parent = parent; - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = child; + xml_node_struct* prev = node->prev_sibling_c; + + if (prev->next_sibling) + prev->next_sibling = child; else parent->first_child = child; - child->prev_sibling_c = node->prev_sibling_c; + child->prev_sibling_c = prev; child->next_sibling = node; node->prev_sibling_c = child; @@ -1308,15 +1312,18 @@ PUGI__NS_BEGIN { xml_node_struct* parent = node->parent; - if (node->next_sibling) - node->next_sibling->prev_sibling_c = node->prev_sibling_c; - else - parent->first_child->prev_sibling_c = node->prev_sibling_c; + xml_node_struct* next = node->next_sibling; + xml_node_struct* prev = node->prev_sibling_c; - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = node->next_sibling; + if (next) + next->prev_sibling_c = prev; else - parent->first_child = node->next_sibling; + parent->first_child->prev_sibling_c = prev; + + if (prev->next_sibling) + prev->next_sibling = next; + else + parent->first_child = next; node->parent = 0; node->prev_sibling_c = 0; @@ -1360,39 +1367,46 @@ PUGI__NS_BEGIN inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { - if (place->next_attribute) - place->next_attribute->prev_attribute_c = attr; + xml_attribute_struct* next = place->next_attribute; + + if (next) + next->prev_attribute_c = attr; else node->first_attribute->prev_attribute_c = attr; - attr->next_attribute = place->next_attribute; + attr->next_attribute = next; attr->prev_attribute_c = place; place->next_attribute = attr; } inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { - if (place->prev_attribute_c->next_attribute) - place->prev_attribute_c->next_attribute = attr; + xml_attribute_struct* prev = place->prev_attribute_c; + + if (prev->next_attribute) + prev->next_attribute = attr; else node->first_attribute = attr; - attr->prev_attribute_c = place->prev_attribute_c; + attr->prev_attribute_c = prev; attr->next_attribute = place; place->prev_attribute_c = attr; } inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) { - if (attr->next_attribute) - attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; - else - node->first_attribute->prev_attribute_c = attr->prev_attribute_c; + xml_attribute_struct* next = attr->next_attribute; + xml_attribute_struct* prev = attr->prev_attribute_c; - if (attr->prev_attribute_c->next_attribute) - attr->prev_attribute_c->next_attribute = attr->next_attribute; + if (next) + next->prev_attribute_c = prev; else - node->first_attribute = attr->next_attribute; + node->first_attribute->prev_attribute_c = prev; + + if (prev->next_attribute) + prev->next_attribute = next; + else + node->first_attribute = next; attr->prev_attribute_c = 0; attr->next_attribute = 0; From fad2d5e4efb1d30c18ec793a6d14673cd2cb78af Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 8 Feb 2022 23:00:17 -0800 Subject: [PATCH 02/31] Optimize compact mode: xml_attribute/xml_node implementation --- src/pugixml.cpp | 154 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 39 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 0627950..5f3bfa8 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -5199,53 +5199,72 @@ namespace pugi PUGI__FN xml_attribute xml_attribute::next_attribute() const { - return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + if (!_attr) return xml_attribute(); + return xml_attribute(_attr->next_attribute); } PUGI__FN xml_attribute xml_attribute::previous_attribute() const { - return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + if (!_attr) return xml_attribute(); + xml_attribute_struct* prev = _attr->prev_attribute_c; + return prev->next_attribute ? xml_attribute(prev) : xml_attribute(); } PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { - return (_attr && _attr->value) ? _attr->value + 0 : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? value : def; } PUGI__FN int xml_attribute::as_int(int def) const { - return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_int(value) : def; } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { - return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_uint(value) : def; } PUGI__FN double xml_attribute::as_double(double def) const { - return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_double(value) : def; } PUGI__FN float xml_attribute::as_float(float def) const { - return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_float(value) : def; } PUGI__FN bool xml_attribute::as_bool(bool def) const { - return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_attribute::as_llong(long long def) const { - return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_llong(value) : def; } PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { - return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_ullong(value) : def; } #endif @@ -5256,12 +5275,16 @@ namespace pugi PUGI__FN const char_t* xml_attribute::name() const { - return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); + if (!_attr) return PUGIXML_TEXT(""); + const char_t* name = _attr->name; + return name ? name : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_attribute::value() const { - return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); + if (!_attr) return PUGIXML_TEXT(""); + const char_t* value = _attr->value; + return value ? value : PUGIXML_TEXT(""); } PUGI__FN size_t xml_attribute::hash_value() const @@ -5535,7 +5558,9 @@ namespace pugi PUGI__FN const char_t* xml_node::name() const { - return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); + if (!_root) return PUGIXML_TEXT(""); + const char_t* name = _root->name; + return name ? name : PUGIXML_TEXT(""); } PUGI__FN xml_node_type xml_node::type() const @@ -5545,7 +5570,9 @@ namespace pugi PUGI__FN const char_t* xml_node::value() const { - return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); + if (!_root) return PUGIXML_TEXT(""); + const char_t* value = _root->value; + return value ? value : PUGIXML_TEXT(""); } PUGI__FN xml_node xml_node::child(const char_t* name_) const @@ -5553,7 +5580,11 @@ namespace pugi if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } @@ -5563,8 +5594,11 @@ namespace pugi if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) return xml_attribute(i); + } return xml_attribute(); } @@ -5574,7 +5608,11 @@ namespace pugi if (!_root) return xml_node(); for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } @@ -5589,7 +5627,11 @@ namespace pugi if (!_root) return xml_node(); for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } @@ -5605,24 +5647,30 @@ namespace pugi // optimistically search from hint up until the end for (xml_attribute_struct* i = hint; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = i->next_attribute; return xml_attribute(i); } + } // wrap around and search from the first attribute until the hint // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) - if (j->name && impl::strequal(name_, j->name)) + { + const char_t* jname = j->name; + if (jname && impl::strequal(name_, jname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = j->next_attribute; return xml_attribute(j); } + } return xml_attribute(); } @@ -5630,9 +5678,8 @@ namespace pugi PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); - - if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); - else return xml_node(); + xml_node_struct* prev = _root->prev_sibling_c; + return prev->next_sibling ? xml_node(prev) : xml_node(); } PUGI__FN xml_node xml_node::parent() const @@ -5659,8 +5706,11 @@ namespace pugi return _root->value; for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (impl::is_text_node(i) && i->value) - return i->value; + { + const char_t* ivalue = i->value; + if (impl::is_text_node(i) && ivalue) + return ivalue; + } return PUGIXML_TEXT(""); } @@ -5672,22 +5722,28 @@ namespace pugi PUGI__FN xml_attribute xml_node::first_attribute() const { - return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + if (!_root) return xml_attribute(); + return xml_attribute(_root->first_attribute); } PUGI__FN xml_attribute xml_node::last_attribute() const { - return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + if (!_root) return xml_attribute(); + xml_attribute_struct* first = _root->first_attribute; + return first ? xml_attribute(first->prev_attribute_c) : xml_attribute(); } PUGI__FN xml_node xml_node::first_child() const { - return _root ? xml_node(_root->first_child) : xml_node(); + if (!_root) return xml_node(); + return xml_node(_root->first_child); } PUGI__FN xml_node xml_node::last_child() const { - return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + if (!_root) return xml_node(); + xml_node_struct* first = _root->first_child; + return first ? xml_node(first->prev_sibling_c) : xml_node(); } PUGI__FN bool xml_node::set_name(const char_t* rhs) @@ -6213,12 +6269,22 @@ namespace pugi if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) - return xml_node(i); + { + const char_t* aname = a->name; + if (aname && impl::strequal(attr_name, aname)) + { + const char_t* avalue = a->value; + if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) + return xml_node(i); + } + } } + } return xml_node(); } @@ -6229,8 +6295,15 @@ namespace pugi for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) - return xml_node(i); + { + const char_t* aname = a->name; + if (aname && impl::strequal(attr_name, aname)) + { + const char_t* avalue = a->value; + if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) + return xml_node(i); + } + } return xml_node(); } @@ -6244,8 +6317,9 @@ namespace pugi for (xml_node_struct* i = _root; i; i = i->parent) { + const char_t* iname = i->name; offset += (i != _root); - offset += i->name ? impl::strlength(i->name) : 0; + offset += iname ? impl::strlength(iname) : 0; } string_t result; @@ -6256,12 +6330,13 @@ namespace pugi if (j != _root) result[--offset] = delimiter; - if (j->name) + const char_t* jname = j->name; + if (jname) { - size_t length = impl::strlength(j->name); + size_t length = impl::strlength(jname); offset -= length; - memcpy(&result[offset], j->name, length * sizeof(char_t)); + memcpy(&result[offset], jname, length * sizeof(char_t)); } } @@ -6299,7 +6374,8 @@ namespace pugi { for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) { - if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) + const char_t* jname = j->name; + if (jname && impl::strequalrange(jname, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); From 2fa9158b4f9fee84348b799f4a81204219c8d592 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 8 Feb 2022 23:04:31 -0800 Subject: [PATCH 03/31] Optimize compact mode: xml_text --- src/pugixml.cpp | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 5f3bfa8..4786c9e 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -6567,65 +6567,74 @@ namespace pugi PUGI__FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); - - return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); + if (!d) return PUGIXML_TEXT(""); + const char_t* value = d->value; + return value ? value : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? d->value + 0 : def; + if (!d) return def; + const char_t* value = d->value; + return value ? value : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_int(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_int(value) : def; } PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_uint(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_uint(value) : def; } PUGI__FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_double(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_double(value) : def; } PUGI__FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_float(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_float(value) : def; } PUGI__FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_bool(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_llong(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_llong(value) : def; } PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_ullong(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_ullong(value) : def; } #endif From effc46f0ed35a70a53db7f56e37141ae3cb3a92a Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Wed, 13 Apr 2022 12:25:01 +0300 Subject: [PATCH 04/31] Added bool set_value(const char_t* rhs, size_t sz). --- src/pugixml.cpp | 9 +++++++-- src/pugixml.hpp | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 60b55da..aada2ac 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -5329,11 +5329,16 @@ namespace pugi return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } - PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + PUGI__FN bool xml_attribute::set_value(const char_t* rhs, size_t sz) { if (!_attr) return false; - return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, sz); + } + + PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + { + return set_value(rhs, impl::strlength(rhs)); } PUGI__FN bool xml_attribute::set_value(int rhs) diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 579f143..f9ffaa2 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -416,6 +416,7 @@ namespace pugi // Set attribute name/value (returns false if attribute is empty or there is not enough memory) bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs, size_t sz); bool set_value(const char_t* rhs); // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") From 6fbf32140b1df7e4ef0da2dfc7269e8fc4ad9957 Mon Sep 17 00:00:00 2001 From: TodorHryn Date: Mon, 16 May 2022 13:21:20 +0300 Subject: [PATCH 05/31] Fix memory leak --- src/pugixml.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 60b55da..13781e1 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -4712,7 +4712,11 @@ PUGI__NS_BEGIN size_t length = 0; // coverity[var_deref_model] - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) + { + if (own && contents) impl::xml_memory::deallocate(contents); + return impl::make_parse_result(status_out_of_memory); + } // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); From 33a75c734bc176d92aa9c958ff3df80424c01f78 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 16 May 2022 19:10:12 -0700 Subject: [PATCH 06/31] Fix memory leak during OOM in convert_buffer This is the same fix as #497, but we're using auto_deleter instead because if allocation function throws, we can't rely on an explicit call to deallocate. Comes along with two tests that validate the behavior. --- src/pugixml.cpp | 12 +++++++----- tests/test_document.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 13781e1..8ff879b 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -4707,16 +4707,18 @@ PUGI__NS_BEGIN // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it + auto_deleter contents_guard(own ? contents : NULL, xml_memory::deallocate); + // get private buffer char_t* buffer = 0; size_t length = 0; // coverity[var_deref_model] - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) - { - if (own && contents) impl::xml_memory::deallocate(contents); - return impl::make_parse_result(status_out_of_memory); - } + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + + // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it + contents_guard.data = NULL; // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 5379092..0e0bad4 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -1817,3 +1817,39 @@ TEST(document_move_assign_empty) CHECK_NODE(doc, STR("")); } #endif + +TEST(document_load_buffer_convert_out_of_memory) +{ + const char* source = "\xe7"; + size_t size = strlen(source); + + test_runner::_memory_fail_threshold = 1; + + xml_document doc; + + xml_parse_result result; + result.status = status_out_of_memory; + CHECK_ALLOC_FAIL(result = doc.load_buffer(source, size, pugi::parse_default, pugi::encoding_latin1)); + + CHECK(result.status == status_out_of_memory); +} + +TEST(document_load_buffer_own_convert_out_of_memory) +{ + const char* source = "\xe7"; + size_t size = strlen(source); + + void* buffer = pugi::get_memory_allocation_function()(size); + CHECK(buffer); + memcpy(buffer, source, size); + + test_runner::_memory_fail_threshold = 1; + + xml_document doc; + + xml_parse_result result; + result.status = status_out_of_memory; + CHECK_ALLOC_FAIL(result = doc.load_buffer_inplace_own(buffer, size, pugi::parse_default, pugi::encoding_latin1)); + + CHECK(result.status == status_out_of_memory); +} From 832a4f4914cc7830b1a7ec675b1c84290bdf2808 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 16 May 2022 19:14:29 -0700 Subject: [PATCH 07/31] Use more idiomatic code in this codebase --- src/pugixml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 8ff879b..47764c8 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -4708,7 +4708,7 @@ PUGI__NS_BEGIN xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it - auto_deleter contents_guard(own ? contents : NULL, xml_memory::deallocate); + auto_deleter contents_guard(own ? contents : 0, xml_memory::deallocate); // get private buffer char_t* buffer = 0; @@ -4718,7 +4718,7 @@ PUGI__NS_BEGIN if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it - contents_guard.data = NULL; + contents_guard.release(); // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); From ab8453c57221917cad0a268aa43e8c7d6826a67d Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 21 Jul 2022 20:51:26 -0700 Subject: [PATCH 08/31] tests: Use snprintf instead of sprintf sprintf now results in a deprecation warning in Xcode 14 beta. --- tests/test_document.cpp | 2 +- tests/test_dom_traverse.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 0e0bad4..6d016c3 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -736,7 +736,7 @@ struct temp_file temp_file() { static int index = 0; - sprintf(path, "%stempfile%d", test_runner::_temp_path, index++); + snprintf(path, sizeof(path), "%stempfile%d", test_runner::_temp_path, index++); } ~temp_file() diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index 4a2403d..b83aa74 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -795,7 +795,7 @@ struct test_walker: xml_tree_walker std::basic_string depthstr() const { char buf[32]; - sprintf(buf, "%d", depth()); + snprintf(buf, sizeof(buf), "%d", depth()); #ifdef PUGIXML_WCHAR_MODE wchar_t wbuf[32]; From 3b5c1fb022dc286b0480ccafa9b79f832deb24ce Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 21 Jul 2022 21:17:17 -0700 Subject: [PATCH 09/31] tests: Fix MSVC 2005 build ... I forgot we still support platforms without C99, 23 years later. --- tests/test_document.cpp | 5 +++++ tests/test_dom_traverse.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 6d016c3..fca6bd9 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -736,7 +736,12 @@ struct temp_file temp_file() { static int index = 0; + + #if __cplusplus >= 201103 snprintf(path, sizeof(path), "%stempfile%d", test_runner::_temp_path, index++); + #else + sprintf(path, "%stempfile%d", test_runner::_temp_path, index++); + #endif } ~temp_file() diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index b83aa74..29b4dfd 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -795,7 +795,12 @@ struct test_walker: xml_tree_walker std::basic_string depthstr() const { char buf[32]; + + #if __cplusplus >= 201103 snprintf(buf, sizeof(buf), "%d", depth()); + #else + sprintf(buf, "%d", depth()); + #endif #ifdef PUGIXML_WCHAR_MODE wchar_t wbuf[32]; From f7de324855c949bf42838203d1b654618e2fc09a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matth=C3=A4us=20Brandl?= Date: Thu, 4 Aug 2022 15:57:11 +0200 Subject: [PATCH 10/31] Enable usage of nullptr for MSVC 16 and newer (MSVS 2010) --- src/pugixml.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 579f143..32a2d7c 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -115,6 +115,8 @@ #ifndef PUGIXML_NULL # if __cplusplus >= 201103 # define PUGIXML_NULL nullptr +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_NULL nullptr # else # define PUGIXML_NULL 0 # endif From 521b2cd854f8d65f173107d056d2b9c6d49b6563 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Fri, 2 Sep 2022 21:42:25 -0700 Subject: [PATCH 11/31] Add issue templates to try to route questions to Discussions --- .github/ISSUE_TEMPLATE/bug_report.md | 8 ++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 +++++ .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++++++ 3 files changed, 21 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ab8ecea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,8 @@ +--- +name: Bug report +about: Create a report if you believe you've found a bug in this project; please use GitHub Discussions instead if you think the bug may be in your code. +title: '' +labels: bug +assignees: '' + +--- diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..caf05df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Help and support + url: https://github.com/zeux/pugixml/discussions + about: Please use GitHub Discussions if you have questions or need help. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..83826c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,8 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- From 173a2634904875b0926615cc659e8f108115ea20 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Wed, 20 Apr 2022 13:00:41 +0200 Subject: [PATCH 12/31] Correctly set default visibility on non-windows compilers This fixes compilation of pugixml with -fvisibility=hidden. Without this patch, one would get lots of unresolved symbols when consuming pugixml as a shared library. --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17f67d1..d309509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,9 @@ if (BUILD_SHARED_LIBS) ${PUGIXML_BUILD_DEFINES} ${PUGIXML_PUBLIC_DEFINITIONS} PRIVATE - $<$:PUGIXML_API=__declspec\(dllexport\)>) + $<$:PUGIXML_API=__declspec\(dllexport\)> + $,,PUGIXML_API=__attribute__\(\(visibility\("default"\)\)\)> + ) target_compile_options(pugixml-shared PRIVATE ${msvc-rt-mtd-shared} From f327371219a452facc5806c20b43ced04b7254a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Thu, 29 Sep 2022 00:00:00 +0000 Subject: [PATCH 13/31] Add overloads with size_t type argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * xml_node::set_value(const char_t* rhs, size_t sz) * xml_text::set(const char_t* rhs, size_t sz) Signed-off-by: Ferenc Géczi --- src/pugixml.cpp | 17 +++++++++++++++++ src/pugixml.hpp | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index aada2ac..f264362 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -5691,6 +5691,16 @@ namespace pugi return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } + PUGI__FN bool xml_node::set_value(const char_t* rhs, size_t sz) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) + return false; + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, sz); + } + PUGI__FN bool xml_node::set_value(const char_t* rhs) { xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; @@ -6544,6 +6554,13 @@ namespace pugi } #endif + PUGI__FN bool xml_text::set(const char_t* rhs, size_t sz) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, sz) : false; + } + PUGI__FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); diff --git a/src/pugixml.hpp b/src/pugixml.hpp index f9ffaa2..82b2d1f 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -551,6 +551,7 @@ namespace pugi // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs, size_t sz); bool set_value(const char_t* rhs); // Add attribute with specified name. Returns added attribute, or empty attribute on errors. @@ -776,6 +777,7 @@ namespace pugi bool as_bool(bool def = false) const; // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t* rhs, size_t sz); bool set(const char_t* rhs); // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") From 09e7cc9b1cee85e0d9ef8b6d91c9b1efbc735c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Thu, 29 Sep 2022 00:00:00 +0000 Subject: [PATCH 14/31] Add test for xml_text::set with size argument --- tests/test_dom_text.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_dom_text.cpp b/tests/test_dom_text.cpp index c71e54e..6146f55 100644 --- a/tests/test_dom_text.cpp +++ b/tests/test_dom_text.cpp @@ -249,6 +249,26 @@ TEST_XML(dom_text_set, "") CHECK_NODE(node, STR("foobarfoobar")); } +TEST_XML(dom_text_set_with_size, "") +{ + xml_node node = doc.child(STR("node")); + xml_text t = node.text(); + + t.set(STR(""), 0); + CHECK(node.first_child().type() == node_pcdata); + CHECK_NODE(node, STR("")); + + t.set(STR("boo"), 3); + CHECK(node.first_child().type() == node_pcdata); + CHECK(node.first_child() == node.last_child()); + CHECK_NODE(node, STR("boo")); + + t.set(STR("foobarfoobar"), 12); + CHECK(node.first_child().type() == node_pcdata); + CHECK(node.first_child() == node.last_child()); + CHECK_NODE(node, STR("foobarfoobar")); +} + TEST_XML(dom_text_assign, "") { xml_node node = doc.child(STR("node")); From d359402311ab89039316eb750d3d8ebca95d9b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Thu, 29 Sep 2022 00:00:00 +0000 Subject: [PATCH 15/31] Add xml_text::set test with size set to substring --- tests/test_dom_text.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_dom_text.cpp b/tests/test_dom_text.cpp index 6146f55..7d77c09 100644 --- a/tests/test_dom_text.cpp +++ b/tests/test_dom_text.cpp @@ -269,6 +269,26 @@ TEST_XML(dom_text_set_with_size, "") CHECK_NODE(node, STR("foobarfoobar")); } +TEST_XML(dom_text_set_partially_with_size, "") +{ + xml_node node = doc.child(STR("node")); + xml_text t = node.text(); + + t.set(STR("foo"), 0); + CHECK(node.first_child().type() == node_pcdata); + CHECK_NODE(node, STR("")); + + t.set(STR("boofoo"), 3); + CHECK(node.first_child().type() == node_pcdata); + CHECK(node.first_child() == node.last_child()); + CHECK_NODE(node, STR("boo")); + + t.set(STR("foobarfoobar"), 3); + CHECK(node.first_child().type() == node_pcdata); + CHECK(node.first_child() == node.last_child()); + CHECK_NODE(node, STR("foo")); +} + TEST_XML(dom_text_assign, "") { xml_node node = doc.child(STR("node")); From 1905284494b020c89e120b1caead984859121c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Thu, 29 Sep 2022 00:00:00 +0000 Subject: [PATCH 16/31] Add test for xml_node::set_value with size argument --- tests/test_dom_modify.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index ff9d440..1ce25fe 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -209,6 +209,24 @@ TEST_XML(dom_node_set_value, "text") CHECK_NODE(doc, STR("no text")); } +TEST_XML(dom_node_set_value_partially_with_size, "text") +{ + CHECK(doc.child(STR("node")).first_child().set_value(STR("no text"), 2)); + CHECK(!doc.child(STR("node")).set_value(STR("no text"), 2)); + CHECK(!xml_node().set_value(STR("no text"), 2)); + + CHECK_NODE(doc, STR("no")); +} + +TEST_XML(dom_node_set_value_with_size, "text") +{ + CHECK(doc.child(STR("node")).first_child().set_value(STR("no text"), 7)); + CHECK(!doc.child(STR("node")).set_value(STR("no text"), 7)); + CHECK(!xml_node().set_value(STR("no text"), 7)); + + CHECK_NODE(doc, STR("no text")); +} + TEST_XML(dom_node_set_value_allocated, "text") { CHECK(doc.child(STR("node")).first_child().set_value(STR("no text"))); From 39e169285cf46edda590fbfa495b46e095c5099b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Thu, 29 Sep 2022 00:00:00 +0000 Subject: [PATCH 17/31] Add test for xml_attribute::set_value with size argument --- tests/test_dom_modify.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 1ce25fe..3451f19 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -70,7 +70,13 @@ TEST_XML(dom_attr_set_value, "") CHECK(node.append_attribute(STR("attr8")).set_value(true)); CHECK(!xml_attribute().set_value(true)); - CHECK_NODE(node, STR("")); + CHECK(node.append_attribute(STR("attr9")).set_value(STR("v2"), 2)); + CHECK(!xml_attribute().set_value(STR("v2"))); + + CHECK(node.append_attribute(STR("attr10")).set_value(STR("v3foobar"), 2)); + CHECK(!xml_attribute().set_value(STR("v3"))); + + CHECK_NODE(node, STR("")); } #if LONG_MAX > 2147483647 From 0cb4f025791f7c17f8ec09a1c12b0cb107518fad Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Fri, 7 Oct 2022 21:46:27 -0700 Subject: [PATCH 18/31] Final tweaks after #522 This cleans up xml_attribute::set_value to be uniform wrt xml_node::set_value and xml_text::set_value - for now we duplicate the body since the logic is trivial and this keeps debug performance excellent. --- src/pugixml.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index bc9fd82..6b16dbb 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -5381,7 +5381,9 @@ namespace pugi PUGI__FN bool xml_attribute::set_value(const char_t* rhs) { - return set_value(rhs, impl::strlength(rhs)); + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN bool xml_attribute::set_value(int rhs) From 444963e2690d0a2a05df62429379e633e1dfb1d4 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Fri, 7 Oct 2022 22:13:04 -0700 Subject: [PATCH 19/31] Fix error handling in xml_document::save_file There were two conditions under which xml_document::save_file could previously return true even though the saving failed: - The last write to the file was buffered in stdio buffer, and it's that last write that would fail due to lack of disk space - The data has been written correctly but fclose failed to update file metadata, which can result in truncated size / missing inode updates. This change fixes both by adjusting save_file to fflush before the check, and also checking fclose results. Note that while fflush here is technically redundant, because it's implied by fclose, we must check ferror explicitly anyway, and so it feels a little cleaner to do most of the error handling in save_file_impl, so that the changes of fclose() failing are very slim. Of course, neither change guarantees that the contents of the file are going to be safe on disk following a power failure. --- src/pugixml.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 6b16dbb..7c84038 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -5070,7 +5070,7 @@ PUGI__NS_BEGIN xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); - return ferror(file) == 0; + return fflush(file) == 0 && ferror(file) == 0; } struct name_null_sentry @@ -7423,7 +7423,7 @@ namespace pugi using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); - return impl::save_file_impl(*this, file.data, indent, flags, encoding); + return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const @@ -7431,7 +7431,7 @@ namespace pugi using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); - return impl::save_file_impl(*this, file.data, indent, flags, encoding); + return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } PUGI__FN xml_node xml_document::document_element() const From ab8af53059ac0374cfba08496d3b2194d57502a2 Mon Sep 17 00:00:00 2001 From: Izzy Muerte <63051+bruxisma@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:35:04 -0700 Subject: [PATCH 20/31] Fix exported symbols under clang-cl (Closes #503) This also turns the define for PUGIXML_API into an `$`, instead of an `$` with an empty true condition. If this is inadequate, I will undo it, and place them on separate lines as they were before, but will most likely use an inverse `$` instead of an `$`. --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d309509..77a5e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,10 @@ if (BUILD_SHARED_LIBS) ${PROJECT_SOURCE_DIR}/src/pugixml.cpp) add_library(pugixml::shared ALIAS pugixml-shared) list(APPEND libs pugixml-shared) + string(CONCAT pugixml.msvc $, + $ + >) set_property(TARGET pugixml-shared PROPERTY EXPORT_NAME shared) target_include_directories(pugixml-shared @@ -111,8 +115,7 @@ if (BUILD_SHARED_LIBS) ${PUGIXML_BUILD_DEFINES} ${PUGIXML_PUBLIC_DEFINITIONS} PRIVATE - $<$:PUGIXML_API=__declspec\(dllexport\)> - $,,PUGIXML_API=__attribute__\(\(visibility\("default"\)\)\)> + PUGIXML_API=$ ) target_compile_options(pugixml-shared PRIVATE From 76dcd894271bc26a4cb9dad15a68e5abf9fc0f14 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 20 Oct 2022 20:08:52 -0700 Subject: [PATCH 21/31] Update version number in preparation for 1.13 --- CMakeLists.txt | 2 +- readme.txt | 2 +- scripts/nuget/pugixml.nuspec | 2 +- scripts/pugixml.podspec | 2 +- scripts/pugixml_dll.rc | 4 ++-- src/pugiconfig.hpp | 2 +- src/pugixml.cpp | 2 +- src/pugixml.hpp | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77a5e7e..9860f38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(pugixml VERSION 1.12 LANGUAGES CXX) +project(pugixml VERSION 1.13 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(CMakeDependentOption) diff --git a/readme.txt b/readme.txt index 747fc0b..9dffb72 100644 --- a/readme.txt +++ b/readme.txt @@ -1,4 +1,4 @@ -pugixml 1.12 - an XML processing library +pugixml 1.13 - an XML processing library Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) Report bugs and download new versions at https://pugixml.org/ diff --git a/scripts/nuget/pugixml.nuspec b/scripts/nuget/pugixml.nuspec index 36d833f..ce88718 100644 --- a/scripts/nuget/pugixml.nuspec +++ b/scripts/nuget/pugixml.nuspec @@ -2,7 +2,7 @@ pugixml - 1.12.0-appveyor + 1.13.0-appveyor pugixml Arseny Kapoulkine Arseny Kapoulkine diff --git a/scripts/pugixml.podspec b/scripts/pugixml.podspec index 520dc9e..52e9064 100644 --- a/scripts/pugixml.podspec +++ b/scripts/pugixml.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "pugixml" - s.version = "1.12" + s.version = "1.13" s.summary = "C++ XML parser library." s.homepage = "https://pugixml.org" s.license = "MIT" diff --git a/scripts/pugixml_dll.rc b/scripts/pugixml_dll.rc index 72c68d0..0ffa019 100644 --- a/scripts/pugixml_dll.rc +++ b/scripts/pugixml_dll.rc @@ -1,9 +1,9 @@ #include #define PUGIXML_VERSION_MAJOR 1 -#define PUGIXML_VERSION_MINOR 12 +#define PUGIXML_VERSION_MINOR 13 #define PUGIXML_VERSION_PATCH 0 -#define PUGIXML_VERSION_NUMBER "1.12.0\0" +#define PUGIXML_VERSION_NUMBER "1.13.0\0" #if defined(GCC_WINDRES) || defined(__MINGW32__) || defined(__CYGWIN__) VS_VERSION_INFO VERSIONINFO diff --git a/src/pugiconfig.hpp b/src/pugiconfig.hpp index 0713b0e..88b2f2a 100644 --- a/src/pugiconfig.hpp +++ b/src/pugiconfig.hpp @@ -1,5 +1,5 @@ /** - * pugixml parser - version 1.12 + * pugixml parser - version 1.13 * -------------------------------------------------------- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 7c84038..c63645b 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1,5 +1,5 @@ /** - * pugixml parser - version 1.12 + * pugixml parser - version 1.13 * -------------------------------------------------------- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 398eec8..050df15 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1,5 +1,5 @@ /** - * pugixml parser - version 1.12 + * pugixml parser - version 1.13 * -------------------------------------------------------- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ @@ -14,7 +14,7 @@ // Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons // Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits #ifndef PUGIXML_VERSION -# define PUGIXML_VERSION 1120 // 1.12 +# define PUGIXML_VERSION 1130 // 1.13 #endif // Include user configuration file (this can define various configuration macros) From 0ef3da1e6e18df24cdf36849c3e0c08e8ef541e5 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 20 Oct 2022 20:18:08 -0700 Subject: [PATCH 22/31] Update release notes and manual for 1.13 This includes a previously unnoticed link fix for xml_text::set --- docs/manual.adoc | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/docs/manual.adoc b/docs/manual.adoc index ac51535..0bff1da 100644 --- a/docs/manual.adoc +++ b/docs/manual.adoc @@ -1254,6 +1254,7 @@ As discussed before, nodes can have name and value, both of which are strings. D ---- bool xml_node::set_name(const char_t* rhs); bool xml_node::set_value(const char_t* rhs); +bool xml_node::set_value(const char_t* rhs, size_t size); ---- Both functions try to set the name/value to the specified string, and return the operation result. The operation fails if the node can not have name or value (for instance, when trying to call `set_name` on a <> node), if the node handle is null, or if there is insufficient memory to handle the request. The provided string is copied into document managed memory and can be destroyed after the function returns (for example, you can safely pass stack-allocated buffers to these functions). The name/value content is not verified, so take care to use only valid XML names, or the document may become malformed. @@ -1275,6 +1276,7 @@ All attributes have name and value, both of which are strings (value may be empt ---- bool xml_attribute::set_name(const char_t* rhs); bool xml_attribute::set_value(const char_t* rhs); +bool xml_attribute::set_value(const char_t* rhs, size_t size); ---- Both functions try to set the name/value to the specified string, and return the operation result. The operation fails if the attribute handle is null, or if there is insufficient memory to handle the request. The provided string is copied into document managed memory and can be destroyed after the function returns (for example, you can safely pass stack-allocated buffers to these functions). The name/value content is not verified, so take care to use only valid XML names, or the document may become malformed. @@ -1428,6 +1430,7 @@ Once you have an `xml_text` object, you can set the text contents using the foll [source] ---- bool xml_text::set(const char_t* rhs); +bool xml_text::set(const char_t* rhs, size_t size); ---- This function tries to set the contents to the specified string, and returns the operation result. The operation fails if the text object was retrieved from a node that can not have a value and is not an element node (i.e. it is a <> node), if the text object is empty, or if there is insufficient memory to handle the request. The provided string is copied into document managed memory and can be destroyed after the function returns (for example, you can safely pass stack-allocated buffers to this function). Note that if the text object was retrieved from an element node, this function creates the PCDATA child node if necessary (i.e. if the element node does not have a PCDATA/CDATA child already). @@ -2138,6 +2141,23 @@ Because of the differences in document object models, performance considerations :!numbered: +[[v1.13]] +=== v1.13 ^2022-11-01^ + +Maintenance release. Changes: + +* Improvements: + . `xml_attribute::set_value`, `xml_node::set_value` and `xml_text::set` now have overloads that accept pointer to non-null-terminated string and size + . Improve performance of tree traversal when using compact mode (`PUGIXML_COMPACT`) + +* Bug fixes: + . Fix error handling in `xml_document::save_file` that could result in the function succeeding while running out of disk space + . Fix memory leak during error handling of some out-of-memory conditions during `xml_document::load` + +* Compatibility improvements: + . Fix exported symbols in CMake DLL builds when using CMake + . Fix exported symbols in CMake shared object builds when using -fvisibility=hidden + [[v1.12]] === v1.12 ^2022-02-09^ @@ -2798,6 +2818,7 @@ const unsigned int +++parse_wnorm_attribute bool +++set_name+++(const char_t* rhs); bool +++set_value+++(const char_t* rhs); + bool +++set_value+++(const char_t* rhs, size_t size); bool +++set_value+++(int rhs); bool +++set_value+++(unsigned int rhs); bool +++set_value+++(long rhs); @@ -2884,6 +2905,7 @@ const unsigned int +++parse_wnorm_attribute bool +++set_name+++(const char_t* rhs); bool +++set_value+++(const char_t* rhs); + bool +++set_value+++(const char_t* rhs, size_t size); xml_attribute +++append_attribute+++(const char_t* name); xml_attribute +++prepend_attribute+++(const char_t* name); @@ -2996,16 +3018,17 @@ const unsigned int +++parse_wnorm_attribute unsigned long long +++as_ullong+++(unsigned long long def = 0) const; bool +++set+++(const char_t* rhs); + bool +++set+++(const char_t* rhs, size_t size); - bool +++set+++(int rhs); - bool +++set+++(unsigned int rhs); - bool +++set+++(long rhs); - bool +++set+++(unsigned long rhs); - bool +++set+++(double rhs); - bool +++set+++(float rhs); - bool +++set+++(bool rhs); - bool +++set+++(long long rhs); - bool +++set+++(unsigned long long rhs); + bool +++set+++(int rhs); + bool +++set+++(unsigned int rhs); + bool +++set+++(long rhs); + bool +++set+++(unsigned long rhs); + bool +++set+++(double rhs); + bool +++set+++(float rhs); + bool +++set+++(bool rhs); + bool +++set+++(long long rhs); + bool +++set+++(unsigned long long rhs); xml_text& +++operator=+++(const char_t* rhs); xml_text& +++operator=+++(int rhs); From c5b288d91a33bd8347f39e3a90dc15b3a9d2b203 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 20 Oct 2022 20:20:45 -0700 Subject: [PATCH 23/31] Update HTML documentation --- docs/manual.html | 216 ++++++++++++++++++++++++++++--------------- docs/quickstart.html | 76 ++++++++------- 2 files changed, 180 insertions(+), 112 deletions(-) diff --git a/docs/manual.html b/docs/manual.html index 6457a9a..01f233c 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -4,9 +4,9 @@ - + -pugixml 1.12 manual +pugixml 1.13 manual