summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/pugixml/src/pugixml.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/pugixml/src/pugixml.cpp')
-rw-r--r--3rdparty/pugixml/src/pugixml.cpp1536
1 files changed, 897 insertions, 639 deletions
diff --git a/3rdparty/pugixml/src/pugixml.cpp b/3rdparty/pugixml/src/pugixml.cpp
index d2f0d6b9a2b..90c48b21594 100644
--- a/3rdparty/pugixml/src/pugixml.cpp
+++ b/3rdparty/pugixml/src/pugixml.cpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.7
+ * pugixml parser - version 1.10
* --------------------------------------------------------
- * Copyright (C) 2006-2016, 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.
@@ -29,9 +29,6 @@
#ifndef PUGIXML_NO_XPATH
# include <math.h>
# include <float.h>
-# ifdef PUGIXML_NO_EXCEPTIONS
-# include <setjmp.h>
-# endif
#endif
#ifndef PUGIXML_NO_STL
@@ -47,10 +44,13 @@
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4324) // structure was padded due to __declspec(align())
-# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // this function or variable may be unsafe
-# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
+#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
@@ -76,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)
@@ -86,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)
@@ -102,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;
@@ -109,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
@@ -121,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 } }
@@ -279,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;
-
- for (size_t probe = 0; probe <= hashmod; ++probe)
- {
- item_t& probe_item = _items[bucket];
-
- if (probe_item.key == key)
- return &probe_item.value;
+ item_t* item = get_item(key);
+ assert(item);
+ assert(item->key == key || (item->key == 0 && item->value == 0));
- 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;
}
@@ -356,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<unsigned int>(reinterpret_cast<uintptr_t>(key));
@@ -373,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<item_t*>(xml_memory::allocate(sizeof(item_t) * rt._capacity));
+ rt._capacity = capacity;
+ rt._items = static_cast<item_t*>(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);
@@ -424,7 +446,8 @@ PUGI__NS_BEGIN
#define PUGI__GETPAGE_IMPL(header) (header).get_page()
#else
#define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))
- #define PUGI__GETPAGE_IMPL(header) const_cast<impl::xml_memory_page*>(reinterpret_cast<const impl::xml_memory_page*>(reinterpret_cast<const char*>(&header) - (header >> 8)))
+ // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
+ #define PUGI__GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))
#endif
#define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
@@ -777,12 +800,12 @@ PUGI__NS_BEGIN
template <int header_offset, typename T> PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
{
- return static_cast<T*>(*compact_get_page(object, header_offset)->allocator->_hash->find(object));
+ return static_cast<T*>(compact_get_page(object, header_offset)->allocator->_hash->find(object));
}
template <int header_offset, typename T> 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 <typename T, int header_offset, int start = -126> class compact_pointer
@@ -829,7 +852,7 @@ PUGI__NS_BEGIN
{
uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
- return reinterpret_cast<T*>(base + ((_data - 1 + start) << compact_alignment_log2));
+ return reinterpret_cast<T*>(base + (_data - 1 + start) * compact_alignment);
}
else
return compact_get_value<header_offset, T>(this);
@@ -907,7 +930,7 @@ PUGI__NS_BEGIN
{
uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
- return reinterpret_cast<T*>(base + ((_data - 1 - 65533) << compact_alignment_log2));
+ return reinterpret_cast<T*>(base + (_data - 1 - 65533) * compact_alignment);
}
else if (_data == 65534)
return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
@@ -1119,9 +1142,6 @@ PUGI__NS_BEGIN
{
xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)
{
- #ifdef PUGIXML_COMPACT
- _hash = &hash;
- #endif
}
const char_t* buffer;
@@ -1841,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, _, -, .
@@ -1849,10 +1869,10 @@ 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
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63
+ 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, 1, 0, // 48-63
0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95
@@ -2147,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
@@ -2252,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
@@ -2465,7 +2485,7 @@ PUGI__NS_BEGIN
for (;;)
{
- if (static_cast<unsigned int>(static_cast<unsigned int>(ch) - '0') <= 9)
+ if (static_cast<unsigned int>(ch - '0') <= 9)
ucsc = 10 * ucsc + (ch - '0');
else if (ch == ';')
break;
@@ -2567,7 +2587,7 @@ PUGI__NS_BEGIN
#define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
#define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
#define PUGI__OPTSET(OPT) ( optmsk & (OPT) )
- #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
+ #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
#define PUGI__POPNODE() { cursor = cursor->parent; }
#define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }
#define PUGI__SCANWHILE(X) { while (X) ++s; }
@@ -2689,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<opt_false, opt_false, opt_false>::parse;
case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;
@@ -2699,7 +2719,7 @@ PUGI__NS_BEGIN
case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;
case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;
case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -2858,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<opt_false>::parse_simple;
case 1: return strconv_attribute_impl<opt_true>::parse_simple;
@@ -2876,7 +2896,7 @@ PUGI__NS_BEGIN
case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;
case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -2891,20 +2911,14 @@ PUGI__NS_BEGIN
struct xml_parser
{
- xml_allocator alloc;
- xml_allocator* alloc_state;
+ xml_allocator* alloc;
char_t* error_offset;
xml_parse_status error_status;
- xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok)
+ xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
{
}
- ~xml_parser()
- {
- *alloc_state = alloc;
- }
-
// DOCTYPE consists of nested sections of the following possible types:
// <!-- ... -->, <? ... ?>, "...", '...'
// <![...]]>
@@ -3263,7 +3277,7 @@ PUGI__NS_BEGIN
if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
{
- xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute.
+ xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.
if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
a->name = s; // Save the offset.
@@ -3355,18 +3369,20 @@ PUGI__NS_BEGIN
{
++s;
+ mark = s;
+
char_t* name = cursor->name;
- if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
while (PUGI__IS_CHARTYPE(*s, ct_symbol))
{
- if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
}
if (*name)
{
if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
- else PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ else PUGI__THROW_ERROR(status_end_element_mismatch, mark);
}
PUGI__POPNODE(); // Pop.
@@ -3629,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
@@ -3668,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
@@ -3887,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)
{
@@ -3914,7 +3930,17 @@ PUGI__NS_BEGIN
++s;
break;
case '"':
- writer.write('&', 'q', 'u', 'o', 't', ';');
+ if (flags & format_attribute_single_quote)
+ writer.write('"');
+ else
+ writer.write('&', 'q', 'u', 'o', 't', ';');
+ ++s;
+ break;
+ case '\'':
+ if (flags & format_attribute_single_quote)
+ writer.write('&', 'a', 'p', 'o', 's', ';');
+ else
+ writer.write('\'');
++s;
break;
default: // s is not a usual symbol
@@ -3922,7 +3948,8 @@ PUGI__NS_BEGIN
unsigned int ch = static_cast<unsigned int>(*s++);
assert(ch < 32);
- writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
+ if (!(flags & format_skip_control_chars))
+ writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
}
}
}
@@ -3933,7 +3960,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)
@@ -4047,6 +4074,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)
{
@@ -4062,12 +4090,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);
}
}
@@ -4087,12 +4115,23 @@ PUGI__NS_BEGIN
{
if (!node->first_child)
{
- if ((flags & format_raw) == 0)
- writer.write(' ');
+ if (flags & format_no_empty_element_tags)
+ {
+ writer.write('>', '<', '/');
+ writer.write_string(name);
+ writer.write('>');
- writer.write('/', '>');
+ return false;
+ }
+ else
+ {
+ if ((flags & format_raw) == 0)
+ writer.write(' ');
- return false;
+ writer.write('/', '>');
+
+ return false;
+ }
}
else
{
@@ -4184,7 +4223,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
}
}
@@ -4396,6 +4435,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 +4486,7 @@ PUGI__NS_BEGIN
}
// get value with conversion functions
- template <typename U> U string_to_integer(const char_t* value, U minneg, U maxpos)
+ template <typename U> 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;
@@ -4516,14 +4556,21 @@ PUGI__NS_BEGIN
}
if (negative)
- return (overflow || result > minneg) ? 0 - minneg : 0 - result;
+ {
+ // Workaround for crayc++ CC-3059: Expected no overflow in routine.
+ #ifdef _CRAYC
+ return (overflow || result > ~minv + 1) ? minv : ~result + 1;
+ #else
+ 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<unsigned int>(value, 0 - static_cast<unsigned int>(INT_MIN), INT_MAX);
+ return string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX);
}
PUGI__FN unsigned int get_value_uint(const char_t* value)
@@ -4561,7 +4608,7 @@ PUGI__NS_BEGIN
#ifdef PUGIXML_HAS_LONG_LONG
PUGI__FN long long get_value_llong(const char_t* value)
{
- return string_to_integer<unsigned long long>(value, 0 - static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
+ return string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
}
PUGI__FN unsigned long long get_value_ullong(const char_t* value)
@@ -4570,27 +4617,10 @@ PUGI__NS_BEGIN
}
#endif
- template <typename T> struct make_unsigned;
-
- template <> struct make_unsigned<int> { typedef unsigned int type; };
- template <> struct make_unsigned<unsigned int> { typedef unsigned int type; };
- template <> struct make_unsigned<long> { typedef unsigned long type; };
- template <> struct make_unsigned<unsigned long> { typedef unsigned long type; };
-
-#ifdef PUGIXML_HAS_LONG_LONG
- template <> struct make_unsigned<long long> { typedef unsigned long long type; };
- template <> struct make_unsigned<unsigned long long> { typedef unsigned long long type; };
-#endif
-
- template <typename T>
- PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, T value)
+ template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
{
- typedef typename make_unsigned<T>::type U;
-
- bool negative = value < 0;
-
char_t* result = end - 1;
- U rest = negative ? 0 - U(value) : U(value);
+ U rest = negative ? 0 - value : value;
do
{
@@ -4624,12 +4654,12 @@ PUGI__NS_BEGIN
#endif
}
- template <typename String, typename Header, typename Integer>
- PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, Integer value)
+ template <typename U, typename String, typename Header>
+ PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
{
char_t buf[64];
char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
- char_t* begin = integer_to_string(buf, end, value);
+ char_t* begin = integer_to_string(buf, end, value, negative);
return strcpy_insitu(dest, header, header_mask, begin, end - begin);
}
@@ -4638,7 +4668,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);
}
@@ -4647,13 +4677,13 @@ 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);
}
template <typename String, typename Header>
- PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, bool value)
+ PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)
{
return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
}
@@ -4670,6 +4700,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
@@ -5283,28 +5314,28 @@ namespace pugi
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
}
PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
}
PUGI__FN bool xml_attribute::set_value(long rhs)
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
}
PUGI__FN bool xml_attribute::set_value(unsigned long rhs)
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
}
PUGI__FN bool xml_attribute::set_value(double rhs)
@@ -5325,7 +5356,7 @@ namespace pugi
{
if (!_attr) return false;
- return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
}
#ifdef PUGIXML_HAS_LONG_LONG
@@ -5333,14 +5364,14 @@ namespace pugi
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
}
PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
{
if (!_attr) return false;
- return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
}
#endif
@@ -6059,11 +6090,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<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));
+ impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(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<impl::xml_extra_buffer*>((reinterpret_cast<uintptr_t>(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1));
+ #endif
+
// add extra buffer to the list
extra->buffer = 0;
extra->next = doc->extra_buffers;
@@ -6123,7 +6160,7 @@ namespace pugi
if (j != _root)
result[--offset] = delimiter;
- if (j->name && *j->name)
+ if (j->name)
{
size_t length = impl::strlength(j->name);
@@ -6142,7 +6179,7 @@ namespace pugi
{
xml_node found = *this; // Current search context.
- if (!_root || !path_ || !path_[0]) return found;
+ if (!_root || !path_[0]) return found;
if (path_[0] == delimiter)
{
@@ -6189,10 +6226,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)
{
@@ -6200,36 +6237,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);
}
@@ -6296,6 +6332,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"); // unreachable
return -1;
}
}
@@ -6438,28 +6475,28 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
}
PUGI__FN bool xml_text::set(unsigned int rhs)
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
}
PUGI__FN bool xml_text::set(long rhs)
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
}
PUGI__FN bool xml_text::set(unsigned long rhs)
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
}
PUGI__FN bool xml_text::set(float rhs)
@@ -6480,7 +6517,7 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
}
#ifdef PUGIXML_HAS_LONG_LONG
@@ -6488,14 +6525,14 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
}
PUGI__FN bool xml_text::set(unsigned long long rhs)
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
}
#endif
@@ -6812,18 +6849,37 @@ namespace pugi
PUGI__FN xml_document::xml_document(): _buffer(0)
{
- create();
+ _create();
}
PUGI__FN xml_document::~xml_document()
{
- destroy();
+ _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();
- create();
+ _destroy();
+ _create();
}
PUGI__FN void xml_document::reset(const xml_document& proto)
@@ -6834,12 +6890,13 @@ namespace pugi
append_copy(cur);
}
- PUGI__FN void xml_document::create()
+ PUGI__FN void xml_document::_create()
{
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
@@ -6867,11 +6924,16 @@ namespace pugi
// setup sentinel page
page->allocator = static_cast<impl::xml_document_struct*>(_root);
+ // setup hash table pointer in allocator
+ #ifdef PUGIXML_COMPACT
+ page->allocator->_hash = &static_cast<impl::xml_document_struct*>(_root)->hash;
+ #endif
+
// verify the document allocation
assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
}
- PUGI__FN void xml_document::destroy()
+ PUGI__FN void xml_document::_destroy()
{
assert(_root);
@@ -6910,6 +6972,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<impl::xml_document_struct*>(_root);
+ impl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(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<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
{
@@ -7175,14 +7344,14 @@ PUGI__NS_BEGIN
}
};
- template <typename T> void swap(T& lhs, T& rhs)
+ template <typename T> inline void swap(T& lhs, T& rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
- template <typename I, typename Pred> I min_element(I begin, I end, const Pred& pred)
+ template <typename I, typename Pred> PUGI__FN I min_element(I begin, I end, const Pred& pred)
{
I result = begin;
@@ -7193,17 +7362,20 @@ PUGI__NS_BEGIN
return result;
}
- template <typename I> void reverse(I begin, I end)
+ template <typename I> PUGI__FN void reverse(I begin, I end)
{
- while (end - begin > 1) swap(*begin++, *--end);
+ while (end - begin > 1)
+ swap(*begin++, *--end);
}
- template <typename I> I unique(I begin, I end)
+ template <typename I> 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++;
@@ -7221,134 +7393,79 @@ PUGI__NS_BEGIN
return write + 1;
}
- template <typename I> void copy_backwards(I begin, I end, I target)
- {
- while (begin != end) *--target = *--end;
- }
-
- template <typename I, typename Pred, typename T> void insertion_sort(I begin, I end, const Pred& pred, T*)
+ template <typename T, typename Pred> PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred)
{
- assert(begin != end);
+ if (begin == end)
+ return;
- for (I it = begin + 1; it != end; ++it)
+ for (T* it = begin + 1; it != end; ++it)
{
T val = *it;
+ T* hole = it;
- if (pred(val, *begin))
+ // move hole backwards
+ while (hole > begin && pred(val, *(hole - 1)))
{
- // move to front
- copy_backwards(begin, it, it + 1);
- *begin = val;
+ *hole = *(hole - 1);
+ hole--;
}
- else
- {
- I hole = it;
-
- // move hole backwards
- while (pred(val, *(hole - 1)))
- {
- *hole = *(hole - 1);
- hole--;
- }
- // fill hole with element
- *hole = val;
- }
+ // fill hole with element
+ *hole = val;
}
}
- // std variant for elements with ==
- template <typename I, typename Pred> void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend)
+ template <typename I, typename Pred> inline I median3(I first, I middle, I last, const Pred& pred)
{
- I eqbeg = middle, eqend = middle + 1;
-
- // expand equal range
- while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg;
- while (eqend != end && *eqend == *eqbeg) ++eqend;
-
- // process outer elements
- I ltend = eqbeg, gtbeg = eqend;
-
- for (;;)
- {
- // find the element from the right side that belongs to the left one
- for (; gtbeg != end; ++gtbeg)
- if (!pred(*eqbeg, *gtbeg))
- {
- if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++);
- else break;
- }
+ if (pred(*middle, *first))
+ swap(middle, first);
+ if (pred(*last, *middle))
+ swap(last, middle);
+ if (pred(*middle, *first))
+ swap(middle, first);
- // find the element from the left side that belongs to the right one
- for (; ltend != begin; --ltend)
- if (!pred(*(ltend - 1), *eqbeg))
- {
- if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg);
- else break;
- }
-
- // scanned all elements
- if (gtbeg == end && ltend == begin)
- {
- *out_eqbeg = eqbeg;
- *out_eqend = eqend;
- return;
- }
-
- // make room for elements by moving equal area
- if (gtbeg == end)
- {
- if (--ltend != --eqbeg) swap(*ltend, *eqbeg);
- swap(*eqbeg, *--eqend);
- }
- else if (ltend == begin)
- {
- if (eqend != gtbeg) swap(*eqbeg, *eqend);
- ++eqend;
- swap(*gtbeg++, *eqbeg++);
- }
- else swap(*gtbeg++, *--ltend);
- }
+ return middle;
}
- template <typename I, typename Pred> void median3(I first, I middle, I last, const Pred& pred)
+ template <typename T, typename Pred> PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
{
- if (pred(*middle, *first)) swap(*middle, *first);
- if (pred(*last, *middle)) swap(*last, *middle);
- if (pred(*middle, *first)) swap(*middle, *first);
- }
+ // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
+ T* eq = begin;
+ T* lt = begin;
+ T* gt = end;
- template <typename I, typename Pred> void median(I first, I middle, I last, const Pred& pred)
- {
- if (last - first <= 40)
+ while (lt < gt)
{
- // median of three for small chunks
- median3(first, middle, last, pred);
+ if (pred(*lt, pivot))
+ lt++;
+ else if (*lt == pivot)
+ swap(*eq++, *lt++);
+ else
+ swap(*lt, *--gt);
}
- else
- {
- // median of nine
- size_t step = (last - first + 1) / 8;
- median3(first, first + step, first + 2 * step, pred);
- median3(middle - step, middle, middle + step, pred);
- median3(last - 2 * step, last - step, last, pred);
- median3(first + step, middle, last - step, pred);
- }
+ // we now have just 4 groups: = < >; move equal elements to the middle
+ T* eqbeg = gt;
+
+ for (T* it = begin; it != eq; ++it)
+ swap(*it, *--eqbeg);
+
+ *out_eqbeg = eqbeg;
+ *out_eqend = gt;
}
- template <typename I, typename Pred> void sort(I begin, I end, const Pred& pred)
+ template <typename I, typename Pred> PUGI__FN void sort(I begin, I end, const Pred& pred)
{
// sort large chunks
- while (end - begin > 32)
+ while (end - begin > 16)
{
// find median element
I middle = begin + (end - begin) / 2;
- median(begin, middle, end - 1, pred);
+ I median = median3(begin, middle, end - 1, pred);
// partition in three chunks (< = >)
I eqbeg, eqend;
- partition(begin, middle, end, pred, &eqbeg, &eqend);
+ partition3(begin, end, *median, pred, &eqbeg, &eqend);
// loop on larger half
if (eqbeg - begin > end - eqend)
@@ -7364,7 +7481,42 @@ PUGI__NS_BEGIN
}
// insertion sort small chunk
- if (begin != end) insertion_sort(begin, end, pred, &*begin);
+ 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<unsigned int>(reinterpret_cast<uintptr_t>(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
@@ -7392,24 +7544,17 @@ PUGI__NS_BEGIN
};
};
- class xpath_allocator
+ struct xpath_allocator
{
xpath_memory_block* _root;
size_t _root_size;
+ bool* _error;
- public:
- #ifdef PUGIXML_NO_EXCEPTIONS
- jmp_buf* error_handler;
- #endif
-
- xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size)
+ xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error)
{
- #ifdef PUGIXML_NO_EXCEPTIONS
- error_handler = 0;
- #endif
}
- void* allocate_nothrow(size_t size)
+ void* allocate(size_t size)
{
// round size up to block alignment boundary
size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
@@ -7430,7 +7575,11 @@ PUGI__NS_BEGIN
size_t block_size = block_capacity + offsetof(xpath_memory_block, data);
xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
- if (!block) return 0;
+ if (!block)
+ {
+ if (_error) *_error = true;
+ return 0;
+ }
block->next = _root;
block->capacity = block_capacity;
@@ -7442,23 +7591,6 @@ PUGI__NS_BEGIN
}
}
- void* allocate(size_t size)
- {
- void* result = allocate_nothrow(size);
-
- if (!result)
- {
- #ifdef PUGIXML_NO_EXCEPTIONS
- assert(error_handler);
- longjmp(*error_handler, 1);
- #else
- throw std::bad_alloc();
- #endif
- }
-
- return result;
- }
-
void* reallocate(void* ptr, size_t old_size, size_t new_size)
{
// round size up to block alignment boundary
@@ -7468,33 +7600,35 @@ PUGI__NS_BEGIN
// we can only reallocate the last object
assert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size);
- // adjust root size so that we have not allocated the object at all
- bool only_object = (_root_size == old_size);
-
- if (ptr) _root_size -= old_size;
+ // try to reallocate the object inplace
+ if (ptr && _root_size - old_size + new_size <= _root->capacity)
+ {
+ _root_size = _root_size - old_size + new_size;
+ return ptr;
+ }
- // allocate a new version (this will obviously reuse the memory if possible)
+ // allocate a new block
void* result = allocate(new_size);
- assert(result);
+ if (!result) return 0;
// we have a new block
- if (result != ptr && ptr)
+ if (ptr)
{
- // copy old data
+ // copy old data (we only support growing)
assert(new_size >= old_size);
memcpy(result, ptr, old_size);
// free the previous page if it had no other objects
- if (only_object)
- {
- assert(_root->data == result);
- assert(_root->next);
+ assert(_root->data == result);
+ assert(_root->next);
+ if (_root->next->data == ptr)
+ {
+ // deallocate the whole page, unless it was the first one
xpath_memory_block* next = _root->next->next;
if (next)
{
- // deallocate the whole page, unless it was the first one
xml_memory::deallocate(_root->next);
_root->next = next;
}
@@ -7566,22 +7700,15 @@ PUGI__NS_BEGIN
xpath_allocator result;
xpath_allocator temp;
xpath_stack stack;
+ bool oom;
- #ifdef PUGIXML_NO_EXCEPTIONS
- jmp_buf error_handler;
- #endif
-
- xpath_stack_data(): result(blocks + 0), temp(blocks + 1)
+ xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false)
{
blocks[0].next = blocks[1].next = 0;
blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);
stack.result = &result;
stack.temp = &temp;
-
- #ifdef PUGIXML_NO_EXCEPTIONS
- result.error_handler = temp.error_handler = &error_handler;
- #endif
}
~xpath_stack_data()
@@ -7603,7 +7730,7 @@ PUGI__NS_BEGIN
static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
{
char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));
- assert(result);
+ if (!result) return 0;
memcpy(result, string, length * sizeof(char_t));
result[length] = 0;
@@ -7632,9 +7759,13 @@ PUGI__NS_BEGIN
{
assert(begin <= end);
+ if (begin == end)
+ return xpath_string();
+
size_t length = static_cast<size_t>(end - begin);
+ const char_t* data = duplicate_string(begin, length, alloc);
- return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length);
+ return data ? xpath_string(data, true, length) : xpath_string();
}
xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0)
@@ -7660,7 +7791,7 @@ PUGI__NS_BEGIN
// allocate new buffer
char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));
- assert(result);
+ if (!result) return;
// append first string to the new buffer in case there was no reallocation
if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));
@@ -7692,8 +7823,11 @@ PUGI__NS_BEGIN
if (!_uses_heap)
{
size_t length_ = strlength(_buffer);
+ const char_t* data_ = duplicate_string(_buffer, length_, alloc);
+
+ if (!data_) return 0;
- _buffer = duplicate_string(_buffer, length_, alloc);
+ _buffer = data_;
_uses_heap = true;
_length_heap = length_;
}
@@ -7973,15 +8107,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))
@@ -7989,7 +8114,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;
@@ -8056,11 +8181,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));
@@ -8070,12 +8195,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');
@@ -8112,12 +8235,12 @@ 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;
char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));
- assert(result);
+ if (!result) return xpath_string();
// make the number!
char_t* s = result;
@@ -8134,7 +8257,7 @@ PUGI__NS_BEGIN
{
while (exponent > 0)
{
- assert(*mantissa == 0 || static_cast<unsigned int>(static_cast<unsigned int>(*mantissa) - '0') <= 9);
+ assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
*s++ = *mantissa ? *mantissa++ : '0';
exponent--;
}
@@ -8401,12 +8524,10 @@ PUGI__NS_BEGIN
if (!table[i])
table[i] = static_cast<unsigned char>(i);
- void* result = alloc->allocate_nothrow(sizeof(table));
+ void* result = alloc->allocate(sizeof(table));
+ if (!result) return 0;
- if (result)
- {
- memcpy(result, table, sizeof(table));
- }
+ memcpy(result, table, sizeof(table));
return static_cast<unsigned char*>(result);
}
@@ -8493,7 +8614,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;
@@ -8576,7 +8697,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
}
}
@@ -8597,7 +8718,7 @@ PUGI__NS_BEGIN
return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return false;
}
}
@@ -8684,7 +8805,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();
}
}
@@ -8749,7 +8870,7 @@ PUGI__NS_BEGIN
{
// reallocate the old array or allocate a new one
xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));
- assert(data);
+ if (!data) return;
// finalize
_begin = data;
@@ -8773,12 +8894,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);
+
+ size_t size_ = static_cast<size_t>(_end - _begin);
+
+ size_t hash_size = 1;
+ while (hash_size < size_ + size_ / 2) hash_size *= 2;
+
+ const void** hash_data = static_cast<const void**>(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 = unique(_begin, _end);
+ _end = write;
+ }
+ else
+ {
+ _end = unique(_begin, _end);
+ }
}
xpath_node_set::type_t type() const
@@ -8801,7 +8952,7 @@ PUGI__NS_BEGIN
// reallocate the old array or allocate a new one
xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
- assert(data);
+ if (!data) return;
// finalize
_begin = data;
@@ -9414,7 +9565,7 @@ PUGI__NS_BEGIN
}
}
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
@@ -9489,7 +9640,7 @@ PUGI__NS_BEGIN
}
else
{
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
}
@@ -9707,7 +9858,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
}
return false;
@@ -9902,7 +10053,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -9983,7 +10134,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -10007,6 +10158,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;
@@ -10039,7 +10191,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;
}
@@ -10199,10 +10351,9 @@ PUGI__NS_BEGIN
if (_rettype == xpath_type_boolean)
return _data.variable->get_boolean();
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
{
switch (_rettype)
@@ -10225,7 +10376,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type boolean");
+ assert(false && "Wrong expression for return type boolean"); // unreachable
return false;
}
}
@@ -10335,10 +10486,9 @@ PUGI__NS_BEGIN
if (_rettype == xpath_type_number)
return _data.variable->get_number();
-
- // fallthrough to type conversion
}
+ // fallthrough
default:
{
switch (_rettype)
@@ -10361,7 +10511,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type number");
+ assert(false && "Wrong expression for return type number"); // unreachable
return 0;
}
@@ -10379,16 +10529,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<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));
- assert(buffer);
- }
+ // allocate a buffer for temporary string objects
+ xpath_string* buffer = static_cast<xpath_string*>(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};
@@ -10405,7 +10548,7 @@ PUGI__NS_BEGIN
// create final string
char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));
- assert(result);
+ if (!result) return xpath_string();
char_t* ri = result;
@@ -10572,6 +10715,8 @@ PUGI__NS_BEGIN
xpath_string s = string_value(c.n, stack.result);
char_t* begin = s.data(stack.result);
+ if (!begin) return xpath_string();
+
char_t* end = normalize_space(begin);
return xpath_string::from_heap_preallocated(begin, end);
@@ -10582,6 +10727,8 @@ PUGI__NS_BEGIN
xpath_string s = _left->eval_string(c, stack);
char_t* begin = s.data(stack.result);
+ if (!begin) return xpath_string();
+
char_t* end = normalize_space(begin);
return xpath_string::from_heap_preallocated(begin, end);
@@ -10598,6 +10745,8 @@ PUGI__NS_BEGIN
xpath_string to = _right->_next->eval_string(c, swapped_stack);
char_t* begin = s.data(stack.result);
+ if (!begin) return xpath_string();
+
char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
return xpath_string::from_heap_preallocated(begin, end);
@@ -10608,6 +10757,8 @@ PUGI__NS_BEGIN
xpath_string s = _left->eval_string(c, stack);
char_t* begin = s.data(stack.result);
+ if (!begin) return xpath_string();
+
char_t* end = translate_table(begin, _data.table);
return xpath_string::from_heap_preallocated(begin, end);
@@ -10619,10 +10770,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)
@@ -10644,7 +10794,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();
}
}
@@ -10661,16 +10811,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:
@@ -10735,7 +10885,7 @@ PUGI__NS_BEGIN
return step_do(c, stack, eval, axis_to_type<axis_self>());
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
return xpath_node_set_raw();
}
}
@@ -10769,22 +10919,27 @@ 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();
}
}
void optimize(xpath_allocator* alloc)
{
- if (_left) _left->optimize(alloc);
- if (_right) _right->optimize(alloc);
- if (_next) _next->optimize(alloc);
+ if (_left)
+ _left->optimize(alloc);
+
+ if (_right)
+ _right->optimize(alloc);
+
+ if (_next)
+ _next->optimize(alloc);
+ // coverity[var_deref_model]
optimize_self(alloc);
}
@@ -10793,13 +10948,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);
@@ -10815,8 +10971,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)
@@ -10828,7 +10984,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);
@@ -10841,6 +10999,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)))
{
@@ -10912,65 +11072,77 @@ PUGI__NS_BEGIN
char_t _scratch[32];
- #ifdef PUGIXML_NO_EXCEPTIONS
- jmp_buf _error_handler;
- #endif
-
- void throw_error(const char* message)
+ xpath_ast_node* error(const char* message)
{
_result->error = message;
_result->offset = _lexer.current_pos() - _query;
- #ifdef PUGIXML_NO_EXCEPTIONS
- longjmp(_error_handler, 1);
- #else
- throw xpath_exception(*_result);
- #endif
+ return 0;
}
- void throw_error_oom()
+ xpath_ast_node* error_oom()
{
- #ifdef PUGIXML_NO_EXCEPTIONS
- throw_error("Out of memory");
- #else
- throw std::bad_alloc();
- #endif
+ assert(_alloc->_error);
+ *_alloc->_error = true;
+
+ return 0;
}
void* alloc_node()
{
- void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node));
+ return _alloc->allocate(sizeof(xpath_ast_node));
+ }
- if (!result) throw_error_oom();
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value)
+ {
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
+ }
- return result;
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value)
+ {
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
}
- const char_t* alloc_string(const xpath_lexer_string& value)
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value)
{
- if (value.begin)
- {
- size_t length = static_cast<size_t>(value.end - value.begin);
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
+ }
- char_t* c = static_cast<char_t*>(_alloc->allocate_nothrow((length + 1) * sizeof(char_t)));
- if (!c) throw_error_oom();
- assert(c); // workaround for clang static analysis
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0)
+ {
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0;
+ }
- memcpy(c, value.begin, length * sizeof(char_t));
- c[length] = 0;
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents)
+ {
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0;
+ }
- return c;
- }
- else return 0;
+ xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test)
+ {
+ void* memory = alloc_node();
+ return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0;
}
- xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2])
+ const char_t* alloc_string(const xpath_lexer_string& value)
{
- assert(argc <= 1);
+ if (!value.begin)
+ return PUGIXML_TEXT("");
+
+ size_t length = static_cast<size_t>(value.end - value.begin);
+
+ char_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t)));
+ if (!c) return 0;
- if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
+ memcpy(c, value.begin, length * sizeof(char_t));
+ c[length] = 0;
- return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]);
+ return c;
}
xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2])
@@ -10979,101 +11151,110 @@ PUGI__NS_BEGIN
{
case 'b':
if (name == PUGIXML_TEXT("boolean") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]);
+ return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]);
break;
case 'c':
if (name == PUGIXML_TEXT("count") && argc == 1)
{
- if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
- return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]);
+ if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+ return alloc_node(ast_func_count, xpath_type_number, args[0]);
}
else if (name == PUGIXML_TEXT("contains") && argc == 2)
- return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);
+ return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);
else if (name == PUGIXML_TEXT("concat") && argc >= 2)
- return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]);
+ return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("ceiling") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]);
+ return alloc_node(ast_func_ceiling, xpath_type_number, args[0]);
break;
case 'f':
if (name == PUGIXML_TEXT("false") && argc == 0)
- return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean);
+ return alloc_node(ast_func_false, xpath_type_boolean);
else if (name == PUGIXML_TEXT("floor") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]);
+ return alloc_node(ast_func_floor, xpath_type_number, args[0]);
break;
case 'i':
if (name == PUGIXML_TEXT("id") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]);
+ return alloc_node(ast_func_id, xpath_type_node_set, args[0]);
break;
case 'l':
if (name == PUGIXML_TEXT("last") && argc == 0)
- return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number);
+ return alloc_node(ast_func_last, xpath_type_number);
else if (name == PUGIXML_TEXT("lang") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]);
+ return alloc_node(ast_func_lang, xpath_type_boolean, args[0]);
else if (name == PUGIXML_TEXT("local-name") && argc <= 1)
- return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args);
+ {
+ if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+ return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]);
+ }
break;
case 'n':
if (name == PUGIXML_TEXT("name") && argc <= 1)
- return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args);
+ {
+ if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+ return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]);
+ }
else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1)
- return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args);
+ {
+ if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+ return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]);
+ }
else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1)
- return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);
+ return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("not") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]);
+ return alloc_node(ast_func_not, xpath_type_boolean, args[0]);
else if (name == PUGIXML_TEXT("number") && argc <= 1)
- return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);
+ return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);
break;
case 'p':
if (name == PUGIXML_TEXT("position") && argc == 0)
- return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number);
+ return alloc_node(ast_func_position, xpath_type_number);
break;
case 'r':
if (name == PUGIXML_TEXT("round") && argc == 1)
- return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]);
+ return alloc_node(ast_func_round, xpath_type_number, args[0]);
break;
case 's':
if (name == PUGIXML_TEXT("string") && argc <= 1)
- return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
+ return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
else if (name == PUGIXML_TEXT("string-length") && argc <= 1)
- return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);
+ return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);
else if (name == PUGIXML_TEXT("starts-with") && argc == 2)
- return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
+ return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
else if (name == PUGIXML_TEXT("substring-before") && argc == 2)
- return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);
+ return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("substring-after") && argc == 2)
- return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);
+ return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3))
- return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);
+ return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("sum") && argc == 1)
{
- if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
- return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]);
+ if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+ return alloc_node(ast_func_sum, xpath_type_number, args[0]);
}
break;
case 't':
if (name == PUGIXML_TEXT("translate") && argc == 3)
- return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]);
+ return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]);
else if (name == PUGIXML_TEXT("true") && argc == 0)
- return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean);
+ return alloc_node(ast_func_true, xpath_type_boolean);
break;
@@ -11081,9 +11262,7 @@ PUGI__NS_BEGIN
break;
}
- throw_error("Unrecognized function or wrong parameter count");
-
- return 0;
+ return error("Unrecognized function or wrong parameter count");
}
axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)
@@ -11199,18 +11378,18 @@ PUGI__NS_BEGIN
xpath_lexer_string name = _lexer.contents();
if (!_variables)
- throw_error("Unknown variable: variable set is not provided");
+ return error("Unknown variable: variable set is not provided");
xpath_variable* var = 0;
if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))
- throw_error_oom();
+ return error_oom();
if (!var)
- throw_error("Unknown variable: variable set does not contain the given name");
+ return error("Unknown variable: variable set does not contain the given name");
_lexer.next();
- return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var);
+ return alloc_node(ast_variable, var->type(), var);
}
case lex_open_brace:
@@ -11218,9 +11397,10 @@ PUGI__NS_BEGIN
_lexer.next();
xpath_ast_node* n = parse_expression();
+ if (!n) return 0;
if (_lexer.current() != lex_close_brace)
- throw_error("Unmatched braces");
+ return error("Expected ')' to match an opening '('");
_lexer.next();
@@ -11230,11 +11410,11 @@ PUGI__NS_BEGIN
case lex_quoted_string:
{
const char_t* value = alloc_string(_lexer.contents());
+ if (!value) return 0;
- xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value);
_lexer.next();
- return n;
+ return alloc_node(ast_string_constant, xpath_type_string, value);
}
case lex_number:
@@ -11242,12 +11422,11 @@ PUGI__NS_BEGIN
double value = 0;
if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))
- throw_error_oom();
+ return error_oom();
- xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value);
_lexer.next();
- return n;
+ return alloc_node(ast_number_constant, xpath_type_number, value);
}
case lex_string:
@@ -11261,19 +11440,20 @@ PUGI__NS_BEGIN
xpath_ast_node* last_arg = 0;
if (_lexer.current() != lex_open_brace)
- throw_error("Unrecognized function call");
+ return error("Unrecognized function call");
_lexer.next();
- if (_lexer.current() != lex_close_brace)
- args[argc++] = parse_expression();
-
while (_lexer.current() != lex_close_brace)
{
- if (_lexer.current() != lex_comma)
- throw_error("No comma between function arguments");
- _lexer.next();
+ if (argc > 0)
+ {
+ if (_lexer.current() != lex_comma)
+ return error("No comma between function arguments");
+ _lexer.next();
+ }
xpath_ast_node* n = parse_expression();
+ if (!n) return 0;
if (argc < 2) args[argc] = n;
else last_arg->set_next(n);
@@ -11288,9 +11468,7 @@ PUGI__NS_BEGIN
}
default:
- throw_error("Unrecognizable primary expression");
-
- return 0;
+ return error("Unrecognizable primary expression");
}
}
@@ -11300,19 +11478,23 @@ PUGI__NS_BEGIN
xpath_ast_node* parse_filter_expression()
{
xpath_ast_node* n = parse_primary_expression();
+ if (!n) return 0;
while (_lexer.current() == lex_open_square_brace)
{
_lexer.next();
- xpath_ast_node* expr = parse_expression();
+ if (n->rettype() != xpath_type_node_set)
+ return error("Predicate has to be applied to node set");
- if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
+ xpath_ast_node* expr = parse_expression();
+ if (!expr) return 0;
- n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default);
+ n = alloc_node(ast_filter, n, expr, predicate_default);
+ if (!n) return 0;
if (_lexer.current() != lex_close_square_brace)
- throw_error("Unmatched square brace");
+ return error("Expected ']' to match an opening '['");
_lexer.next();
}
@@ -11328,7 +11510,7 @@ PUGI__NS_BEGIN
xpath_ast_node* parse_step(xpath_ast_node* set)
{
if (set && set->rettype() != xpath_type_node_set)
- throw_error("Step has to be applied to node set");
+ return error("Step has to be applied to node set");
bool axis_specified = false;
axis_t axis = axis_child; // implied child axis
@@ -11344,13 +11526,19 @@ PUGI__NS_BEGIN
{
_lexer.next();
- return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0);
+ if (_lexer.current() == lex_open_square_brace)
+ return error("Predicates are not allowed after an abbreviated step");
+
+ return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0);
}
else if (_lexer.current() == lex_double_dot)
{
_lexer.next();
- return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0);
+ if (_lexer.current() == lex_open_square_brace)
+ return error("Predicates are not allowed after an abbreviated step");
+
+ return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0);
}
nodetest_t nt_type = nodetest_none;
@@ -11366,11 +11554,13 @@ PUGI__NS_BEGIN
if (_lexer.current() == lex_double_colon)
{
// parse axis name
- if (axis_specified) throw_error("Two axis specifiers in one step");
+ if (axis_specified)
+ return error("Two axis specifiers in one step");
axis = parse_axis_name(nt_name, axis_specified);
- if (!axis_specified) throw_error("Unknown axis");
+ if (!axis_specified)
+ return error("Unknown axis");
// read actual node test
_lexer.next();
@@ -11386,7 +11576,10 @@ PUGI__NS_BEGIN
nt_name = _lexer.contents();
_lexer.next();
}
- else throw_error("Unrecognized node test");
+ else
+ {
+ return error("Unrecognized node test");
+ }
}
if (nt_type == nodetest_none)
@@ -11402,26 +11595,28 @@ PUGI__NS_BEGIN
nt_type = parse_node_test_type(nt_name);
- if (nt_type == nodetest_none) throw_error("Unrecognized node type");
+ if (nt_type == nodetest_none)
+ return error("Unrecognized node type");
nt_name = xpath_lexer_string();
}
else if (nt_name == PUGIXML_TEXT("processing-instruction"))
{
if (_lexer.current() != lex_quoted_string)
- throw_error("Only literals are allowed as arguments to processing-instruction()");
+ return error("Only literals are allowed as arguments to processing-instruction()");
nt_type = nodetest_pi;
nt_name = _lexer.contents();
_lexer.next();
if (_lexer.current() != lex_close_brace)
- throw_error("Unmatched brace near processing-instruction()");
+ return error("Unmatched brace near processing-instruction()");
_lexer.next();
}
else
- throw_error("Unmatched brace near node type test");
-
+ {
+ return error("Unmatched brace near node type test");
+ }
}
// QName or NCName:*
else
@@ -11432,7 +11627,10 @@ PUGI__NS_BEGIN
nt_type = nodetest_all_in_namespace;
}
- else nt_type = nodetest_name;
+ else
+ {
+ nt_type = nodetest_name;
+ }
}
}
}
@@ -11441,9 +11639,16 @@ PUGI__NS_BEGIN
nt_type = nodetest_all;
_lexer.next();
}
- else throw_error("Unrecognized node test");
+ else
+ {
+ return error("Unrecognized node test");
+ }
+
+ const char_t* nt_name_copy = alloc_string(nt_name);
+ if (!nt_name_copy) return 0;
- xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name));
+ xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy);
+ if (!n) return 0;
xpath_ast_node* last = 0;
@@ -11452,11 +11657,13 @@ PUGI__NS_BEGIN
_lexer.next();
xpath_ast_node* expr = parse_expression();
+ if (!expr) return 0;
- xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default);
+ xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default);
+ if (!pred) return 0;
if (_lexer.current() != lex_close_square_brace)
- throw_error("Unmatched square brace");
+ return error("Expected ']' to match an opening '['");
_lexer.next();
if (last) last->set_next(pred);
@@ -11472,6 +11679,7 @@ PUGI__NS_BEGIN
xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)
{
xpath_ast_node* n = parse_step(set);
+ if (!n) return 0;
while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
{
@@ -11479,9 +11687,13 @@ PUGI__NS_BEGIN
_lexer.next();
if (l == lex_double_slash)
- n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ {
+ n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ if (!n) return 0;
+ }
n = parse_step(n);
+ if (!n) return 0;
}
return n;
@@ -11495,7 +11707,8 @@ PUGI__NS_BEGIN
{
_lexer.next();
- xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);
+ xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
+ if (!n) return 0;
// relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path
lexeme_t l = _lexer.current();
@@ -11509,8 +11722,11 @@ PUGI__NS_BEGIN
{
_lexer.next();
- xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);
- n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
+ if (!n) return 0;
+
+ n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ if (!n) return 0;
return parse_relative_location_path(n);
}
@@ -11533,7 +11749,6 @@ PUGI__NS_BEGIN
// PrimaryExpr begins with '$' in case of it being a variable reference,
// '(' in case of it being an expression, string literal, number constant or
// function call.
-
if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace ||
_lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||
_lexer.current() == lex_string)
@@ -11545,13 +11760,16 @@ PUGI__NS_BEGIN
while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;
- if (*state != '(') return parse_location_path();
+ if (*state != '(')
+ return parse_location_path();
// This looks like a function call; however this still can be a node-test. Check it.
- if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path();
+ if (parse_node_test_type(_lexer.contents()) != nodetest_none)
+ return parse_location_path();
}
xpath_ast_node* n = parse_filter_expression();
+ if (!n) return 0;
if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
{
@@ -11560,9 +11778,11 @@ PUGI__NS_BEGIN
if (l == lex_double_slash)
{
- if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set");
+ if (n->rettype() != xpath_type_node_set)
+ return error("Step has to be applied to node set");
- n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ if (!n) return 0;
}
// select from location path
@@ -11576,12 +11796,15 @@ PUGI__NS_BEGIN
_lexer.next();
// precedence 7+ - only parses union expressions
- xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7);
+ xpath_ast_node* n = parse_expression(7);
+ if (!n) return 0;
- return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
+ return alloc_node(ast_op_negate, xpath_type_number, n);
}
else
+ {
return parse_location_path();
+ }
}
struct binary_op_t
@@ -11659,20 +11882,23 @@ PUGI__NS_BEGIN
_lexer.next();
xpath_ast_node* rhs = parse_path_or_unary_expression();
+ if (!rhs) return 0;
binary_op_t nextop = binary_op_t::parse(_lexer);
while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)
{
rhs = parse_expression_rec(rhs, nextop.precedence);
+ if (!rhs) return 0;
nextop = binary_op_t::parse(_lexer);
}
if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))
- throw_error("Union operator has to be applied to node sets");
+ return error("Union operator has to be applied to node sets");
- lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs);
+ lhs = alloc_node(op.asttype, op.rettype, lhs, rhs);
+ if (!lhs) return 0;
op = binary_op_t::parse(_lexer);
}
@@ -11698,9 +11924,12 @@ PUGI__NS_BEGIN
// | MultiplicativeExpr '*' UnaryExpr
// | MultiplicativeExpr 'div' UnaryExpr
// | MultiplicativeExpr 'mod' UnaryExpr
- xpath_ast_node* parse_expression()
+ xpath_ast_node* parse_expression(int limit = 0)
{
- return parse_expression_rec(parse_path_or_unary_expression(), 0);
+ xpath_ast_node* n = parse_path_or_unary_expression();
+ if (!n) return 0;
+
+ return parse_expression_rec(n, limit);
}
xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
@@ -11709,28 +11938,21 @@ PUGI__NS_BEGIN
xpath_ast_node* parse()
{
- xpath_ast_node* result = parse_expression();
+ xpath_ast_node* n = parse_expression();
+ if (!n) return 0;
+ // check if there are unparsed tokens left
if (_lexer.current() != lex_eof)
- {
- // there are still unparsed tokens left, error
- throw_error("Incorrect query");
- }
+ return error("Incorrect query");
- return result;
+ return n;
}
static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)
{
xpath_parser parser(query, variables, alloc, result);
- #ifdef PUGIXML_NO_EXCEPTIONS
- int error = setjmp(parser._error_handler);
-
- return (error == 0) ? parser.parse() : 0;
- #else
return parser.parse();
- #endif
}
};
@@ -11753,7 +11975,7 @@ PUGI__NS_BEGIN
xml_memory::deallocate(impl);
}
- xpath_query_impl(): root(0), alloc(&block)
+ xpath_query_impl(): root(0), alloc(&block, &oom), oom(false)
{
block.next = 0;
block.capacity = sizeof(block.data);
@@ -11762,21 +11984,9 @@ PUGI__NS_BEGIN
xpath_ast_node* root;
xpath_allocator alloc;
xpath_memory_block block;
+ bool oom;
};
- PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd)
- {
- if (!impl) return xpath_string();
-
- #ifdef PUGIXML_NO_EXCEPTIONS
- if (setjmp(sd.error_handler)) return xpath_string();
- #endif
-
- xpath_context c(n, 1, 1);
-
- return impl->root->eval_string(c, sd.stack);
- }
-
PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
{
if (!impl) return 0;
@@ -11885,74 +12095,61 @@ namespace pugi
size_t size_ = static_cast<size_t>(end_ - begin_);
- if (size_ <= 1)
- {
- // deallocate old buffer
- if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
-
- // use internal buffer
- if (begin_ != end_) _storage = *begin_;
+ // use internal buffer for 0 or 1 elements, heap buffer otherwise
+ xpath_node* storage = (size_ <= 1) ? _storage : static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
- _begin = &_storage;
- _end = &_storage + size_;
- _type = type_;
- }
- else
+ if (!storage)
{
- // make heap copy
- xpath_node* storage = static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return;
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
- 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_;
}
-#if __cplusplus >= 201103
- PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs)
+#ifdef PUGIXML_HAS_MOVE
+ 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);
}
@@ -11966,17 +12163,17 @@ namespace pugi
return *this;
}
-#if __cplusplus >= 201103
- PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+#ifdef PUGIXML_HAS_MOVE
+ 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);
@@ -12061,7 +12258,7 @@ namespace pugi
return static_cast<const impl::xpath_variable_boolean*>(this)->name;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return 0;
}
}
@@ -12166,8 +12363,8 @@ namespace pugi
return *this;
}
-#if __cplusplus >= 201103
- PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs)
+#ifdef PUGIXML_HAS_MOVE
+ 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)
{
@@ -12176,7 +12373,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)
{
@@ -12347,6 +12544,15 @@ namespace pugi
_impl = impl.release();
_result.error = 0;
}
+ else
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ if (qimpl->oom) _result.error = "Out of memory";
+ #else
+ if (qimpl->oom) throw std::bad_alloc();
+ throw xpath_exception(_result);
+ #endif
+ }
}
}
@@ -12360,8 +12566,8 @@ namespace pugi
impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
}
-#if __cplusplus >= 201103
- PUGI__FN xpath_query::xpath_query(xpath_query&& rhs)
+#ifdef PUGIXML_HAS_MOVE
+ PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT
{
_impl = rhs._impl;
_result = rhs._result;
@@ -12369,7 +12575,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;
@@ -12399,11 +12605,18 @@ namespace pugi
impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- #ifdef PUGIXML_NO_EXCEPTIONS
- if (setjmp(sd.error_handler)) return false;
- #endif
+ bool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);
- return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return false;
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
+ return r;
}
PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const
@@ -12413,19 +12626,38 @@ namespace pugi
impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- #ifdef PUGIXML_NO_EXCEPTIONS
- if (setjmp(sd.error_handler)) return impl::gen_nan();
- #endif
+ double r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);
- return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return impl::gen_nan();
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
+ return r;
}
#ifndef PUGIXML_NO_STL
PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const
{
+ if (!_impl) return string_t();
+
+ impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);
+ impl::xpath_string r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack);
+
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return string_t();
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
return string_t(r.c_str(), r.length());
}
@@ -12433,9 +12665,19 @@ namespace pugi
PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
{
+ impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);
+ impl::xpath_string r = _impl ? static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string();
+
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ r = impl::xpath_string();
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
size_t full_size = r.length() + 1;
@@ -12459,12 +12701,17 @@ namespace pugi
impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- #ifdef PUGIXML_NO_EXCEPTIONS
- if (setjmp(sd.error_handler)) return xpath_node_set();
- #endif
-
impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return xpath_node_set();
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
return xpath_node_set(r.begin(), r.end(), r.type());
}
@@ -12476,12 +12723,17 @@ namespace pugi
impl::xpath_context c(n, 1, 1);
impl::xpath_stack_data sd;
- #ifdef PUGIXML_NO_EXCEPTIONS
- if (setjmp(sd.error_handler)) return xpath_node();
- #endif
-
impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);
+ if (sd.oom)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return xpath_node();
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
return r.first();
}
@@ -12507,7 +12759,7 @@ namespace pugi
PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
{
xpath_query q(query, variables);
- return select_node(q);
+ return q.evaluate_node(*this);
}
PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
@@ -12518,7 +12770,7 @@ namespace pugi
PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
{
xpath_query q(query, variables);
- return select_nodes(q);
+ return q.evaluate_node_set(*this);
}
PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
@@ -12529,7 +12781,7 @@ namespace pugi
PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
{
xpath_query q(query, variables);
- return select_single_node(q);
+ return q.evaluate_node(*this);
}
PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
@@ -12550,12 +12802,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
@@ -12582,7 +12840,7 @@ namespace pugi
#endif
/**
- * Copyright (c) 2006-2016 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