Index: XmlTools2/trunk/XmlTools.pro
===================================================================
--- XmlTools2/trunk/XmlTools.pro	(revision 1003)
+++ XmlTools2/trunk/XmlTools.pro	(revision 1055)
@@ -1,4 +1,5 @@
 TEMPLATE = app
 CONFIG += console
+CONFIG   += c++14
 QT       += core
 QT       -= gui # not needed
Index: XmlTools2/trunk/libs/pugiconfig.hpp
===================================================================
--- XmlTools2/trunk/libs/pugiconfig.hpp	(revision 1003)
+++ XmlTools2/trunk/libs/pugiconfig.hpp	(revision 1055)
@@ -1,6 +1,6 @@
 /**
- * pugixml parser - version 1.2
+ * pugixml parser - version 1.7
  * --------------------------------------------------------
- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at http://pugixml.org/
  *
@@ -17,4 +17,7 @@
 // Uncomment this to enable wchar_t mode
 // #define PUGIXML_WCHAR_MODE
+
+// Uncomment this to enable compact mode
+// #define PUGIXML_COMPACT
 
 // Uncomment this to disable XPath
@@ -33,8 +36,4 @@
 // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
 
-// Uncomment this to switch to header-only version
-// #define PUGIXML_HEADER_ONLY
-// #include "pugixml.cpp"
-
 // Tune these constants to adjust memory-related behavior
 // #define PUGIXML_MEMORY_PAGE_SIZE 32768
@@ -42,8 +41,14 @@
 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
 
+// Uncomment this to switch to header-only version
+// #define PUGIXML_HEADER_ONLY
+
+// Uncomment this to enable long long support
+// #define PUGIXML_HAS_LONG_LONG
+
 #endif
 
 /**
- * Copyright (c) 2006-2012 Arseny Kapoulkine
+ * Copyright (c) 2006-2015 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
Index: XmlTools2/trunk/libs/pugixml.cpp
===================================================================
--- XmlTools2/trunk/libs/pugixml.cpp	(revision 1003)
+++ XmlTools2/trunk/libs/pugixml.cpp	(revision 1055)
@@ -1,6 +1,6 @@
 /**
- * pugixml parser - version 1.2
+ * pugixml parser - version 1.7
  * --------------------------------------------------------
- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at http://pugixml.org/
  *
@@ -21,5 +21,9 @@
 #include <string.h>
 #include <assert.h>
-#include <wchar.h>
+#include <limits.h>
+
+#ifdef PUGIXML_WCHAR_MODE
+#	include <wchar.h>
+#endif
 
 #ifndef PUGIXML_NO_XPATH
@@ -82,4 +86,11 @@
 #endif
 
+// Branch weight controls
+#if defined(__GNUC__)
+#	define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
+#else
+#	define PUGI__UNLIKELY(cond) (cond)
+#endif
+
 // Simple static assertion
 #define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
@@ -96,4 +107,5 @@
 using std::memcpy;
 using std::memmove;
+using std::memset;
 #endif
 
@@ -124,14 +136,14 @@
 #	include <stdint.h>
 #else
+namespace pugi
+{
 #	ifndef _UINTPTR_T_DEFINED
-// No native uintptr_t in MSVC6 and in some WinCE versions
-typedef size_t uintptr_t;
-#define _UINTPTR_T_DEFINED
+	typedef size_t uintptr_t;
 #	endif
-PUGI__NS_BEGIN
+
 	typedef unsigned __int8 uint8_t;
 	typedef unsigned __int16 uint16_t;
 	typedef unsigned __int32 uint32_t;
-PUGI__NS_END
+}
 #endif
 
@@ -155,4 +167,6 @@
 	};
 
+	// Global allocation functions are stored in class statics so that in header mode linker deduplicates them
+	// Without a template<> we'll get multiple definitions of the same static
 	template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
 	template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
@@ -196,39 +210,185 @@
 		return lhs[count] == 0;
 	}
-	
-#ifdef PUGIXML_WCHAR_MODE
-	// Convert string to wide string, assuming all symbols are ASCII
-	PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
-	{
-		for (const char* i = source; *i; ++i) *dest++ = *i;
-		*dest = 0;
-	}
-#endif
+
+	// Get length of wide string, even if CRT lacks wide character support
+	PUGI__FN size_t strlength_wide(const wchar_t* s)
+	{
+		assert(s);
+
+	#ifdef PUGIXML_WCHAR_MODE
+		return wcslen(s);
+	#else
+		const wchar_t* end = s;
+		while (*end) end++;
+		return static_cast<size_t>(end - s);
+	#endif
+	}
 PUGI__NS_END
 
-#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
-// auto_ptr-like buffer holder for exception recovery
+// auto_ptr-like object for exception recovery
 PUGI__NS_BEGIN
-	struct buffer_holder
-	{
-		void* data;
-		void (*deleter)(void*);
-
-		buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
-		{
-		}
-
-		~buffer_holder()
+	template <typename T, typename D = void(*)(T*)> struct auto_deleter
+	{
+		T* data;
+		D deleter;
+
+		auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)
+		{
+		}
+
+		~auto_deleter()
 		{
 			if (data) deleter(data);
 		}
 
-		void* release()
-		{
-			void* result = data;
+		T* release()
+		{
+			T* result = data;
 			data = 0;
 			return result;
 		}
 	};
+PUGI__NS_END
+
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+	class compact_hash_table
+	{
+	public:
+		compact_hash_table(): _items(0), _capacity(0), _count(0)
+		{
+		}
+
+		void clear()
+		{
+			if (_items)
+			{
+				xml_memory::deallocate(_items);
+				_items = 0;
+				_capacity = 0;
+				_count = 0;
+			}
+		}
+
+		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;
+
+				if (probe_item.key == 0)
+					return 0;
+
+				// hash collision, quadratic probing
+				bucket = (bucket + probe + 1) & hashmod;
+			}
+
+			assert(!"Hash table is full");
+			return 0;
+		}
+
+		void** insert(const void* key)
+		{
+			assert(key);
+			assert(_capacity != 0 && _count < _capacity - _capacity / 4);
+
+			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 == 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;
+			}
+
+			assert(!"Hash table is full");
+			return 0;
+		}
+
+		bool reserve()
+		{
+			if (_count + 16 >= _capacity - _capacity / 4)
+				return rehash();
+
+			return true;
+		}
+
+	private:
+		struct item_t
+		{
+			const void* key;
+			void* value;
+		};
+
+		item_t* _items;
+		size_t _capacity;
+
+		size_t _count;
+
+		bool rehash();
+
+		static unsigned int hash(const void* 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;
+
+			return h;
+		}
+	};
+
+	PUGI__FN_NO_INLINE bool compact_hash_table::rehash()
+	{
+		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));
+
+		if (!rt._items)
+			return false;
+
+		memset(rt._items, 0, sizeof(item_t) * rt._capacity);
+
+		for (size_t i = 0; i < _capacity; ++i)
+			if (_items[i].key)
+				*rt.insert(_items[i].key) = _items[i].value;
+
+		if (_items)
+			xml_memory::deallocate(_items);
+
+		_capacity = rt._capacity;
+		_items = rt._items;
+
+		assert(_count == rt._count);
+
+		return true;
+	}
+
 PUGI__NS_END
 #endif
@@ -243,10 +403,34 @@
 		;
 
-	static const uintptr_t xml_memory_page_alignment = 32;
+#ifdef PUGIXML_COMPACT
+	static const uintptr_t xml_memory_block_alignment = 4;
+
+	static const uintptr_t xml_memory_page_alignment = sizeof(void*);
+#else
+	static const uintptr_t xml_memory_block_alignment = sizeof(void*);
+
+	static const uintptr_t xml_memory_page_alignment = 64;
 	static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
+#endif
+
+	// extra metadata bits
+	static const uintptr_t xml_memory_page_contents_shared_mask = 32;
 	static const uintptr_t xml_memory_page_name_allocated_mask = 16;
 	static const uintptr_t xml_memory_page_value_allocated_mask = 8;
 	static const uintptr_t xml_memory_page_type_mask = 7;
 
+	// combined masks for string uniqueness
+	static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask;
+	static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask;
+
+#ifdef PUGIXML_COMPACT
+	#define PUGI__GETPAGE_IMPL(header) (header).get_page()
+#else
+	#define PUGI__GETPAGE_IMPL(header) reinterpret_cast<impl::xml_memory_page*>((header) & impl::xml_memory_page_pointer_mask)
+#endif
+
+	#define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
+	#define PUGI__NODETYPE(n) static_cast<xml_node_type>(((n)->header & impl::xml_memory_page_type_mask) + 1)
+
 	struct xml_allocator;
 
@@ -255,10 +439,7 @@
 		static xml_memory_page* construct(void* memory)
 		{
-			if (!memory) return 0; //$ redundant, left for performance
-
 			xml_memory_page* result = static_cast<xml_memory_page*>(memory);
 
 			result->allocator = 0;
-			result->memory = 0;
 			result->prev = 0;
 			result->next = 0;
@@ -266,10 +447,14 @@
 			result->freed_size = 0;
 
+		#ifdef PUGIXML_COMPACT
+			result->compact_string_base = 0;
+			result->compact_shared_parent = 0;
+			result->compact_page_marker = 0;
+		#endif
+
 			return result;
 		}
 
 		xml_allocator* allocator;
-
-		void* memory;
 
 		xml_memory_page* prev;
@@ -279,5 +464,9 @@
 		size_t freed_size;
 
-		char data[1];
+	#ifdef PUGIXML_COMPACT
+		char_t* compact_string_base;
+		void* compact_shared_parent;
+		uint32_t* compact_page_marker;
+	#endif
 	};
 
@@ -292,9 +481,12 @@
 		xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
 		{
+		#ifdef PUGIXML_COMPACT
+			_hash = 0;
+		#endif
 		}
 
 		xml_memory_page* allocate_page(size_t data_size)
 		{
-			size_t size = offsetof(xml_memory_page, data) + data_size;
+			size_t size = sizeof(xml_memory_page) + data_size;
 
 			// allocate block with some alignment, leaving memory for worst-case padding
@@ -302,13 +494,17 @@
 			if (!memory) return 0;
 
-			// align upwards to page boundary
-			void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
+			// align to next page boundary (note: this guarantees at least 1 usable byte before the page)
+			char* page_memory = reinterpret_cast<char*>((reinterpret_cast<uintptr_t>(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1));
 
 			// prepare page structure
 			xml_memory_page* page = xml_memory_page::construct(page_memory);
-
-			page->memory = memory;
+			assert(page);
+
 			page->allocator = _root->allocator;
 
+			// record the offset for freeing the memory block
+			assert(page_memory > memory && page_memory - static_cast<char*>(memory) <= 127);
+			page_memory[-1] = static_cast<char>(page_memory - static_cast<char*>(memory));
+
 			return page;
 		}
@@ -316,5 +512,7 @@
 		static void deallocate_page(xml_memory_page* page)
 		{
-			xml_memory::deallocate(page->memory);
+			char* page_memory = reinterpret_cast<char*>(page);
+
+			xml_memory::deallocate(page_memory - page_memory[-1]);
 		}
 
@@ -323,7 +521,8 @@
 		void* allocate_memory(size_t size, xml_memory_page*& out_page)
 		{
-			if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
-
-			void* buf = _root->data + _busy_size;
+			if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size))
+				return allocate_memory_oob(size, out_page);
+
+			void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;
 
 			_busy_size += size;
@@ -334,9 +533,47 @@
 		}
 
+	#ifdef PUGIXML_COMPACT
+		void* allocate_object(size_t size, xml_memory_page*& out_page)
+		{
+			void* result = allocate_memory(size + sizeof(uint32_t), out_page);
+			if (!result) return 0;
+
+			// adjust for marker
+			ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);
+
+			if (PUGI__UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
+			{
+				// insert new marker
+				uint32_t* marker = static_cast<uint32_t*>(result);
+
+				*marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));
+				out_page->compact_page_marker = marker;
+
+				// since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block
+				// this will make sure deallocate_memory correctly tracks the size
+				out_page->freed_size += sizeof(uint32_t);
+
+				return marker + 1;
+			}
+			else
+			{
+				// roll back uint32_t part
+				_busy_size -= sizeof(uint32_t);
+
+				return result;
+			}
+		}
+	#else
+		void* allocate_object(size_t size, xml_memory_page*& out_page)
+		{
+			return allocate_memory(size, out_page);
+		}
+	#endif
+
 		void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
 		{
 			if (page == _root) page->busy_size = _busy_size;
 
-			assert(ptr >= page->data && ptr < page->data + page->busy_size);
+			assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);
 			(void)!ptr;
 
@@ -351,5 +588,14 @@
 
 					// top page freed, just reset sizes
-					page->busy_size = page->freed_size = 0;
+					page->busy_size = 0;
+					page->freed_size = 0;
+
+				#ifdef PUGIXML_COMPACT
+					// reset compact state to maximize efficiency
+					page->compact_string_base = 0;
+					page->compact_shared_parent = 0;
+					page->compact_page_marker = 0;
+				#endif
+
 					_busy_size = 0;
 				}
@@ -371,9 +617,13 @@
 		char_t* allocate_string(size_t length)
 		{
+			static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
+
+			PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
+
 			// allocate memory for string and header block
 			size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
 			
-			// round size up to pointer alignment boundary
-			size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
+			// round size up to block alignment boundary
+			size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);
 
 			xml_memory_page* page;
@@ -383,12 +633,14 @@
 
 			// setup header
-			ptrdiff_t page_offset = reinterpret_cast<char*>(header) - page->data;
-
-			assert(page_offset >= 0 && page_offset < (1 << 16));
-			header->page_offset = static_cast<uint16_t>(page_offset);
+			ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
+
+			assert(page_offset % xml_memory_block_alignment == 0);
+			assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);
+			header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);
 
 			// full_size == 0 for large strings that occupy the whole page
-			assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
-			header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);
+			assert(full_size % xml_memory_block_alignment == 0);
+			assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));
+			header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0);
 
 			// round-trip through void* to avoid 'cast increases required alignment of target type' warning
@@ -404,17 +656,31 @@
 			// get header
 			xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
+			assert(header);
 
 			// deallocate
-			size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;
+			size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
 			xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
 
 			// if full_size == 0 then this string occupies the whole page
-			size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
+			size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;
 
 			deallocate_memory(header, full_size, page);
+		}
+
+		bool reserve()
+		{
+		#ifdef PUGIXML_COMPACT
+			return _hash->reserve();
+		#else
+			return true;
+		#endif
 		}
 
 		xml_memory_page* _root;
 		size_t _busy_size;
+
+	#ifdef PUGIXML_COMPACT
+		compact_hash_table* _hash;
+	#endif
 	};
 
@@ -450,72 +716,435 @@
 			_root->prev->next = page;
 			_root->prev = page;
-		}
-
-		// allocate inside page
-		page->busy_size = size;
-
-		return page->data;
+
+			page->busy_size = size;
+		}
+
+		return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
 	}
 PUGI__NS_END
 
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+	static const uintptr_t compact_alignment_log2 = 2;
+	static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
+
+	class compact_header
+	{
+	public:
+		compact_header(xml_memory_page* page, unsigned int flags)
+		{
+			PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);
+
+			ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));
+			assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);
+
+			_page = static_cast<unsigned char>(offset >> compact_alignment_log2);
+			_flags = static_cast<unsigned char>(flags);
+		}
+
+		void operator&=(uintptr_t mod)
+		{
+			_flags &= static_cast<unsigned char>(mod);
+		}
+
+		void operator|=(uintptr_t mod)
+		{
+			_flags |= static_cast<unsigned char>(mod);
+		}
+
+		uintptr_t operator&(uintptr_t mod) const
+		{
+			return _flags & mod;
+		}
+
+		xml_memory_page* get_page() const
+		{
+			// round-trip through void* to silence 'cast increases required alignment of target type' warnings
+			const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);
+			const char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker));
+
+			return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page)));
+		}
+
+	private:
+		unsigned char _page;
+		unsigned char _flags;
+	};
+
+	PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
+	{
+		const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);
+
+		return header->get_page();
+	}
+
+	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));
+	}
+
+	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;
+	}
+
+	template <typename T, int header_offset, int start = -126> class compact_pointer
+	{
+	public:
+		compact_pointer(): _data(0)
+		{
+		}
+
+		void operator=(const compact_pointer& rhs)
+		{
+			*this = rhs + 0;
+		}
+
+		void operator=(T* value)
+		{
+			if (value)
+			{
+				// value is guaranteed to be compact-aligned; 'this' is not
+				// our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+				// so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+				// compensate for arithmetic shift rounding for negative values
+				ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+				ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;
+
+				if (static_cast<uintptr_t>(offset) <= 253)
+					_data = static_cast<unsigned char>(offset + 1);
+				else
+				{
+					compact_set_value<header_offset>(this, value);
+
+					_data = 255;
+				}
+			}
+			else
+				_data = 0;
+		}
+
+		operator T*() const
+		{
+			if (_data)
+			{
+				if (_data < 255)
+				{
+					uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+					return reinterpret_cast<T*>(base + ((_data - 1 + start) << compact_alignment_log2));
+				}
+				else
+					return compact_get_value<header_offset, T>(this);
+			}
+			else
+				return 0;
+		}
+
+		T* operator->() const
+		{
+			return *this;
+		}
+
+	private:
+		unsigned char _data;
+	};
+
+	template <typename T, int header_offset> class compact_pointer_parent
+	{
+	public:
+		compact_pointer_parent(): _data(0)
+		{
+		}
+
+		void operator=(const compact_pointer_parent& rhs)
+		{
+			*this = rhs + 0;
+		}
+
+		void operator=(T* value)
+		{
+			if (value)
+			{
+				// value is guaranteed to be compact-aligned; 'this' is not
+				// our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+				// so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+				// compensate for arithmetic shift behavior for negative values
+				ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+				ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;
+
+				if (static_cast<uintptr_t>(offset) <= 65533)
+				{
+					_data = static_cast<unsigned short>(offset + 1);
+				}
+				else
+				{
+					xml_memory_page* page = compact_get_page(this, header_offset);
+
+					if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
+						page->compact_shared_parent = value;
+
+					if (page->compact_shared_parent == value)
+					{
+						_data = 65534;
+					}
+					else
+					{
+						compact_set_value<header_offset>(this, value);
+
+						_data = 65535;
+					}
+				}
+			}
+			else
+			{
+				_data = 0;
+			}
+		}
+
+		operator T*() const
+		{
+			if (_data)
+			{
+				if (_data < 65534)
+				{
+					uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+					return reinterpret_cast<T*>(base + ((_data - 1 - 65533) << compact_alignment_log2));
+				}
+				else if (_data == 65534)
+					return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
+				else
+					return compact_get_value<header_offset, T>(this);
+			}
+			else
+				return 0;
+		}
+
+		T* operator->() const
+		{
+			return *this;
+		}
+
+	private:
+		uint16_t _data;
+	};
+
+	template <int header_offset, int base_offset> class compact_string
+	{
+	public:
+		compact_string(): _data(0)
+		{
+		}
+
+		void operator=(const compact_string& rhs)
+		{
+			*this = rhs + 0;
+		}
+
+		void operator=(char_t* value)
+		{
+			if (value)
+			{
+				xml_memory_page* page = compact_get_page(this, header_offset);
+
+				if (PUGI__UNLIKELY(page->compact_string_base == 0))
+					page->compact_string_base = value;
+
+				ptrdiff_t offset = value - page->compact_string_base;
+
+				if (static_cast<uintptr_t>(offset) < (65535 << 7))
+				{
+					// round-trip through void* to silence 'cast increases required alignment of target type' warnings
+					uint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset));
+
+					if (*base == 0)
+					{
+						*base = static_cast<uint16_t>((offset >> 7) + 1);
+						_data = static_cast<unsigned char>((offset & 127) + 1);
+					}
+					else
+					{
+						ptrdiff_t remainder = offset - ((*base - 1) << 7);
+
+						if (static_cast<uintptr_t>(remainder) <= 253)
+						{
+							_data = static_cast<unsigned char>(remainder + 1);
+						}
+						else
+						{
+							compact_set_value<header_offset>(this, value);
+
+							_data = 255;
+						}
+					}
+				}
+				else
+				{
+					compact_set_value<header_offset>(this, value);
+
+					_data = 255;
+				}
+			}
+			else
+			{
+				_data = 0;
+			}
+		}
+
+		operator char_t*() const
+		{
+			if (_data)
+			{
+				if (_data < 255)
+				{
+					xml_memory_page* page = compact_get_page(this, header_offset);
+
+					// round-trip through void* to silence 'cast increases required alignment of target type' warnings
+					const uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset));
+					assert(*base);
+
+					ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);
+
+					return page->compact_string_base + offset;
+				}
+				else
+				{
+					return compact_get_value<header_offset, char_t>(this);
+				}
+			}
+			else
+				return 0;
+		}
+
+	private:
+		unsigned char _data;
+	};
+PUGI__NS_END
+#endif
+
+#ifdef PUGIXML_COMPACT
 namespace pugi
 {
-	/// A 'name=value' XML attribute structure.
 	struct xml_attribute_struct
 	{
-		/// Default ctor
-		xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
-		{
-		}
-
-		uintptr_t header;
-
-		char_t* name;	///< Pointer to attribute name.
-		char_t*	value;	///< Pointer to attribute value.
-
-		xml_attribute_struct* prev_attribute_c;	///< Previous attribute (cyclic list)
-		xml_attribute_struct* next_attribute;	///< Next attribute
+		xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
+		{
+			PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
+		}
+
+		impl::compact_header header;
+
+		uint16_t namevalue_base;
+
+		impl::compact_string<4, 2> name;
+		impl::compact_string<5, 3> value;
+
+		impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c;
+		impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute;
 	};
 
-	/// An XML document tree node.
 	struct xml_node_struct
 	{
-		/// Default ctor
-		/// \param type - node type
-		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
-		{
-		}
-
-		uintptr_t header;
-
-		xml_node_struct*		parent;					///< Pointer to parent
-
-		char_t*					name;					///< Pointer to element name.
-		char_t*					value;					///< Pointer to any associated string data.
-
-		xml_node_struct*		first_child;			///< First child
-		
-		xml_node_struct*		prev_sibling_c;			///< Left brother (cyclic list)
-		xml_node_struct*		next_sibling;			///< Right brother
-		
-		xml_attribute_struct*	first_attribute;		///< First attribute
+		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type - 1), namevalue_base(0)
+		{
+			PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12);
+		}
+
+		impl::compact_header header;
+
+		uint16_t namevalue_base;
+
+		impl::compact_string<4, 2> name;
+		impl::compact_string<5, 3> value;
+
+		impl::compact_pointer_parent<xml_node_struct, 6> parent;
+
+		impl::compact_pointer<xml_node_struct, 8, 0> first_child;
+
+		impl::compact_pointer<xml_node_struct,  9>    prev_sibling_c;
+		impl::compact_pointer<xml_node_struct, 10, 0> next_sibling;
+
+		impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute;
 	};
 }
+#else
+namespace pugi
+{
+	struct xml_attribute_struct
+	{
+		xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
+		{
+		}
+
+		uintptr_t header;
+
+		char_t*	name;
+		char_t*	value;
+
+		xml_attribute_struct* prev_attribute_c;
+		xml_attribute_struct* next_attribute;
+	};
+
+	struct xml_node_struct
+	{
+		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
+		{
+		}
+
+		uintptr_t header;
+
+		char_t* name;
+		char_t* value;
+
+		xml_node_struct* parent;
+
+		xml_node_struct* first_child;
+
+		xml_node_struct* prev_sibling_c;
+		xml_node_struct* next_sibling;
+
+		xml_attribute_struct* first_attribute;
+	};
+}
+#endif
 
 PUGI__NS_BEGIN
+	struct xml_extra_buffer
+	{
+		char_t* buffer;
+		xml_extra_buffer* next;
+	};
+
 	struct xml_document_struct: public xml_node_struct, public xml_allocator
 	{
-		xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0)
-		{
+		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;
+
+		xml_extra_buffer* extra_buffers;
+
+	#ifdef PUGIXML_COMPACT
+		compact_hash_table hash;
+	#endif
 	};
 
-	inline xml_allocator& get_allocator(const xml_node_struct* node)
-	{
-		assert(node);
-
-		return *reinterpret_cast<xml_memory_page*>(node->header & xml_memory_page_pointer_mask)->allocator;
+	template <typename Object> inline xml_allocator& get_allocator(const Object* object)
+	{
+		assert(object);
+
+		return *PUGI__GETPAGE(object)->allocator;
+	}
+
+	template <typename Object> inline xml_document_struct& get_document(const Object* object)
+	{
+		assert(object);
+
+		return *static_cast<xml_document_struct*>(PUGI__GETPAGE(object)->allocator);
 	}
 PUGI__NS_END
@@ -526,5 +1155,6 @@
 	{
 		xml_memory_page* page;
-		void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
+		void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);
+		if (!memory) return 0;
 
 		return new (memory) xml_attribute_struct(page);
@@ -534,5 +1164,6 @@
 	{
 		xml_memory_page* page;
-		void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
+		void* memory = alloc.allocate_object(sizeof(xml_node_struct), page);
+		if (!memory) return 0;
 
 		return new (memory) xml_node_struct(page, type);
@@ -541,18 +1172,20 @@
 	inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
 	{
-		uintptr_t header = a->header;
-
-		if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
-		if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
-
-		alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
+		if (a->header & impl::xml_memory_page_name_allocated_mask)
+			alloc.deallocate_string(a->name);
+
+		if (a->header & impl::xml_memory_page_value_allocated_mask)
+			alloc.deallocate_string(a->value);
+
+		alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a));
 	}
 
 	inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
 	{
-		uintptr_t header = n->header;
-
-		if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
-		if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
+		if (n->header & impl::xml_memory_page_name_allocated_mask)
+			alloc.deallocate_string(n->name);
+
+		if (n->header & impl::xml_memory_page_value_allocated_mask)
+			alloc.deallocate_string(n->value);
 
 		for (xml_attribute_struct* attr = n->first_attribute; attr; )
@@ -574,55 +1207,196 @@
 		}
 
-		alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
-	}
-
-	PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
-	{
+		alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));
+	}
+
+	inline void append_node(xml_node_struct* child, xml_node_struct* node)
+	{
+		child->parent = node;
+
+		xml_node_struct* head = node->first_child;
+
+		if (head)
+		{
+			xml_node_struct* tail = head->prev_sibling_c;
+
+			tail->next_sibling = child;
+			child->prev_sibling_c = tail;
+			head->prev_sibling_c = child;
+		}
+		else
+		{
+			node->first_child = child;
+			child->prev_sibling_c = child;
+		}
+	}
+
+	inline void prepend_node(xml_node_struct* child, xml_node_struct* node)
+	{
+		child->parent = node;
+
+		xml_node_struct* head = node->first_child;
+
+		if (head)
+		{
+			child->prev_sibling_c = head->prev_sibling_c;
+			head->prev_sibling_c = child;
+		}
+		else
+			child->prev_sibling_c = child;
+
+		child->next_sibling = head;
+		node->first_child = child;
+	}
+
+	inline void insert_node_after(xml_node_struct* child, xml_node_struct* node)
+	{
+		xml_node_struct* parent = node->parent;
+
+		child->parent = parent;
+
+		if (node->next_sibling)
+			node->next_sibling->prev_sibling_c = child;
+		else
+			parent->first_child->prev_sibling_c = child;
+
+		child->next_sibling = node->next_sibling;
+		child->prev_sibling_c = node;
+
+		node->next_sibling = child;
+	}
+
+	inline void insert_node_before(xml_node_struct* child, xml_node_struct* node)
+	{
+		xml_node_struct* parent = node->parent;
+
+		child->parent = parent;
+
+		if (node->prev_sibling_c->next_sibling)
+			node->prev_sibling_c->next_sibling = child;
+		else
+			parent->first_child = child;
+
+		child->prev_sibling_c = node->prev_sibling_c;
+		child->next_sibling = node;
+
+		node->prev_sibling_c = child;
+	}
+
+	inline void remove_node(xml_node_struct* node)
+	{
+		xml_node_struct* parent = node->parent;
+
+		if (node->next_sibling)
+			node->next_sibling->prev_sibling_c = node->prev_sibling_c;
+		else
+			parent->first_child->prev_sibling_c = node->prev_sibling_c;
+
+		if (node->prev_sibling_c->next_sibling)
+			node->prev_sibling_c->next_sibling = node->next_sibling;
+		else
+			parent->first_child = node->next_sibling;
+
+		node->parent = 0;
+		node->prev_sibling_c = 0;
+		node->next_sibling = 0;
+	}
+
+	inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+	{
+		xml_attribute_struct* head = node->first_attribute;
+
+		if (head)
+		{
+			xml_attribute_struct* tail = head->prev_attribute_c;
+
+			tail->next_attribute = attr;
+			attr->prev_attribute_c = tail;
+			head->prev_attribute_c = attr;
+		}
+		else
+		{
+			node->first_attribute = attr;
+			attr->prev_attribute_c = attr;
+		}
+	}
+
+	inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+	{
+		xml_attribute_struct* head = node->first_attribute;
+
+		if (head)
+		{
+			attr->prev_attribute_c = head->prev_attribute_c;
+			head->prev_attribute_c = attr;
+		}
+		else
+			attr->prev_attribute_c = attr;
+
+		attr->next_attribute = head;
+		node->first_attribute = attr;
+	}
+
+	inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
+	{
+		if (place->next_attribute)
+			place->next_attribute->prev_attribute_c = attr;
+		else
+			node->first_attribute->prev_attribute_c = attr;
+
+		attr->next_attribute = place->next_attribute;
+		attr->prev_attribute_c = place;
+		place->next_attribute = attr;
+	}
+
+	inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
+	{
+		if (place->prev_attribute_c->next_attribute)
+			place->prev_attribute_c->next_attribute = attr;
+		else
+			node->first_attribute = attr;
+
+		attr->prev_attribute_c = place->prev_attribute_c;
+		attr->next_attribute = place;
+		place->prev_attribute_c = attr;
+	}
+
+	inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+	{
+		if (attr->next_attribute)
+			attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
+		else
+			node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
+
+		if (attr->prev_attribute_c->next_attribute)
+			attr->prev_attribute_c->next_attribute = attr->next_attribute;
+		else
+			node->first_attribute = attr->next_attribute;
+
+		attr->prev_attribute_c = 0;
+		attr->next_attribute = 0;
+	}
+
+	PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
+	{
+		if (!alloc.reserve()) return 0;
+
 		xml_node_struct* child = allocate_node(alloc, type);
 		if (!child) return 0;
 
-		child->parent = node;
-
-		xml_node_struct* first_child = node->first_child;
-			
-		if (first_child)
-		{
-			xml_node_struct* last_child = first_child->prev_sibling_c;
-
-			last_child->next_sibling = child;
-			child->prev_sibling_c = last_child;
-			first_child->prev_sibling_c = child;
-		}
-		else
-		{
-			node->first_child = child;
-			child->prev_sibling_c = child;
-		}
-			
+		append_node(child, node);
+
 		return child;
 	}
 
-	PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)
-	{
-		xml_attribute_struct* a = allocate_attribute(alloc);
-		if (!a) return 0;
-
-		xml_attribute_struct* first_attribute = node->first_attribute;
-
-		if (first_attribute)
-		{
-			xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;
-
-			last_attribute->next_attribute = a;
-			a->prev_attribute_c = last_attribute;
-			first_attribute->prev_attribute_c = a;
-		}
-		else
-		{
-			node->first_attribute = a;
-			a->prev_attribute_c = a;
-		}
-			
-		return a;
+	PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
+	{
+		if (!alloc.reserve()) return 0;
+
+		xml_attribute_struct* attr = allocate_attribute(alloc);
+		if (!attr) return 0;
+
+		append_attribute(attr, node);
+
+		return attr;
 	}
 PUGI__NS_END
@@ -824,26 +1598,9 @@
 	};
 
-	template <size_t size> struct wchar_selector;
-
-	template <> struct wchar_selector<2>
-	{
-		typedef uint16_t type;
-		typedef utf16_counter counter;
-		typedef utf16_writer writer;
-	};
-
-	template <> struct wchar_selector<4>
-	{
-		typedef uint32_t type;
-		typedef utf32_counter counter;
-		typedef utf32_writer writer;
-	};
-
-	typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
-	typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
-
-	template <typename Traits, typename opt_swap = opt_false> struct utf_decoder
-	{
-		static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)
+	struct utf8_decoder
+	{
+		typedef uint8_t type;
+
+		template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
 		{
 			const uint8_t utf8_byte_mask = 0x3f;
@@ -906,10 +1663,13 @@
 			return result;
 		}
-
-		static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)
-		{
-			const uint16_t* end = data + size;
-
-			while (data < end)
+	};
+
+	template <typename opt_swap> struct utf16_decoder
+	{
+		typedef uint16_t type;
+
+		template <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits)
+		{
+			while (size)
 			{
 				uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;
@@ -920,4 +1680,5 @@
 					result = Traits::low(result, lead);
 					data += 1;
+					size -= 1;
 				}
 				// U+E000..U+FFFF
@@ -926,7 +1687,8 @@
 					result = Traits::low(result, lead);
 					data += 1;
+					size -= 1;
 				}
 				// surrogate pair lead
-				else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && data + 1 < end)
+				else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2)
 				{
 					uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
@@ -936,8 +1698,10 @@
 						result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
 						data += 2;
+						size -= 2;
 					}
 					else
 					{
 						data += 1;
+						size -= 1;
 					}
 				}
@@ -945,4 +1709,5 @@
 				{
 					data += 1;
+					size -= 1;
 				}
 			}
@@ -950,10 +1715,13 @@
 			return result;
 		}
-
-		static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)
-		{
-			const uint32_t* end = data + size;
-
-			while (data < end)
+	};
+
+	template <typename opt_swap> struct utf32_decoder
+	{
+		typedef uint32_t type;
+
+		template <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits)
+		{
+			while (size)
 			{
 				uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
@@ -964,4 +1732,5 @@
 					result = Traits::low(result, lead);
 					data += 1;
+					size -= 1;
 				}
 				// U+10000..U+10FFFF
@@ -970,4 +1739,5 @@
 					result = Traits::high(result, lead);
 					data += 1;
+					size -= 1;
 				}
 			}
@@ -975,40 +1745,61 @@
 			return result;
 		}
-
-		static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)
-		{
-			for (size_t i = 0; i < size; ++i)
-			{
-				result = Traits::low(result, data[i]);
+	};
+
+	struct latin1_decoder
+	{
+		typedef uint8_t type;
+
+		template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
+		{
+			while (size)
+			{
+				result = Traits::low(result, *data);
+				data += 1;
+				size -= 1;
 			}
 
 			return result;
 		}
-
-		static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_utf16_block(data, size, result);
-		}
-
-		static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_utf32_block(data, size, result);
-		}
-
-		static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_wchar_block_impl(reinterpret_cast<const wchar_selector<sizeof(wchar_t)>::type*>(data), size, result);
-		}
 	};
 
-	template <typename T> PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)
-	{
-		for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);
-	}
+	template <size_t size> struct wchar_selector;
+
+	template <> struct wchar_selector<2>
+	{
+		typedef uint16_t type;
+		typedef utf16_counter counter;
+		typedef utf16_writer writer;
+		typedef utf16_decoder<opt_false> decoder;
+	};
+
+	template <> struct wchar_selector<4>
+	{
+		typedef uint32_t type;
+		typedef utf32_counter counter;
+		typedef utf32_writer writer;
+		typedef utf32_decoder<opt_false> decoder;
+	};
+
+	typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
+	typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
+
+	struct wchar_decoder
+	{
+		typedef wchar_t type;
+
+		template <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits)
+		{
+			typedef wchar_selector<sizeof(wchar_t)>::decoder decoder;
+
+			return decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits);
+		}
+	};
 
 #ifdef PUGIXML_WCHAR_MODE
 	PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
 	{
-		for (size_t i = 0; i < length; ++i) result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
+		for (size_t i = 0; i < length; ++i)
+			result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
 	}
 #endif
@@ -1157,19 +1948,26 @@
 	PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
 	{
+		size_t length = size / sizeof(char_t);
+
 		if (is_mutable)
 		{
 			out_buffer = static_cast<char_t*>(const_cast<void*>(contents));
+			out_length = length;
 		}
 		else
 		{
-			void* buffer = xml_memory::allocate(size > 0 ? size : 1);
+			char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
 			if (!buffer) return false;
 
-			memcpy(buffer, contents, size);
-
-			out_buffer = static_cast<char_t*>(buffer);
-		}
-
-		out_length = size / sizeof(char_t);
+			if (contents)
+				memcpy(buffer, contents, length * sizeof(char_t));
+			else
+				assert(length == 0);
+
+			buffer[length] = 0;
+
+			out_buffer = buffer;
+			out_length = length + 1;
+		}
 
 		return true;
@@ -1186,104 +1984,51 @@
 	{
 		const char_t* data = static_cast<const char_t*>(contents);
-	
+		size_t length = size / sizeof(char_t);
+
 		if (is_mutable)
 		{
-			out_buffer = const_cast<char_t*>(data);
+			char_t* buffer = const_cast<char_t*>(data);
+
+			convert_wchar_endian_swap(buffer, data, length);
+
+			out_buffer = buffer;
+			out_length = length;
 		}
 		else
 		{
-			out_buffer = static_cast<char_t*>(xml_memory::allocate(size > 0 ? size : 1));
-			if (!out_buffer) return false;
-		}
-
-		out_length = size / sizeof(char_t);
-
-		convert_wchar_endian_swap(out_buffer, data, out_length);
+			char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+			if (!buffer) return false;
+
+			convert_wchar_endian_swap(buffer, data, length);
+			buffer[length] = 0;
+
+			out_buffer = buffer;
+			out_length = length + 1;
+		}
 
 		return true;
 	}
 
-	PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
-	{
-		const uint8_t* data = static_cast<const uint8_t*>(contents);
+	template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+	{
+		const typename D::type* data = static_cast<const typename D::type*>(contents);
+		size_t data_length = size / sizeof(typename D::type);
 
 		// first pass: get length in wchar_t units
-		out_length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
+		size_t length = D::process(data, data_length, 0, wchar_counter());
 
 		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
-
-		// second pass: convert utf8 input to wchar_t
-		wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
-		wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint16_t* data = static_cast<const uint16_t*>(contents);
-		size_t length = size / sizeof(uint16_t);
-
-		// first pass: get length in wchar_t units
-		out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf16_block(data, length, 0);
-
-		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
+		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+		if (!buffer) return false;
 
 		// second pass: convert utf16 input to wchar_t
-		wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
-		wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint32_t* data = static_cast<const uint32_t*>(contents);
-		size_t length = size / sizeof(uint32_t);
-
-		// first pass: get length in wchar_t units
-		out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf32_block(data, length, 0);
-
-		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
-
-		// second pass: convert utf32 input to wchar_t
-		wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
-		wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
-
-		return true;
-	}
-
-	PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
-	{
-		const uint8_t* data = static_cast<const uint8_t*>(contents);
-
-		// get length in wchar_t units
-		out_length = size;
-
-		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
-
-		// convert latin1 input to wchar_t
-		wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
-		wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_latin1_block(data, size, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
+		wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
+		wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer());
+
+		assert(oend == obegin + length);
+		*oend = 0;
+
+		out_buffer = buffer;
+		out_length = length + 1;
 
 		return true;
@@ -1296,11 +2041,14 @@
 
 		// fast path: no conversion required
-		if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+		if (encoding == wchar_encoding)
+			return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
 
 		// only endian-swapping is required
-		if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
+		if (need_endian_swap_utf(encoding, wchar_encoding))
+			return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
 
 		// source encoding is utf8
-		if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size);
+		if (encoding == encoding_utf8)
+			return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder());
 
 		// source encoding is utf16
@@ -1310,6 +2058,6 @@
 
 			return (native_encoding == encoding) ?
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
 		}
 
@@ -1320,10 +2068,11 @@
 
 			return (native_encoding == encoding) ?
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
 		}
 
 		// source encoding is latin1
-		if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);
+		if (encoding == encoding_latin1)
+			return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
 
 		assert(!"Invalid encoding");
@@ -1331,44 +2080,25 @@
 	}
 #else
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint16_t* data = static_cast<const uint16_t*>(contents);
-		size_t length = size / sizeof(uint16_t);
+	template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+	{
+		const typename D::type* data = static_cast<const typename D::type*>(contents);
+		size_t data_length = size / sizeof(typename D::type);
 
 		// first pass: get length in utf8 units
-		out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf16_block(data, length, 0);
+		size_t length = D::process(data, data_length, 0, utf8_counter());
 
 		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
+		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+		if (!buffer) return false;
 
 		// second pass: convert utf16 input to utf8
-		uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
-		uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint32_t* data = static_cast<const uint32_t*>(contents);
-		size_t length = size / sizeof(uint32_t);
-
-		// first pass: get length in utf8 units
-		out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf32_block(data, length, 0);
-
-		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
-
-		// second pass: convert utf32 input to utf8
-		uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
-		uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
+		uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
+		uint8_t* oend = D::process(data, data_length, obegin, utf8_writer());
+
+		assert(oend == obegin + length);
+		*oend = 0;
+
+		out_buffer = buffer;
+		out_length = length + 1;
 
 		return true;
@@ -1387,11 +2117,12 @@
 	{
 		const uint8_t* data = static_cast<const uint8_t*>(contents);
+		size_t data_length = size;
 
 		// get size of prefix that does not need utf8 conversion
-		size_t prefix_length = get_latin1_7bit_prefix_length(data, size);
-		assert(prefix_length <= size);
+		size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);
+		assert(prefix_length <= data_length);
 
 		const uint8_t* postfix = data + prefix_length;
-		size_t postfix_length = size - prefix_length;
+		size_t postfix_length = data_length - prefix_length;
 
 		// if no conversion is needed, just return the original buffer
@@ -1399,18 +2130,21 @@
 
 		// first pass: get length in utf8 units
-		out_length = prefix_length + utf_decoder<utf8_counter>::decode_latin1_block(postfix, postfix_length, 0);
+		size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter());
 
 		// allocate buffer of suitable length
-		out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
-		if (!out_buffer) return false;
+		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+		if (!buffer) return false;
 
 		// second pass: convert latin1 input to utf8
-		memcpy(out_buffer, data, prefix_length);
-
-		uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
-		uint8_t* out_end = utf_decoder<utf8_writer>::decode_latin1_block(postfix, postfix_length, out_begin + prefix_length);
-
-		assert(out_end == out_begin + out_length);
-		(void)!out_end;
+		memcpy(buffer, data, prefix_length);
+
+		uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
+		uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer());
+
+		assert(oend == obegin + length);
+		*oend = 0;
+
+		out_buffer = buffer;
+		out_length = length + 1;
 
 		return true;
@@ -1420,5 +2154,6 @@
 	{
 		// fast path: no conversion required
-		if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+		if (encoding == encoding_utf8)
+			return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
 
 		// source encoding is utf16
@@ -1428,6 +2163,6 @@
 
 			return (native_encoding == encoding) ?
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
 		}
 
@@ -1438,10 +2173,11 @@
 
 			return (native_encoding == encoding) ?
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
+				convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
 		}
 
 		// source encoding is latin1
-		if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
+		if (encoding == encoding_latin1)
+			return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
 
 		assert(!"Invalid encoding");
@@ -1453,5 +2189,5 @@
 	{
 		// get length in utf8 characters
-		return utf_decoder<utf8_counter>::decode_wchar_block(str, length, 0);
+		return wchar_decoder::process(str, length, 0, utf8_counter());
 	}
 
@@ -1460,11 +2196,9 @@
 		// convert to utf8
 		uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
-		uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(str, length, begin);
+		uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer());
 	
 		assert(begin + size == end);
 		(void)!end;
-
-		// zero-terminate
-		buffer[size] = 0;
+		(void)!size;
 	}
 	
@@ -1490,5 +2224,5 @@
 
 		// first pass: get length in wchar_t units
-		size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
+		size_t length = utf8_decoder::process(data, size, 0, wchar_counter());
 
 		// allocate resulting string
@@ -1500,5 +2234,5 @@
 		{
 			wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
-			wchar_writer::value_type end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, begin);
+			wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer());
 
 			assert(begin + length == end);
@@ -1510,11 +2244,14 @@
 #endif
 
-	inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)
-	{
-		assert(target);
+	template <typename Header>
+	inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)
+	{
+		// never reuse shared memory
+		if (header & xml_memory_page_contents_shared_mask) return false;
+
 		size_t target_length = strlength(target);
 
 		// always reuse document buffer memory if possible
-		if (!allocated) return target_length >= length;
+		if ((header & header_mask) == 0) return target_length >= length;
 
 		// reuse heap memory if waste is not too great
@@ -1524,12 +2261,11 @@
 	}
 
-	PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
-	{
-		size_t source_length = strlength(source);
-
+	template <typename String, typename Header>
+	PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)
+	{
 		if (source_length == 0)
 		{
 			// empty string and null pointer are equivalent, so just deallocate old memory
-			xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+			xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
 
 			if (header & header_mask) alloc->deallocate_string(dest);
@@ -1541,8 +2277,9 @@
 			return true;
 		}
-		else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))
+		else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest))
 		{
 			// we can reuse old buffer, so just copy the new data (including zero terminator)
-			memcpy(dest, source, (source_length + 1) * sizeof(char_t));
+			memcpy(dest, source, source_length * sizeof(char_t));
+			dest[source_length] = 0;
 			
 			return true;
@@ -1550,5 +2287,7 @@
 		else
 		{
-			xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+			xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+
+			if (!alloc->reserve()) return false;
 
 			// allocate new buffer
@@ -1557,5 +2296,6 @@
 
 			// copy the string (including zero terminator)
-			memcpy(buf, source, (source_length + 1) * sizeof(char_t));
+			memcpy(buf, source, source_length * sizeof(char_t));
+			buf[source_length] = 0;
 
 			// deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
@@ -1654,5 +2394,5 @@
 					for (;;)
 					{
-						if (static_cast<unsigned int>(ch - '0') <= 9)
+						if (static_cast<unsigned int>(static_cast<unsigned int>(ch) - '0') <= 9)
 							ucsc = 10 * ucsc + (ch - '0');
 						else if (ch == ';')
@@ -1752,6 +2492,16 @@
 	}
 
-	// Utility macro for last character handling
-	#define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
+	// Parser utilities
+	#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__POPNODE()             { cursor = cursor->parent; }
+	#define PUGI__SCANFOR(X)            { while (*s != 0 && !(X)) ++s; }
+	#define PUGI__SCANWHILE(X)          { while (X) ++s; }
+	#define PUGI__SCANWHILE_UNROLL(X)   { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } }
+	#define PUGI__ENDSEG()              { ch = *s; *s = 0; ++s; }
+	#define PUGI__THROW_ERROR(err, m)   return error_offset = m, error_status = err, static_cast<char_t*>(0)
+	#define PUGI__CHECK_ERROR(err, m)   { if (*s == 0) PUGI__THROW_ERROR(err, m); }
 
 	PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
@@ -1761,5 +2511,5 @@
 		while (true)
 		{
-			while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;
+			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment));
 		
 			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
@@ -1769,5 +2519,5 @@
 				if (*s == '\n') g.push(s, 1);
 			}
-			else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here
+			else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
 			{
 				*g.flush(s) = 0;
@@ -1789,5 +2539,5 @@
 		while (true)
 		{
-			while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;
+			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata));
 			
 			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
@@ -1797,5 +2547,5 @@
 				if (*s == '\n') g.push(s, 1);
 			}
-			else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here
+			else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
 			{
 				*g.flush(s) = 0;
@@ -1813,17 +2563,25 @@
 	typedef char_t* (*strconv_pcdata_t)(char_t*);
 		
-	template <typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
+	template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
 	{
 		static char_t* parse(char_t* s)
 		{
 			gap g;
-			
+
+			char_t* begin = s;
+
 			while (true)
 			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;
-					
+				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata));
+
 				if (*s == '<') // PCDATA ends here
 				{
-					*g.flush(s) = 0;
+					char_t* end = g.flush(s);
+
+					if (opt_trim::value)
+						while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+							--end;
+
+					*end = 0;
 					
 					return s + 1;
@@ -1841,4 +2599,12 @@
 				else if (*s == 0)
 				{
+					char_t* end = g.flush(s);
+
+					if (opt_trim::value)
+						while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+							--end;
+
+					*end = 0;
+
 					return s;
 				}
@@ -1850,13 +2616,17 @@
 	PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
 	{
-		PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20);
-
-		switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes)
-		{
-		case 0: return strconv_pcdata_impl<opt_false, opt_false>::parse;
-		case 1: return strconv_pcdata_impl<opt_false, opt_true>::parse;
-		case 2: return strconv_pcdata_impl<opt_true, opt_false>::parse;
-		case 3: return strconv_pcdata_impl<opt_true, opt_true>::parse;
-		default: return 0; // should not get here
+		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)
+		{
+		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;
+		case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;
+		case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;
+		case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;
+		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
 		}
 	}
@@ -1883,5 +2653,5 @@
 			while (true)
 			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;
+				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
 				
 				if (*s == end_quote)
@@ -1924,5 +2694,5 @@
 			while (true)
 			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;
+				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws));
 				
 				if (*s == end_quote)
@@ -1960,5 +2730,5 @@
 			while (true)
 			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
+				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
 				
 				if (*s == end_quote)
@@ -1992,5 +2762,5 @@
 			while (true)
 			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
+				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
 				
 				if (*s == end_quote)
@@ -2035,5 +2805,5 @@
 		case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
 		case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
-		default: return 0; // should not get here
+		default: assert(false); return 0; // should not get here
 		}
 	}
@@ -2051,20 +2821,15 @@
 	{
 		xml_allocator alloc;
+		xml_allocator* alloc_state;
 		char_t* error_offset;
 		xml_parse_status error_status;
 		
-		// Parser utilities.
-		#define PUGI__SKIPWS()			{ while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
-		#define PUGI__OPTSET(OPT)			( optmsk & (OPT) )
-		#define PUGI__PUSHNODE(TYPE)		{ cursor = append_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; }
-		#define PUGI__ENDSEG()			{ ch = *s; *s = 0; ++s; }
-		#define PUGI__THROW_ERROR(err, m)	return error_offset = m, error_status = err, static_cast<char_t*>(0)
-		#define PUGI__CHECK_ERROR(err, m)	{ if (*s == 0) PUGI__THROW_ERROR(err, m); }
-		
-		xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
-		{
+		xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok)
+		{
+		}
+
+		~xml_parser()
+		{
+			*alloc_state = alloc;
 		}
 
@@ -2102,5 +2867,5 @@
 				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
 
-				s += 4;
+				s += 3;
 			}
 			else PUGI__THROW_ERROR(status_bad_doctype, s);
@@ -2111,6 +2876,8 @@
 		char_t* parse_doctype_ignore(char_t* s)
 		{
+			size_t depth = 0;
+
 			assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
-			s++;
+			s += 3;
 
 			while (*s)
@@ -2119,6 +2886,6 @@
 				{
 					// nested ignore section
-					s = parse_doctype_ignore(s);
-					if (!s) return s;
+					s += 3;
+					depth++;
 				}
 				else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
@@ -2127,5 +2894,8 @@
 					s += 3;
 
-					return s;
+					if (depth == 0)
+						return s;
+
+					depth--;
 				}
 				else s++;
@@ -2135,8 +2905,10 @@
 		}
 
-		char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
-		{
-			assert(s[0] == '<' && s[1] == '!');
-			s++;
+		char_t* parse_doctype_group(char_t* s, char_t endch)
+		{
+			size_t depth = 0;
+
+			assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
+			s += 2;
 
 			while (*s)
@@ -2153,6 +2925,6 @@
 					{
 						// some control group
-						s = parse_doctype_group(s, endch, false);
-						if (!s) return s;
+						s += 2;
+						depth++;
 					}
 				}
@@ -2165,12 +2937,14 @@
 				else if (*s == '>')
 				{
+					if (depth == 0)
+						return s;
+
+					depth--;
 					s++;
-
-					return s;
 				}
 				else s++;
 			}
 
-			if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
+			if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
 
 			return s;
@@ -2205,5 +2979,5 @@
 					{
 						// Scan for terminating '-->'.
-						PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));
+						PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>'));
 						PUGI__CHECK_ERROR(status_bad_comment, s);
 
@@ -2237,5 +3011,5 @@
 						{
 							// Scan for terminating ']]>'.
-							PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
+							PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
 							PUGI__CHECK_ERROR(status_bad_cdata, s);
 
@@ -2246,5 +3020,5 @@
 					{
 						// Scan for terminating ']]>'.
-						PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
+						PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
 						PUGI__CHECK_ERROR(status_bad_cdata, s);
 
@@ -2256,5 +3030,5 @@
 				else PUGI__THROW_ERROR(status_bad_cdata, s);
 			}
-			else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))
+			else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E'))
 			{
 				s -= 2;
@@ -2264,7 +3038,10 @@
 				char_t* mark = s + 9;
 
-				s = parse_doctype_group(s, endch, true);
+				s = parse_doctype_group(s, endch);
 				if (!s) return s;
 
+				assert((*s == 0 && endch == '>') || *s == '>');
+				if (*s) *s++ = 0;
+
 				if (PUGI__OPTSET(parse_doctype))
 				{
@@ -2274,9 +3051,4 @@
 
 					cursor->value = mark;
-
-					assert((s[0] == 0 && endch == '>') || s[-1] == '>');
-					s[*s == 0 ? 0 : -1] = 0;
-
-					PUGI__POPNODE();
 				}
 			}
@@ -2330,5 +3102,5 @@
 				{
 					// empty node
-					if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
+					if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
 					s += (*s == '>');
 
@@ -2342,5 +3114,5 @@
 					char_t* value = s;
 
-					PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
+					PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
 					PUGI__CHECK_ERROR(status_bad_pi, s);
 
@@ -2357,4 +3129,5 @@
 						// store value and step over >
 						cursor->value = value;
+
 						PUGI__POPNODE();
 
@@ -2369,5 +3142,5 @@
 			{
 				// scan for tag end
-				PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
+				PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
 				PUGI__CHECK_ERROR(status_bad_pi, s);
 
@@ -2381,5 +3154,5 @@
 		}
 
-		char_t* parse(char_t* s, xml_node_struct* xmldoc, unsigned int optmsk, char_t endch)
+		char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)
 		{
 			strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
@@ -2387,5 +3160,5 @@
 			
 			char_t ch = 0;
-			xml_node_struct* cursor = xmldoc;
+			xml_node_struct* cursor = root;
 			char_t* mark = s;
 
@@ -2403,5 +3176,5 @@
 						cursor->name = s;
 
-						PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
+						PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
 						PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
 
@@ -2419,19 +3192,15 @@
 								if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
 								{
-									xml_attribute_struct* a = append_attribute_ll(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.
 
-									PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
-									PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
+									PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
 									PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
-									PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
 
 									if (PUGI__IS_CHARTYPE(ch, ct_space))
 									{
 										PUGI__SKIPWS(); // Eat any whitespace.
-										PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
 
 										ch = *s;
@@ -2496,5 +3265,5 @@
 						else if (ch == '/') // '<#.../'
 						{
-							if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
+							if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
 
 							PUGI__POPNODE(); // Pop.
@@ -2549,5 +3318,5 @@
 
 						assert(cursor);
-						if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;
+						if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
 					}
 					else if (*s == '!') // '<!...'
@@ -2565,10 +3334,10 @@
 					PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
 
-					if (*s == '<')
+					if (*s == '<' || !*s)
 					{
 						// We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
 						assert(mark != s);
 
-						if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single))
+						if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))
 						{
 							continue;
@@ -2576,11 +3345,12 @@
 						else if (PUGI__OPTSET(parse_ws_pcdata_single))
 						{
-							if (s[1] != '/' || cursor->first_child) continue;
+							if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
 						}
 					}
 
-					s = mark;
+					if (!PUGI__OPTSET(parse_trim_pcdata))
+						s = mark;
 							
-					if (cursor->parent)
+					if (cursor->parent || PUGI__OPTSET(parse_fragment))
 					{
 						PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
@@ -2607,21 +3377,45 @@
 
 			// check that last tag is closed
-			if (cursor != xmldoc) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+			if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);
 
 			return s;
 		}
 
-		static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk)
-		{
-			xml_document_struct* xmldoc = static_cast<xml_document_struct*>(root);
-
-			// store buffer for offset_debug
-			xmldoc->buffer = buffer;
-
+	#ifdef PUGIXML_WCHAR_MODE
+		static char_t* parse_skip_bom(char_t* s)
+		{
+			unsigned int bom = 0xfeff;
+			return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;
+		}
+	#else
+		static char_t* parse_skip_bom(char_t* s)
+		{
+			return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s;
+		}
+	#endif
+
+		static bool has_element_node_siblings(xml_node_struct* node)
+		{
+			while (node)
+			{
+				if (PUGI__NODETYPE(node) == node_element) return true;
+
+				node = node->next_sibling;
+			}
+
+			return false;
+		}
+
+		static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
+		{
 			// early-out for empty documents
-			if (length == 0) return make_parse_result(status_ok);
-
+			if (length == 0)
+				return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
+
+			// get last child of the root before parsing
+			xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
+	
 			// create parser on stack
-			xml_parser parser(*xmldoc);
+			xml_parser parser(static_cast<xml_allocator*>(xmldoc));
 
 			// save last character and make buffer zero-terminated (speeds up parsing)
@@ -2629,18 +3423,30 @@
 			buffer[length - 1] = 0;
 			
+			// skip BOM to make sure it does not end up as part of parse output
+			char_t* buffer_data = parse_skip_bom(buffer);
+
 			// perform actual parsing
-			parser.parse(buffer, xmldoc, optmsk, endch);
+			parser.parse_tree(buffer_data, root, optmsk, endch);
 
 			xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
 			assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
 
-			// update allocator state
-			*static_cast<xml_allocator*>(xmldoc) = parser.alloc;
-
-			// since we removed last character, we have to handle the only possible false positive
-			if (result && endch == '<')
-			{
-				// there's no possible well-formed document with < at the end
-				return make_parse_result(status_unrecognized_tag, length);
+			if (result)
+			{
+				// since we removed last character, we have to handle the only possible false positive (stray <)
+				if (endch == '<')
+					return make_parse_result(status_unrecognized_tag, length - 1);
+
+				// check if there are any element nodes parsed
+				xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0;
+
+				if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
+					return make_parse_result(status_no_document_element, length - 1);
+			}
+			else
+			{
+				// roll back offset if it occurs on a null terminator in the source buffer
+				if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)
+					result.offset--;
 			}
 
@@ -2677,8 +3483,32 @@
 	}
 
+	template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T)
+	{
+		PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
+
+		typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
+
+		return static_cast<size_t>(end - dest) * sizeof(*dest);
+	}
+
+	template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap)
+	{
+		PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
+
+		typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
+
+		if (opt_swap)
+		{
+			for (typename T::value_type i = dest; i != end; ++i)
+				*i = endian_swap(*i);
+		}
+
+		return static_cast<size_t>(end - dest) * sizeof(*dest);
+	}
+
 #ifdef PUGIXML_WCHAR_MODE
 	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
 	{
-		assert(length > 0);
+		if (length < 1) return 0;
 
 		// discard last character if it's the lead of a surrogate pair 
@@ -2686,5 +3516,5 @@
 	}
 
-	PUGI__FN size_t convert_buffer(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+	PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
 	{
 		// only endian-swapping is required
@@ -2698,25 +3528,12 @@
 		// convert to utf8
 		if (encoding == encoding_utf8)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(data, length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
+			return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer());
 
 		// convert to utf16
 		if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
 		{
-			uint16_t* dest = r_u16;
-
-			// convert to native utf16
-			uint16_t* end = utf_decoder<utf16_writer>::decode_wchar_block(data, length, dest);
-
-			// swap if necessary
 			xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
 
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint16_t);
+			return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding);
 		}
 
@@ -2724,25 +3541,12 @@
 		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
 		{
-			uint32_t* dest = r_u32;
-
-			// convert to native utf32
-			uint32_t* end = utf_decoder<utf32_writer>::decode_wchar_block(data, length, dest);
-
-			// swap if necessary
 			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
 
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint32_t);
+			return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding);
 		}
 
 		// convert to latin1
 		if (encoding == encoding_latin1)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<latin1_writer>::decode_wchar_block(data, length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
+			return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
 
 		assert(!"Invalid encoding");
@@ -2752,5 +3556,5 @@
 	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
 	{
-		assert(length > 4);
+		if (length < 5) return 0;
 
 		for (size_t i = 1; i <= 4; ++i)
@@ -2766,43 +3570,22 @@
 	}
 
-	PUGI__FN size_t convert_buffer(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+	PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
 	{
 		if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
 		{
-			uint16_t* dest = r_u16;
-
-			// convert to native utf16
-			uint16_t* end = utf_decoder<utf16_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			// swap if necessary
 			xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
 
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint16_t);
+			return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding);
 		}
 
 		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
 		{
-			uint32_t* dest = r_u32;
-
-			// convert to native utf32
-			uint32_t* end = utf_decoder<utf32_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			// swap if necessary
 			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
 
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint32_t);
+			return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding);
 		}
 
 		if (encoding == encoding_latin1)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<latin1_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
+			return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
 
 		assert(!"Invalid encoding");
@@ -2822,13 +3605,9 @@
 		}
 
-		~xml_buffered_writer()
-		{
-			flush();
-		}
-
-		void flush()
+		size_t flush()
 		{
 			flush(buffer, bufsize);
 			bufsize = 0;
+			return 0;
 		}
 
@@ -2843,5 +3622,5 @@
 			{
 				// convert chunk
-				size_t result = convert_buffer(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
+				size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
 				assert(result <= sizeof(scratch));
 
@@ -2851,39 +3630,37 @@
 		}
 
-		void write(const char_t* data, size_t length)
-		{
-			if (bufsize + length > bufcapacity)
-			{
-				// flush the remaining buffer contents
-				flush();
-
-				// handle large chunks
-				if (length > bufcapacity)
-				{
-					if (encoding == get_write_native_encoding())
-					{
-						// fast path, can just write data chunk
-						writer.write(data, length * sizeof(char_t));
-						return;
-					}
-
-					// need to convert in suitable chunks
-					while (length > bufcapacity)
-					{
-						// get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
-						// and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
-						size_t chunk_size = get_valid_length(data, bufcapacity);
-
-						// convert chunk and write
-						flush(data, chunk_size);
-
-						// iterate
-						data += chunk_size;
-						length -= chunk_size;
-					}
-
-					// small tail is copied below
-					bufsize = 0;
-				}
+		void write_direct(const char_t* data, size_t length)
+		{
+			// flush the remaining buffer contents
+			flush();
+
+			// handle large chunks
+			if (length > bufcapacity)
+			{
+				if (encoding == get_write_native_encoding())
+				{
+					// fast path, can just write data chunk
+					writer.write(data, length * sizeof(char_t));
+					return;
+				}
+
+				// need to convert in suitable chunks
+				while (length > bufcapacity)
+				{
+					// get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
+					// and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
+					size_t chunk_size = get_valid_length(data, bufcapacity);
+					assert(chunk_size);
+
+					// convert chunk and write
+					flush(data, chunk_size);
+
+					// iterate
+					data += chunk_size;
+					length -= chunk_size;
+				}
+
+				// small tail is copied below
+				bufsize = 0;
 			}
 
@@ -2892,70 +3669,111 @@
 		}
 
-		void write(const char_t* data)
-		{
-			write(data, strlength(data));
+		void write_buffer(const char_t* data, size_t length)
+		{
+			size_t offset = bufsize;
+
+			if (offset + length <= bufcapacity)
+			{
+				memcpy(buffer + offset, data, length * sizeof(char_t));
+				bufsize = offset + length;
+			}
+			else
+			{
+				write_direct(data, length);
+			}
+		}
+
+		void write_string(const char_t* data)
+		{
+			// write the part of the string that fits in the buffer
+			size_t offset = bufsize;
+
+			while (*data && offset < bufcapacity)
+				buffer[offset++] = *data++;
+
+			// write the rest
+			if (offset < bufcapacity)
+			{
+				bufsize = offset;
+			}
+			else
+			{
+				// backtrack a bit if we have split the codepoint
+				size_t length = offset - bufsize;
+				size_t extra = length - get_valid_length(data - length, length);
+
+				bufsize = offset - extra;
+
+				write_direct(data - extra, strlength(data) + extra);
+			}
 		}
 
 		void write(char_t d0)
 		{
-			if (bufsize + 1 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			bufsize += 1;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 1) offset = flush();
+
+			buffer[offset + 0] = d0;
+			bufsize = offset + 1;
 		}
 
 		void write(char_t d0, char_t d1)
 		{
-			if (bufsize + 2 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			bufsize += 2;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 2) offset = flush();
+
+			buffer[offset + 0] = d0;
+			buffer[offset + 1] = d1;
+			bufsize = offset + 2;
 		}
 
 		void write(char_t d0, char_t d1, char_t d2)
 		{
-			if (bufsize + 3 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			bufsize += 3;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 3) offset = flush();
+
+			buffer[offset + 0] = d0;
+			buffer[offset + 1] = d1;
+			buffer[offset + 2] = d2;
+			bufsize = offset + 3;
 		}
 
 		void write(char_t d0, char_t d1, char_t d2, char_t d3)
 		{
-			if (bufsize + 4 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			bufsize += 4;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 4) offset = flush();
+
+			buffer[offset + 0] = d0;
+			buffer[offset + 1] = d1;
+			buffer[offset + 2] = d2;
+			buffer[offset + 3] = d3;
+			bufsize = offset + 4;
 		}
 
 		void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
 		{
-			if (bufsize + 5 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			buffer[bufsize + 4] = d4;
-			bufsize += 5;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 5) offset = flush();
+
+			buffer[offset + 0] = d0;
+			buffer[offset + 1] = d1;
+			buffer[offset + 2] = d2;
+			buffer[offset + 3] = d3;
+			buffer[offset + 4] = d4;
+			bufsize = offset + 5;
 		}
 
 		void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
 		{
-			if (bufsize + 6 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			buffer[bufsize + 4] = d4;
-			buffer[bufsize + 5] = d5;
-			bufsize += 6;
+			size_t offset = bufsize;
+			if (offset > bufcapacity - 6) offset = flush();
+
+			buffer[offset + 0] = d0;
+			buffer[offset + 1] = d1;
+			buffer[offset + 2] = d2;
+			buffer[offset + 3] = d3;
+			buffer[offset + 4] = d4;
+			buffer[offset + 5] = d5;
+			bufsize = offset + 6;
 		}
 
@@ -2997,7 +3815,7 @@
 			
 			// While *s is a usual symbol
-			while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;
+			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type));
 		
-			writer.write(prev, static_cast<size_t>(s - prev));
+			writer.write_buffer(prev, static_cast<size_t>(s - prev));
 
 			switch (*s)
@@ -3034,5 +3852,5 @@
 	{
 		if (flags & format_no_escapes)
-			writer.write(s);
+			writer.write_string(s);
 		else
 			text_output_escaped(writer, s, type);
@@ -3054,5 +3872,5 @@
 			if (*s) s += 2;
 
-			writer.write(prev, static_cast<size_t>(s - prev));
+			writer.write_buffer(prev, static_cast<size_t>(s - prev));
 
 			writer.write(']', ']', '>');
@@ -3061,15 +3879,112 @@
 	}
 
-	PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
+	PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
+	{
+		switch (indent_length)
+		{
+		case 1:
+		{
+			for (unsigned int i = 0; i < depth; ++i)
+				writer.write(indent[0]);
+			break;
+		}
+
+		case 2:
+		{
+			for (unsigned int i = 0; i < depth; ++i)
+				writer.write(indent[0], indent[1]);
+			break;
+		}
+
+		case 3:
+		{
+			for (unsigned int i = 0; i < depth; ++i)
+				writer.write(indent[0], indent[1], indent[2]);
+			break;
+		}
+
+		case 4:
+		{
+			for (unsigned int i = 0; i < depth; ++i)
+				writer.write(indent[0], indent[1], indent[2], indent[3]);
+			break;
+		}
+
+		default:
+		{
+			for (unsigned int i = 0; i < depth; ++i)
+				writer.write_buffer(indent, indent_length);
+		}
+		}
+	}
+
+	PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
+	{
+		writer.write('<', '!', '-', '-');
+
+		while (*s)
+		{
+			const char_t* prev = s;
+
+			// look for -\0 or -- sequence - we can't output it since -- is illegal in comment body
+			while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;
+
+			writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+			if (*s)
+			{
+				assert(*s == '-');
+
+				writer.write('-', ' ');
+				++s;
+			}
+		}
+
+		writer.write('-', '-', '>');
+	}
+
+	PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
+	{
+		while (*s)
+		{
+			const char_t* prev = s;
+
+			// look for ?> sequence - we can't output it since ?> terminates PI
+			while (*s && !(s[0] == '?' && s[1] == '>')) ++s;
+
+			writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+			if (*s)
+			{
+				assert(s[0] == '?' && s[1] == '>');
+
+				writer.write('?', ' ', '>');
+				s += 2;
+			}
+		}
+	}
+
+	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");
 
-		for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
-		{
-			writer.write(' ');
-			writer.write(a.name()[0] ? a.name() : default_name);
+		for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
+		{
+			if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes)
+			{
+				writer.write('\n');
+
+				text_output_indent(writer, indent, indent_length, depth + 1);
+			}
+			else
+			{
+				writer.write(' ');
+			}
+
+			writer.write_string(a->name ? a->name + 0 : default_name);
 			writer.write('=', '"');
 
-			text_output(writer, a.value(), ctx_special_attr, flags);
+			if (a->value)
+				text_output(writer, a->value, ctx_special_attr, flags);
 
 			writer.write('"');
@@ -3077,138 +3992,195 @@
 	}
 
-	PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)
+	PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
-
-		if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
-			for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-
-		switch (node.type())
-		{
-		case node_document:
-		{
-			for (xml_node n = node.first_child(); n; n = n.next_sibling())
-				node_output(writer, n, indent, flags, depth);
-			break;
-		}
-			
-		case node_element:
-		{
-			const char_t* name = node.name()[0] ? node.name() : default_name;
-
-			writer.write('<');
-			writer.write(name);
-
-			node_output_attributes(writer, node, flags);
-
-			if (flags & format_raw)
-			{
-				if (!node.first_child())
-					writer.write(' ', '/', '>');
+		const char_t* name = node->name ? node->name + 0 : default_name;
+
+		writer.write('<');
+		writer.write_string(name);
+
+		if (node->first_attribute)
+			node_output_attributes(writer, node, indent, indent_length, flags, depth);
+
+		if (!node->first_child)
+		{
+			writer.write(' ', '/', '>');
+
+			return false;
+		}
+		else
+		{
+			writer.write('>');
+
+			return true;
+		}
+	}
+
+	PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node)
+	{
+		const char_t* default_name = PUGIXML_TEXT(":anonymous");
+		const char_t* name = node->name ? node->name + 0 : default_name;
+
+		writer.write('<', '/');
+		writer.write_string(name);
+		writer.write('>');
+	}
+
+	PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+	{
+		const char_t* default_name = PUGIXML_TEXT(":anonymous");
+
+		switch (PUGI__NODETYPE(node))
+		{
+			case node_pcdata:
+				text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
+				break;
+
+			case node_cdata:
+				text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
+				break;
+
+			case node_comment:
+				node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
+				break;
+
+			case node_pi:
+				writer.write('<', '?');
+				writer.write_string(node->name ? node->name + 0 : default_name);
+
+				if (node->value)
+				{
+					writer.write(' ');
+					node_output_pi_value(writer, node->value);
+				}
+
+				writer.write('?', '>');
+				break;
+
+			case node_declaration:
+				writer.write('<', '?');
+				writer.write_string(node->name ? node->name + 0 : default_name);
+				node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0);
+				writer.write('?', '>');
+				break;
+
+			case node_doctype:
+				writer.write('<', '!', 'D', 'O', 'C');
+				writer.write('T', 'Y', 'P', 'E');
+
+				if (node->value)
+				{
+					writer.write(' ');
+					writer.write_string(node->value);
+				}
+
+				writer.write('>');
+				break;
+
+			default:
+				assert(!"Invalid node type");
+		}
+	}
+
+	enum indent_flags_t
+	{
+		indent_newline = 1,
+		indent_indent = 2
+	};
+
+	PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
+	{
+		size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0;
+		unsigned int indent_flags = indent_indent;
+
+		xml_node_struct* node = root;
+
+		do
+		{
+			assert(node);
+
+			// begin writing current node
+			if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
+			{
+				node_output_simple(writer, node, flags);
+
+				indent_flags = 0;
+			}
+			else
+			{
+				if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+					writer.write('\n');
+
+				if ((indent_flags & indent_indent) && indent_length)
+					text_output_indent(writer, indent, indent_length, depth);
+
+				if (PUGI__NODETYPE(node) == node_element)
+				{
+					indent_flags = indent_newline | indent_indent;
+
+					if (node_output_start(writer, node, indent, indent_length, flags, depth))
+					{
+						node = node->first_child;
+						depth++;
+						continue;
+					}
+				}
+				else if (PUGI__NODETYPE(node) == node_document)
+				{
+					indent_flags = indent_indent;
+
+					if (node->first_child)
+					{
+						node = node->first_child;
+						continue;
+					}
+				}
 				else
 				{
-					writer.write('>');
-
-					for (xml_node n = node.first_child(); n; n = n.next_sibling())
-						node_output(writer, n, indent, flags, depth + 1);
-
-					writer.write('<', '/');
-					writer.write(name);
-					writer.write('>');
-				}
-			}
-			else if (!node.first_child())
-				writer.write(' ', '/', '>', '\n');
-			else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
-			{
-				writer.write('>');
-
-				if (node.first_child().type() == node_pcdata)
-					text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
-				else
-					text_output_cdata(writer, node.first_child().value());
-
-				writer.write('<', '/');
-				writer.write(name);
-				writer.write('>', '\n');
-			}
-			else
-			{
-				writer.write('>', '\n');
-				
-				for (xml_node n = node.first_child(); n; n = n.next_sibling())
-					node_output(writer, n, indent, flags, depth + 1);
-
-				if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
-					for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-				
-				writer.write('<', '/');
-				writer.write(name);
-				writer.write('>', '\n');
-			}
-
-			break;
-		}
-		
-		case node_pcdata:
-			text_output(writer, node.value(), ctx_special_pcdata, flags);
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_cdata:
-			text_output_cdata(writer, node.value());
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_comment:
-			writer.write('<', '!', '-', '-');
-			writer.write(node.value());
-			writer.write('-', '-', '>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_pi:
-		case node_declaration:
-			writer.write('<', '?');
-			writer.write(node.name()[0] ? node.name() : default_name);
-
-			if (node.type() == node_declaration)
-			{
-				node_output_attributes(writer, node, flags);
-			}
-			else if (node.value()[0])
-			{
-				writer.write(' ');
-				writer.write(node.value());
-			}
-
-			writer.write('?', '>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_doctype:
-			writer.write('<', '!', 'D', 'O', 'C');
-			writer.write('T', 'Y', 'P', 'E');
-
-			if (node.value()[0])
-			{
-				writer.write(' ');
-				writer.write(node.value());
-			}
-
-			writer.write('>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		default:
-			assert(!"Invalid node type");
-		}
-	}
-
-	inline bool has_declaration(const xml_node& node)
-	{
-		for (xml_node child = node.first_child(); child; child = child.next_sibling())
-		{
-			xml_node_type type = child.type();
+					node_output_simple(writer, node, flags);
+
+					indent_flags = indent_newline | indent_indent;
+				}
+			}
+
+			// continue to the next node
+			while (node != root)
+			{
+				if (node->next_sibling)
+				{
+					node = node->next_sibling;
+					break;
+				}
+
+				node = node->parent;
+
+				// write closing node
+				if (PUGI__NODETYPE(node) == node_element)
+				{
+					depth--;
+
+					if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+						writer.write('\n');
+
+					if ((indent_flags & indent_indent) && indent_length)
+						text_output_indent(writer, indent, indent_length, depth);
+
+					node_output_end(writer, node);
+
+					indent_flags = indent_newline | indent_indent;
+				}
+			}
+		}
+		while (node != root);
+
+		if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+			writer.write('\n');
+	}
+
+	PUGI__FN bool has_declaration(xml_node_struct* node)
+	{
+		for (xml_node_struct* child = node->first_child; child; child = child->next_sibling)
+		{
+			xml_node_type type = PUGI__NODETYPE(child);
 
 			if (type == node_declaration) return true;
@@ -3219,5 +4191,19 @@
 	}
 
-	inline bool allow_insert_child(xml_node_type parent, xml_node_type child)
+	PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)
+	{
+		for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
+			if (a == attr)
+				return true;
+
+		return false;
+	}
+
+	PUGI__FN bool allow_insert_attribute(xml_node_type parent)
+	{
+		return parent == node_element || parent == node_declaration;
+	}
+
+	PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
 	{
 		if (parent != node_document && parent != node_element) return false;
@@ -3228,60 +4214,122 @@
 	}
 
-	PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)
-	{
-		assert(dest.type() == source.type());
-
-		switch (source.type())
-		{
-		case node_element:
-		{
-			dest.set_name(source.name());
-
-			for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
-				dest.append_attribute(a.name()).set_value(a.value());
-
-			for (xml_node c = source.first_child(); c; c = c.next_sibling())
-			{
-				if (c == skip) continue;
-
-				xml_node cc = dest.append_child(c.type());
-				assert(cc);
-
-				recursive_copy_skip(cc, c, skip);
-			}
-
-			break;
-		}
-
-		case node_pcdata:
-		case node_cdata:
-		case node_comment:
-		case node_doctype:
-			dest.set_value(source.value());
-			break;
-
-		case node_pi:
-			dest.set_name(source.name());
-			dest.set_value(source.value());
-			break;
-
-		case node_declaration:
-		{
-			dest.set_name(source.name());
-
-			for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
-				dest.append_attribute(a.name()).set_value(a.value());
-
-			break;
-		}
-
-		default:
-			assert(!"Invalid node type");
-		}
+	PUGI__FN bool allow_move(xml_node parent, xml_node child)
+	{
+		// check that child can be a child of parent
+		if (!allow_insert_child(parent.type(), child.type()))
+			return false;
+
+		// check that node is not moved between documents
+		if (parent.root() != child.root())
+			return false;
+
+		// check that new parent is not in the child subtree
+		xml_node cur = parent;
+
+		while (cur)
+		{
+			if (cur == child)
+				return false;
+
+			cur = cur.parent();
+		}
+
+		return true;
+	}
+
+	template <typename String, typename Header>
+	PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)
+	{
+		assert(!dest && (header & header_mask) == 0);
+
+		if (source)
+		{
+			if (alloc && (source_header & header_mask) == 0)
+			{
+				dest = source;
+
+				// since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared
+				header |= xml_memory_page_contents_shared_mask;
+				source_header |= xml_memory_page_contents_shared_mask;
+			}
+			else
+				strcpy_insitu(dest, header, header_mask, source, strlength(source));
+		}
+	}
+
+	PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc)
+	{
+		node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc);
+		node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc);
+
+		for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute)
+		{
+			xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
+
+			if (da)
+			{
+				node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
+				node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
+			}
+		}
+	}
+
+	PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)
+	{
+		xml_allocator& alloc = get_allocator(dn);
+		xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0;
+
+		node_copy_contents(dn, sn, shared_alloc);
+
+		xml_node_struct* dit = dn;
+		xml_node_struct* sit = sn->first_child;
+
+		while (sit && sit != sn)
+		{
+			if (sit != dn)
+			{
+				xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
+
+				if (copy)
+				{
+					node_copy_contents(copy, sit, shared_alloc);
+
+					if (sit->first_child)
+					{
+						dit = copy;
+						sit = sit->first_child;
+						continue;
+					}
+				}
+			}
+
+			// continue to the next node
+			do
+			{
+				if (sit->next_sibling)
+				{
+					sit = sit->next_sibling;
+					break;
+				}
+
+				sit = sit->parent;
+				dit = dit->parent;
+			}
+			while (sit != sn);
+		}
+	}
+
+	PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)
+	{
+		xml_allocator& alloc = get_allocator(da);
+		xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0;
+
+		node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
+		node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
 	}
 
 	inline bool is_text_node(xml_node_struct* node)
 	{
-		xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
+		xml_node_type type = PUGI__NODETYPE(node);
 
 		return type == node_pcdata || type == node_cdata;
@@ -3289,30 +4337,91 @@
 
 	// get value with conversion functions
-	PUGI__FN int get_value_int(const char_t* value, int def)
-	{
-		if (!value) return def;
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return static_cast<int>(wcstol(value, 0, 10));
-	#else
-		return static_cast<int>(strtol(value, 0, 10));
-	#endif
-	}
-
-	PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)
-	{
-		if (!value) return def;
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return static_cast<unsigned int>(wcstoul(value, 0, 10));
-	#else
-		return static_cast<unsigned int>(strtoul(value, 0, 10));
-	#endif
-	}
-
-	PUGI__FN double get_value_double(const char_t* value, double def)
-	{
-		if (!value) return def;
-
+	template <typename U> U string_to_integer(const char_t* value, U minneg, U maxpos)
+	{
+		U result = 0;
+		const char_t* s = value;
+
+		while (PUGI__IS_CHARTYPE(*s, ct_space))
+			s++;
+
+		bool negative = (*s == '-');
+
+		s += (*s == '+' || *s == '-');
+
+		bool overflow = false;
+
+		if (s[0] == '0' && (s[1] | ' ') == 'x')
+		{
+			s += 2;
+
+			// since overflow detection relies on length of the sequence skip leading zeros
+			while (*s == '0')
+				s++;
+
+			const char_t* start = s;
+
+			for (;;)
+			{
+				if (static_cast<unsigned>(*s - '0') < 10)
+					result = result * 16 + (*s - '0');
+				else if (static_cast<unsigned>((*s | ' ') - 'a') < 6)
+					result = result * 16 + ((*s | ' ') - 'a' + 10);
+				else
+					break;
+
+				s++;
+			}
+
+			size_t digits = static_cast<size_t>(s - start);
+
+			overflow = digits > sizeof(U) * 2;
+		}
+		else
+		{
+			// since overflow detection relies on length of the sequence skip leading zeros
+			while (*s == '0')
+				s++;
+
+			const char_t* start = s;
+
+			for (;;)
+			{
+				if (static_cast<unsigned>(*s - '0') < 10)
+					result = result * 10 + (*s - '0');
+				else
+					break;
+
+				s++;
+			}
+
+			size_t digits = static_cast<size_t>(s - start);
+
+			PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);
+
+			const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5;
+			const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6';
+			const size_t high_bit = sizeof(U) * 8 - 1;
+
+			overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit)));
+		}
+
+		if (negative)
+			return (overflow || result > minneg) ? 0 - minneg : 0 - result;
+		else
+			return (overflow || result > maxpos) ? maxpos : 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);
+	}
+
+	PUGI__FN unsigned int get_value_uint(const char_t* value)
+	{
+		return string_to_integer<unsigned int>(value, 0, UINT_MAX);
+	}
+
+	PUGI__FN double get_value_double(const char_t* value)
+	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return wcstod(value, 0);
@@ -3322,8 +4431,6 @@
 	}
 
-	PUGI__FN float get_value_float(const char_t* value, float def)
-	{
-		if (!value) return def;
-
+	PUGI__FN float get_value_float(const char_t* value)
+	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return static_cast<float>(wcstod(value, 0));
@@ -3333,8 +4440,6 @@
 	}
 
-	PUGI__FN bool get_value_bool(const char_t* value, bool def)
-	{
-		if (!value) return def;
-
+	PUGI__FN bool get_value_bool(const char_t* value)
+	{
 		// only look at first char
 		char_t first = *value;
@@ -3344,44 +4449,150 @@
 	}
 
+#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);
+	}
+
+	PUGI__FN unsigned long long get_value_ullong(const char_t* value)
+	{
+		return string_to_integer<unsigned long long>(value, 0, ULLONG_MAX);
+	}
+#endif
+
+	template <typename U>
+	PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
+	{
+		char_t* result = end - 1;
+		U rest = negative ? 0 - value : value;
+
+		do
+		{
+			*result-- = static_cast<char_t>('0' + (rest % 10));
+			rest /= 10;
+		}
+		while (rest);
+
+		assert(result >= begin);
+		(void)begin;
+
+		*result = '-';
+
+		return result + !negative;
+	}
+
 	// set value with conversion functions
-	PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		char_t wbuf[128];
-		impl::widen_ascii(wbuf, buf);
-
-		return strcpy_insitu(dest, header, header_mask, wbuf);
+		assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0]));
+
+		size_t offset = 0;
+		for (; buf[offset]; ++offset) wbuf[offset] = buf[offset];
+
+		return strcpy_insitu(dest, header, header_mask, wbuf, offset);
 	#else
-		return strcpy_insitu(dest, header, header_mask, buf);
+		return strcpy_insitu(dest, header, header_mask, buf, strlen(buf));
 	#endif
 	}
 
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value)
+	{
+		char_t buf[64];
+		char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
+		char_t* begin = integer_to_string<unsigned int>(buf, end, value, value < 0);
+
+		return strcpy_insitu(dest, header, header_mask, begin, end - begin);
+	}
+
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned int value)
+	{
+		char_t buf[64];
+		char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
+		char_t* begin = integer_to_string<unsigned int>(buf, end, value, false);
+
+		return strcpy_insitu(dest, header, header_mask, begin, end - begin);
+	}
+
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
 	{
 		char buf[128];
-		sprintf(buf, "%d", value);
+		sprintf(buf, "%.9g", 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, double value)
+	{
+		char buf[128];
+		sprintf(buf, "%.17g", value);
+
+		return set_value_ascii(dest, header, header_mask, buf);
+	}
 	
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
-	{
-		char buf[128];
-		sprintf(buf, "%u", value);
-
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
-	{
-		char buf[128];
-		sprintf(buf, "%g", value);
-
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-	
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
-	{
-		return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(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);
+	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, long long value)
+	{
+		char_t buf[64];
+		char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
+		char_t* begin = integer_to_string<unsigned long long>(buf, end, value, value < 0);
+
+		return strcpy_insitu(dest, header, header_mask, begin, end - begin);
+	}
+
+	template <typename String, typename Header>
+	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned long long value)
+	{
+		char_t buf[64];
+		char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
+		char_t* begin = integer_to_string<unsigned long long>(buf, end, value, false);
+
+		return strcpy_insitu(dest, header, header_mask, begin, end - begin);
+	}
+#endif
+
+	PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)
+	{
+		// check input buffer
+		if (!contents && size) return make_parse_result(status_io_error);
+
+		// get actual encoding
+		xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
+
+		// get private buffer
+		char_t* buffer = 0;
+		size_t length = 0;
+
+		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
+		if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
+
+		// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
+		if (own || buffer != contents) *out_buffer = buffer;
+
+		// store buffer for offset_debug
+		doc->buffer = buffer;
+
+		// parse
+		xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);
+
+		// remember encoding
+		res.encoding = buffer_encoding;
+
+		return res;
 	}
 
@@ -3396,5 +4607,5 @@
 		length_type length = _ftelli64(file);
 		_fseeki64(file, 0, SEEK_SET);
-	#elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)
+	#elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))
 		// there are 64-bit versions of fseek/ftell, let's use them
 		typedef off64_t length_type;
@@ -3426,5 +4637,30 @@
 	}
 
-	PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
+	// This function assumes that buffer has extra sizeof(char_t) writable bytes after size
+	PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) 
+	{
+		// We only need to zero-terminate if encoding conversion does not do it for us
+	#ifdef PUGIXML_WCHAR_MODE
+		xml_encoding wchar_encoding = get_wchar_encoding();
+
+		if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))
+		{
+			size_t length = size / sizeof(char_t);
+
+			static_cast<char_t*>(buffer)[length] = 0;
+			return (length + 1) * sizeof(char_t);
+		}
+	#else
+		if (encoding == encoding_utf8)
+		{
+			static_cast<char*>(buffer)[size] = 0;
+			return size + 1;
+		}
+	#endif
+
+		return size;
+	}
+
+	PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer)
 	{
 		if (!file) return make_parse_result(status_file_not_found);
@@ -3433,23 +4669,14 @@
 		size_t size = 0;
 		xml_parse_status size_status = get_file_size(file, size);
-
-		if (size_status != status_ok)
-		{
-			fclose(file);
-			return make_parse_result(size_status);
-		}
+		if (size_status != status_ok) return make_parse_result(size_status);
 		
+		size_t max_suffix_size = sizeof(char_t);
+
 		// allocate buffer for the whole file
-		char* contents = static_cast<char*>(xml_memory::allocate(size > 0 ? size : 1));
-
-		if (!contents)
-		{
-			fclose(file);
-			return make_parse_result(status_out_of_memory);
-		}
+		char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
+		if (!contents) return make_parse_result(status_out_of_memory);
 
 		// read file in memory
 		size_t read_size = fread(contents, 1, size, file);
-		fclose(file);
 
 		if (read_size != size)
@@ -3458,6 +4685,8 @@
 			return make_parse_result(status_io_error);
 		}
-		
-		return doc.load_buffer_inplace_own(contents, size, options, encoding);
+
+		xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
+
+		return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);
 	}
 
@@ -3468,18 +4697,19 @@
 		{
 			void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
+			if (!memory) return 0;
 			
 			return new (memory) xml_stream_chunk();
 		}
 
-		static void destroy(void* ptr)
-		{
-			xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);
-
+		static void destroy(xml_stream_chunk* chunk)
+		{
 			// free chunk chain
 			while (chunk)
 			{
-				xml_stream_chunk* next = chunk->next;
+				xml_stream_chunk* next_ = chunk->next;
+
 				xml_memory::deallocate(chunk);
-				chunk = next;
+
+				chunk = next_;
 			}
 		}
@@ -3497,5 +4727,5 @@
 	template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
 	{
-		buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
+		auto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);
 
 		// read file to a chunk list
@@ -3525,11 +4755,13 @@
 		}
 
+		size_t max_suffix_size = sizeof(char_t);
+
 		// copy chunk list to a contiguous buffer
-		char* buffer = static_cast<char*>(xml_memory::allocate(total));
+		char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));
 		if (!buffer) return status_out_of_memory;
 
 		char* write = buffer;
 
-		for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)
+		for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)
 		{
 			assert(write + chunk->size <= buffer + total);
@@ -3562,6 +4794,8 @@
 		if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
 
+		size_t max_suffix_size = sizeof(char_t);
+
 		// read stream data into memory (guard against stream exceptions with buffer holder)
-		buffer_holder buffer(xml_memory::allocate((read_length > 0 ? read_length : 1) * sizeof(T)), xml_memory::deallocate);
+		auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
 		if (!buffer.data) return status_out_of_memory;
 
@@ -3574,5 +4808,5 @@
 		size_t actual_length = static_cast<size_t>(stream.gcount());
 		assert(actual_length <= read_length);
-
+		
 		*out_buffer = buffer.release();
 		*out_size = actual_length * sizeof(T);
@@ -3581,18 +4815,31 @@
 	}
 
-	template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)
+	template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer)
 	{
 		void* buffer = 0;
 		size_t size = 0;
+		xml_parse_status status = status_ok;
+
+		// if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)
+		if (stream.fail()) return make_parse_result(status_io_error);
 
 		// load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
-		xml_parse_status status = (stream.tellg() < 0) ? load_stream_data_noseek(stream, &buffer, &size) : load_stream_data_seek(stream, &buffer, &size);
+		if (stream.tellg() < 0)
+		{
+			stream.clear(); // clear error flags that could be set by a failing tellg
+			status = load_stream_data_noseek(stream, &buffer, &size);
+		}
+		else
+			status = load_stream_data_seek(stream, &buffer, &size);
+
 		if (status != status_ok) return make_parse_result(status);
 
-		return doc.load_buffer_inplace_own(buffer, size, options, encoding);
+		xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);
+		
+		return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer);
 	}
 #endif
 
-#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
+#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
 	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
 	{
@@ -3605,5 +4852,5 @@
 
 		// first pass: get length in utf8 characters
-		size_t length = wcslen(str);
+		size_t length = strlength_wide(str);
 		size_t size = as_utf8_begin(str, length);
 
@@ -3615,4 +4862,7 @@
 		as_utf8_end(result, size, str, length);
 
+		// zero-terminate
+		result[size] = 0;
+
 		return result;
 	}
@@ -3645,10 +4895,22 @@
 		doc.save(writer, indent, flags, encoding);
 
-		int result = ferror(file);
-
-		fclose(file);
-
-		return result == 0;
-	}
+		return ferror(file) == 0;
+	}
+
+	struct name_null_sentry
+	{
+		xml_node_struct* node;
+		char_t* name;
+
+		name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name)
+		{
+			node->name = 0;
+		}
+
+		~name_null_sentry()
+		{
+			node->name = name;
+		}
+	};
 PUGI__NS_END
 
@@ -3778,31 +5040,43 @@
 	PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
 	{
-		return (_attr && _attr->value) ? _attr->value : def;
+		return (_attr && _attr->value) ? _attr->value + 0 : def;
 	}
 
 	PUGI__FN int xml_attribute::as_int(int def) const
 	{
-		return impl::get_value_int(_attr ? _attr->value : 0, def);
+		return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
 	}
 
 	PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
 	{
-		return impl::get_value_uint(_attr ? _attr->value : 0, def);
+		return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
 	}
 
 	PUGI__FN double xml_attribute::as_double(double def) const
 	{
-		return impl::get_value_double(_attr ? _attr->value : 0, def);
+		return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
 	}
 
 	PUGI__FN float xml_attribute::as_float(float def) const
 	{
-		return impl::get_value_float(_attr ? _attr->value : 0, def);
+		return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
 	}
 
 	PUGI__FN bool xml_attribute::as_bool(bool def) const
 	{
-		return impl::get_value_bool(_attr ? _attr->value : 0, def);
-	}
+		return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
+	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN long long xml_attribute::as_llong(long long def) const
+	{
+		return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
+	}
+
+	PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
+	{
+		return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
+	}
+#endif
 
 	PUGI__FN bool xml_attribute::empty() const
@@ -3813,10 +5087,10 @@
 	PUGI__FN const char_t* xml_attribute::name() const
 	{
-		return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");
+		return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
 	}
 
 	PUGI__FN const char_t* xml_attribute::value() const
 	{
-		return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
+		return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
 	}
 
@@ -3855,5 +5129,5 @@
 	}
 	
-	PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
+	PUGI__FN xml_attribute& xml_attribute::operator=(float rhs)
 	{
 		set_value(rhs);
@@ -3861,9 +5135,29 @@
 	}
 
+	PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
+	{
+		set_value(rhs);
+		return *this;
+	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs)
+	{
+		set_value(rhs);
+		return *this;
+	}
+
+	PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)
+	{
+		set_value(rhs);
+		return *this;
+	}
+#endif
+
 	PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
 	{
 		if (!_attr) return false;
 		
-		return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs);
+		return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
 	}
 		
@@ -3872,5 +5166,5 @@
 		if (!_attr) return false;
 
-		return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+		return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
 	}
 
@@ -3896,4 +5190,11 @@
 	}
 	
+	PUGI__FN bool xml_attribute::set_value(float rhs)
+	{
+		if (!_attr) return false;
+
+		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+	}
+
 	PUGI__FN bool xml_attribute::set_value(bool rhs)
 	{
@@ -3902,4 +5203,20 @@
 		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
 	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN bool xml_attribute::set_value(long long rhs)
+	{
+		if (!_attr) return false;
+
+		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+	}
+
+	PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
+	{
+		if (!_attr) return false;
+
+		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+	}
+#endif
 
 #ifdef __BORLANDC__
@@ -3939,5 +5256,5 @@
 	PUGI__FN xml_node::iterator xml_node::begin() const
 	{
-		return iterator(_root ? _root->first_child : 0, _root);
+		return iterator(_root ? _root->first_child + 0 : 0, _root);
 	}
 
@@ -3949,5 +5266,5 @@
 	PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
 	{
-		return attribute_iterator(_root ? _root->first_attribute : 0, _root);
+		return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
 	}
 
@@ -3964,5 +5281,5 @@
 	PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
 	{
-		return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_), name_), xml_named_node_iterator());
+		return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
 	}
 
@@ -4009,15 +5326,15 @@
 	PUGI__FN const char_t* xml_node::name() const
 	{
-		return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");
+		return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
 	}
 
 	PUGI__FN xml_node_type xml_node::type() const
 	{
-		return _root ? static_cast<xml_node_type>((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null;
+		return _root ? PUGI__NODETYPE(_root) : node_null;
 	}
 	
 	PUGI__FN const char_t* xml_node::value() const
 	{
-		return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");
+		return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
 	}
 	
@@ -4055,8 +5372,5 @@
 	PUGI__FN xml_node xml_node::next_sibling() const
 	{
-		if (!_root) return xml_node();
-		
-		if (_root->next_sibling) return xml_node(_root->next_sibling);
-		else return xml_node();
+		return _root ? xml_node(_root->next_sibling) : xml_node();
 	}
 
@@ -4071,4 +5385,37 @@
 	}
 
+	PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
+	{
+		xml_attribute_struct* hint = hint_._attr;
+
+		// if hint is not an attribute of node, behavior is not defined
+		assert(!hint || (_root && impl::is_attribute_of(hint, _root)));
+
+		if (!_root) return xml_attribute();
+
+		// optimistically search from hint up until the end
+		for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
+			if (i->name && impl::strequal(name_, i->name))
+			{
+				// update hint to maximize efficiency of searching for consecutive attributes
+				hint_._attr = i->next_attribute;
+
+				return xml_attribute(i);
+			}
+
+		// wrap around and search from the first attribute until the hint
+		// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
+		for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
+			if (j->name && impl::strequal(name_, j->name))
+			{
+				// update hint to maximize efficiency of searching for consecutive attributes
+				hint_._attr = j->next_attribute;
+
+				return xml_attribute(j);
+			}
+
+		return xml_attribute();
+	}
+
 	PUGI__FN xml_node xml_node::previous_sibling() const
 	{
@@ -4086,9 +5433,5 @@
 	PUGI__FN xml_node xml_node::root() const
 	{
-		if (!_root) return xml_node();
-
-		impl::xml_memory_page* page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
-
-		return xml_node(static_cast<impl::xml_document_struct*>(page->allocator));
+		return _root ? xml_node(&impl::get_document(_root)) : xml_node();
 	}
 
@@ -4103,5 +5446,5 @@
 		
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (i->value && impl::is_text_node(i))
+			if (impl::is_text_node(i) && i->value)
 				return i->value;
 
@@ -4136,37 +5479,34 @@
 	PUGI__FN bool xml_node::set_name(const char_t* rhs)
 	{
-		switch (type())
-		{
-		case node_pi:
-		case node_declaration:
-		case node_element:
-			return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
-
-		default:
+		xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+
+		if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
 			return false;
-		}
+
+		return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
 	}
 		
 	PUGI__FN bool xml_node::set_value(const char_t* rhs)
 	{
-		switch (type())
-		{
-		case node_pi:
-		case node_cdata:
-		case node_pcdata:
-		case node_comment:
-		case node_doctype:
-			return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
-
-		default:
+		xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+
+		if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
 			return false;
-		}
+
+		return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
 	}
 
 	PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
 	{
-		if (type() != node_element && type() != node_declaration) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
 		
-		xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root)));
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::append_attribute(a._attr, _root);
+
 		a.set_name(name_);
 		
@@ -4176,155 +5516,153 @@
 	PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
 	{
-		if (type() != node_element && type() != node_declaration) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
 		
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
 		if (!a) return xml_attribute();
 
+		impl::prepend_attribute(a._attr, _root);
+
 		a.set_name(name_);
+
+		return a;
+	}
+
+	PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
+	{
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
 		
-		xml_attribute_struct* head = _root->first_attribute;
-
-		if (head)
-		{
-			a._attr->prev_attribute_c = head->prev_attribute_c;
-			head->prev_attribute_c = a._attr;
-		}
-		else
-			a._attr->prev_attribute_c = a._attr;
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::insert_attribute_after(a._attr, attr._attr, _root);
+
+		a.set_name(name_);
+
+		return a;
+	}
+
+	PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
+	{
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
 		
-		a._attr->next_attribute = head;
-		_root->first_attribute = a._attr;
-				
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::insert_attribute_before(a._attr, attr._attr, _root);
+
+		a.set_name(name_);
+
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
-	{
-		if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
+	PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
+	{
+		if (!proto) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::append_attribute(a._attr, _root);
+		impl::node_copy_attribute(a._attr, proto._attr);
+
+		return a;
+	}
+
+	PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
+	{
+		if (!proto) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::prepend_attribute(a._attr, _root);
+		impl::node_copy_attribute(a._attr, proto._attr);
+
+		return a;
+	}
+
+	PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
+	{
+		if (!proto) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::insert_attribute_after(a._attr, attr._attr, _root);
+		impl::node_copy_attribute(a._attr, proto._attr);
+
+		return a;
+	}
+
+	PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
+	{
+		if (!proto) return xml_attribute();
+		if (!impl::allow_insert_attribute(type())) return xml_attribute();
+		if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_attribute();
+
+		xml_attribute a(impl::allocate_attribute(alloc));
+		if (!a) return xml_attribute();
+
+		impl::insert_attribute_before(a._attr, attr._attr, _root);
+		impl::node_copy_attribute(a._attr, proto._attr);
+
+		return a;
+	}
+
+	PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
+	{
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
 		
-		// check that attribute belongs to *this
-		xml_attribute_struct* cur = attr._attr;
-
-		while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
-
-		if (cur != _root->first_attribute) return xml_attribute();
-
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
-		if (!a) return xml_attribute();
-
-		a.set_name(name_);
-
-		if (attr._attr->prev_attribute_c->next_attribute)
-			attr._attr->prev_attribute_c->next_attribute = a._attr;
-		else
-			_root->first_attribute = a._attr;
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::append_node(n._root, _root);
+
+		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
+	{
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
 		
-		a._attr->prev_attribute_c = attr._attr->prev_attribute_c;
-		a._attr->next_attribute = attr._attr;
-		attr._attr->prev_attribute_c = a._attr;
-				
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
-	{
-		if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
-		
-		// check that attribute belongs to *this
-		xml_attribute_struct* cur = attr._attr;
-
-		while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
-
-		if (cur != _root->first_attribute) return xml_attribute();
-
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
-		if (!a) return xml_attribute();
-
-		a.set_name(name_);
-
-		if (attr._attr->next_attribute)
-			attr._attr->next_attribute->prev_attribute_c = a._attr;
-		else
-			_root->first_attribute->prev_attribute_c = a._attr;
-		
-		a._attr->next_attribute = attr._attr->next_attribute;
-		a._attr->prev_attribute_c = attr._attr;
-		attr._attr->next_attribute = a._attr;
-
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = append_attribute(proto.name());
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = prepend_attribute(proto.name());
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = insert_attribute_after(proto.name(), attr);
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = insert_attribute_before(proto.name(), attr);
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
-	{
-		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
-		
-		xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_));
-
-		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
-
-		return n;
-	}
-
-	PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
-	{
-		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
-		
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+		xml_node n(impl::allocate_node(alloc, type_));
 		if (!n) return xml_node();
 
-		n._root->parent = _root;
-
-		xml_node_struct* head = _root->first_child;
-
-		if (head)
-		{
-			n._root->prev_sibling_c = head->prev_sibling_c;
-			head->prev_sibling_c = n._root;
-		}
-		else
-			n._root->prev_sibling_c = n._root;
-		
-		n._root->next_sibling = head;
-		_root->first_child = n._root;
+		impl::prepend_node(n._root, _root);
 				
 		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
@@ -4335,167 +5673,276 @@
 	PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
 	{
-		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
 		if (!node._root || node._root->parent != _root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
 	
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+		xml_node n(impl::allocate_node(alloc, type_));
 		if (!n) return xml_node();
 
-		n._root->parent = _root;
+		impl::insert_node_before(n._root, node._root);
+
+		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
+	{
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+		if (!node._root || node._root->parent != _root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+	
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::insert_node_after(n._root, node._root);
+
+		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::append_child(const char_t* name_)
+	{
+		xml_node result = append_child(node_element);
+
+		result.set_name(name_);
+
+		return result;
+	}
+
+	PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
+	{
+		xml_node result = prepend_child(node_element);
+
+		result.set_name(name_);
+
+		return result;
+	}
+
+	PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
+	{
+		xml_node result = insert_child_after(node_element, node);
+
+		result.set_name(name_);
+
+		return result;
+	}
+
+	PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
+	{
+		xml_node result = insert_child_before(node_element, node);
+
+		result.set_name(name_);
+
+		return result;
+	}
+
+	PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
+	{
+		xml_node_type type_ = proto.type();
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::append_node(n._root, _root);
+		impl::node_copy_tree(n._root, proto._root);
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
+	{
+		xml_node_type type_ = proto.type();
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::prepend_node(n._root, _root);
+		impl::node_copy_tree(n._root, proto._root);
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
+	{
+		xml_node_type type_ = proto.type();
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+		if (!node._root || node._root->parent != _root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::insert_node_after(n._root, node._root);
+		impl::node_copy_tree(n._root, proto._root);
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
+	{
+		xml_node_type type_ = proto.type();
+		if (!impl::allow_insert_child(type(), type_)) return xml_node();
+		if (!node._root || node._root->parent != _root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		xml_node n(impl::allocate_node(alloc, type_));
+		if (!n) return xml_node();
+
+		impl::insert_node_before(n._root, node._root);
+		impl::node_copy_tree(n._root, proto._root);
+
+		return n;
+	}
+
+	PUGI__FN xml_node xml_node::append_move(const xml_node& moved)
+	{
+		if (!impl::allow_move(*this, moved)) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+		impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+		impl::remove_node(moved._root);
+		impl::append_node(moved._root, _root);
+
+		return moved;
+	}
+
+	PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved)
+	{
+		if (!impl::allow_move(*this, moved)) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+		impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+		impl::remove_node(moved._root);
+		impl::prepend_node(moved._root, _root);
+
+		return moved;
+	}
+
+	PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node)
+	{
+		if (!impl::allow_move(*this, moved)) return xml_node();
+		if (!node._root || node._root->parent != _root) return xml_node();
+		if (moved._root == node._root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+		impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+		impl::remove_node(moved._root);
+		impl::insert_node_after(moved._root, node._root);
+
+		return moved;
+	}
+
+	PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node)
+	{
+		if (!impl::allow_move(*this, moved)) return xml_node();
+		if (!node._root || node._root->parent != _root) return xml_node();
+		if (moved._root == node._root) return xml_node();
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return xml_node();
+
+		// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+		impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+		impl::remove_node(moved._root);
+		impl::insert_node_before(moved._root, node._root);
+
+		return moved;
+	}
+
+	PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
+	{
+		return remove_attribute(attribute(name_));
+	}
+
+	PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
+	{
+		if (!_root || !a._attr) return false;
+		if (!impl::is_attribute_of(a._attr, _root)) return false;
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return false;
+
+		impl::remove_attribute(a._attr, _root);
+		impl::destroy_attribute(a._attr, alloc);
+
+		return true;
+	}
+
+	PUGI__FN bool xml_node::remove_child(const char_t* name_)
+	{
+		return remove_child(child(name_));
+	}
+
+	PUGI__FN bool xml_node::remove_child(const xml_node& n)
+	{
+		if (!_root || !n._root || n._root->parent != _root) return false;
+
+		impl::xml_allocator& alloc = impl::get_allocator(_root);
+		if (!alloc.reserve()) return false;
+
+		impl::remove_node(n._root);
+		impl::destroy_node(n._root, alloc);
+
+		return true;
+	}
+
+	PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
+	{
+		// append_buffer is only valid for elements/documents
+		if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);
+
+		// get document node
+		impl::xml_document_struct* doc = &impl::get_document(_root);
+
+		// disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense
+		doc->header |= impl::xml_memory_page_contents_shared_mask;
 		
-		if (node._root->prev_sibling_c->next_sibling)
-			node._root->prev_sibling_c->next_sibling = n._root;
-		else
-			_root->first_child = n._root;
-		
-		n._root->prev_sibling_c = node._root->prev_sibling_c;
-		n._root->next_sibling = node._root;
-		node._root->prev_sibling_c = n._root;
-
-		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
-
-		return n;
-	}
-
-	PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
-	{
-		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
-		if (!node._root || node._root->parent != _root) return xml_node();
-	
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
-		if (!n) return xml_node();
-
-		n._root->parent = _root;
-	
-		if (node._root->next_sibling)
-			node._root->next_sibling->prev_sibling_c = n._root;
-		else
-			_root->first_child->prev_sibling_c = n._root;
-		
-		n._root->next_sibling = node._root->next_sibling;
-		n._root->prev_sibling_c = node._root;
-		node._root->next_sibling = n._root;
-
-		if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
-
-		return n;
-	}
-
-	PUGI__FN xml_node xml_node::append_child(const char_t* name_)
-	{
-		xml_node result = append_child(node_element);
-
-		result.set_name(name_);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
-	{
-		xml_node result = prepend_child(node_element);
-
-		result.set_name(name_);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
-	{
-		xml_node result = insert_child_after(node_element, node);
-
-		result.set_name(name_);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
-	{
-		xml_node result = insert_child_before(node_element, node);
-
-		result.set_name(name_);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
-	{
-		xml_node result = append_child(proto.type());
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
-	{
-		xml_node result = prepend_child(proto.type());
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
-	{
-		xml_node result = insert_child_after(proto.type(), node);
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
-	{
-		xml_node result = insert_child_before(proto.type(), node);
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
-	{
-		return remove_attribute(attribute(name_));
-	}
-
-	PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
-	{
-		if (!_root || !a._attr) return false;
-
-		// check that attribute belongs to *this
-		xml_attribute_struct* attr = a._attr;
-
-		while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c;
-
-		if (attr != _root->first_attribute) return false;
-
-		if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c;
-		else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c;
-		
-		if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute;
-		else _root->first_attribute = a._attr->next_attribute;
-
-		impl::destroy_attribute(a._attr, impl::get_allocator(_root));
-
-		return true;
-	}
-
-	PUGI__FN bool xml_node::remove_child(const char_t* name_)
-	{
-		return remove_child(child(name_));
-	}
-
-	PUGI__FN bool xml_node::remove_child(const xml_node& n)
-	{
-		if (!_root || !n._root || n._root->parent != _root) return false;
-
-		if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c;
-		else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c;
-		
-		if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling;
-		else _root->first_child = n._root->next_sibling;
-		
-		impl::destroy_node(n._root, impl::get_allocator(_root));
-
-		return true;
+		// 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));
+		(void)page;
+
+		if (!extra) return impl::make_parse_result(status_out_of_memory);
+
+		// add extra buffer to the list
+		extra->buffer = 0;
+		extra->next = doc->extra_buffers;
+		doc->extra_buffers = extra;
+
+		// name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
+		impl::name_null_sentry sentry(_root);
+
+		return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
 	}
 
@@ -4508,5 +5955,5 @@
 			{
 				for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
-					if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
+					if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
 						return xml_node(i);
 			}
@@ -4521,5 +5968,5 @@
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 			for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
-				if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
+				if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
 					return xml_node(i);
 
@@ -4530,17 +5977,32 @@
 	PUGI__FN string_t xml_node::path(char_t delimiter) const
 	{
-		xml_node cursor = *this; // Make a copy.
-		
-		string_t result = cursor.name();
-
-		while (cursor.parent())
-		{
-			cursor = cursor.parent();
-			
-			string_t temp = cursor.name();
-			temp += delimiter;
-			temp += result;
-			result.swap(temp);
-		}
+		if (!_root) return string_t();
+
+		size_t offset = 0;
+
+		for (xml_node_struct* i = _root; i; i = i->parent)
+		{
+			offset += (i != _root);
+			offset += i->name ? impl::strlength(i->name) : 0;
+		}
+
+		string_t result;
+		result.resize(offset);
+
+		for (xml_node_struct* j = _root; j; j = j->parent)
+		{
+			if (j != _root)
+				result[--offset] = delimiter;
+
+			if (j->name && *j->name)
+			{
+				size_t length = impl::strlength(j->name);
+
+				offset -= length;
+				memcpy(&result[offset], j->name, length * sizeof(char_t));
+			}
+		}
+
+		assert(offset == 0);
 
 		return result;
@@ -4659,5 +6121,7 @@
 		impl::xml_buffered_writer buffered_writer(writer, encoding);
 
-		impl::node_output(buffered_writer, *this, indent, flags, depth);
+		impl::node_output(buffered_writer, _root, indent, flags, depth);
+
+		buffered_writer.flush();
 	}
 
@@ -4680,11 +6144,10 @@
 	PUGI__FN ptrdiff_t xml_node::offset_debug() const
 	{
-		xml_node_struct* r = root()._root;
-
-		if (!r) return -1;
-
-		const char_t* buffer = static_cast<impl::xml_document_struct*>(r)->buffer;
-
-		if (!buffer) return -1;
+		if (!_root) return -1;
+
+		impl::xml_document_struct& doc = impl::get_document(_root);
+
+		// we can determine the offset reliably only if there is exactly once parse buffer
+		if (!doc.buffer || doc.extra_buffers) return -1;
 
 		switch (type())
@@ -4696,5 +6159,5 @@
 		case node_declaration:
 		case node_pi:
-			return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;
+			return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1;
 
 		case node_pcdata:
@@ -4702,5 +6165,5 @@
 		case node_comment:
 		case node_doctype:
-			return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;
+			return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
 
 		default:
@@ -4771,5 +6234,5 @@
 		xml_node_struct* d = _data();
 
-		return (d && d->value) ? d->value : PUGIXML_TEXT("");
+		return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
 	}
 
@@ -4778,5 +6241,5 @@
 		xml_node_struct* d = _data();
 
-		return (d && d->value) ? d->value : def;
+		return (d && d->value) ? d->value + 0 : def;
 	}
 
@@ -4785,5 +6248,5 @@
 		xml_node_struct* d = _data();
 
-		return impl::get_value_int(d ? d->value : 0, def);
+		return (d && d->value) ? impl::get_value_int(d->value) : def;
 	}
 
@@ -4792,5 +6255,5 @@
 		xml_node_struct* d = _data();
 
-		return impl::get_value_uint(d ? d->value : 0, def);
+		return (d && d->value) ? impl::get_value_uint(d->value) : def;
 	}
 
@@ -4799,5 +6262,5 @@
 		xml_node_struct* d = _data();
 
-		return impl::get_value_double(d ? d->value : 0, def);
+		return (d && d->value) ? impl::get_value_double(d->value) : def;
 	}
 
@@ -4806,5 +6269,5 @@
 		xml_node_struct* d = _data();
 
-		return impl::get_value_float(d ? d->value : 0, def);
+		return (d && d->value) ? impl::get_value_float(d->value) : def;
 	}
 
@@ -4813,6 +6276,22 @@
 		xml_node_struct* d = _data();
 
-		return impl::get_value_bool(d ? d->value : 0, def);
-	}
+		return (d && d->value) ? impl::get_value_bool(d->value) : def;
+	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN long long xml_text::as_llong(long long def) const
+	{
+		xml_node_struct* d = _data();
+
+		return (d && d->value) ? impl::get_value_llong(d->value) : def;
+	}
+
+	PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
+	{
+		xml_node_struct* d = _data();
+
+		return (d && d->value) ? impl::get_value_ullong(d->value) : def;
+	}
+#endif
 
 	PUGI__FN bool xml_text::set(const char_t* rhs)
@@ -4820,5 +6299,5 @@
 		xml_node_struct* dn = _data_new();
 
-		return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+		return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;
 	}
 
@@ -4837,4 +6316,11 @@
 	}
 
+	PUGI__FN bool xml_text::set(float rhs)
+	{
+		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;
+	}
+
 	PUGI__FN bool xml_text::set(double rhs)
 	{
@@ -4850,4 +6336,20 @@
 		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
 	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN bool xml_text::set(long long rhs)
+	{
+		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;
+	}
+
+	PUGI__FN bool xml_text::set(unsigned long long rhs)
+	{
+		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;
+	}
+#endif
 
 	PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)
@@ -4875,9 +6377,29 @@
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(bool rhs)
+	PUGI__FN xml_text& xml_text::operator=(float rhs)
 	{
 		set(rhs);
 		return *this;
 	}
+
+	PUGI__FN xml_text& xml_text::operator=(bool rhs)
+	{
+		set(rhs);
+		return *this;
+	}
+
+#ifdef PUGIXML_HAS_LONG_LONG
+	PUGI__FN xml_text& xml_text::operator=(long long rhs)
+	{
+		set(rhs);
+		return *this;
+	}
+
+	PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)
+	{
+		set(rhs);
+		return *this;
+	}
+#endif
 
 	PUGI__FN xml_node xml_text::data() const
@@ -5024,5 +6546,9 @@
 	}
 
-	PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _node(node), _name(name)
+	PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)
+	{
+	}
+
+	PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name)
 	{
 	}
@@ -5030,28 +6556,28 @@
 	PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
 	{
-		return _node == rhs._node;
+		return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
 	}
 
 	PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const
 	{
-		return _node != rhs._node;
+		return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
 	}
 
 	PUGI__FN xml_node& xml_named_node_iterator::operator*() const
 	{
-		assert(_node._root);
-		return _node;
+		assert(_wrap._root);
+		return _wrap;
 	}
 
 	PUGI__FN xml_node* xml_named_node_iterator::operator->() const
 	{
-		assert(_node._root);
-		return const_cast<xml_node*>(&_node); // BCC32 workaround
+		assert(_wrap._root);
+		return const_cast<xml_node*>(&_wrap); // BCC32 workaround
 	}
 
 	PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
 	{
-		assert(_node._root);
-		_node = _node.next_sibling(_name);
+		assert(_wrap._root);
+		_wrap = _wrap.next_sibling(_name);
 		return *this;
 	}
@@ -5061,4 +6587,26 @@
 		xml_named_node_iterator temp = *this;
 		++*this;
+		return temp;
+	}
+
+	PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--()
+	{
+		if (_wrap._root)
+			_wrap = _wrap.previous_sibling(_name);
+		else
+		{
+			_wrap = _parent.last_child();
+
+			if (!impl::strequal(_wrap.name(), _name))
+				_wrap = _wrap.previous_sibling(_name);
+		}
+
+		return *this;
+	}
+
+	PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int)
+	{
+		xml_named_node_iterator temp = *this;
+		--*this;
 		return temp;
 	}
@@ -5096,4 +6644,8 @@
 		case status_end_element_mismatch: return "Start-end tags mismatch";
 
+		case status_append_invalid_root: return "Unable to append nodes: root is not an element or document";
+
+		case status_no_document_element: return "No document element found";
+
 		default: return "Unknown error";
 		}
@@ -5126,6 +6678,14 @@
 	PUGI__FN void xml_document::create()
 	{
+		assert(!_root);
+
+	#ifdef PUGIXML_COMPACT
+		const size_t page_offset = sizeof(uint32_t);
+	#else
+		const size_t page_offset = 0;
+	#endif
+
 		// initialize sentinel page
-		PUGI__STATIC_ASSERT(offsetof(impl::xml_memory_page, data) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory));
+		PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) + page_offset <= sizeof(_memory));
 
 		// align upwards to page boundary
@@ -5134,17 +6694,30 @@
 		// prepare page structure
 		impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory);
+		assert(page);
 
 		page->busy_size = impl::xml_memory_page_size;
 
+		// setup first page marker
+	#ifdef PUGIXML_COMPACT
+		// round-trip through void* to avoid 'cast increases required alignment of target type' warning
+		page->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)));
+		*page->compact_page_marker = sizeof(impl::xml_memory_page);
+	#endif
+
 		// allocate new root
-		_root = new (page->data) impl::xml_document_struct(page);
+		_root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);
 		_root->prev_sibling_c = _root;
 
 		// setup sentinel page
 		page->allocator = static_cast<impl::xml_document_struct*>(_root);
+
+		// verify the document allocation
+		assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
 	}
 
 	PUGI__FN void xml_document::destroy()
 	{
+		assert(_root);
+
 		// destroy static storage
 		if (_buffer)
@@ -5154,27 +6727,30 @@
 		}
 
+		// destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)
+		for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)
+		{
+			if (extra->buffer) impl::xml_memory::deallocate(extra->buffer);
+		}
+
 		// destroy dynamic storage, leave sentinel page (it's in static memory)
-		if (_root)
-		{
-			impl::xml_memory_page* root_page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
-			assert(root_page && !root_page->prev && !root_page->memory);
-
-			// destroy all pages
-			for (impl::xml_memory_page* page = root_page->next; page; )
-			{
-				impl::xml_memory_page* next = page->next;
-
-				impl::xml_allocator::deallocate_page(page);
-
-				page = next;
-			}
-
-			// cleanup root page
-			root_page->allocator = 0;
-			root_page->next = 0;
-			root_page->busy_size = root_page->freed_size = 0;
-
-			_root = 0;
-		}
+		impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
+		assert(root_page && !root_page->prev);
+		assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
+
+		for (impl::xml_memory_page* page = root_page->next; page; )
+		{
+			impl::xml_memory_page* next = page->next;
+
+			impl::xml_allocator::deallocate_page(page);
+
+			page = next;
+		}
+
+	#ifdef PUGIXML_COMPACT
+		// destroy hash table
+		static_cast<impl::xml_document_struct*>(_root)->hash.clear();
+	#endif
+
+		_root = 0;
 	}
 
@@ -5184,5 +6760,5 @@
 		reset();
 
-		return impl::load_stream_impl(*this, stream, options, encoding);
+		return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);
 	}
 
@@ -5191,9 +6767,9 @@
 		reset();
 
-		return impl::load_stream_impl(*this, stream, options, encoding_wchar);
+		return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer);
 	}
 #endif
 
-	PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
+	PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
 	{
 		// Force native encoding (skip autodetection)
@@ -5207,11 +6783,17 @@
 	}
 
+	PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
+	{
+		return load_string(contents, options);
+	}
+
 	PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
-		FILE* file = fopen(path_, "rb");
-
-		return impl::load_file_impl(*this, file, options, encoding);
+		using impl::auto_deleter; // MSVC7 workaround
+		auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, "rb"), fclose);
+
+		return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
 	}
 
@@ -5220,53 +6802,29 @@
 		reset();
 
-		FILE* file = impl::open_file_wide(path_, L"rb");
-
-		return impl::load_file_impl(*this, file, options, encoding);
-	}
-
-	PUGI__FN xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own)
+		using impl::auto_deleter; // MSVC7 workaround
+		auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, L"rb"), fclose);
+
+		return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
+	}
+
+	PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
-		// check input buffer
-		assert(contents || size == 0);
-
-		// get actual encoding
-		xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
-
-		// get private buffer
-		char_t* buffer = 0;
-		size_t length = 0;
-
-		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
-		if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
-
-		// parse
-		xml_parse_result res = impl::xml_parser::parse(buffer, length, _root, options);
-
-		// remember encoding
-		res.encoding = buffer_encoding;
-
-		// grab onto buffer if it's our buffer, user is responsible for deallocating contens himself
-		if (own || buffer != contents) _buffer = buffer;
-
-		return res;
-	}
-
-	PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
-	{
-		return load_buffer_impl(const_cast<void*>(contents), size, options, encoding, false, false);
+		return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);
 	}
 
 	PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
-		return load_buffer_impl(contents, size, options, encoding, true, false);
-	}
-		
+		reset();
+
+		return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);
+	}
+
 	PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
-		return load_buffer_impl(contents, size, options, encoding, true, true);
+		reset();
+
+		return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);
 	}
 
@@ -5286,13 +6844,15 @@
 		}
 
-		if (!(flags & format_no_declaration) && !impl::has_declaration(*this))
-		{
-			buffered_writer.write(PUGIXML_TEXT("<?xml version=\"1.0\""));
-			if (encoding == encoding_latin1) buffered_writer.write(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
+		if (!(flags & format_no_declaration) && !impl::has_declaration(_root))
+		{
+			buffered_writer.write_string(PUGIXML_TEXT("<?xml version=\"1.0\""));
+			if (encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
 			buffered_writer.write('?', '>');
 			if (!(flags & format_raw)) buffered_writer.write('\n');
 		}
 
-		impl::node_output(buffered_writer, *this, indent, flags, 0);
+		impl::node_output(buffered_writer, _root, indent, flags, 0);
+
+		buffered_writer.flush();
 	}
 
@@ -5315,18 +6875,24 @@
 	PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
-		FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
-		return impl::save_file_impl(*this, file, indent, flags, encoding);
+		using impl::auto_deleter; // MSVC7 workaround
+		auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), fclose);
+
+		return impl::save_file_impl(*this, file.data, indent, flags, encoding);
 	}
 
 	PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
-		FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
-		return impl::save_file_impl(*this, file, indent, flags, encoding);
+		using impl::auto_deleter; // MSVC7 workaround
+		auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), fclose);
+
+		return impl::save_file_impl(*this, file.data, indent, flags, encoding);
 	}
 
 	PUGI__FN xml_node xml_document::document_element() const
 	{
+		assert(_root);
+
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element)
+			if (PUGI__NODETYPE(i) == node_element)
 				return xml_node(i);
 
@@ -5339,5 +6905,5 @@
 		assert(str);
 
-		return impl::as_utf8_impl(str, wcslen(str));
+		return impl::as_utf8_impl(str, impl::strlength_wide(str));
 	}
 
@@ -5391,7 +6957,7 @@
 	}
 
-	PUGI__FN std::forward_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
-	{
-		return std::forward_iterator_tag();
+	PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
+	{
+		return std::bidirectional_iterator_tag();
 	}
 }
@@ -5412,7 +6978,7 @@
 	}
 
-	PUGI__FN std::forward_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
-	{
-		return std::forward_iterator_tag();
+	PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
+	{
+		return std::bidirectional_iterator_tag();
 	}
 }
@@ -5420,5 +6986,4 @@
 
 #ifndef PUGIXML_NO_XPATH
-
 // STL replacements
 PUGI__NS_BEGIN
@@ -5475,5 +7040,5 @@
 	template <typename I> void reverse(I begin, I end)
 	{
-		while (begin + 1 < end) swap(*begin++, *--end);
+		while (end - begin > 1) swap(*begin++, *--end);
 	}
 
@@ -5481,5 +7046,5 @@
 	{
 		// fast skip head
-		while (begin + 1 < end && *begin != *(begin + 1)) begin++;
+		while (end - begin > 1 && *begin != *(begin + 1)) begin++;
 
 		if (begin == end) return begin;
@@ -5650,15 +7215,24 @@
 // Allocator used for AST and evaluation stacks
 PUGI__NS_BEGIN
+	static const size_t xpath_memory_page_size =
+	#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
+		PUGIXML_MEMORY_XPATH_PAGE_SIZE
+	#else
+		4096
+	#endif
+		;
+
+	static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
+
 	struct xpath_memory_block
 	{	
 		xpath_memory_block* next;
-
-		char data[
-	#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
-			PUGIXML_MEMORY_XPATH_PAGE_SIZE
-	#else
-			4096
-	#endif
-		];
+		size_t capacity;
+
+		union
+		{
+			char data[xpath_memory_page_size];
+			double alignment;
+		};
 	};
 		
@@ -5682,12 +7256,10 @@
 		void* allocate_nothrow(size_t size)
 		{
-			const size_t block_capacity = sizeof(_root->data);
-
-			// align size so that we're able to store pointers in subsequent blocks
-			size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
-
-			if (_root_size + size <= block_capacity)
-			{
-				void* buf = _root->data + _root_size;
+			// round size up to block alignment boundary
+			size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
+
+			if (_root_size + size <= _root->capacity)
+			{
+				void* buf = &_root->data[0] + _root_size;
 				_root_size += size;
 				return buf;
@@ -5695,6 +7267,10 @@
 			else
 			{
-				size_t block_data_size = (size > block_capacity) ? size : block_capacity;
-				size_t block_size = block_data_size + offsetof(xpath_memory_block, data);
+				// make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests
+				size_t block_capacity_base = sizeof(_root->data);
+				size_t block_capacity_req = size + block_capacity_base / 4;
+				size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req;
+
+				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));
@@ -5702,4 +7278,5 @@
 				
 				block->next = _root;
+				block->capacity = block_capacity;
 				
 				_root = block;
@@ -5729,10 +7306,10 @@
 		void* reallocate(void* ptr, size_t old_size, size_t new_size)
 		{
-			// align size so that we're able to store pointers in subsequent blocks
-			old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
-			new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+			// round size up to block alignment boundary
+			old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
+			new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
 
 			// we can only reallocate the last object
-			assert(ptr == 0 || static_cast<char*>(ptr) + old_size == _root->data + _root_size);
+			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
@@ -5749,5 +7326,5 @@
 			{
 				// copy old data
-				assert(new_size > old_size);
+				assert(new_size >= old_size);
 				memcpy(result, ptr, old_size);
 
@@ -5842,4 +7419,5 @@
 		{
 			blocks[0].next = blocks[1].next = 0;
+			blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);
 
 			stack.result = &result;
@@ -5865,4 +7443,5 @@
 		const char_t* _buffer;
 		bool _uses_heap;
+		size_t _length_heap;
 
 		static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
@@ -5877,34 +7456,32 @@
 		}
 
-		static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc)
-		{
-			return duplicate_string(string, strlength(string), alloc);
+		xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap)
+		{
 		}
 
 	public:
-		xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
-		{
-		}
-
-		explicit xpath_string(const char_t* str, xpath_allocator* alloc)
-		{
-			bool empty_ = (*str == 0);
-
-			_buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc);
-			_uses_heap = !empty_;
-		}
-
-		explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap)
-		{
-		}
-
-		xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc)
+		static xpath_string from_const(const char_t* str)
+		{
+			return xpath_string(str, false, 0);
+		}
+
+		static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end)
+		{
+			assert(begin <= end && *end == 0);
+
+			return xpath_string(begin, true, static_cast<size_t>(end - begin));
+		}
+
+		static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc)
 		{
 			assert(begin <= end);
 
-			bool empty_ = (begin == end);
-
-			_buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast<size_t>(end - begin), alloc);
-			_uses_heap = !empty_;
+			size_t length = static_cast<size_t>(end - begin);
+
+			return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length);
+		}
+
+		xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0)
+		{
 		}
 
@@ -5922,6 +7499,6 @@
 			{
 				// need to make heap copy
-				size_t target_length = strlength(_buffer);
-				size_t source_length = strlength(o._buffer);
+				size_t target_length = length();
+				size_t source_length = o.length();
 				size_t result_length = target_length + source_length;
 
@@ -5940,4 +7517,5 @@
 				_buffer = result;
 				_uses_heap = true;
+				_length_heap = result_length;
 			}
 		}
@@ -5950,5 +7528,5 @@
 		size_t length() const
 		{
-			return strlength(_buffer);
+			return _uses_heap ? _length_heap : strlength(_buffer);
 		}
 		
@@ -5958,6 +7536,9 @@
 			if (!_uses_heap)
 			{
-				_buffer = duplicate_string(_buffer, alloc);
+				size_t length_ = strlength(_buffer);
+
+				_buffer = duplicate_string(_buffer, length_, alloc);
 				_uses_heap = true;
+				_length_heap = length_;
 			}
 
@@ -5985,9 +7566,4 @@
 		}
 	};
-
-	PUGI__FN xpath_string xpath_string_const(const char_t* str)
-	{
-		return xpath_string(str, false);
-	}
 PUGI__NS_END
 
@@ -6032,8 +7608,8 @@
 	{
 		if (na.attribute())
-			return xpath_string_const(na.attribute().value());
+			return xpath_string::from_const(na.attribute().value());
 		else
 		{
-			const xml_node& n = na.node();
+			xml_node n = na.node();
 
 			switch (n.type())
@@ -6043,5 +7619,5 @@
 			case node_comment:
 			case node_pi:
-				return xpath_string_const(n.value());
+				return xpath_string::from_const(n.value());
 			
 			case node_document:
@@ -6055,5 +7631,5 @@
 				{
 					if (cur.type() == node_pcdata || cur.type() == node_cdata)
-						result.append(xpath_string_const(cur.value()), alloc);
+						result.append(xpath_string::from_const(cur.value()), alloc);
 
 					if (cur.first_child())
@@ -6079,52 +7655,79 @@
 	}
 	
-	PUGI__FN unsigned int node_height(xml_node n)
-	{
-		unsigned int result = 0;
-		
-		while (n)
-		{
-			++result;
-			n = n.parent();
-		}
-		
-		return result;
+	PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
+	{
+		assert(ln->parent == rn->parent);
+
+		// there is no common ancestor (the shared parent is null), nodes are from different documents
+		if (!ln->parent) return ln < rn;
+
+		// determine sibling order
+		xml_node_struct* ls = ln;
+		xml_node_struct* rs = rn;
+
+		while (ls && rs)
+		{
+			if (ls == rn) return true;
+			if (rs == ln) return false;
+
+			ls = ls->next_sibling;
+			rs = rs->next_sibling;
+		}
+
+		// if rn sibling chain ended ln must be before rn
+		return !rs;
 	}
 	
-	PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh)
-	{
-		// normalize heights
-		for (unsigned int i = rh; i < lh; i++) ln = ln.parent();
-		for (unsigned int j = lh; j < rh; j++) rn = rn.parent();
-		
+	PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)
+	{
+		// find common ancestor at the same depth, if any
+		xml_node_struct* lp = ln;
+		xml_node_struct* rp = rn;
+
+		while (lp && rp && lp->parent != rp->parent)
+		{
+			lp = lp->parent;
+			rp = rp->parent;
+		}
+
+		// parents are the same!
+		if (lp && rp) return node_is_before_sibling(lp, rp);
+
+		// nodes are at different depths, need to normalize heights
+		bool left_higher = !lp;
+
+		while (lp)
+		{
+			lp = lp->parent;
+			ln = ln->parent;
+		}
+
+		while (rp)
+		{
+			rp = rp->parent;
+			rn = rn->parent;
+		}
+
 		// one node is the ancestor of the other
-		if (ln == rn) return lh < rh;
-		
-		// find common ancestor
-		while (ln.parent() != rn.parent())
-		{
-			ln = ln.parent();
-			rn = rn.parent();
-		}
-
-		// there is no common ancestor (the shared parent is null), nodes are from different documents
-		if (!ln.parent()) return ln < rn;
-
-		// determine sibling order
-		for (; ln; ln = ln.next_sibling())
-			if (ln == rn)
-				return true;
-				
-		return false;
-	}
-
-	PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node)
-	{
-		while (node && node != parent) node = node.parent();
+		if (ln == rn) return left_higher;
+
+		// find common ancestor... again
+		while (ln->parent != rn->parent)
+		{
+			ln = ln->parent;
+			rn = rn->parent;
+		}
+
+		return node_is_before_sibling(ln, rn);
+	}
+
+	PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)
+	{
+		while (node && node != parent) node = node->parent;
 
 		return parent && node == parent;
 	}
 
-	PUGI__FN const void* document_order(const xpath_node& xnode)
+	PUGI__FN const void* document_buffer_order(const xpath_node& xnode)
 	{
 		xml_node_struct* node = xnode.node().internal_object();
@@ -6132,6 +7735,10 @@
 		if (node)
 		{
-			if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name;
-			if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value;
+			if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0)
+			{
+				if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name;
+				if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value;
+			}
+
 			return 0;
 		}
@@ -6141,6 +7748,10 @@
 		if (attr)
 		{
-			if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name;
-			if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value;
+			if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0)
+			{
+				if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name;
+				if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value;
+			}
+
 			return 0;
 		}
@@ -6154,6 +7765,6 @@
 		{
 			// optimized document order based check
-			const void* lo = document_order(lhs);
-			const void* ro = document_order(rhs);
+			const void* lo = document_buffer_order(lhs);
+			const void* ro = document_buffer_order(rhs);
 
 			if (lo && ro) return lo < ro;
@@ -6196,9 +7807,8 @@
 
 			if (ln == rn) return false;
+
+			if (!ln || !rn) return ln < rn;
 			
-			unsigned int lh = node_height(ln);
-			unsigned int rh = node_height(rn);
-			
-			return node_is_before(ln, lh, rn, rh);
+			return node_is_before(ln.internal_object(), rn.internal_object());
 		}
 	};
@@ -6334,8 +7944,8 @@
 		// try special number conversion
 		const char_t* special = convert_number_to_string_special(value);
-		if (special) return xpath_string_const(special);
+		if (special) return xpath_string::from_const(special);
 
 		// get mantissa + exponent form
-		char mantissa_buffer[64];
+		char mantissa_buffer[32];
 
 		char* mantissa;
@@ -6343,6 +7953,10 @@
 		convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(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);
+
 		// make the number!
-		char_t result[512];
 		char_t* s = result;
 
@@ -6359,5 +7973,5 @@
 			while (exponent > 0)
 			{
-				assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
+				assert(*mantissa == 0 || static_cast<unsigned int>(static_cast<unsigned int>(*mantissa) - '0') <= 9);
 				*s++ = *mantissa ? *mantissa++ : '0';
 				exponent--;
@@ -6387,8 +8001,8 @@
 
 		// zero-terminate
-		assert(s < result + sizeof(result) / sizeof(result[0]));
+		assert(s < result + result_size);
 		*s = 0;
 
-		return xpath_string(result, alloc);
+		return xpath_string::from_heap_preallocated(result, s);
 	}
 	
@@ -6432,12 +8046,10 @@
 		return wcstod(string, 0);
 	#else
-		return atof(string);
+		return strtod(string, 0);
 	#endif
 	}
 
-	PUGI__FN bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result)
-	{
-		char_t buffer[32];
-
+	PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)
+	{
 		size_t length = static_cast<size_t>(end - begin);
 		char_t* scratch = buffer;
@@ -6500,5 +8112,5 @@
 		}
 
-		bool operator()(const xml_attribute& a) const
+		bool operator()(xml_attribute a) const
 		{
 			const char_t* name = a.name();
@@ -6510,5 +8122,5 @@
 	};
 
-	PUGI__FN const char_t* namespace_uri(const xml_node& node)
+	PUGI__FN const char_t* namespace_uri(xml_node node)
 	{
 		namespace_uri_predicate pred = node.name();
@@ -6528,5 +8140,5 @@
 	}
 
-	PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent)
+	PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
 	{
 		namespace_uri_predicate pred = attr.name();
@@ -6554,5 +8166,5 @@
 	}
 
-	PUGI__FN void normalize_space(char_t* buffer)
+	PUGI__FN char_t* normalize_space(char_t* buffer)
 	{
 		char_t* write = buffer;
@@ -6578,10 +8190,10 @@
 		// zero-terminate
 		*write = 0;
-	}
-
-	PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to)
-	{
-		size_t to_length = strlength(to);
-
+
+		return write;
+	}
+
+	PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
+	{
 		char_t* write = buffer;
 
@@ -6600,9 +8212,80 @@
 		// zero-terminate
 		*write = 0;
+
+		return write;
+	}
+
+	PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
+	{
+		unsigned char table[128] = {0};
+
+		while (*from)
+		{
+			unsigned int fc = static_cast<unsigned int>(*from);
+			unsigned int tc = static_cast<unsigned int>(*to);
+
+			if (fc >= 128 || tc >= 128)
+				return 0;
+
+			// code=128 means "skip character"
+			if (!table[fc])
+				table[fc] = static_cast<unsigned char>(tc ? tc : 128);
+
+			from++;
+			if (tc) to++;
+		}
+
+		for (int i = 0; i < 128; ++i)
+			if (!table[i])
+				table[i] = static_cast<unsigned char>(i);
+
+		void* result = alloc->allocate_nothrow(sizeof(table));
+
+		if (result)
+		{
+			memcpy(result, table, sizeof(table));
+		}
+
+		return static_cast<unsigned char*>(result);
+	}
+
+	PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
+	{
+		char_t* write = buffer;
+
+		while (*buffer)
+		{
+			char_t ch = *buffer++;
+			unsigned int index = static_cast<unsigned int>(ch);
+
+			if (index < 128)
+			{
+				unsigned char code = table[index];
+
+				// code=128 means "skip character" (table size is 128 so 128 can be a special value)
+				// this code skips these characters without extra branches
+				*write = static_cast<char_t>(code);
+				write += 1 - (code >> 7);
+			}
+			else
+			{
+				*write++ = ch;
+			}
+		}
+
+		// zero-terminate
+		*write = 0;
+
+		return write;
+	}
+
+	inline bool is_xpath_attribute(const char_t* name)
+	{
+		return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':'));
 	}
 
 	struct xpath_variable_boolean: xpath_variable
 	{
-		xpath_variable_boolean(): value(false)
+		xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false)
 		{
 		}
@@ -6614,5 +8297,5 @@
 	struct xpath_variable_number: xpath_variable
 	{
-		xpath_variable_number(): value(0)
+		xpath_variable_number(): xpath_variable(xpath_type_number), value(0)
 		{
 		}
@@ -6624,5 +8307,5 @@
 	struct xpath_variable_string: xpath_variable
 	{
-		xpath_variable_string(): value(0)
+		xpath_variable_string(): xpath_variable(xpath_type_string), value(0)
 		{
 		}
@@ -6639,4 +8322,8 @@
 	struct xpath_variable_node_set: xpath_variable
 	{
+		xpath_variable_node_set(): xpath_variable(xpath_type_node_set)
+		{
+		}
+
 		xpath_node_set value;
 		char_t name[1];
@@ -6732,8 +8419,28 @@
 	}
 
-	PUGI__FN xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end)
-	{
-		char_t buffer[32];
-
+	PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
+	{
+		switch (rhs->type())
+		{
+		case xpath_type_node_set:
+			return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);
+
+		case xpath_type_number:
+			return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);
+
+		case xpath_type_string:
+			return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);
+
+		case xpath_type_boolean:
+			return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
+
+		default:
+			assert(!"Invalid variable type");
+			return false;
+		}
+	}
+
+	PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result)
+	{
 		size_t length = static_cast<size_t>(end - begin);
 		char_t* scratch = buffer;
@@ -6743,5 +8450,5 @@
 			// need to make dummy on-heap copy
 			scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-			if (!scratch) return 0;
+			if (!scratch) return false;
 		}
 
@@ -6750,10 +8457,10 @@
 		scratch[length] = 0;
 
-		xpath_variable* result = set->get(scratch);
+		*out_result = set->get(scratch);
 
 		// free dummy buffer
 		if (scratch != buffer) xml_memory::deallocate(scratch);
 
-		return result;
+		return true;
 	}
 PUGI__NS_END
@@ -6761,4 +8468,20 @@
 // Internal node set class
 PUGI__NS_BEGIN
+	PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end)
+	{
+		if (end - begin < 2)
+			return xpath_node_set::type_sorted;
+
+		document_order_comparator cmp;
+
+		bool first = cmp(begin[0], begin[1]);
+
+		for (const xpath_node* it = begin + 1; it + 1 < end; ++it)
+			if (cmp(it[0], it[1]) != first)
+				return xpath_node_set::type_unsorted;
+
+		return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse;
+	}
+
 	PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
 	{
@@ -6767,7 +8490,14 @@
 		if (type == xpath_node_set::type_unsorted)
 		{
-			sort(begin, end, document_order_comparator());
-
-			type = xpath_node_set::type_sorted;
+			xpath_node_set::type_t sorted = xpath_get_order(begin, end);
+
+			if (sorted == xpath_node_set::type_unsorted)
+			{
+				sort(begin, end, document_order_comparator());
+
+				type = xpath_node_set::type_sorted;
+			}
+			else
+				type = sorted;
 		}
 		
@@ -6836,28 +8566,18 @@
 		}
 
+		void push_back_grow(const xpath_node& node, xpath_allocator* alloc);
+
 		void push_back(const xpath_node& node, xpath_allocator* alloc)
 		{
-			if (_end == _eos)
-			{
-				size_t capacity = static_cast<size_t>(_eos - _begin);
-
-				// get new capacity (1.5x rule)
-				size_t new_capacity = capacity + capacity / 2 + 1;
-
-				// 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);
-
-				// finalize
-				_begin = data;
-				_end = data + capacity;
-				_eos = data + new_capacity;
-			}
-
-			*_end++ = node;
+			if (_end != _eos)
+				*_end++ = node;
+			else
+				push_back_grow(node, alloc);
 		}
 
 		void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
 		{
+			if (begin_ == end_) return;
+
 			size_t size_ = static_cast<size_t>(_end - _begin);
 			size_t capacity = static_cast<size_t>(_eos - _begin);
@@ -6910,4 +8630,24 @@
 		}
 	};
+
+	PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc)
+	{
+		size_t capacity = static_cast<size_t>(_eos - _begin);
+
+		// get new capacity (1.5x rule)
+		size_t new_capacity = capacity + capacity / 2 + 1;
+
+		// 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);
+
+		// finalize
+		_begin = data;
+		_end = data + capacity;
+		_eos = data + new_capacity;
+
+		// push
+		*_end++ = node;
+	}
 PUGI__NS_END
 
@@ -7279,4 +9019,5 @@
 	enum ast_type_t
 	{
+		ast_unknown,
 		ast_op_or,						// left or right
 		ast_op_and,						// left and right
@@ -7296,5 +9037,4 @@
 		ast_predicate,					// apply predicate to set; next points to next predicate
 		ast_filter,						// select * from left where right
-		ast_filter_posinv,				// select * from left where right; proximity position invariant
 		ast_string_constant,			// string constant
 		ast_number_constant,			// number constant
@@ -7336,5 +9076,8 @@
 		ast_func_round,					// round(left)
 		ast_step,						// process set left with step
-		ast_step_root					// select root node
+		ast_step_root,					// select root node
+
+		ast_opt_translate_table,		// translate(left, right, third) where right/third are constants
+		ast_opt_compare_attribute		// @name = 'string'
 	};
 
@@ -7369,4 +9112,19 @@
 	};
 
+	enum predicate_t
+	{
+		predicate_default,
+		predicate_posinv,
+		predicate_constant,
+		predicate_constant_one
+	};
+
+	enum nodeset_eval_t
+	{
+		nodeset_eval_all,
+		nodeset_eval_any,
+		nodeset_eval_first
+	};
+
 	template <axis_t N> struct axis_to_type
 	{
@@ -7383,6 +9141,8 @@
 		char _rettype;
 
-		// for ast_step / ast_predicate
+		// for ast_step
 		char _axis;
+
+		// for ast_step/ast_predicate/ast_filter
 		char _test;
 
@@ -7402,4 +9162,6 @@
 			// node test for ast_step (node name/namespace/node type/pi target)
 			const char_t* nodetest;
+			// table for ast_opt_translate_table
+			const unsigned char* table;
 		} _data;
 
@@ -7431,6 +9193,6 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
-				xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+				xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
+				xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
 
 				for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
@@ -7460,5 +9222,5 @@
 
 					double l = lhs->eval_number(c, stack);
-					xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+					xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
 
 					for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
@@ -7477,5 +9239,5 @@
 
 					xpath_string l = lhs->eval_string(c, stack);
-					xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+					xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
 
 					for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
@@ -7495,4 +9257,9 @@
 		}
 
+		static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)
+		{
+			return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any;
+		}
+
 		template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
 		{
@@ -7505,6 +9272,6 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
-				xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+				xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
+				xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
 
 				for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
@@ -7530,5 +9297,5 @@
 
 				double l = lhs->eval_number(c, stack);
-				xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+				xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
 
 				for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
@@ -7546,5 +9313,5 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
+				xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
 				double r = rhs->eval_number(c, stack);
 
@@ -7566,64 +9333,139 @@
 		}
 
-		void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
+		static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
 		{
 			assert(ns.size() >= first);
+			assert(expr->rettype() != xpath_type_number);
 
 			size_t i = 1;
 			size_t size = ns.size() - first;
-				
+
 			xpath_node* last = ns.begin() + first;
-				
+
 			// remove_if... or well, sort of
 			for (xpath_node* it = last; it != ns.end(); ++it, ++i)
 			{
 				xpath_context c(*it, i, size);
-			
-				if (expr->rettype() == xpath_type_number)
-				{
-					if (expr->eval_number(c, stack) == i)
-						*last++ = *it;
-				}
-				else if (expr->eval_boolean(c, stack))
+
+				if (expr->eval_boolean(c, stack))
+				{
 					*last++ = *it;
-			}
-			
+
+					if (once) break;
+				}
+			}
+
 			ns.truncate(last);
 		}
 
-		void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack)
+		static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
+		{
+			assert(ns.size() >= first);
+			assert(expr->rettype() == xpath_type_number);
+
+			size_t i = 1;
+			size_t size = ns.size() - first;
+
+			xpath_node* last = ns.begin() + first;
+
+			// remove_if... or well, sort of
+			for (xpath_node* it = last; it != ns.end(); ++it, ++i)
+			{
+				xpath_context c(*it, i, size);
+
+				if (expr->eval_number(c, stack) == i)
+				{
+					*last++ = *it;
+
+					if (once) break;
+				}
+			}
+
+			ns.truncate(last);
+		}
+
+		static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
+		{
+			assert(ns.size() >= first);
+			assert(expr->rettype() == xpath_type_number);
+
+			size_t size = ns.size() - first;
+
+			xpath_node* last = ns.begin() + first;
+
+			xpath_context c(xpath_node(), 1, size);
+
+			double er = expr->eval_number(c, stack);
+
+			if (er >= 1.0 && er <= size)
+			{
+				size_t eri = static_cast<size_t>(er);
+
+				if (er == eri)
+				{
+					xpath_node r = last[eri - 1];
+
+					*last++ = r;
+				}
+			}
+
+			ns.truncate(last);
+		}
+
+		void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once)
 		{
 			if (ns.size() == first) return;
-			
+
+			assert(_type == ast_filter || _type == ast_predicate);
+
+			if (_test == predicate_constant || _test == predicate_constant_one)
+				apply_predicate_number_const(ns, first, _right, stack);
+			else if (_right->rettype() == xpath_type_number)
+				apply_predicate_number(ns, first, _right, stack, once);
+			else
+				apply_predicate_boolean(ns, first, _right, stack, once);
+		}
+
+		void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
+		{
+			if (ns.size() == first) return;
+
+			bool last_once = eval_once(ns.type(), eval);
+
 			for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
-			{
-				apply_predicate(ns, first, pred->_left, stack);
-			}
-		}
-
-		void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc)
-		{
-			if (!a) return;
-
-			const char_t* name = a.name();
-
-			// There are no attribute nodes corresponding to attributes that declare namespaces
-			// That is, "xmlns:..." or "xmlns"
-			if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;
-			
+				pred->apply_predicate(ns, first, stack, !pred->_next && last_once);
+		}
+
+		bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)
+		{
+			assert(a);
+
+			const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT("");
+
 			switch (_test)
 			{
 			case nodetest_name:
-				if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc);
+				if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
+				{
+					ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_type_node:
 			case nodetest_all:
-				ns.push_back(xpath_node(a, parent), alloc);
+				if (is_xpath_attribute(name))
+				{
+					ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_all_in_namespace:
-				if (starts_with(name, _data.nodetest))
-					ns.push_back(xpath_node(a, parent), alloc);
+				if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
+				{
+					ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+					return true;
+				}
 				break;
 			
@@ -7631,56 +9473,84 @@
 				;
 			}
+
+			return false;
 		}
 		
-		void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc)
-		{
-			if (!n) return;
+		bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc)
+		{
+			assert(n);
+
+			xml_node_type type = PUGI__NODETYPE(n);
 
 			switch (_test)
 			{
 			case nodetest_name:
-				if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc);
+				if (type == node_element && n->name && strequal(n->name, _data.nodetest))
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_type_node:
-				ns.push_back(n, alloc);
-				break;
+				ns.push_back(xml_node(n), alloc);
+				return true;
 				
 			case nodetest_type_comment:
-				if (n.type() == node_comment)
-					ns.push_back(n, alloc);
+				if (type == node_comment)
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_type_text:
-				if (n.type() == node_pcdata || n.type() == node_cdata)
-					ns.push_back(n, alloc);
+				if (type == node_pcdata || type == node_cdata)
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_type_pi:
-				if (n.type() == node_pi)
-					ns.push_back(n, alloc);
+				if (type == node_pi)
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 									
 			case nodetest_pi:
-				if (n.type() == node_pi && strequal(n.name(), _data.nodetest))
-					ns.push_back(n, alloc);
+				if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_all:
-				if (n.type() == node_element)
-					ns.push_back(n, alloc);
+				if (type == node_element)
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 				
 			case nodetest_all_in_namespace:
-				if (n.type() == node_element && starts_with(n.name(), _data.nodetest))
-					ns.push_back(n, alloc);
+				if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
+				{
+					ns.push_back(xml_node(n), alloc);
+					return true;
+				}
 				break;
 
 			default:
 				assert(!"Unknown axis");
-			} 
-		}
-
-		template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T)
+			}
+
+			return false;
+		}
+
+		template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)
 		{
 			const axis_t axis = T::axis;
@@ -7690,6 +9560,7 @@
 			case axis_attribute:
 			{
-				for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
-					step_push(ns, a, n, alloc);
+				for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)
+					if (step_push(ns, a, n, alloc) & once)
+						return;
 				
 				break;
@@ -7698,6 +9569,7 @@
 			case axis_child:
 			{
-				for (xml_node c = n.first_child(); c; c = c.next_sibling())
-					step_push(ns, c, alloc);
+				for (xml_node_struct* c = n->first_child; c; c = c->next_sibling)
+					if (step_push(ns, c, alloc) & once)
+						return;
 					
 				break;
@@ -7708,22 +9580,26 @@
 			{
 				if (axis == axis_descendant_or_self)
-					step_push(ns, n, alloc);
+					if (step_push(ns, n, alloc) & once)
+						return;
 					
-				xml_node cur = n.first_child();
+				xml_node_struct* cur = n->first_child;
 				
-				while (cur && cur != n)
-				{
-					step_push(ns, cur, alloc);
+				while (cur)
+				{
+					if (step_push(ns, cur, alloc) & once)
+						return;
 					
-					if (cur.first_child())
-						cur = cur.first_child();
-					else if (cur.next_sibling())
-						cur = cur.next_sibling();
+					if (cur->first_child)
+						cur = cur->first_child;
 					else
 					{
-						while (!cur.next_sibling() && cur != n)
-							cur = cur.parent();
+						while (!cur->next_sibling)
+						{
+							cur = cur->parent;
+
+							if (cur == n) return;
+						}
 					
-						if (cur != n) cur = cur.next_sibling();
+						cur = cur->next_sibling;
 					}
 				}
@@ -7734,6 +9610,7 @@
 			case axis_following_sibling:
 			{
-				for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
-					step_push(ns, c, alloc);
+				for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)
+					if (step_push(ns, c, alloc) & once)
+						return;
 				
 				break;
@@ -7742,6 +9619,7 @@
 			case axis_preceding_sibling:
 			{
-				for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
-					step_push(ns, c, alloc);
+				for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)
+					if (step_push(ns, c, alloc) & once)
+						return;
 				
 				break;
@@ -7750,24 +9628,33 @@
 			case axis_following:
 			{
-				xml_node cur = n;
+				xml_node_struct* cur = n;
 
 				// exit from this node so that we don't include descendants
-				while (cur && !cur.next_sibling()) cur = cur.parent();
-				cur = cur.next_sibling();
-
-				for (;;)
-				{
-					step_push(ns, cur, alloc);
-
-					if (cur.first_child())
-						cur = cur.first_child();
-					else if (cur.next_sibling())
-						cur = cur.next_sibling();
+				while (!cur->next_sibling)
+				{
+					cur = cur->parent;
+
+					if (!cur) return;
+				}
+
+				cur = cur->next_sibling;
+
+				while (cur)
+				{
+					if (step_push(ns, cur, alloc) & once)
+						return;
+
+					if (cur->first_child)
+						cur = cur->first_child;
 					else
 					{
-						while (cur && !cur.next_sibling()) cur = cur.parent();
-						cur = cur.next_sibling();
-
-						if (!cur) break;
+						while (!cur->next_sibling)
+						{
+							cur = cur->parent;
+
+							if (!cur) return;
+						}
+
+						cur = cur->next_sibling;
 					}
 				}
@@ -7778,35 +9665,38 @@
 			case axis_preceding:
 			{
-				xml_node cur = n;
-
-				while (cur && !cur.previous_sibling()) cur = cur.parent();
-				cur = cur.previous_sibling();
-
-				for (;;)
-				{
-					if (cur.last_child())
-						cur = cur.last_child();
+				xml_node_struct* cur = n;
+
+				// exit from this node so that we don't include descendants
+				while (!cur->prev_sibling_c->next_sibling)
+				{
+					cur = cur->parent;
+
+					if (!cur) return;
+				}
+
+				cur = cur->prev_sibling_c;
+
+				while (cur)
+				{
+					if (cur->first_child)
+						cur = cur->first_child->prev_sibling_c;
 					else
 					{
 						// leaf node, can't be ancestor
-						step_push(ns, cur, alloc);
-
-						if (cur.previous_sibling())
-							cur = cur.previous_sibling();
-						else
+						if (step_push(ns, cur, alloc) & once)
+							return;
+
+						while (!cur->prev_sibling_c->next_sibling)
 						{
-							do 
-							{
-								cur = cur.parent();
-								if (!cur) break;
-
-								if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);
-							}
-							while (!cur.previous_sibling());
-
-							cur = cur.previous_sibling();
-
-							if (!cur) break;
+							cur = cur->parent;
+
+							if (!cur) return;
+
+							if (!node_is_ancestor(cur, n))
+								if (step_push(ns, cur, alloc) & once)
+									return;
 						}
+
+						cur = cur->prev_sibling_c;
 					}
 				}
@@ -7819,13 +9709,15 @@
 			{
 				if (axis == axis_ancestor_or_self)
-					step_push(ns, n, alloc);
-
-				xml_node cur = n.parent();
+					if (step_push(ns, n, alloc) & once)
+						return;
+
+				xml_node_struct* cur = n->parent;
 				
 				while (cur)
 				{
-					step_push(ns, cur, alloc);
+					if (step_push(ns, cur, alloc) & once)
+						return;
 					
-					cur = cur.parent();
+					cur = cur->parent;
 				}
 				
@@ -7842,5 +9734,6 @@
 			case axis_parent:
 			{
-				if (n.parent()) step_push(ns, n.parent(), alloc);
+				if (n->parent)
+					step_push(ns, n->parent, alloc);
 
 				break;
@@ -7852,5 +9745,5 @@
 		}
 		
-		template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v)
+		template <class T> void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v)
 		{
 			const axis_t axis = T::axis;
@@ -7862,13 +9755,15 @@
 			{
 				if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
-					step_push(ns, a, p, alloc);
-
-				xml_node cur = p;
+					if (step_push(ns, a, p, alloc) & once)
+						return;
+
+				xml_node_struct* cur = p;
 				
 				while (cur)
 				{
-					step_push(ns, cur, alloc);
+					if (step_push(ns, cur, alloc) & once)
+						return;
 					
-					cur = cur.parent();
+					cur = cur->parent;
 				}
 				
@@ -7887,21 +9782,24 @@
 			case axis_following:
 			{
-				xml_node cur = p;
+				xml_node_struct* cur = p;
 				
-				for (;;)
-				{
-					if (cur.first_child())
-						cur = cur.first_child();
-					else if (cur.next_sibling())
-						cur = cur.next_sibling();
+				while (cur)
+				{
+					if (cur->first_child)
+						cur = cur->first_child;
 					else
 					{
-						while (cur && !cur.next_sibling()) cur = cur.parent();
-						cur = cur.next_sibling();
-						
-						if (!cur) break;
+						while (!cur->next_sibling)
+						{
+							cur = cur->parent;
+
+							if (!cur) return;
+						}
+
+						cur = cur->next_sibling;
 					}
 
-					step_push(ns, cur, alloc);
+					if (step_push(ns, cur, alloc) & once)
+						return;
 				}
 
@@ -7919,5 +9817,5 @@
 			{
 				// preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
-				step_fill(ns, p, alloc, v);
+				step_fill(ns, p, alloc, once, v);
 				break;
 			}
@@ -7927,16 +9825,33 @@
 			}
 		}
-		
-		template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)
+
+		template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)
 		{
 			const axis_t axis = T::axis;
-			bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
+			const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
+
+			if (xn.node())
+				step_fill(ns, xn.node().internal_object(), alloc, once, v);
+			else if (axis_has_attributes && xn.attribute() && xn.parent())
+				step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);
+		}
+
+		template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
+		{
+			const axis_t axis = T::axis;
+			const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
+			const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
+
+			bool once =
+				(axis == axis_attribute && _test == nodetest_name) ||
+				(!_right && eval_once(axis_type, eval)) ||
+				(_right && !_right->_next && _right->_test == predicate_constant_one);
 
 			xpath_node_set_raw ns;
-			ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
+			ns.set_type(axis_type);
 
 			if (_left)
 			{
-				xpath_node_set_raw s = _left->eval_node_set(c, stack);
+				xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all);
 
 				// self axis preserves the original order
@@ -7950,20 +9865,12 @@
 					if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
 					
-					if (it->node())
-						step_fill(ns, it->node(), stack.result, v);
-					else if (attributes)
-						step_fill(ns, it->attribute(), it->parent(), stack.result, v);
-						
-					apply_predicates(ns, size, stack);
+					step_fill(ns, *it, stack.result, once, v);
+					if (_right) apply_predicates(ns, size, stack, eval);
 				}
 			}
 			else
 			{
-				if (c.n.node())
-					step_fill(ns, c.n.node(), stack.result, v);
-				else if (attributes)
-					step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);
-				
-				apply_predicates(ns, 0, stack);
+				step_fill(ns, c.n, stack.result, once, v);
+				if (_right) apply_predicates(ns, 0, stack, eval);
 			}
 
@@ -8006,5 +9913,12 @@
 			_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
 		{
+			assert(type == ast_step);
 			_data.nodetest = contents;
+		}
+
+		xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):
+			_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
+		{
+			assert(type == ast_filter || type == ast_predicate);
 		}
 
@@ -8109,4 +10023,13 @@
 			}
 
+			case ast_opt_compare_attribute:
+			{
+				const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();
+
+				xml_attribute attr = c.n.node().attribute(_left->_data.nodetest);
+
+				return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());
+			}
+
 			case ast_variable:
 			{
@@ -8137,5 +10060,5 @@
 					xpath_allocator_capture cr(stack.result);
 
-					return !eval_node_set(c, stack).empty();
+					return !eval_node_set(c, stack, nodeset_eval_any).empty();
 				}
 
@@ -8183,5 +10106,5 @@
 				xpath_allocator_capture cr(stack.result);
 
-				return static_cast<double>(_left->eval_node_set(c, stack).size());
+				return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());
 			}
 			
@@ -8216,5 +10139,5 @@
 				double r = 0;
 				
-				xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+				xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all);
 				
 				for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
@@ -8331,5 +10254,5 @@
 			*ri = 0;
 
-			return xpath_string(result, true);
+			return xpath_string::from_heap_preallocated(result, ri);
 		}
 
@@ -8339,5 +10262,5 @@
 			{
 			case ast_string_constant:
-				return xpath_string_const(_data.string);
+				return xpath_string::from_const(_data.string);
 			
 			case ast_func_local_name_0:
@@ -8345,5 +10268,5 @@
 				xpath_node na = c.n;
 				
-				return xpath_string_const(local_name(na));
+				return xpath_string::from_const(local_name(na));
 			}
 
@@ -8352,8 +10275,8 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+				xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
 				xpath_node na = ns.first();
 				
-				return xpath_string_const(local_name(na));
+				return xpath_string::from_const(local_name(na));
 			}
 
@@ -8362,5 +10285,5 @@
 				xpath_node na = c.n;
 				
-				return xpath_string_const(qualified_name(na));
+				return xpath_string::from_const(qualified_name(na));
 			}
 
@@ -8369,8 +10292,8 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+				xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
 				xpath_node na = ns.first();
 				
-				return xpath_string_const(qualified_name(na));
+				return xpath_string::from_const(qualified_name(na));
 			}
 
@@ -8379,5 +10302,5 @@
 				xpath_node na = c.n;
 				
-				return xpath_string_const(namespace_uri(na));
+				return xpath_string::from_const(namespace_uri(na));
 			}
 
@@ -8386,8 +10309,8 @@
 				xpath_allocator_capture cr(stack.result);
 
-				xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+				xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
 				xpath_node na = ns.first();
 				
-				return xpath_string_const(namespace_uri(na));
+				return xpath_string::from_const(namespace_uri(na));
 			}
 
@@ -8412,5 +10335,5 @@
 				const char_t* pos = find_substring(s.c_str(), p.c_str());
 				
-				return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string();
+				return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string();
 			}
 			
@@ -8427,7 +10350,8 @@
 				if (!pos) return xpath_string();
 
-				const char_t* result = pos + p.length();
-
-				return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result);
+				const char_t* rbegin = pos + p.length();
+				const char_t* rend = s.c_str() + s.length();
+
+				return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
 			}
 
@@ -8450,6 +10374,7 @@
 
 				const char_t* rbegin = s.c_str() + (pos - 1);
+				const char_t* rend = s.c_str() + s.length();
 				
-				return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin);
+				return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
 			}
 			
@@ -8478,5 +10403,5 @@
 				const char_t* rend = s.c_str() + (end - 1);
 
-				return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result);
+				return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result);
 			}
 
@@ -8485,7 +10410,8 @@
 				xpath_string s = string_value(c.n, stack.result);
 
-				normalize_space(s.data(stack.result));
-
-				return s;
+				char_t* begin = s.data(stack.result);
+				char_t* end = normalize_space(begin);
+
+				return xpath_string::from_heap_preallocated(begin, end);
 			}
 
@@ -8494,7 +10420,8 @@
 				xpath_string s = _left->eval_string(c, stack);
 
-				normalize_space(s.data(stack.result));
+				char_t* begin = s.data(stack.result);
+				char_t* end = normalize_space(begin);
 			
-				return s;
+				return xpath_string::from_heap_preallocated(begin, end);
 			}
 
@@ -8509,7 +10436,18 @@
 				xpath_string to = _right->_next->eval_string(c, swapped_stack);
 
-				translate(s.data(stack.result), from.c_str(), to.c_str());
-
-				return s;
+				char_t* begin = s.data(stack.result);
+				char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
+
+				return xpath_string::from_heap_preallocated(begin, end);
+			}
+
+			case ast_opt_translate_table:
+			{
+				xpath_string s = _left->eval_string(c, stack);
+
+				char_t* begin = s.data(stack.result);
+				char_t* end = translate_table(begin, _data.table);
+
+				return xpath_string::from_heap_preallocated(begin, end);
 			}
 
@@ -8519,5 +10457,5 @@
 
 				if (_rettype == xpath_type_string)
-					return xpath_string_const(_data.variable->get_string());
+					return xpath_string::from_const(_data.variable->get_string());
 
 				// fallthrough to type conversion
@@ -8529,5 +10467,5 @@
 				{
 				case xpath_type_boolean:
-					return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
+					return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
 					
 				case xpath_type_number:
@@ -8540,5 +10478,5 @@
 					xpath_stack swapped_stack = {stack.temp, stack.result};
 
-					xpath_node_set_raw ns = eval_node_set(c, swapped_stack);
+					xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);
 					return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
 				}
@@ -8552,5 +10490,5 @@
 		}
 
-		xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)
+		xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval)
 		{
 			switch (_type)
@@ -8562,7 +10500,7 @@
 				xpath_stack swapped_stack = {stack.temp, stack.result};
 
-				xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack);
-				xpath_node_set_raw rs = _right->eval_node_set(c, stack);
-				
+				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);
+
 				// 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);
@@ -8570,17 +10508,18 @@
 				rs.append(ls.begin(), ls.end(), stack.result);
 				rs.remove_duplicates();
-				
+
 				return rs;
 			}
 
 			case ast_filter:
-			case ast_filter_posinv:
-			{
-				xpath_node_set_raw set = _left->eval_node_set(c, stack);
+			{
+				xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);
 
 				// either expression is a number or it contains position() call; sort by document order
-				if (_type == ast_filter) set.sort_do();
-
-				apply_predicate(set, 0, _right, stack);
+				if (_test != predicate_posinv) set.sort_do();
+
+				bool once = eval_once(set.type(), eval);
+
+				apply_predicate(set, 0, stack, once);
 			
 				return set;
@@ -8595,26 +10534,26 @@
 				{
 				case axis_ancestor:
-					return step_do(c, stack, axis_to_type<axis_ancestor>());
+					return step_do(c, stack, eval, axis_to_type<axis_ancestor>());
 					
 				case axis_ancestor_or_self:
-					return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());
+					return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());
 
 				case axis_attribute:
-					return step_do(c, stack, axis_to_type<axis_attribute>());
+					return step_do(c, stack, eval, axis_to_type<axis_attribute>());
 
 				case axis_child:
-					return step_do(c, stack, axis_to_type<axis_child>());
+					return step_do(c, stack, eval, axis_to_type<axis_child>());
 				
 				case axis_descendant:
-					return step_do(c, stack, axis_to_type<axis_descendant>());
+					return step_do(c, stack, eval, axis_to_type<axis_descendant>());
 
 				case axis_descendant_or_self:
-					return step_do(c, stack, axis_to_type<axis_descendant_or_self>());
+					return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());
 
 				case axis_following:
-					return step_do(c, stack, axis_to_type<axis_following>());
+					return step_do(c, stack, eval, axis_to_type<axis_following>());
 				
 				case axis_following_sibling:
-					return step_do(c, stack, axis_to_type<axis_following_sibling>());
+					return step_do(c, stack, eval, axis_to_type<axis_following_sibling>());
 				
 				case axis_namespace:
@@ -8623,14 +10562,14 @@
 				
 				case axis_parent:
-					return step_do(c, stack, axis_to_type<axis_parent>());
+					return step_do(c, stack, eval, axis_to_type<axis_parent>());
 				
 				case axis_preceding:
-					return step_do(c, stack, axis_to_type<axis_preceding>());
+					return step_do(c, stack, eval, axis_to_type<axis_preceding>());
 
 				case axis_preceding_sibling:
-					return step_do(c, stack, axis_to_type<axis_preceding_sibling>());
+					return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());
 				
 				case axis_self:
-					return step_do(c, stack, axis_to_type<axis_self>());
+					return step_do(c, stack, eval, axis_to_type<axis_self>());
 
 				default:
@@ -8678,10 +10617,80 @@
 			}
 		}
+
+		void optimize(xpath_allocator* alloc)
+		{
+			if (_left) _left->optimize(alloc);
+			if (_right) _right->optimize(alloc);
+			if (_next) _next->optimize(alloc);
+
+			optimize_self(alloc);
+		}
+
+		void optimize_self(xpath_allocator* alloc)
+		{
+			// 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->_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)
+			{
+				assert(_test == predicate_default);
+
+				if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
+					_test = predicate_constant_one;
+				else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
+					_test = predicate_constant;
+				else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
+					_test = predicate_posinv;
+			}
+
+			// Rewrite descendant-or-self::node()/child::foo with descendant::foo
+			// 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 &&
+				is_posinv_step())
+			{
+				if (_axis == axis_child || _axis == axis_descendant)
+					_axis = axis_descendant;
+				else
+					_axis = axis_descendant_or_self;
+
+				_left = _left->_left;
+			}
+
+			// 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)
+			{
+				unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
+
+				if (table)
+				{
+					_type = ast_opt_translate_table;
+					_data.table = table;
+				}
+			}
+
+			// Use optimized path for @attr = 'value' or @attr = $value
+			if (_type == ast_op_equal &&
+				_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)))
+			{
+				_type = ast_opt_compare_attribute;
+			}
+		}
 		
-		bool is_posinv()
+		bool is_posinv_expr() const
 		{
 			switch (_type)
 			{
 			case ast_func_position:
+			case ast_func_last:
 				return false;
 
@@ -8697,12 +10706,11 @@
 			case ast_predicate:
 			case ast_filter:
-			case ast_filter_posinv:
 				return true;
 
 			default:
-				if (_left && !_left->is_posinv()) return false;
+				if (_left && !_left->is_posinv_expr()) return false;
 				
 				for (xpath_ast_node* n = _right; n; n = n->_next)
-					if (!n->is_posinv()) return false;
+					if (!n->is_posinv_expr()) return false;
 					
 				return true;
@@ -8710,4 +10718,19 @@
 		}
 
+		bool is_posinv_step() const
+		{
+			assert(_type == ast_step);
+
+			for (xpath_ast_node* n = _right; n; n = n->_next)
+			{
+				assert(n->_type == ast_predicate);
+
+				if (n->_test != predicate_posinv)
+					return false;
+			}
+
+			return true;
+		}
+
 		xpath_value_type rettype() const
 		{
@@ -8725,4 +10748,6 @@
 
 		xpath_parse_result* _result;
+
+		char_t _scratch[32];
 
 	#ifdef PUGIXML_NO_EXCEPTIONS
@@ -8768,4 +10793,5 @@
 				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
 
 				memcpy(c, value.begin, length * sizeof(char_t));
@@ -8803,5 +10829,5 @@
 				}
 				else if (name == PUGIXML_TEXT("contains") && argc == 2)
-					return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]);
+					return new (alloc_node()) xpath_ast_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]);
@@ -8865,5 +10891,5 @@
 					return new (alloc_node()) xpath_ast_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_string, args[0]);
+					return new (alloc_node()) xpath_ast_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]);
@@ -9014,5 +11040,7 @@
 					throw_error("Unknown variable: variable set is not provided");
 
-				xpath_variable* var = get_variable(_variables, name.begin, name.end);
+				xpath_variable* var = 0;
+				if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))
+					throw_error_oom();
 
 				if (!var)
@@ -9052,5 +11080,5 @@
 				double value = 0;
 
-				if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value))
+				if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))
 					throw_error_oom();
 
@@ -9120,7 +11148,5 @@
 				if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
 
-				bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv();
-
-				n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
+				n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default);
 
 				if (_lexer.current() != lex_close_square_brace)
@@ -9266,5 +11292,5 @@
 				xpath_ast_node* expr = parse_expression();
 
-				xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);
+				xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default);
 				
 				if (_lexer.current() != lex_close_square_brace)
@@ -9277,5 +11303,5 @@
 				last = pred;
 			}
-			
+
 			return n;
 		}
@@ -9336,5 +11362,7 @@
 		//				| FilterExpr '/' RelativeLocationPath
 		//				| FilterExpr '//' RelativeLocationPath
-		xpath_ast_node* parse_path_expression()
+		// UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+		// UnaryExpr ::= UnionExpr | '-' UnaryExpr
+		xpath_ast_node* parse_path_or_unary_expression()
 		{
 			// Clarification.
@@ -9382,85 +11410,119 @@
 				return n;
 			}
-			else return parse_location_path();
-		}
-
-		// UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
-		xpath_ast_node* parse_union_expression()
-		{
-			xpath_ast_node* n = parse_path_expression();
-
-			while (_lexer.current() == lex_union)
+			else if (_lexer.current() == lex_minus)
 			{
 				_lexer.next();
 
-				xpath_ast_node* expr = parse_union_expression();
-
-				if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set)
+				// precedence 7+ - only parses union expressions
+				xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7);
+
+				return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
+			}
+			else
+				return parse_location_path();
+		}
+
+		struct binary_op_t
+		{
+			ast_type_t asttype;
+			xpath_value_type rettype;
+			int precedence;
+
+			binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0)
+			{
+			}
+
+			binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)
+			{
+			}
+
+			static binary_op_t parse(xpath_lexer& lexer)
+			{
+				switch (lexer.current())
+				{
+				case lex_string:
+					if (lexer.contents() == PUGIXML_TEXT("or"))
+						return binary_op_t(ast_op_or, xpath_type_boolean, 1);
+					else if (lexer.contents() == PUGIXML_TEXT("and"))
+						return binary_op_t(ast_op_and, xpath_type_boolean, 2);
+					else if (lexer.contents() == PUGIXML_TEXT("div"))
+						return binary_op_t(ast_op_divide, xpath_type_number, 6);
+					else if (lexer.contents() == PUGIXML_TEXT("mod"))
+						return binary_op_t(ast_op_mod, xpath_type_number, 6);
+					else
+						return binary_op_t();
+
+				case lex_equal:
+					return binary_op_t(ast_op_equal, xpath_type_boolean, 3);
+
+				case lex_not_equal:
+					return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);
+
+				case lex_less:
+					return binary_op_t(ast_op_less, xpath_type_boolean, 4);
+
+				case lex_greater:
+					return binary_op_t(ast_op_greater, xpath_type_boolean, 4);
+
+				case lex_less_or_equal:
+					return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);
+
+				case lex_greater_or_equal:
+					return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);
+
+				case lex_plus:
+					return binary_op_t(ast_op_add, xpath_type_number, 5);
+
+				case lex_minus:
+					return binary_op_t(ast_op_subtract, xpath_type_number, 5);
+
+				case lex_multiply:
+					return binary_op_t(ast_op_multiply, xpath_type_number, 6);
+
+				case lex_union:
+					return binary_op_t(ast_op_union, xpath_type_node_set, 7);
+
+				default:
+					return binary_op_t();
+				}
+			}
+		};
+
+		xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit)
+		{
+			binary_op_t op = binary_op_t::parse(_lexer);
+
+			while (op.asttype != ast_unknown && op.precedence >= limit)
+			{
+				_lexer.next();
+
+				xpath_ast_node* rhs = parse_path_or_unary_expression();
+
+				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);
+
+					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");
 
-				n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr);
-			}
-
-			return n;
-		}
-
-		// UnaryExpr ::= UnionExpr | '-' UnaryExpr
-		xpath_ast_node* parse_unary_expression()
-		{
-			if (_lexer.current() == lex_minus)
-			{
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_unary_expression();
-
-				return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
-			}
-			else return parse_union_expression();
-		}
-		
-		// MultiplicativeExpr ::= UnaryExpr
-		//						  | MultiplicativeExpr '*' UnaryExpr
-		//						  | MultiplicativeExpr 'div' UnaryExpr
-		//						  | MultiplicativeExpr 'mod' UnaryExpr
-		xpath_ast_node* parse_multiplicative_expression()
-		{
-			xpath_ast_node* n = parse_unary_expression();
-
-			while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string &&
-				   (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div"))))
-			{
-				ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply :
-					_lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod;
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_unary_expression();
-
-				n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr);
-			}
-
-			return n;
-		}
-
-		// AdditiveExpr ::= MultiplicativeExpr
-		//					| AdditiveExpr '+' MultiplicativeExpr
-		//					| AdditiveExpr '-' MultiplicativeExpr
-		xpath_ast_node* parse_additive_expression()
-		{
-			xpath_ast_node* n = parse_multiplicative_expression();
-
-			while (_lexer.current() == lex_plus || _lexer.current() == lex_minus)
-			{
-				lexeme_t l = _lexer.current();
-
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_multiplicative_expression();
-
-				n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr);
-			}
-
-			return n;
-		}
-
+				lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs);
+
+				op = binary_op_t::parse(_lexer);
+			}
+
+			return lhs;
+		}
+
+		// Expr ::= OrExpr
+		// OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+		// AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+		// EqualityExpr ::= RelationalExpr
+		//					| EqualityExpr '=' RelationalExpr
+		//					| EqualityExpr '!=' RelationalExpr
 		// RelationalExpr ::= AdditiveExpr
 		//					  | RelationalExpr '<' AdditiveExpr
@@ -9468,82 +11530,14 @@
 		//					  | RelationalExpr '<=' AdditiveExpr
 		//					  | RelationalExpr '>=' AdditiveExpr
-		xpath_ast_node* parse_relational_expression()
-		{
-			xpath_ast_node* n = parse_additive_expression();
-
-			while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal || 
-				   _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal)
-			{
-				lexeme_t l = _lexer.current();
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_additive_expression();
-
-				n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater :
-								l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr);
-			}
-
-			return n;
-		}
-		
-		// EqualityExpr ::= RelationalExpr
-		//					| EqualityExpr '=' RelationalExpr
-		//					| EqualityExpr '!=' RelationalExpr
-		xpath_ast_node* parse_equality_expression()
-		{
-			xpath_ast_node* n = parse_relational_expression();
-
-			while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal)
-			{
-				lexeme_t l = _lexer.current();
-
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_relational_expression();
-
-				n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr);
-			}
-
-			return n;
-		}
-		
-		// AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
-		xpath_ast_node* parse_and_expression()
-		{
-			xpath_ast_node* n = parse_equality_expression();
-
-			while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and"))
-			{
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_equality_expression();
-
-				n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr);
-			}
-
-			return n;
-		}
-
-		// OrExpr ::= AndExpr | OrExpr 'or' AndExpr
-		xpath_ast_node* parse_or_expression()
-		{
-			xpath_ast_node* n = parse_and_expression();
-
-			while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or"))
-			{
-				_lexer.next();
-
-				xpath_ast_node* expr = parse_and_expression();
-
-				n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr);
-			}
-
-			return n;
-		}
-		
-		// Expr ::= OrExpr
+		// AdditiveExpr ::= MultiplicativeExpr
+		//					| AdditiveExpr '+' MultiplicativeExpr
+		//					| AdditiveExpr '-' MultiplicativeExpr
+		// MultiplicativeExpr ::= UnaryExpr
+		//						  | MultiplicativeExpr '*' UnaryExpr
+		//						  | MultiplicativeExpr 'div' UnaryExpr
+		//						  | MultiplicativeExpr 'mod' UnaryExpr
 		xpath_ast_node* parse_expression()
 		{
-			return parse_or_expression();
+			return parse_expression_rec(parse_path_or_unary_expression(), 0);
 		}
 
@@ -9584,17 +11578,16 @@
 		{
 			void* memory = xml_memory::allocate(sizeof(xpath_query_impl));
+			if (!memory) return 0;
 
 			return new (memory) xpath_query_impl();
 		}
 
-		static void destroy(void* ptr)
-		{
-			if (!ptr) return;
-			
+		static void destroy(xpath_query_impl* impl)
+		{
 			// free all allocated pages
-			static_cast<xpath_query_impl*>(ptr)->alloc.release();
+			impl->alloc.release();
 
 			// free allocator memory (with the first page)
-			xml_memory::deallocate(ptr);
+			xml_memory::deallocate(impl);
 		}
 
@@ -9602,4 +11595,5 @@
 		{
 			block.next = 0;
+			block.capacity = sizeof(block.data);
 		}
 
@@ -9621,4 +11615,23 @@
 		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;
+
+		if (impl->root->rettype() != xpath_type_node_set)
+		{
+		#ifdef PUGIXML_NO_EXCEPTIONS
+			return 0;
+		#else
+			xpath_parse_result res;
+			res.error = "Expression does not evaluate to node set";
+
+			throw xpath_exception(res);
+		#endif
+		}
+
+		return impl->root;
+	}
 PUGI__NS_END
 
@@ -9705,5 +11718,5 @@
 #endif
 
-	PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_)
+	PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)
 	{
 		assert(begin_ <= end_);
@@ -9721,4 +11734,5 @@
 			_begin = &_storage;
 			_end = &_storage + size_;
+			_type = type_;
 		}
 		else
@@ -9744,6 +11758,21 @@
 			_begin = storage;
 			_end = storage + size_;
-		}
-	}
+			_type = type_;
+		}
+	}
+
+#if __cplusplus >= 201103
+	PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs)
+	{
+		_type = rhs._type;
+		_storage = rhs._storage;
+		_begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin;
+		_end = _begin + (rhs._end - rhs._begin);
+
+		rhs._type = type_unsorted;
+		rhs._begin = &rhs._storage;
+		rhs._end = rhs._begin;
+	}
+#endif
 
 	PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
@@ -9751,17 +11780,18 @@
 	}
 
-	PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage)
-	{
-		_assign(begin_, end_);
+	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) impl::xml_memory::deallocate(_begin);
+		if (_begin != &_storage)
+			impl::xml_memory::deallocate(_begin);
 	}
 		
-	PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage)
-	{
-		_assign(ns._begin, ns._end);
+	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);
 	}
 	
@@ -9769,10 +11799,28 @@
 	{
 		if (this == &ns) return *this;
-		
-		_type = ns._type;
-		_assign(ns._begin, ns._end);
+
+		_assign(ns._begin, ns._end, ns._type);
 
 		return *this;
 	}
+
+#if __cplusplus >= 201103
+	PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+	{
+		_move(rhs);
+	}
+
+	PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs)
+	{
+		if (this == &rhs) return *this;
+
+		if (_begin != &_storage)
+			impl::xml_memory::deallocate(_begin);
+
+		_move(rhs);
+
+		return *this;
+	}
+#endif
 
 	PUGI__FN xpath_node_set::type_t xpath_node_set::type() const
@@ -9831,5 +11879,5 @@
 	}
 
-	PUGI__FN xpath_variable::xpath_variable()
+	PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)
 	{
 	}
@@ -9930,5 +11978,6 @@
 	PUGI__FN xpath_variable_set::xpath_variable_set()
 	{
-		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0;
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+			_data[i] = 0;
 	}
 
@@ -9936,19 +11985,71 @@
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
-		{
-			xpath_variable* var = _data[i];
-
-			while (var)
-			{
-				xpath_variable* next = var->_next;
-
-				impl::delete_xpath_variable(var->_type, var);
-
-				var = next;
-			}
-		}
-	}
-
-	PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const
+			_destroy(_data[i]);
+	}
+
+	PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)
+	{
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+			_data[i] = 0;
+
+		_assign(rhs);
+	}
+
+	PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
+	{
+		if (this == &rhs) return *this;
+
+		_assign(rhs);
+
+		return *this;
+	}
+
+#if __cplusplus >= 201103
+	PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs)
+	{
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+		{
+			_data[i] = rhs._data[i];
+			rhs._data[i] = 0;
+		}
+	}
+
+	PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs)
+	{
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+		{
+			_destroy(_data[i]);
+
+			_data[i] = rhs._data[i];
+			rhs._data[i] = 0;
+		}
+
+		return *this;
+	}
+#endif
+
+	PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
+	{
+		xpath_variable_set temp;
+
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+			if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))
+				return;
+
+		_swap(temp);
+	}
+
+	PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
+	{
+		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+		{
+			xpath_variable* chain = _data[i];
+
+			_data[i] = rhs._data[i];
+			rhs._data[i] = chain;
+		}
+	}
+
+	PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
 	{
 		const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
@@ -9963,4 +12064,43 @@
 	}
 
+	PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
+	{
+		xpath_variable* last = 0;
+
+		while (var)
+		{
+			// allocate storage for new variable
+			xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());
+			if (!nvar) return false;
+
+			// link the variable to the result immediately to handle failures gracefully
+			if (last)
+				last->_next = nvar;
+			else
+				*out_result = nvar;
+
+			last = nvar;
+
+			// copy the value; this can fail due to out-of-memory conditions
+			if (!impl::copy_xpath_variable(nvar, var)) return false;
+
+			var = var->_next;
+		}
+
+		return true;
+	}
+
+	PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var)
+	{
+		while (var)
+		{
+			xpath_variable* next = var->_next;
+
+			impl::delete_xpath_variable(var->_type, var);
+
+			var = next;
+		}
+	}
+
 	PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
 	{
@@ -9978,5 +12118,4 @@
 		if (result)
 		{
-			result->_type = type;
 			result->_next = _data[hash];
 
@@ -10013,10 +12152,10 @@
 	PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
 	{
-		return find(name);
+		return _find(name);
 	}
 
 	PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
 	{
-		return find(name);
+		return _find(name);
 	}
 
@@ -10035,5 +12174,6 @@
 		else
 		{
-			impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);
+			using impl::auto_deleter; // MSVC7 workaround
+			auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);
 
 			qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
@@ -10041,5 +12181,7 @@
 			if (qimpl->root)
 			{
-				_impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());
+				qimpl->root->optimize(&qimpl->alloc);
+
+				_impl = impl.release();
 				_result.error = 0;
 			}
@@ -10047,8 +12189,38 @@
 	}
 
+	PUGI__FN xpath_query::xpath_query(): _impl(0)
+	{
+	}
+
 	PUGI__FN xpath_query::~xpath_query()
 	{
-		impl::xpath_query_impl::destroy(_impl);
-	}
+		if (_impl)
+			impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
+	}
+
+#if __cplusplus >= 201103
+	PUGI__FN xpath_query::xpath_query(xpath_query&& rhs)
+	{
+		_impl = rhs._impl;
+		_result = rhs._result;
+		rhs._impl = 0;
+		rhs._result = xpath_parse_result();
+	}
+
+	PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs)
+	{
+		if (this == &rhs) return *this;
+
+		if (_impl)
+			impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
+
+		_impl = rhs._impl;
+		_result = rhs._result;
+		rhs._impl = 0;
+		rhs._result = xpath_parse_result();
+
+		return *this;
+	}
+#endif
 
 	PUGI__FN xpath_value_type xpath_query::return_type() const
@@ -10092,5 +12264,7 @@
 		impl::xpath_stack_data sd;
 
-		return impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd).c_str();
+		impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);
+
+		return string_t(r.c_str(), r.length());
 	}
 #endif
@@ -10118,20 +12292,7 @@
 	PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
 	{
-		if (!_impl) return xpath_node_set();
-
-		impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;
-
-		if (root->rettype() != xpath_type_node_set)
-		{
-		#ifdef PUGIXML_NO_EXCEPTIONS
-			return xpath_node_set();
-		#else
-			xpath_parse_result res;
-			res.error = "Expression does not evaluate to node set";
-
-			throw xpath_exception(res);
-		#endif
-		}
-		
+		impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
+		if (!root) return xpath_node_set();
+
 		impl::xpath_context c(n, 1, 1);
 		impl::xpath_stack_data sd;
@@ -10141,9 +12302,26 @@
 	#endif
 
-		impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack);
+		impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
 
 		return xpath_node_set(r.begin(), r.end(), r.type());
 	}
 
+	PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const
+	{
+		impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
+		if (!root) return xpath_node();
+
+		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);
+
+		return r.first();
+	}
+
 	PUGI__FN const xpath_parse_result& xpath_query::result() const
 	{
@@ -10163,4 +12341,26 @@
 	{
 		return !_impl;
+	}
+
+	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);
+	}
+
+	PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
+	{
+		return query.evaluate_node(*this);
+	}
+
+	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);
+	}
+
+	PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
+	{
+		return query.evaluate_node_set(*this);
 	}
 
@@ -10173,17 +12373,5 @@
 	PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
 	{
-		xpath_node_set s = query.evaluate_node_set(*this);
-		return s.empty() ? xpath_node() : s.first();
-	}
-
-	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);
-	}
-
-	PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
-	{
-		return query.evaluate_node_set(*this);
+		return query.evaluate_node(*this);
 	}
 }
@@ -10203,4 +12391,5 @@
 // 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
@@ -10210,7 +12399,11 @@
 #undef PUGI__FN
 #undef PUGI__FN_NO_INLINE
+#undef PUGI__GETPAGE_IMPL
+#undef PUGI__GETPAGE
+#undef PUGI__NODETYPE
 #undef PUGI__IS_CHARTYPE_IMPL
 #undef PUGI__IS_CHARTYPE
 #undef PUGI__IS_CHARTYPEX
+#undef PUGI__ENDSWITH
 #undef PUGI__SKIPWS
 #undef PUGI__OPTSET
@@ -10219,4 +12412,5 @@
 #undef PUGI__SCANFOR
 #undef PUGI__SCANWHILE
+#undef PUGI__SCANWHILE_UNROLL
 #undef PUGI__ENDSEG
 #undef PUGI__THROW_ERROR
@@ -10226,5 +12420,5 @@
 
 /**
- * Copyright (c) 2006-2012 Arseny Kapoulkine
+ * Copyright (c) 2006-2015 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
Index: XmlTools2/trunk/libs/pugixml.hpp
===================================================================
--- XmlTools2/trunk/libs/pugixml.hpp	(revision 1003)
+++ XmlTools2/trunk/libs/pugixml.hpp	(revision 1055)
@@ -1,6 +1,6 @@
 /**
- * pugixml parser - version 1.2
+ * pugixml parser - version 1.7
  * --------------------------------------------------------
- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at http://pugixml.org/
  *
@@ -14,5 +14,5 @@
 #ifndef PUGIXML_VERSION
 // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
-#	define PUGIXML_VERSION 120
+#	define PUGIXML_VERSION 170
 #endif
 
@@ -64,4 +64,13 @@
 #endif
 
+// If the platform is known to have long long support, enable long long functions
+#ifndef PUGIXML_HAS_LONG_LONG
+#	if __cplusplus >= 201103
+#		define PUGIXML_HAS_LONG_LONG
+#	elif defined(_MSC_VER) && _MSC_VER >= 1400
+#		define PUGIXML_HAS_LONG_LONG
+#	endif
+#endif
+
 // Character interface macros
 #ifdef PUGIXML_WCHAR_MODE
@@ -142,4 +151,11 @@
 	// This flag is off by default; turning it on may result in slower parsing and more memory consumption.
 	const unsigned int parse_ws_pcdata_single = 0x0400;
+
+	// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
+	const unsigned int parse_trim_pcdata = 0x0800;
+
+	// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
+	// is a valid document. This flag is off by default.
+	const unsigned int parse_fragment = 0x1000;
 
 	// The default parsing mode.
@@ -188,8 +204,11 @@
 	const unsigned int format_save_file_text = 0x20;
 
+	// Write every attribute on a new line with appropriate indentation. This flag is off by default.
+	const unsigned int format_indent_attributes = 0x40;
+
 	// The default set of formatting flags.
 	// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
 	const unsigned int format_default = format_indent;
-		
+
 	// Forward declarations
 	struct xml_attribute_struct;
@@ -201,4 +220,6 @@
 
 	class xml_tree_walker;
+
+	struct xml_parse_result;
 
 	class xml_node;
@@ -218,4 +239,5 @@
 	public:
 		typedef It const_iterator;
+		typedef It iterator;
 
 		xml_object_range(It b, It e): _begin(b), _end(e)
@@ -318,4 +340,9 @@
 		float as_float(float def = 0) const;
 
+	#ifdef PUGIXML_HAS_LONG_LONG
+		long long as_llong(long long def = 0) const;
+		unsigned long long as_ullong(unsigned long long def = 0) const;
+	#endif
+
 		// Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
 		bool as_bool(bool def = false) const;
@@ -329,5 +356,11 @@
 		bool set_value(unsigned int rhs);
 		bool set_value(double rhs);
+		bool set_value(float rhs);
 		bool set_value(bool rhs);
+
+	#ifdef PUGIXML_HAS_LONG_LONG
+		bool set_value(long long rhs);
+		bool set_value(unsigned long long rhs);
+	#endif
 
 		// Set attribute value (equivalent to set_value without error checking)
@@ -336,5 +369,11 @@
 		xml_attribute& operator=(unsigned int rhs);
 		xml_attribute& operator=(double rhs);
+		xml_attribute& operator=(float rhs);
 		xml_attribute& operator=(bool rhs);
+
+	#ifdef PUGIXML_HAS_LONG_LONG
+		xml_attribute& operator=(long long rhs);
+		xml_attribute& operator=(unsigned long long rhs);
+	#endif
 
 		// Get next/previous attribute in the attribute list of the parent node
@@ -394,6 +433,9 @@
 		xml_node_type type() const;
 
-		// Get node name/value, or "" if node is empty or it has no name/value
+		// Get node name, or "" if node is empty or it has no name
 		const char_t* name() const;
+
+		// Get node value, or "" if node is empty or it has no value
+		// Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
 		const char_t* value() const;
 	
@@ -425,4 +467,7 @@
 		xml_node previous_sibling(const char_t* name) const;
 
+		// Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast)
+		xml_attribute attribute(const char_t* name, xml_attribute& hint) const;
+
 		// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
 		const char_t* child_value() const;
@@ -465,4 +510,10 @@
 		xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
 
+		// Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
+		xml_node append_move(const xml_node& moved);
+		xml_node prepend_move(const xml_node& moved);
+		xml_node insert_move_after(const xml_node& moved, const xml_node& node);
+		xml_node insert_move_before(const xml_node& moved, const xml_node& node);
+
 		// Remove specified attribute
 		bool remove_attribute(const xml_attribute& a);
@@ -472,4 +523,9 @@
 		bool remove_child(const xml_node& n);
 		bool remove_child(const char_t* name);
+
+		// Parses buffer as an XML document fragment and appends all nodes as children of the current node.
+		// Copies/converts the buffer, so it may be deleted or changed after the function returns.
+		// Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
+		xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
 
 		// Find attribute using predicate. Returns first attribute for which predicate returned true.
@@ -538,10 +594,15 @@
 	#ifndef PUGIXML_NO_XPATH
 		// Select single node by evaluating XPath query. Returns first node from the resulting node set.
-		xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
-		xpath_node select_single_node(const xpath_query& query) const;
+		xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
+		xpath_node select_node(const xpath_query& query) const;
 
 		// Select node set by evaluating XPath query
 		xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
 		xpath_node_set select_nodes(const xpath_query& query) const;
+
+		// (deprecated: use select_node instead) Select single node by evaluating XPath query.
+		xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+		xpath_node select_single_node(const xpath_query& query) const;
+
 	#endif
 		
@@ -627,4 +688,9 @@
 		float as_float(float def = 0) const;
 
+	#ifdef PUGIXML_HAS_LONG_LONG
+		long long as_llong(long long def = 0) const;
+		unsigned long long as_ullong(unsigned long long def = 0) const;
+	#endif
+
 		// Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
 		bool as_bool(bool def = false) const;
@@ -637,5 +703,11 @@
 		bool set(unsigned int rhs);
 		bool set(double rhs);
+		bool set(float rhs);
 		bool set(bool rhs);
+
+	#ifdef PUGIXML_HAS_LONG_LONG
+		bool set(long long rhs);
+		bool set(unsigned long long rhs);
+	#endif
 
 		// Set text (equivalent to set without error checking)
@@ -644,5 +716,11 @@
 		xml_text& operator=(unsigned int rhs);
 		xml_text& operator=(double rhs);
+		xml_text& operator=(float rhs);
 		xml_text& operator=(bool rhs);
+
+	#ifdef PUGIXML_HAS_LONG_LONG
+		xml_text& operator=(long long rhs);
+		xml_text& operator=(unsigned long long rhs);
+	#endif
 
 		// Get the data node (node_pcdata or node_cdata) for this object
@@ -741,6 +819,8 @@
 
 	// Named node range helper
-	class xml_named_node_iterator
-	{
+	class PUGIXML_CLASS xml_named_node_iterator
+	{
+		friend class xml_node;
+
 	public:
 		// Iterator traits
@@ -751,5 +831,5 @@
 
 	#ifndef PUGIXML_NO_STL
-		typedef std::forward_iterator_tag iterator_category;
+		typedef std::bidirectional_iterator_tag iterator_category;
 	#endif
 
@@ -770,7 +850,13 @@
 		xml_named_node_iterator operator++(int);
 
+		const xml_named_node_iterator& operator--();
+		xml_named_node_iterator operator--(int);
+
 	private:
-		mutable xml_node _node;
+		mutable xml_node _wrap;
+		xml_node _parent;
 		const char_t* _name;
+
+		xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
 	};
 
@@ -821,5 +907,9 @@
 		status_bad_attribute,		// Parsing error occurred while parsing element attribute
 		status_bad_end_element,		// Parsing error occurred while parsing end element tag
-		status_end_element_mismatch // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
+		status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
+
+		status_append_invalid_root,	// Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
+
+		status_no_document_element	// Parsing resulted in a document without element nodes
 	};
 
@@ -856,10 +946,8 @@
 		// Non-copyable semantics
 		xml_document(const xml_document&);
-		const xml_document& operator=(const xml_document&);
+		xml_document& operator=(const xml_document&);
 
 		void create();
 		void destroy();
-
-		xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own);
 
 	public:
@@ -882,6 +970,9 @@
 	#endif
 
+		// (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
+		xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
+
 		// Load document from zero-terminated string. No encoding conversions are applied.
-		xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
+		xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
 
 		// Load document from file
@@ -956,5 +1047,5 @@
 		xpath_variable* _next;
 
-		xpath_variable();
+		xpath_variable(xpath_value_type type);
 
 		// Non-copyable semantics
@@ -988,9 +1079,11 @@
 		xpath_variable* _data[64];
 
-		// Non-copyable semantics
-		xpath_variable_set(const xpath_variable_set&);
-		xpath_variable_set& operator=(const xpath_variable_set&);
-
-		xpath_variable* find(const char_t* name) const;
+		void _assign(const xpath_variable_set& rhs);
+		void _swap(xpath_variable_set& rhs);
+
+		xpath_variable* _find(const char_t* name) const;
+
+		static bool _clone(xpath_variable* var, xpath_variable** out_result);
+		static void _destroy(xpath_variable* var);
 
 	public:
@@ -998,4 +1091,14 @@
 		xpath_variable_set();
 		~xpath_variable_set();
+
+		// Copy constructor/assignment operator
+		xpath_variable_set(const xpath_variable_set& rhs);
+		xpath_variable_set& operator=(const xpath_variable_set& rhs);
+
+	#if __cplusplus >= 201103
+		// Move semantics support
+		xpath_variable_set(xpath_variable_set&& rhs);
+		xpath_variable_set& operator=(xpath_variable_set&& rhs);
+	#endif
 
 		// Add a new variable or get the existing one, if the types match
@@ -1031,6 +1134,15 @@
 		explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
 
+		// Constructor
+		xpath_query();
+
 		// Destructor
 		~xpath_query();
+
+	#if __cplusplus >= 201103
+		// Move semantics support
+		xpath_query(xpath_query&& rhs);
+		xpath_query& operator=(xpath_query&& rhs);
+	#endif
 
 		// Get query expression return type
@@ -1061,4 +1173,10 @@
 		// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
 		xpath_node_set evaluate_node_set(const xpath_node& n) const;
+
+		// Evaluate expression as node set in the specified context.
+		// Return first node in document order, or empty node if node set is empty.
+		// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
+		// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
+		xpath_node evaluate_node(const xpath_node& n) const;
 
 		// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
@@ -1146,4 +1264,7 @@
 		// Constant iterator type
 		typedef const xpath_node* const_iterator;
+
+		// We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
+		typedef const xpath_node* iterator;
 	
 		// Default constructor. Constructs empty set.
@@ -1160,4 +1281,10 @@
 		xpath_node_set& operator=(const xpath_node_set& ns);
 
+	#if __cplusplus >= 201103
+		// Move semantics support
+		xpath_node_set(xpath_node_set&& rhs);
+		xpath_node_set& operator=(xpath_node_set&& rhs);
+	#endif
+
 		// Get collection type
 		type_t type() const;
@@ -1190,5 +1317,6 @@
 		xpath_node* _end;
 
-		void _assign(const_iterator begin, const_iterator end);
+		void _assign(const_iterator begin, const_iterator end, type_t type);
+		void _move(xpath_node_set& rhs);
 	};
 #endif
@@ -1224,5 +1352,5 @@
 	std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
 	std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
-	std::forward_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
+	std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
 }
 #endif
@@ -1234,5 +1362,5 @@
 	std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
 	std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
-	std::forward_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
+	std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
 }
 #endif
@@ -1240,6 +1368,13 @@
 #endif
 
+// Make sure implementation is included in header-only mode
+// Use macro expansion in #include to work around QMake (QTBUG-11923)
+#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)
+#	define PUGIXML_SOURCE "pugixml.cpp"
+#	include PUGIXML_SOURCE
+#endif
+
 /**
- * Copyright (c) 2006-2012 Arseny Kapoulkine
+ * Copyright (c) 2006-2015 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
Index: XmlTools2/trunk/optionsparser.cpp
===================================================================
--- XmlTools2/trunk/optionsparser.cpp	(revision 1003)
+++ XmlTools2/trunk/optionsparser.cpp	(revision 1055)
@@ -11,5 +11,5 @@
     parser.setApplicationDescription("Additional documentation can be found at: http://wiki.oni2.net/XmlTools");
 
-    XmlTools *myXmlTools;
+    std::unique_ptr<XmlTools> myXmlTools;
     QString filesWildCard, patchFilesWildCard, forceTargetFilesWildcard;
     QString currentVal, newVal, diffOldNewVal, positions;
@@ -219,8 +219,8 @@
 
     if(parser.isSet(elementNameOption)){
-        myXmlTools=new XmlTools(filesWildCard,filters,noBackups,noVerbose);
+        myXmlTools = std::make_unique<XmlTools>(filesWildCard,filters,noBackups,noVerbose);
     }
     else{
-        myXmlTools=new XmlTools(filesWildCard,xPathExpression,noBackups,noVerbose);
+        myXmlTools = std::make_unique<XmlTools>(filesWildCard,xPathExpression,noBackups,noVerbose);
     }
 
@@ -276,5 +276,3 @@
                                           Util::toQString("--patch-files\n--add-values\n--remove-values\n--replace-value\n--replace-all-values\n--update-elements\n--invert-elements"));
     }
-
-    delete myXmlTools;
 }
Index: XmlTools2/trunk/readme.txt
===================================================================
--- XmlTools2/trunk/readme.txt	(revision 1003)
+++ XmlTools2/trunk/readme.txt	(revision 1055)
@@ -1,5 +1,5 @@
 Readme.txt
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-XmlTools v2.0b
+XmlTools v2.0c
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -62,4 +62,10 @@
 Change Log:
 ----------------------------------
+2.0c, 28-10-2016
+-Fixed bug in --update-elements operation
+-Added some exception handling
+-Started migrating some of the source code to C++14
+-Upgraded pugixml to latest 1.7 version
+----------------------------------
 2.0b, 13-06-2014
 -Minor code fixes (use of singleton pattern)
Index: XmlTools2/trunk/util.cpp
===================================================================
--- XmlTools2/trunk/util.cpp	(revision 1003)
+++ XmlTools2/trunk/util.cpp	(revision 1055)
@@ -8,5 +8,5 @@
 QString AppExecutable="./"+AppName; // Mac OS needs unix like executing
 #endif
-QString AppVersion="2.0b";
+QString AppVersion="2.0c";
 }
 
@@ -133,5 +133,11 @@
 
     foreach(QString value, stringList){
-        intList << value.toInt();
+        bool ok;
+
+        intList << value.toInt(&ok);
+
+        if(!ok){
+            throw std::runtime_error(QString("Impossible to convert string '" + value + "' to int!").toUtf8().constData());
+        }
     }
 
@@ -146,5 +152,11 @@
 
     foreach(QString value, stringList){
-        doubleList << value.toDouble();
+        bool ok;
+
+        doubleList << value.toDouble(&ok);
+
+        if(!ok){
+            throw std::runtime_error(QString("Impossible to convert string '" + value + "' to double!").toUtf8().constData());
+        }
     }
 
Index: XmlTools2/trunk/util.h
===================================================================
--- XmlTools2/trunk/util.h	(revision 1003)
+++ XmlTools2/trunk/util.h	(revision 1055)
@@ -8,4 +8,5 @@
 #include <QVector>
 #include <iostream> // cout, cin etc.
+#include <memory>
 
 namespace GlobalVars{
Index: XmlTools2/trunk/xmlpatch.cpp
===================================================================
--- XmlTools2/trunk/xmlpatch.cpp	(revision 1003)
+++ XmlTools2/trunk/xmlpatch.cpp	(revision 1055)
@@ -52,60 +52,66 @@
     pugi::xml_parse_result result;
 
-    filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
-
-    if(filesToProcess.isEmpty()){
-        UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","No XML files were found for the wildcard: "+filesWildcard);
-    }
-
-    result=newNode.load(xmlString.toUtf8().constData()); // load xml to insert
-
-    if(result.status!=pugi::status_ok){
-        UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
-    }
-
-    // Process all XmlFiles
-    for(int i=0; i<filesToProcess.size(); i++){
-
-        UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@ADD_INSIDE_NODES");
-
-        // Check how the element will be fetched via element name or xpath expression
-        if(xPathExpression.isEmpty()){
-            UtilXmlTools::getAllNamedElements(this->rootNode,nodesToInsertion,filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToInsertion);
-        }
-
-        if(nodesToInsertion.isEmpty() || nodesToInsertion[0].type()==pugi::node_null){
-
-            QString errMessage;
-
+    try{
+
+        filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
+
+        if(filesToProcess.isEmpty()){
+            UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","No XML files were found for the wildcard: "+filesWildcard);
+        }
+
+        result=newNode.load(xmlString.toUtf8().constData()); // load xml to insert
+
+        if(result.status!=pugi::status_ok){
+            UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
+        }
+
+        // Process all XmlFiles
+        for(int i=0; i<filesToProcess.size(); i++){
+
+            UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@ADD_INSIDE_NODES");
+
+            // Check how the element will be fetched via element name or xpath expression
             if(xPathExpression.isEmpty()){
-                errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
-                if(filters.getParentElementName()!=""){
-                    errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
-                }
-                if(filters.getAttributeName()!=""){
-                    errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
-                }
+                UtilXmlTools::getAllNamedElements(this->rootNode,nodesToInsertion,filters);
             }
             else{
-                errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
-            }
-
-            UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES",errMessage);
-        }
-
-        for(int j=0; j<nodesToInsertion.size(); j++){
-            for (pugi::xml_node currNodeToInsert = newNode.first_child(); currNodeToInsert; currNodeToInsert = currNodeToInsert.next_sibling())
-            {
-                nodesToInsertion[j].append_copy(currNodeToInsert); // append the new node
-            }
-
-        }
-
-        UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODES");
-
-        nodesToInsertion.clear();
+                UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToInsertion);
+            }
+
+            if(nodesToInsertion.isEmpty() || nodesToInsertion[0].type()==pugi::node_null){
+
+                QString errMessage;
+
+                if(xPathExpression.isEmpty()){
+                    errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
+                    if(filters.getParentElementName()!=""){
+                        errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
+                    }
+                    if(filters.getAttributeName()!=""){
+                        errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
+                    }
+                }
+                else{
+                    errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
+                }
+
+                UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES",errMessage);
+            }
+
+            for(int j=0; j<nodesToInsertion.size(); j++){
+                for (pugi::xml_node currNodeToInsert = newNode.first_child(); currNodeToInsert; currNodeToInsert = currNodeToInsert.next_sibling())
+                {
+                    nodesToInsertion[j].append_copy(currNodeToInsert); // append the new node
+                }
+
+            }
+
+            UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODES");
+
+            nodesToInsertion.clear();
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", QString("An exception was caught: ") + e.what());
     }
 
@@ -119,66 +125,72 @@
     QList<pugi::xml_node> nodesToDeletion;
 
-    filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
-
-    if(filesToProcess.isEmpty()){
-        UtilXmlTools::displayErrorMessage("@REMOVE_NODES","No XML files were found for the wildcard: "+filesWildcard);
-    }
-
-    // Process all XmlFiles
-    for(int i=0; i<filesToProcess.size(); i++){
-
-        UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@REMOVE_NODES");
-
-        // Check how the element will be fetched via element name or xpath expression
-        if(xPathExpression.isEmpty()){
-            UtilXmlTools::getAllNamedElements(this->rootNode,nodesToDeletion,filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToDeletion);
-        }
-
-        if(nodesToDeletion.isEmpty() || nodesToDeletion[0].type()==pugi::node_null){
-
-            QString errMessage;
-
+    try{
+
+        filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
+
+        if(filesToProcess.isEmpty()){
+            UtilXmlTools::displayErrorMessage("@REMOVE_NODES","No XML files were found for the wildcard: "+filesWildcard);
+        }
+
+        // Process all XmlFiles
+        for(int i=0; i<filesToProcess.size(); i++){
+
+            UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@REMOVE_NODES");
+
+            // Check how the element will be fetched via element name or xpath expression
             if(xPathExpression.isEmpty()){
-                errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
-                if(filters.getParentElementName()!=""){
-                    errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
-                }
-                if(filters.getAttributeName()!=""){
-                    errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
-                }
+                UtilXmlTools::getAllNamedElements(this->rootNode,nodesToDeletion,filters);
             }
             else{
-                errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
-            }
-
-            UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
-        }
-
-        // Delete all the specified nodes
-        for(int j=0; j<nodesToDeletion.size(); j++){
-            if(!nodesToDeletion[j].parent().remove_child(nodesToDeletion[j])){  // remove the node
+                UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToDeletion);
+            }
+
+            if(nodesToDeletion.isEmpty() || nodesToDeletion[0].type()==pugi::node_null){
 
                 QString errMessage;
+
                 if(xPathExpression.isEmpty()){
-                    errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
-
+                    errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
                     if(filters.getParentElementName()!=""){
-                        errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
+                        errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
+                    }
+                    if(filters.getAttributeName()!=""){
+                        errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
                     }
                 }
                 else{
-                    errMessage = "Couldn't remove the node with the XPathExpression '" + xPathExpression + "'";
+                    errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
                 }
 
                 UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
             }
-        }
-
-        UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODES");
-
-        nodesToDeletion.clear();
+
+            // Delete all the specified nodes
+            for(int j=0; j<nodesToDeletion.size(); j++){
+                if(!nodesToDeletion[j].parent().remove_child(nodesToDeletion[j])){  // remove the node
+
+                    QString errMessage;
+                    if(xPathExpression.isEmpty()){
+                        errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
+
+                        if(filters.getParentElementName()!=""){
+                            errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
+                        }
+                    }
+                    else{
+                        errMessage = "Couldn't remove the node with the XPathExpression '" + xPathExpression + "'";
+                    }
+
+                    UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
+                }
+            }
+
+            UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODES");
+
+            nodesToDeletion.clear();
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("@REMOVE_NODES", QString("An exception was caught: ") + e.what());
     }
 
@@ -216,10 +228,14 @@
     QVector<QString> filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
 
-    if(filesToProcess.isEmpty()){
-        UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","No XML files were found for the wildcard: "+filesWildcard);
-    }
-
-    XmlCustomCode::getInstance()->executeCustomCode(jsString,filesToProcess,this->backupsEnabled,this->verboseEnabled);
-
+    try{
+        if(filesToProcess.isEmpty()){
+            UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","No XML files were found for the wildcard: "+filesWildcard);
+        }
+
+        XmlCustomCode::getInstance()->executeCustomCode(jsString,filesToProcess,this->backupsEnabled,this->verboseEnabled);
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("@CUSTOM_CODE", QString("An exception was caught: ") + e.what());
+    }
 
     UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@CUSTOM_CODE");
Index: XmlTools2/trunk/xmltools.cpp
===================================================================
--- XmlTools2/trunk/xmltools.cpp	(revision 1003)
+++ XmlTools2/trunk/xmltools.cpp	(revision 1055)
@@ -26,36 +26,41 @@
 void XmlTools::addValues(QString newValues){
 
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-
-        QStringList newValuesList, currValuesList;
-        QList<pugi::xml_node> elements;
-
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"add-values");
-
-        newValuesList=Util::qStringListFromSpacedString(newValues);
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-        for(int j=0; j<elements.size(); j++){
-
-            currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
-
-            for(int k=0; k<newValuesList.size(); k++){
-                if(currValuesList.contains(newValuesList[k])){ // If the current element already contains this value proceed to the next
-                    continue;
-                }
-
-                elements[j].text()=QString(Util::toQString(elements[j].text().as_string()) + " " + newValuesList[k]).toUtf8().constData(); // If it doesn't exists yet let's add it
-            }
-        }
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document,"add-values");
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+
+            QStringList newValuesList, currValuesList;
+            QList<pugi::xml_node> elements;
+
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"add-values");
+
+            newValuesList=Util::qStringListFromSpacedString(newValues);
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+            for(int j=0; j<elements.size(); j++){
+
+                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
+
+                for(int k=0; k<newValuesList.size(); k++){
+                    if(currValuesList.contains(newValuesList[k])){ // If the current element already contains this value proceed to the next
+                        continue;
+                    }
+
+                    elements[j].text()=QString(Util::toQString(elements[j].text().as_string()) + " " + newValuesList[k]).toUtf8().constData(); // If it doesn't exists yet let's add it
+                }
+            }
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document,"add-values");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("add-values", QString("An exception was caught: ") + e.what());
     }
 
@@ -66,85 +71,94 @@
 void XmlTools::removeValues(QString valuesToRemove){
 
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-
-        QList<pugi::xml_node> elements;
-        QStringList valuesToRemoveList, currValuesList;
-        bool elementChanged=false;
-
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "remove-values");
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-        valuesToRemoveList=Util::qStringListFromSpacedString(valuesToRemove);
-
-        for(int j=0; j<elements.size(); j++){ // O(3)... Optimization may be necessary.
-            currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
-
-            for(int k=0; k<currValuesList.size(); k++){
-                for(int m=0; m<valuesToRemoveList.size(); m++){
-                    if(currValuesList[k]==valuesToRemoveList[m]){
-                        currValuesList[k]=""; // flag to deletion
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+
+            QList<pugi::xml_node> elements;
+            QStringList valuesToRemoveList, currValuesList;
+            bool elementChanged=false;
+
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "remove-values");
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+            valuesToRemoveList=Util::qStringListFromSpacedString(valuesToRemove);
+
+            for(int j=0; j<elements.size(); j++){ // O(3)... Optimization may be necessary.
+                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
+
+                for(int k=0; k<currValuesList.size(); k++){
+                    for(int m=0; m<valuesToRemoveList.size(); m++){
+                        if(currValuesList[k]==valuesToRemoveList[m]){
+                            currValuesList[k]=""; // flag to deletion
+                            elementChanged=true;
+                        }
+                    }
+                }
+
+                if(elementChanged){ // If curr element changed update the XML
+                    currValuesList.removeAll(""); // remove all files flagged to deletion
+                    elements[j].text()=currValuesList.join(' ').toUtf8().constData();
+                    elementChanged=false;
+                }
+            }
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "remove-values");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("remove-values", QString("An exception was caught: ") + e.what());
+    }
+
+    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"remove-values");
+}
+
+void XmlTools::replaceValue(QString oldValue, QString newValue){
+
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+
+            QList<pugi::xml_node> elements;
+            QStringList currValuesList;
+            bool elementChanged=false;
+
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-value");
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+            for(int j=0; j<elements.size(); j++){
+                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
+
+                for(int k=0; k<currValuesList.size(); k++){
+                    if(currValuesList[k]==oldValue){ // Found a match with the old value?
+                        currValuesList[k]=newValue; // If found replace it with the new value
                         elementChanged=true;
                     }
                 }
-            }
-
-            if(elementChanged){ // If curr element changed update the XML
-                currValuesList.removeAll(""); // remove all files flagged to deletion
-                elements[j].text()=currValuesList.join(' ').toUtf8().constData();
+
+                if(elementChanged){ // If curr element changed update the XML
+                    elements[j].text()=currValuesList.join(" ").toUtf8().constData();
+                }
                 elementChanged=false;
             }
-        }
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "remove-values");
-    }
-
-
-    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"remove-values");
-}
-
-void XmlTools::replaceValue(QString oldValue, QString newValue){
-
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-
-        QList<pugi::xml_node> elements;
-        QStringList currValuesList;
-        bool elementChanged=false;
-
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-value");
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-        for(int j=0; j<elements.size(); j++){
-            currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
-
-            for(int k=0; k<currValuesList.size(); k++){
-                if(currValuesList[k]==oldValue){ // Found a match with the old value?
-                    currValuesList[k]=newValue; // If found replace it with the new value
-                    elementChanged=true;
-                }
-            }
-
-            if(elementChanged){ // If curr element changed update the XML
-                elements[j].text()=currValuesList.join(" ").toUtf8().constData();
-            }
-            elementChanged=false;
-        }
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-value");
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-value");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("replace-value", QString("An exception was caught: ") + e.what());
     }
 
@@ -155,31 +169,36 @@
 void XmlTools::replaceAll(QString value, QString valuePositions){
 
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-
-        QList<pugi::xml_node> elements;
-
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-all");
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-
-        // Let's start the override
-        for(int j=0; j<elements.size(); j++){
-            if(valuePositions!=""){
-                elements[j].text()=replaceSpecificPositions(value, Util::toQString(elements[j].text().as_string()),valuePositions).toUtf8().constData();
-            }
-            else{
-                elements[j].text()=value.toUtf8().constData();
-            }
-        }
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-all");
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+
+            QList<pugi::xml_node> elements;
+
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-all");
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+
+            // Let's start the override
+            for(int j=0; j<elements.size(); j++){
+                if(valuePositions!=""){
+                    elements[j].text()=replaceSpecificPositions(value, Util::toQString(elements[j].text().as_string()),valuePositions).toUtf8().constData();
+                }
+                else{
+                    elements[j].text()=value.toUtf8().constData();
+                }
+            }
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-all");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("replace-all", QString("An exception was caught: ") + e.what());
     }
 
@@ -192,48 +211,57 @@
 void XmlTools::updateElements(QString diffBetweenOldAndNewValue){
 
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-
-        QList<pugi::xml_node> elements;
-        MultiDimVar lastXmlValue(0); // inicialize with any value or dimension
-        MultiDimVar currXmlValue(0);
-        MultiDimVar newXmlValue(0); // value that will update currValue
-
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled, "update-elements");
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-
-        if(elements.size()>1){
-            lastXmlValue=MultiDimVar(Util::toQString(elements[0].text().as_string())); // the lastXmlValue will begin to be the first one of the node
-            currXmlValue=MultiDimVar(Util::toQString(elements[1].text().as_string())); // the currXmlValue will begin to be the second one of the node
-            newXmlValue=MultiDimVar::sub(lastXmlValue, MultiDimVar(diffBetweenOldAndNewValue));
-            elements[0].text() = newXmlValue.toString().toUtf8().constData(); // update the first eblement with the new one already
-        }
-
-        // Let's start the node update
-        for(int j=1; j<elements.size()-1; j++){ // We start in 1 because the 0 is already saved in lastXmlValue // -1 because we will also work with the next one in the current
-
-            newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
-            elements[j].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
-            lastXmlValue=currXmlValue;
-            currXmlValue=MultiDimVar(Util::toQString(elements[j+1].text().as_string()));
-
-        }
-
-        // To update too last element (avoid out of bound because i+1)
-        newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
-        elements[elements.size()-1].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "update-elements");
-    }
-
-    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"updateNode");
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+
+            QList<pugi::xml_node> elements;
+            MultiDimVar lastXmlValue(0); // inicialize with any value or dimension
+            MultiDimVar currXmlValue(0);
+            MultiDimVar newXmlValue(0); // value that will update currValue
+
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled, "update-elements");
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+            if(elements.size() > 0){
+
+                lastXmlValue=MultiDimVar(Util::toQString(elements[0].text().as_string())); // the lastXmlValue will begin to be the first one of the node
+                newXmlValue=MultiDimVar::sub(lastXmlValue, MultiDimVar(diffBetweenOldAndNewValue));
+                elements[0].text() = newXmlValue.toString().toUtf8().constData(); // update the first element with the new one already
+
+                // If we have more than 1 element
+                if(elements.size()>1){
+                    currXmlValue=MultiDimVar(Util::toQString(elements[1].text().as_string())); // the currXmlValue will begin to be the second one of the node
+
+                    // Let's start the node update
+                    for(int j=1; j<elements.size()-1; j++){ // We start in 1 because the 0 is already saved in lastXmlValue // -1 because we will also work with the next one in the current
+
+                        newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
+                        elements[j].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
+                        lastXmlValue=currXmlValue;
+                        currXmlValue=MultiDimVar(Util::toQString(elements[j+1].text().as_string()));
+
+                    }
+
+                    // To update too last element (avoid out of bound because i+1)
+                    newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
+                    elements[elements.size()-1].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
+                }
+            }
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "update-elements");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("update-elements", QString("An exception was caught: ") + e.what());
+    }
+
+    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"update-elements");
 
 }
@@ -242,30 +270,35 @@
 void XmlTools::invertElements(){
 
-    // Process all XmlFiles
-    for(int i=0; i<this->filesToProcess.size(); i++){
-        UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "invert-elements");
-
-        QList<pugi::xml_node> elements;
-        QStringList invertedElements; //Inverting the element order
-
-        // Check how the elements will be fetched via element name or xpath expression
-        if(this->xPathExpression==""){
-            UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
-        }
-        else{
-            UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
-        }
-
-        // Read all elements and save to the list
-        for(int j=elements.size()-1; j>=0; j--){
-            invertedElements << Util::toQString(elements[j].text().as_string());
-        }
-
-        // Override the tree with the inverted order
-        for(int j=0; j<elements.size(); j++){
-            elements[j].text()= invertedElements[j].toUtf8().constData();
-        }
-
-        UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "invert-elements");
+    try{
+        // Process all XmlFiles
+        for(int i=0; i<this->filesToProcess.size(); i++){
+            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "invert-elements");
+
+            QList<pugi::xml_node> elements;
+            QStringList invertedElements; //Inverting the element order
+
+            // Check how the elements will be fetched via element name or xpath expression
+            if(this->xPathExpression==""){
+                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
+            }
+            else{
+                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
+            }
+
+            // Read all elements and save to the list
+            for(int j=elements.size()-1; j>=0; j--){
+                invertedElements << Util::toQString(elements[j].text().as_string());
+            }
+
+            // Override the tree with the inverted order
+            for(int j=0; j<elements.size(); j++){
+                elements[j].text()= invertedElements[j].toUtf8().constData();
+            }
+
+            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "invert-elements");
+        }
+    }
+    catch(const std::exception &e){
+        UtilXmlTools::displayErrorMessage("invert-elements", QString("An exception was caught: ") + e.what());
     }
 
