diff --git a/cbp/ModXML.cbp b/cbp/ModXML.cbp
index da0e59fe..2cde12d4 100644
--- a/cbp/ModXML.cbp
+++ b/cbp/ModXML.cbp
@@ -436,6 +436,7 @@
+
diff --git a/config/common/pugiconfig.hpp b/config/common/pugiconfig.hpp
index 6d962cfe..5669cda9 100644
--- a/config/common/pugiconfig.hpp
+++ b/config/common/pugiconfig.hpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.8
+ * pugixml parser - version 1.9
* --------------------------------------------------------
- * Copyright (C) 2006-2017, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
+ * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
@@ -49,7 +49,7 @@
#endif
/**
- * Copyright (c) 2006-2017 Arseny Kapoulkine
+ * Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
diff --git a/external/PUGIXML/pugixml.cpp b/external/PUGIXML/pugixml.cpp
index 7368184b..87e58fbe 100644
--- a/external/PUGIXML/pugixml.cpp
+++ b/external/PUGIXML/pugixml.cpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.8
+ * pugixml parser - version 1.9
* --------------------------------------------------------
- * Copyright (C) 2006-2017, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
+ * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
@@ -48,6 +48,11 @@
# pragma warning(disable: 4996) // this function or variable may be unsafe
#endif
+#if defined(_MSC_VER) && defined(__c2__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe
+#endif
+
#ifdef __INTEL_COMPILER
# pragma warning(disable: 177) // function was declared but never referenced
# pragma warning(disable: 279) // controlling expression is constant
@@ -71,6 +76,10 @@
# pragma diag_suppress=237 // controlling expression is constant
#endif
+#ifdef __TI_COMPILER_VERSION__
+# pragma diag_suppress 179 // function was declared but never referenced
+#endif
+
// Inlining controls
#if defined(_MSC_VER) && _MSC_VER >= 1300
# define PUGI__NO_INLINE __declspec(noinline)
@@ -81,7 +90,7 @@
#endif
// Branch weight controls
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(__c2__)
# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define PUGI__UNLIKELY(cond) (cond)
@@ -97,6 +106,17 @@
# define PUGI__DMC_VOLATILE
#endif
+// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings
+#if defined(__clang__) && defined(__has_attribute)
+# if __has_attribute(no_sanitize)
+# define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+# else
+# define PUGI__UNSIGNED_OVERFLOW
+# endif
+#else
+# define PUGI__UNSIGNED_OVERFLOW
+#endif
+
// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
using std::memcpy;
@@ -104,11 +124,11 @@ using std::memmove;
using std::memset;
#endif
-// Some MinGW versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions in strict ANSI mode
-#if defined(PUGIXML_HAS_LONG_LONG) && defined(__MINGW32__) && defined(__STRICT_ANSI__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)
-# define LLONG_MAX 9223372036854775807LL
-# define LLONG_MIN (-LLONG_MAX-1)
-# define ULLONG_MAX (2ULL*LLONG_MAX+1)
+// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations
+#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+# define LLONG_MAX __LONG_LONG_MAX__
+# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
#endif
// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
@@ -116,6 +136,16 @@ using std::memset;
# define PUGI__MSVC_CRT_VERSION _MSC_VER
#endif
+// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
+#if __cplusplus >= 201103
+# define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
+#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
+#else
+# define PUGI__SNPRINTF sprintf
+#endif
+
+// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat.
#ifdef PUGIXML_HEADER_ONLY
# define PUGI__NS_BEGIN namespace pugi { namespace impl {
# define PUGI__NS_END } }
@@ -274,67 +304,37 @@ PUGI__NS_BEGIN
}
}
- void** find(const void* key)
+ void* find(const void* key)
{
- assert(key);
-
if (_capacity == 0) return 0;
- size_t hashmod = _capacity - 1;
- size_t bucket = hash(key) & hashmod;
+ item_t* item = get_item(key);
+ assert(item);
+ assert(item->key == key || (item->key == 0 && item->value == 0));
- for (size_t probe = 0; probe <= hashmod; ++probe)
- {
- item_t& probe_item = _items[bucket];
-
- if (probe_item.key == key)
- return &probe_item.value;
-
- if (probe_item.key == 0)
- return 0;
-
- // hash collision, quadratic probing
- bucket = (bucket + probe + 1) & hashmod;
- }
-
- assert(false && "Hash table is full");
- return 0;
+ return item->value;
}
- void** insert(const void* key)
+ void insert(const void* key, void* value)
{
- assert(key);
assert(_capacity != 0 && _count < _capacity - _capacity / 4);
- size_t hashmod = _capacity - 1;
- size_t bucket = hash(key) & hashmod;
+ item_t* item = get_item(key);
+ assert(item);
- for (size_t probe = 0; probe <= hashmod; ++probe)
+ if (item->key == 0)
{
- item_t& probe_item = _items[bucket];
-
- if (probe_item.key == 0)
- {
- probe_item.key = key;
- _count++;
- return &probe_item.value;
- }
-
- if (probe_item.key == key)
- return &probe_item.value;
-
- // hash collision, quadratic probing
- bucket = (bucket + probe + 1) & hashmod;
+ _count++;
+ item->key = key;
}
- assert(false && "Hash table is full");
- return 0;
+ item->value = value;
}
- bool reserve()
+ bool reserve(size_t extra = 16)
{
- if (_count + 16 >= _capacity - _capacity / 4)
- return rehash();
+ if (_count + extra >= _capacity - _capacity / 4)
+ return rehash(_count + extra);
return true;
}
@@ -351,9 +351,32 @@ PUGI__NS_BEGIN
size_t _count;
- bool rehash();
+ bool rehash(size_t count);
- static unsigned int hash(const void* key)
+ item_t* get_item(const void* key)
+ {
+ assert(key);
+ assert(_capacity > 0);
+
+ size_t hashmod = _capacity - 1;
+ size_t bucket = hash(key) & hashmod;
+
+ for (size_t probe = 0; probe <= hashmod; ++probe)
+ {
+ item_t& probe_item = _items[bucket];
+
+ if (probe_item.key == key || probe_item.key == 0)
+ return &probe_item;
+
+ // hash collision, quadratic probing
+ bucket = (bucket + probe + 1) & hashmod;
+ }
+
+ assert(false && "Hash table is full"); // unreachable
+ return 0;
+ }
+
+ static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
{
unsigned int h = static_cast(reinterpret_cast(key));
@@ -368,25 +391,29 @@ PUGI__NS_BEGIN
}
};
- PUGI__FN_NO_INLINE bool compact_hash_table::rehash()
+ PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count)
{
+ size_t capacity = 32;
+ while (count >= capacity - capacity / 4)
+ capacity *= 2;
+
compact_hash_table rt;
- rt._capacity = (_capacity == 0) ? 32 : _capacity * 2;
- rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * rt._capacity));
+ rt._capacity = capacity;
+ rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * capacity));
if (!rt._items)
return false;
- memset(rt._items, 0, sizeof(item_t) * rt._capacity);
+ memset(rt._items, 0, sizeof(item_t) * capacity);
for (size_t i = 0; i < _capacity; ++i)
if (_items[i].key)
- *rt.insert(_items[i].key) = _items[i].value;
+ rt.insert(_items[i].key, _items[i].value);
if (_items)
xml_memory::deallocate(_items);
- _capacity = rt._capacity;
+ _capacity = capacity;
_items = rt._items;
assert(_count == rt._count);
@@ -773,12 +800,12 @@ PUGI__NS_BEGIN
template PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
{
- return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object));
+ return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object));
}
template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
{
- *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value;
+ compact_get_page(object, header_offset)->allocator->_hash->insert(object, value);
}
template class compact_pointer
@@ -825,7 +852,7 @@ PUGI__NS_BEGIN
{
uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1);
- return reinterpret_cast(base + ((_data - 1 + start) << compact_alignment_log2));
+ return reinterpret_cast(base + (_data - 1 + start) * compact_alignment);
}
else
return compact_get_value(this);
@@ -903,7 +930,7 @@ PUGI__NS_BEGIN
{
uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1);
- return reinterpret_cast(base + ((_data - 1 - 65533) << compact_alignment_log2));
+ return reinterpret_cast(base + (_data - 1 - 65533) * compact_alignment);
}
else if (_data == 65534)
return static_cast(compact_get_page(this, header_offset)->compact_shared_parent);
@@ -1834,7 +1861,7 @@ PUGI__NS_BEGIN
enum chartypex_t
{
ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, >
- ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, "
+ ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, >, ", '
ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _
ctx_digit = 8, // 0-9
ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, .
@@ -1842,9 +1869,9 @@ PUGI__NS_BEGIN
static const unsigned char chartypex_table[256] =
{
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31
- 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47
+ 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63
0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79
@@ -2140,7 +2167,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return false;
}
#else
@@ -2245,7 +2272,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return false;
}
#endif
@@ -2458,7 +2485,7 @@ PUGI__NS_BEGIN
for (;;)
{
- if (static_cast(static_cast(ch) - '0') <= 9)
+ if (static_cast(ch - '0') <= 9)
ucsc = 10 * ucsc + (ch - '0');
else if (ch == ';')
break;
@@ -2682,7 +2709,7 @@ PUGI__NS_BEGIN
{
PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
- switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim)
+ switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above
{
case 0: return strconv_pcdata_impl::parse;
case 1: return strconv_pcdata_impl::parse;
@@ -2692,7 +2719,7 @@ PUGI__NS_BEGIN
case 5: return strconv_pcdata_impl::parse;
case 6: return strconv_pcdata_impl::parse;
case 7: return strconv_pcdata_impl::parse;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -2851,7 +2878,7 @@ PUGI__NS_BEGIN
{
PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
- switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
+ switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above
{
case 0: return strconv_attribute_impl::parse_simple;
case 1: return strconv_attribute_impl::parse_simple;
@@ -2869,7 +2896,7 @@ PUGI__NS_BEGIN
case 13: return strconv_attribute_impl::parse_wnorm;
case 14: return strconv_attribute_impl::parse_wnorm;
case 15: return strconv_attribute_impl::parse_wnorm;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -3618,7 +3645,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return 0;
}
#else
@@ -3657,7 +3684,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return 0;
}
#endif
@@ -3876,7 +3903,7 @@ PUGI__NS_BEGIN
xml_encoding encoding;
};
- PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)
+ PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
{
while (*s)
{
@@ -3906,12 +3933,17 @@ PUGI__NS_BEGIN
writer.write('&', 'q', 'u', 'o', 't', ';');
++s;
break;
+ case '\'':
+ writer.write('&', 'a', 'p', 'o', 's', ';');
+ ++s;
+ break;
default: // s is not a usual symbol
{
unsigned int ch = static_cast(*s++);
assert(ch < 32);
- writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';');
+ if (!(flags & format_skip_control_chars))
+ writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';');
}
}
}
@@ -3922,7 +3954,7 @@ PUGI__NS_BEGIN
if (flags & format_no_escapes)
writer.write_string(s);
else
- text_output_escaped(writer, s, type);
+ text_output_escaped(writer, s, type, flags);
}
PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
@@ -4036,6 +4068,7 @@ 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)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
+ const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"';
for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
{
@@ -4051,12 +4084,12 @@ PUGI__NS_BEGIN
}
writer.write_string(a->name ? a->name + 0 : default_name);
- writer.write('=', '"');
+ writer.write('=', enquotation_char);
if (a->value)
text_output(writer, a->value, ctx_special_attr, flags);
- writer.write('"');
+ writer.write(enquotation_char);
}
}
@@ -4184,7 +4217,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
}
}
@@ -4396,6 +4429,7 @@ PUGI__NS_BEGIN
while (sit && sit != sn)
{
+ // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop
if (sit != dn)
{
xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
@@ -4446,7 +4480,7 @@ PUGI__NS_BEGIN
}
// get value with conversion functions
- template U string_to_integer(const char_t* value, U minneg, U maxpos)
+ template PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
{
U result = 0;
const char_t* s = value;
@@ -4519,18 +4553,18 @@ PUGI__NS_BEGIN
{
// Workaround for crayc++ CC-3059: Expected no overflow in routine.
#ifdef _CRAYC
- return (overflow || result > minneg) ? ~minneg + 1 : ~result + 1;
+ return (overflow || result > ~minv + 1) ? minv : ~result + 1;
#else
- return (overflow || result > minneg) ? 0 - minneg : 0 - result;
+ return (overflow || result > 0 - minv) ? minv : 0 - result;
#endif
}
else
- return (overflow || result > maxpos) ? maxpos : result;
+ return (overflow || result > maxv) ? maxv : result;
}
PUGI__FN int get_value_int(const char_t* value)
{
- return string_to_integer(value, 0 - static_cast(INT_MIN), INT_MAX);
+ return string_to_integer(value, static_cast(INT_MIN), INT_MAX);
}
PUGI__FN unsigned int get_value_uint(const char_t* value)
@@ -4568,7 +4602,7 @@ PUGI__NS_BEGIN
#ifdef PUGIXML_HAS_LONG_LONG
PUGI__FN long long get_value_llong(const char_t* value)
{
- return string_to_integer(value, 0 - static_cast(LLONG_MIN), LLONG_MAX);
+ return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX);
}
PUGI__FN unsigned long long get_value_ullong(const char_t* value)
@@ -4577,7 +4611,7 @@ PUGI__NS_BEGIN
}
#endif
- template PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
+ template PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
{
char_t* result = end - 1;
U rest = negative ? 0 - value : value;
@@ -4628,7 +4662,7 @@ PUGI__NS_BEGIN
PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
{
char buf[128];
- sprintf(buf, "%.9g", value);
+ PUGI__SNPRINTF(buf, "%.9g", double(value));
return set_value_ascii(dest, header, header_mask, buf);
}
@@ -4637,7 +4671,7 @@ PUGI__NS_BEGIN
PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
{
char buf[128];
- sprintf(buf, "%.17g", value);
+ PUGI__SNPRINTF(buf, "%.17g", value);
return set_value_ascii(dest, header, header_mask, buf);
}
@@ -4660,6 +4694,7 @@ PUGI__NS_BEGIN
char_t* buffer = 0;
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);
// delete original buffer if we performed a conversion
@@ -6049,11 +6084,17 @@ namespace pugi
// get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)
impl::xml_memory_page* page = 0;
- impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));
+ impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page));
(void)page;
if (!extra) return impl::make_parse_result(status_out_of_memory);
+ #ifdef PUGIXML_COMPACT
+ // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned
+ // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account
+ extra = reinterpret_cast((reinterpret_cast(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1));
+ #endif
+
// add extra buffer to the list
extra->buffer = 0;
extra->next = doc->extra_buffers;
@@ -6179,10 +6220,10 @@ namespace pugi
{
walker._depth = -1;
- xml_node arg_begin = *this;
+ xml_node arg_begin(_root);
if (!walker.begin(arg_begin)) return false;
- xml_node cur = first_child();
+ xml_node_struct* cur = _root ? _root->first_child + 0 : 0;
if (cur)
{
@@ -6190,36 +6231,35 @@ namespace pugi
do
{
- xml_node arg_for_each = cur;
+ xml_node arg_for_each(cur);
if (!walker.for_each(arg_for_each))
return false;
- if (cur.first_child())
+ if (cur->first_child)
{
++walker._depth;
- cur = cur.first_child();
+ cur = cur->first_child;
}
- else if (cur.next_sibling())
- cur = cur.next_sibling();
+ else if (cur->next_sibling)
+ cur = cur->next_sibling;
else
{
- // Borland C++ workaround
- while (!cur.next_sibling() && cur != *this && !cur.parent().empty())
+ while (!cur->next_sibling && cur != _root && cur->parent)
{
--walker._depth;
- cur = cur.parent();
+ cur = cur->parent;
}
- if (cur != *this)
- cur = cur.next_sibling();
+ if (cur != _root)
+ cur = cur->next_sibling;
}
}
- while (cur && cur != *this);
+ while (cur && cur != _root);
}
assert(walker._depth == -1);
- xml_node arg_end = *this;
+ xml_node arg_end(_root);
return walker.end(arg_end);
}
@@ -6286,7 +6326,7 @@ namespace pugi
return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
default:
- assert(false && "Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
return -1;
}
}
@@ -6811,6 +6851,25 @@ namespace pugi
_destroy();
}
+#ifdef PUGIXML_HAS_MOVE
+ PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)
+ {
+ _create();
+ _move(rhs);
+ }
+
+ PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+ {
+ if (this == &rhs) return *this;
+
+ _destroy();
+ _create();
+ _move(rhs);
+
+ return *this;
+ }
+#endif
+
PUGI__FN void xml_document::reset()
{
_destroy();
@@ -6830,7 +6889,8 @@ namespace pugi
assert(!_root);
#ifdef PUGIXML_COMPACT
- const size_t page_offset = sizeof(uint32_t);
+ // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit
+ const size_t page_offset = sizeof(void*);
#else
const size_t page_offset = 0;
#endif
@@ -6906,6 +6966,113 @@ namespace pugi
_root = 0;
}
+#ifdef PUGIXML_HAS_MOVE
+ PUGI__FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+ {
+ impl::xml_document_struct* doc = static_cast(_root);
+ impl::xml_document_struct* other = static_cast(rhs._root);
+
+ // save first child pointer for later; this needs hash access
+ xml_node_struct* other_first_child = other->first_child;
+
+ #ifdef PUGIXML_COMPACT
+ // reserve space for the hash table up front; this is the only operation that can fail
+ // if it does, we have no choice but to throw (if we have exceptions)
+ if (other_first_child)
+ {
+ size_t other_children = 0;
+ for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
+ other_children++;
+
+ // in compact mode, each pointer assignment could result in a hash table request
+ // during move, we have to relocate document first_child and parents of all children
+ // normally there's just one child and its parent has a pointerless encoding but
+ // we assume the worst here
+ if (!other->_hash->reserve(other_children + 1))
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return;
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+ }
+ #endif
+
+ // move allocation state
+ doc->_root = other->_root;
+ doc->_busy_size = other->_busy_size;
+
+ // move buffer state
+ doc->buffer = other->buffer;
+ doc->extra_buffers = other->extra_buffers;
+ _buffer = rhs._buffer;
+
+ #ifdef PUGIXML_COMPACT
+ // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child
+ doc->hash = other->hash;
+ doc->_hash = &doc->hash;
+
+ // make sure we don't access other hash up until the end when we reinitialize other document
+ other->_hash = 0;
+ #endif
+
+ // move page structure
+ impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc);
+ assert(doc_page && !doc_page->prev && !doc_page->next);
+
+ impl::xml_memory_page* other_page = PUGI__GETPAGE(other);
+ assert(other_page && !other_page->prev);
+
+ // relink pages since root page is embedded into xml_document
+ if (impl::xml_memory_page* page = other_page->next)
+ {
+ assert(page->prev == other_page);
+
+ page->prev = doc_page;
+
+ doc_page->next = page;
+ other_page->next = 0;
+ }
+
+ // make sure pages point to the correct document state
+ for (impl::xml_memory_page* page = doc_page->next; page; page = page->next)
+ {
+ assert(page->allocator == other);
+
+ page->allocator = doc;
+
+ #ifdef PUGIXML_COMPACT
+ // this automatically migrates most children between documents and prevents ->parent assignment from allocating
+ if (page->compact_shared_parent == other)
+ page->compact_shared_parent = doc;
+ #endif
+ }
+
+ // move tree structure
+ assert(!doc->first_child);
+
+ doc->first_child = other_first_child;
+
+ for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
+ {
+ #ifdef PUGIXML_COMPACT
+ // most children will have migrated when we reassigned compact_shared_parent
+ assert(node->parent == other || node->parent == doc);
+
+ node->parent = doc;
+ #else
+ assert(node->parent == other);
+ node->parent = doc;
+ #endif
+ }
+
+ // reset other document
+ new (other) impl::xml_document_struct(PUGI__GETPAGE(other));
+ rhs._buffer = 0;
+ }
+#endif
+
#ifndef PUGIXML_NO_STL
PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding)
{
@@ -7171,14 +7338,14 @@ PUGI__NS_BEGIN
}
};
- template void swap(T& lhs, T& rhs)
+ template inline void swap(T& lhs, T& rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
- template I min_element(I begin, I end, const Pred& pred)
+ template PUGI__FN I min_element(I begin, I end, const Pred& pred)
{
I result = begin;
@@ -7189,17 +7356,20 @@ PUGI__NS_BEGIN
return result;
}
- template void reverse(I begin, I end)
+ template PUGI__FN void reverse(I begin, I end)
{
- while (end - begin > 1) swap(*begin++, *--end);
+ while (end - begin > 1)
+ swap(*begin++, *--end);
}
- template I unique(I begin, I end)
+ template PUGI__FN I unique(I begin, I end)
{
// fast skip head
- while (end - begin > 1 && *begin != *(begin + 1)) begin++;
+ while (end - begin > 1 && *begin != *(begin + 1))
+ begin++;
- if (begin == end) return begin;
+ if (begin == end)
+ return begin;
// last written element
I write = begin++;
@@ -7217,7 +7387,7 @@ PUGI__NS_BEGIN
return write + 1;
}
- template void insertion_sort(T* begin, T* end, const Pred& pred)
+ template PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred)
{
if (begin == end)
return;
@@ -7239,16 +7409,19 @@ PUGI__NS_BEGIN
}
}
- template I median3(I first, I middle, I last, const Pred& pred)
+ template inline I median3(I first, I middle, I last, const Pred& pred)
{
- if (pred(*middle, *first)) swap(middle, first);
- if (pred(*last, *middle)) swap(last, middle);
- if (pred(*middle, *first)) swap(middle, first);
+ if (pred(*middle, *first))
+ swap(middle, first);
+ if (pred(*last, *middle))
+ swap(last, middle);
+ if (pred(*middle, *first))
+ swap(middle, first);
return middle;
}
- template void partition(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
+ template PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
{
// invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
T* eq = begin;
@@ -7275,7 +7448,7 @@ PUGI__NS_BEGIN
*out_eqend = gt;
}
- template void sort(I begin, I end, const Pred& pred)
+ template PUGI__FN void sort(I begin, I end, const Pred& pred)
{
// sort large chunks
while (end - begin > 16)
@@ -7286,7 +7459,7 @@ PUGI__NS_BEGIN
// partition in three chunks (< = >)
I eqbeg, eqend;
- partition(begin, end, *median, pred, &eqbeg, &eqend);
+ partition3(begin, end, *median, pred, &eqbeg, &eqend);
// loop on larger half
if (eqbeg - begin > end - eqend)
@@ -7304,6 +7477,41 @@ PUGI__NS_BEGIN
// insertion sort small chunk
insertion_sort(begin, end, pred);
}
+
+ PUGI__FN bool hash_insert(const void** table, size_t size, const void* key)
+ {
+ assert(key);
+
+ unsigned int h = static_cast(reinterpret_cast(key));
+
+ // MurmurHash3 32-bit finalizer
+ h ^= h >> 16;
+ h *= 0x85ebca6bu;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35u;
+ h ^= h >> 16;
+
+ size_t hashmod = size - 1;
+ size_t bucket = h & hashmod;
+
+ for (size_t probe = 0; probe <= hashmod; ++probe)
+ {
+ if (table[bucket] == 0)
+ {
+ table[bucket] = key;
+ return true;
+ }
+
+ if (table[bucket] == key)
+ return false;
+
+ // hash collision, quadratic probing
+ bucket = (bucket + probe + 1) & hashmod;
+ }
+
+ assert(false && "Hash table is full"); // unreachable
+ return false;
+ }
PUGI__NS_END
// Allocator used for AST and evaluation stacks
@@ -7893,15 +8101,6 @@ PUGI__NS_BEGIN
}
};
- struct duplicate_comparator
- {
- bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
- {
- if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
- else return rhs.attribute() ? false : lhs.node() < rhs.node();
- }
- };
-
PUGI__FN double gen_nan()
{
#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
@@ -7909,7 +8108,7 @@ PUGI__NS_BEGIN
typedef uint32_t UI; // BCC5 workaround
union { float f; UI i; } u;
u.i = 0x7fc00000;
- return u.f;
+ return double(u.f);
#else
// fallback
const volatile double zero = 0.0;
@@ -7976,11 +8175,11 @@ PUGI__NS_BEGIN
// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
- PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
{
// get base values
int sign, exponent;
- _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign);
+ _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign);
// truncate redundant zeros
truncate_zeros(buffer, buffer + strlen(buffer));
@@ -7990,12 +8189,10 @@ PUGI__NS_BEGIN
*out_exponent = exponent;
}
#else
- PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
{
// get a scientific notation value with IEEE DBL_DIG decimals
- sprintf(buffer, "%.*e", DBL_DIG, value);
- assert(strlen(buffer) < buffer_size);
- (void)!buffer_size;
+ PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
// get the exponent (possibly negative)
char* exponent_string = strchr(buffer, 'e');
@@ -8032,7 +8229,7 @@ PUGI__NS_BEGIN
char* mantissa;
int exponent;
- convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent);
+ convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent);
// allocate a buffer of suitable length for the number
size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;
@@ -8054,7 +8251,7 @@ PUGI__NS_BEGIN
{
while (exponent > 0)
{
- assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9);
+ assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9);
*s++ = *mantissa ? *mantissa++ : '0';
exponent--;
}
@@ -8411,7 +8608,7 @@ PUGI__NS_BEGIN
static const xpath_node_set dummy_node_set;
- PUGI__FN unsigned int hash_string(const char_t* str)
+ PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)
{
// Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
unsigned int result = 0;
@@ -8494,7 +8691,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
}
}
@@ -8515,7 +8712,7 @@ PUGI__NS_BEGIN
return lhs->set(static_cast(rhs)->value);
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return false;
}
}
@@ -8602,7 +8799,7 @@ PUGI__NS_BEGIN
return *min_element(begin, end, document_order_comparator());
default:
- assert(false && "Invalid node set type");
+ assert(false && "Invalid node set type"); // unreachable
return xpath_node();
}
}
@@ -8691,12 +8888,42 @@ PUGI__NS_BEGIN
_end = pos;
}
- void remove_duplicates()
+ void remove_duplicates(xpath_allocator* alloc)
{
- if (_type == xpath_node_set::type_unsorted)
- sort(_begin, _end, duplicate_comparator());
+ if (_type == xpath_node_set::type_unsorted && _end - _begin > 2)
+ {
+ xpath_allocator_capture cr(alloc);
- _end = unique(_begin, _end);
+ size_t size_ = static_cast(_end - _begin);
+
+ size_t hash_size = 1;
+ while (hash_size < size_ + size_ / 2) hash_size *= 2;
+
+ const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**)));
+ if (!hash_data) return;
+
+ memset(hash_data, 0, hash_size * sizeof(const void**));
+
+ xpath_node* write = _begin;
+
+ for (xpath_node* it = _begin; it != _end; ++it)
+ {
+ const void* attr = it->attribute().internal_object();
+ const void* node = it->node().internal_object();
+ const void* key = attr ? attr : node;
+
+ if (key && hash_insert(hash_data, hash_size, key))
+ {
+ *write++ = *it;
+ }
+ }
+
+ _end = write;
+ }
+ else
+ {
+ _end = unique(_begin, _end);
+ }
}
xpath_node_set::type_t type() const
@@ -9332,7 +9559,7 @@ PUGI__NS_BEGIN
}
}
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
@@ -9407,7 +9634,7 @@ PUGI__NS_BEGIN
}
else
{
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
}
@@ -9625,7 +9852,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
}
return false;
@@ -9820,7 +10047,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -9901,7 +10128,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -9925,6 +10152,7 @@ PUGI__NS_BEGIN
bool once =
(axis == axis_attribute && _test == nodetest_name) ||
(!_right && eval_once(axis_type, eval)) ||
+ // coverity[mixed_enums]
(_right && !_right->_next && _right->_test == predicate_constant_one);
xpath_node_set_raw ns;
@@ -9957,7 +10185,7 @@ PUGI__NS_BEGIN
// child, attribute and self axes always generate unique set of nodes
// for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice
if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)
- ns.remove_duplicates();
+ ns.remove_duplicates(stack.temp);
return ns;
}
@@ -10117,10 +10345,9 @@ PUGI__NS_BEGIN
if (_rettype == xpath_type_boolean)
return _data.variable->get_boolean();
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
{
switch (_rettype)
@@ -10143,7 +10370,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type boolean");
+ assert(false && "Wrong expression for return type boolean"); // unreachable
return false;
}
}
@@ -10253,10 +10480,9 @@ PUGI__NS_BEGIN
if (_rettype == xpath_type_number)
return _data.variable->get_number();
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
{
switch (_rettype)
@@ -10279,7 +10505,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type number");
+ assert(false && "Wrong expression for return type number"); // unreachable
return 0;
}
@@ -10297,16 +10523,9 @@ PUGI__NS_BEGIN
size_t count = 1;
for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;
- // gather all strings
- xpath_string static_buffer[4];
- xpath_string* buffer = static_buffer;
-
- // allocate on-heap for large concats
- if (count > sizeof(static_buffer) / sizeof(static_buffer[0]))
- {
- buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string)));
- if (!buffer) return xpath_string();
- }
+ // allocate a buffer for temporary string objects
+ xpath_string* buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string)));
+ if (!buffer) return xpath_string();
// evaluate all strings to temporary stack
xpath_stack swapped_stack = {stack.temp, stack.result};
@@ -10545,10 +10764,9 @@ PUGI__NS_BEGIN
if (_rettype == xpath_type_string)
return xpath_string::from_const(_data.variable->get_string());
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
{
switch (_rettype)
@@ -10570,7 +10788,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type string");
+ assert(false && "Wrong expression for return type string"); // unreachable
return xpath_string();
}
}
@@ -10587,16 +10805,16 @@ PUGI__NS_BEGIN
xpath_stack swapped_stack = {stack.temp, stack.result};
- xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
- xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
+ xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval);
+ xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval);
// we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
- rs.set_type(xpath_node_set::type_unsorted);
+ ls.set_type(xpath_node_set::type_unsorted);
- rs.append(ls.begin(), ls.end(), stack.result);
- rs.remove_duplicates();
+ ls.append(rs.begin(), rs.end(), stack.result);
+ ls.remove_duplicates(stack.temp);
- return rs;
+ return ls;
}
case ast_filter:
@@ -10661,7 +10879,7 @@ PUGI__NS_BEGIN
return step_do(c, stack, eval, axis_to_type());
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
return xpath_node_set_raw();
}
}
@@ -10695,12 +10913,11 @@ PUGI__NS_BEGIN
return ns;
}
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
- assert(false && "Wrong expression for return type node set");
+ assert(false && "Wrong expression for return type node set"); // unreachable
return xpath_node_set_raw();
}
}
@@ -10716,6 +10933,7 @@ PUGI__NS_BEGIN
if (_next)
_next->optimize(alloc);
+ // coverity[var_deref_model]
optimize_self(alloc);
}
@@ -10724,13 +10942,14 @@ PUGI__NS_BEGIN
// Rewrite [position()=expr] with [expr]
// Note that this step has to go before classification to recognize [position()=1]
if ((_type == ast_filter || _type == ast_predicate) &&
+ _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate)
_right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
{
_right = _right->_right;
}
// Classify filter/predicate ops to perform various optimizations during evaluation
- if (_type == ast_filter || _type == ast_predicate)
+ if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate)
{
assert(_test == predicate_default);
@@ -10746,8 +10965,8 @@ PUGI__NS_BEGIN
// The former is a full form of //foo, the latter is much faster since it executes the node test immediately
// Do a similar kind of rewrite for self/descendant/descendant-or-self axes
// Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
- if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
- _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
+ if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) &&
+ _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
is_posinv_step())
{
if (_axis == axis_child || _axis == axis_descendant)
@@ -10759,7 +10978,9 @@ PUGI__NS_BEGIN
}
// Use optimized lookup table implementation for translate() with constant arguments
- if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
+ if (_type == ast_func_translate &&
+ _right && // workaround for clang static analyzer (_right is never null for ast_func_translate)
+ _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
{
unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
@@ -10772,6 +10993,8 @@ PUGI__NS_BEGIN
// Use optimized path for @attr = 'value' or @attr = $value
if (_type == ast_op_equal &&
+ _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal)
+ // coverity[mixed_enums]
_left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
(_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
{
@@ -11866,74 +12089,61 @@ namespace pugi
size_t size_ = static_cast(end_ - begin_);
- if (size_ <= 1)
+ // use internal buffer for 0 or 1 elements, heap buffer otherwise
+ xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
+
+ if (!storage)
{
- // deallocate old buffer
- if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
-
- // use internal buffer
- if (begin_ != end_) _storage = *begin_;
-
- _begin = &_storage;
- _end = &_storage + size_;
- _type = type_;
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return;
+ #else
+ throw std::bad_alloc();
+ #endif
}
- else
- {
- // make heap copy
- xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
- if (!storage)
- {
- #ifdef PUGIXML_NO_EXCEPTIONS
- return;
- #else
- throw std::bad_alloc();
- #endif
- }
+ // deallocate old buffer
+ if (_begin != _storage)
+ impl::xml_memory::deallocate(_begin);
+ // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB
+ if (size_)
memcpy(storage, begin_, size_ * sizeof(xpath_node));
- // deallocate old buffer
- if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
-
- // finalize
- _begin = storage;
- _end = storage + size_;
- _type = type_;
- }
+ _begin = storage;
+ _end = storage + size_;
+ _type = type_;
}
#ifdef PUGIXML_HAS_MOVE
- PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs)
+ PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT
{
_type = rhs._type;
- _storage = rhs._storage;
- _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin;
+ _storage[0] = rhs._storage[0];
+ _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;
+ rhs._begin = rhs._storage;
+ rhs._end = rhs._storage;
}
#endif
- PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+ PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _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)
+ 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_, type_);
}
PUGI__FN xpath_node_set::~xpath_node_set()
{
- if (_begin != &_storage)
+ 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)
+ 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, ns._type);
}
@@ -11948,16 +12158,16 @@ namespace pugi
}
#ifdef PUGIXML_HAS_MOVE
- PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+ PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage)
{
_move(rhs);
}
- PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs)
+ PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT
{
if (this == &rhs) return *this;
- if (_begin != &_storage)
+ if (_begin != _storage)
impl::xml_memory::deallocate(_begin);
_move(rhs);
@@ -12042,7 +12252,7 @@ namespace pugi
return static_cast(this)->name;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return 0;
}
}
@@ -12148,7 +12358,7 @@ namespace pugi
}
#ifdef PUGIXML_HAS_MOVE
- PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs)
+ PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
{
for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
{
@@ -12157,7 +12367,7 @@ namespace pugi
}
}
- PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs)
+ PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
{
for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
{
@@ -12351,7 +12561,7 @@ namespace pugi
}
#ifdef PUGIXML_HAS_MOVE
- PUGI__FN xpath_query::xpath_query(xpath_query&& rhs)
+ PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT
{
_impl = rhs._impl;
_result = rhs._result;
@@ -12359,7 +12569,7 @@ namespace pugi
rhs._result = xpath_parse_result();
}
- PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs)
+ PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT
{
if (this == &rhs) return *this;
@@ -12586,12 +12796,18 @@ namespace pugi
# pragma warning(pop)
#endif
+#if defined(_MSC_VER) && defined(__c2__)
+# pragma clang diagnostic pop
+#endif
+
// Undefine all local macros (makes sure we're not leaking macros in header-only mode)
#undef PUGI__NO_INLINE
#undef PUGI__UNLIKELY
#undef PUGI__STATIC_ASSERT
#undef PUGI__DMC_VOLATILE
+#undef PUGI__UNSIGNED_OVERFLOW
#undef PUGI__MSVC_CRT_VERSION
+#undef PUGI__SNPRINTF
#undef PUGI__NS_BEGIN
#undef PUGI__NS_END
#undef PUGI__FN
@@ -12618,7 +12834,7 @@ namespace pugi
#endif
/**
- * Copyright (c) 2006-2017 Arseny Kapoulkine
+ * Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
diff --git a/include/pugixml.hpp b/include/pugixml.hpp
index 4d76bfa2..972f8a35 100644
--- a/include/pugixml.hpp
+++ b/include/pugixml.hpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.8
+ * pugixml parser - version 1.9
* --------------------------------------------------------
- * Copyright (C) 2006-2017, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
+ * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
@@ -12,8 +12,8 @@
*/
#ifndef PUGIXML_VERSION
-// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
-# define PUGIXML_VERSION 180
+// Define version macro; evaluates to major * 100 + minor * 10 + patch so that it's safe to use in less-than comparisons
+# define PUGIXML_VERSION 190
#endif
// Include user configuration file (this can define various configuration macros)
@@ -81,10 +81,30 @@
# endif
#endif
+// If C++ is 2011 or higher, add 'noexcept' specifiers
+#ifndef PUGIXML_NOEXCEPT
+# if __cplusplus >= 201103
+# define PUGIXML_NOEXCEPT noexcept
+# elif defined(_MSC_VER) && _MSC_VER >= 1900
+# define PUGIXML_NOEXCEPT noexcept
+# else
+# define PUGIXML_NOEXCEPT
+# endif
+#endif
+
+// Some functions can not be noexcept in compact mode
+#ifdef PUGIXML_COMPACT
+# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+#else
+# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT
+#endif
+
// If C++ is 2011 or higher, add 'override' qualifiers
#ifndef PUGIXML_OVERRIDE
# if __cplusplus >= 201103
# define PUGIXML_OVERRIDE override
+# elif defined(_MSC_VER) && _MSC_VER >= 1700
+# define PUGIXML_OVERRIDE override
# else
# define PUGIXML_OVERRIDE
# endif
@@ -232,6 +252,12 @@ namespace pugi
// Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default.
const unsigned int format_no_empty_element_tags = 0x80;
+ // Skip characters belonging to range [0; 32) instead of "NN;" encoding. This flag is off by default.
+ const unsigned int format_skip_control_chars = 0x100;
+
+ // Use single quotes ' instead of double quotes " for enclosing attribute values. This flag is off by default.
+ const unsigned int format_attribute_single_quote = 0x200;
+
// 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;
@@ -631,8 +657,8 @@ namespace pugi
xpath_node_set select_nodes(const xpath_query& query) const;
// (deprecated: use select_node instead) Select single node by evaluating XPath query.
- xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
- xpath_node select_single_node(const xpath_query& query) const;
+ PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+ PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const;
#endif
@@ -983,6 +1009,7 @@ namespace pugi
void _create();
void _destroy();
+ void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
public:
// Default constructor, makes empty document
@@ -991,6 +1018,12 @@ namespace pugi
// Destructor, invalidates all node/attribute handles to this document
~xml_document();
+ #ifdef PUGIXML_HAS_MOVE
+ // Move semantics support
+ xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
+ xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
+ #endif
+
// Removes all nodes, leaving the empty document
void reset();
@@ -1004,7 +1037,7 @@ namespace pugi
#endif
// (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
- xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
+ PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
// Load document from zero-terminated string. No encoding conversions are applied.
xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
@@ -1131,8 +1164,8 @@ namespace pugi
#ifdef PUGIXML_HAS_MOVE
// Move semantics support
- xpath_variable_set(xpath_variable_set&& rhs);
- xpath_variable_set& operator=(xpath_variable_set&& rhs);
+ xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;
+ xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;
#endif
// Add a new variable or get the existing one, if the types match
@@ -1175,8 +1208,8 @@ namespace pugi
#ifdef PUGIXML_HAS_MOVE
// Move semantics support
- xpath_query(xpath_query&& rhs);
- xpath_query& operator=(xpath_query&& rhs);
+ xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT;
+ xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT;
#endif
// Get query expression return type
@@ -1224,6 +1257,12 @@ namespace pugi
};
#ifndef PUGIXML_NO_EXCEPTIONS
+ #if defined(_MSC_VER)
+ // C4275 can be ignored in Visual C++ if you are deriving
+ // from a type in the Standard C++ Library
+ #pragma warning(push)
+ #pragma warning(disable: 4275)
+ #endif
// XPath exception class
class PUGIXML_CLASS xpath_exception: public std::exception
{
@@ -1240,6 +1279,9 @@ namespace pugi
// Get parse result
const xpath_parse_result& result() const;
};
+ #if defined(_MSC_VER)
+ #pragma warning(pop)
+ #endif
#endif
// XPath node class (either xml_node or xml_attribute)
@@ -1316,8 +1358,8 @@ namespace pugi
#ifdef PUGIXML_HAS_MOVE
// Move semantics support
- xpath_node_set(xpath_node_set&& rhs);
- xpath_node_set& operator=(xpath_node_set&& rhs);
+ xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;
+ xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;
#endif
// Get collection type
@@ -1345,13 +1387,13 @@ namespace pugi
private:
type_t _type;
- xpath_node _storage;
+ xpath_node _storage[1];
xpath_node* _begin;
xpath_node* _end;
void _assign(const_iterator begin, const_iterator end, type_t type);
- void _move(xpath_node_set& rhs);
+ void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT;
};
#endif
@@ -1409,7 +1451,7 @@ namespace std
#endif
/**
- * Copyright (c) 2006-2017 Arseny Kapoulkine
+ * Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation