diff --git a/thirdparty/pugixml/pugixml/src/pugiconfig.hpp b/thirdparty/pugixml/pugixml/src/pugiconfig.hpp
index 0713b0efbb..3dc839da6e 100644
--- a/thirdparty/pugixml/pugixml/src/pugiconfig.hpp
+++ b/thirdparty/pugixml/pugixml/src/pugiconfig.hpp
@@ -1,7 +1,7 @@
 /**
- * pugixml parser - version 1.12
+ * pugixml parser - version 1.14
  * --------------------------------------------------------
- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at https://pugixml.org/
  *
  * This library is distributed under the MIT License. See notice at the end
@@ -52,7 +52,7 @@
 #endif
 
 /**
- * Copyright (c) 2006-2022 Arseny Kapoulkine
+ * Copyright (c) 2006-2023 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
  * obtaining a copy of this software and associated documentation
diff --git a/thirdparty/pugixml/pugixml/src/pugixml.cpp b/thirdparty/pugixml/pugixml/src/pugixml.cpp
index 60b55da3ab..89123c75d0 100644
--- a/thirdparty/pugixml/pugixml/src/pugixml.cpp
+++ b/thirdparty/pugixml/pugixml/src/pugixml.cpp
@@ -1,7 +1,7 @@
 /**
- * pugixml parser - version 1.12
+ * pugixml parser - version 1.14
  * --------------------------------------------------------
- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at https://pugixml.org/
  *
  * This library is distributed under the MIT License. See notice at the end
@@ -40,6 +40,11 @@
 // For placement new
 #include <new>
 
+// For load_file
+#if defined(__linux__) || defined(__APPLE__)
+#include <sys/stat.h>
+#endif
+
 #ifdef _MSC_VER
 #	pragma warning(push)
 #	pragma warning(disable: 4127) // conditional expression is constant
@@ -82,39 +87,39 @@
 
 // Inlining controls
 #if defined(_MSC_VER) && _MSC_VER >= 1300
-#	define PUGI__NO_INLINE __declspec(noinline)
+#	define PUGI_IMPL_NO_INLINE __declspec(noinline)
 #elif defined(__GNUC__)
-#	define PUGI__NO_INLINE __attribute__((noinline))
+#	define PUGI_IMPL_NO_INLINE __attribute__((noinline))
 #else
-#	define PUGI__NO_INLINE
+#	define PUGI_IMPL_NO_INLINE
 #endif
 
 // Branch weight controls
 #if defined(__GNUC__) && !defined(__c2__)
-#	define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
+#	define PUGI_IMPL_UNLIKELY(cond) __builtin_expect(cond, 0)
 #else
-#	define PUGI__UNLIKELY(cond) (cond)
+#	define PUGI_IMPL_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]; }
+#define PUGI_IMPL_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
 
 // Digital Mars C++ bug workaround for passing char loaded from memory via stack
 #ifdef __DMC__
-#	define PUGI__DMC_VOLATILE volatile
+#	define PUGI_IMPL_DMC_VOLATILE volatile
 #else
-#	define PUGI__DMC_VOLATILE
+#	define PUGI_IMPL_DMC_VOLATILE
 #endif
 
 // Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings
 #if defined(__clang__) && defined(__has_attribute)
 #	if __has_attribute(no_sanitize)
-#		define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+#		define PUGI_IMPL_UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
 #	else
-#		define PUGI__UNSIGNED_OVERFLOW
+#		define PUGI_IMPL_UNSIGNED_OVERFLOW
 #	endif
 #else
-#	define PUGI__UNSIGNED_OVERFLOW
+#	define PUGI_IMPL_UNSIGNED_OVERFLOW
 #endif
 
 // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
@@ -124,6 +129,12 @@ using std::memmove;
 using std::memset;
 #endif
 
+// Old versions of GCC do not define ::malloc and ::free depending on header include order
+#if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4))
+using std::malloc;
+using std::free;
+#endif
+
 // Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations
 #if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)
 #	define LLONG_MIN (-LLONG_MAX - 1LL)
@@ -133,36 +144,38 @@ using std::memset;
 
 // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
 #if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE)
-#	define PUGI__MSVC_CRT_VERSION _MSC_VER
+#	define PUGI_IMPL_MSVC_CRT_VERSION _MSC_VER
 #elif defined(_WIN32_WCE)
-#	define PUGI__MSVC_CRT_VERSION 1310 // MSVC7.1
+#	define PUGI_IMPL_MSVC_CRT_VERSION 1310 // MSVC7.1
 #endif
 
 // Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
 #if __cplusplus >= 201103
-#	define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
-#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
-#	define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
+#	define PUGI_IMPL_SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
+#elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400
+#	define PUGI_IMPL_SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
+#elif defined(__APPLE__) && __clang_major__ >= 14 // Xcode 14 marks sprintf as deprecated while still using C++98 by default
+#	define PUGI_IMPL_SNPRINTF(buf, fmt, arg1, arg2) snprintf(buf, sizeof(buf), fmt, arg1, arg2)
 #else
-#	define PUGI__SNPRINTF sprintf
+#	define PUGI_IMPL_SNPRINTF sprintf
 #endif
 
 // We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat.
 #ifdef PUGIXML_HEADER_ONLY
-#	define PUGI__NS_BEGIN namespace pugi { namespace impl {
-#	define PUGI__NS_END } }
-#	define PUGI__FN inline
-#	define PUGI__FN_NO_INLINE inline
+#	define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl {
+#	define PUGI_IMPL_NS_END } }
+#	define PUGI_IMPL_FN inline
+#	define PUGI_IMPL_FN_NO_INLINE inline
 #else
 #	if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
-#		define PUGI__NS_BEGIN namespace pugi { namespace impl {
-#		define PUGI__NS_END } }
+#		define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl {
+#		define PUGI_IMPL_NS_END } }
 #	else
-#		define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
-#		define PUGI__NS_END } } }
+#		define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { namespace {
+#		define PUGI_IMPL_NS_END } } }
 #	endif
-#	define PUGI__FN
-#	define PUGI__FN_NO_INLINE PUGI__NO_INLINE
+#	define PUGI_IMPL_FN
+#	define PUGI_IMPL_FN_NO_INLINE PUGI_IMPL_NO_INLINE
 #endif
 
 // uintptr_t
@@ -182,13 +195,13 @@ namespace pugi
 #endif
 
 // Memory allocation
-PUGI__NS_BEGIN
-	PUGI__FN void* default_allocate(size_t size)
+PUGI_IMPL_NS_BEGIN
+	PUGI_IMPL_FN void* default_allocate(size_t size)
 	{
 		return malloc(size);
 	}
 
-	PUGI__FN void default_deallocate(void* ptr)
+	PUGI_IMPL_FN void default_deallocate(void* ptr)
 	{
 		free(ptr);
 	}
@@ -206,12 +219,12 @@ PUGI__NS_BEGIN
 	template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
 
 	typedef xml_memory_management_function_storage<int> xml_memory;
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // String utilities
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	// Get string length
-	PUGI__FN size_t strlength(const char_t* s)
+	PUGI_IMPL_FN size_t strlength(const char_t* s)
 	{
 		assert(s);
 
@@ -223,7 +236,7 @@ PUGI__NS_BEGIN
 	}
 
 	// Compare two strings
-	PUGI__FN bool strequal(const char_t* src, const char_t* dst)
+	PUGI_IMPL_FN bool strequal(const char_t* src, const char_t* dst)
 	{
 		assert(src && dst);
 
@@ -235,7 +248,7 @@ PUGI__NS_BEGIN
 	}
 
 	// Compare lhs with [rhs_begin, rhs_end)
-	PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
+	PUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
 	{
 		for (size_t i = 0; i < count; ++i)
 			if (lhs[i] != rhs[i])
@@ -245,7 +258,7 @@ PUGI__NS_BEGIN
 	}
 
 	// Get length of wide string, even if CRT lacks wide character support
-	PUGI__FN size_t strlength_wide(const wchar_t* s)
+	PUGI_IMPL_FN size_t strlength_wide(const wchar_t* s)
 	{
 		assert(s);
 
@@ -257,10 +270,10 @@ PUGI__NS_BEGIN
 		return static_cast<size_t>(end - s);
 	#endif
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // auto_ptr-like object for exception recovery
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	template <typename T> struct auto_deleter
 	{
 		typedef void (*D)(T*);
@@ -284,10 +297,10 @@ PUGI__NS_BEGIN
 			return result;
 		}
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 #ifdef PUGIXML_COMPACT
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	class compact_hash_table
 	{
 	public:
@@ -378,7 +391,7 @@ PUGI__NS_BEGIN
 			return 0;
 		}
 
-		static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
+		static PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash(const void* key)
 		{
 			unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key) & 0xffffffff);
 
@@ -393,7 +406,7 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count)
+	PUGI_IMPL_FN_NO_INLINE bool compact_hash_table::rehash(size_t count)
 	{
 		size_t capacity = 32;
 		while (count >= capacity - capacity / 4)
@@ -423,10 +436,10 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-PUGI__NS_END
+PUGI_IMPL_NS_END
 #endif
 
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 #ifdef PUGIXML_COMPACT
 	static const uintptr_t xml_memory_block_alignment = 4;
 #else
@@ -444,16 +457,16 @@ PUGI__NS_BEGIN
 	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__GETHEADER_IMPL(object, page, flags) // unused
-	#define PUGI__GETPAGE_IMPL(header) (header).get_page()
+	#define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) // unused
+	#define PUGI_IMPL_GETPAGE_IMPL(header) (header).get_page()
 #else
-	#define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))
+	#define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))
 	// this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
-	#define PUGI__GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))
+	#define PUGI_IMPL_GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))
 #endif
 
-	#define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
-	#define PUGI__NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask)
+	#define PUGI_IMPL_GETPAGE(n) PUGI_IMPL_GETPAGE_IMPL((n)->header)
+	#define PUGI_IMPL_NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask)
 
 	struct xml_allocator;
 
@@ -543,7 +556,7 @@ PUGI__NS_BEGIN
 
 		void* allocate_memory(size_t size, xml_memory_page*& out_page)
 		{
-			if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size))
+			if (PUGI_IMPL_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;
@@ -564,7 +577,7 @@ PUGI__NS_BEGIN
 			// 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))
+			if (PUGI_IMPL_UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
 			{
 				// insert new marker
 				uint32_t* marker = static_cast<uint32_t*>(result);
@@ -641,7 +654,7 @@ PUGI__NS_BEGIN
 		{
 			static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
 
-			PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
+			PUGI_IMPL_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);
@@ -707,7 +720,7 @@ PUGI__NS_BEGIN
 	#endif
 	};
 
-	PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
+	PUGI_IMPL_FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
 	{
 		const size_t large_allocation_threshold = xml_memory_page_size / 4;
 
@@ -744,10 +757,10 @@ PUGI__NS_BEGIN
 
 		return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 #ifdef PUGIXML_COMPACT
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	static const uintptr_t compact_alignment_log2 = 2;
 	static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
 
@@ -756,7 +769,7 @@ PUGI__NS_BEGIN
 	public:
 		compact_header(xml_memory_page* page, unsigned int flags)
 		{
-			PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);
+			PUGI_IMPL_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);
@@ -794,19 +807,19 @@ PUGI__NS_BEGIN
 		unsigned char _flags;
 	};
 
-	PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
+	PUGI_IMPL_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)
+	template <int header_offset, typename T> PUGI_IMPL_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)
+	template <int header_offset, typename T> PUGI_IMPL_FN_NO_INLINE void compact_set_value(const void* object, T* value)
 	{
 		compact_get_page(object, header_offset)->allocator->_hash->insert(object, value);
 	}
@@ -904,7 +917,7 @@ PUGI__NS_BEGIN
 				{
 					xml_memory_page* page = compact_get_page(this, header_offset);
 
-					if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
+					if (PUGI_IMPL_UNLIKELY(page->compact_shared_parent == 0))
 						page->compact_shared_parent = value;
 
 					if (page->compact_shared_parent == value)
@@ -971,7 +984,7 @@ PUGI__NS_BEGIN
 			{
 				xml_memory_page* page = compact_get_page(this, header_offset);
 
-				if (PUGI__UNLIKELY(page->compact_string_base == 0))
+				if (PUGI_IMPL_UNLIKELY(page->compact_string_base == 0))
 					page->compact_string_base = value;
 
 				ptrdiff_t offset = value - page->compact_string_base;
@@ -1043,7 +1056,7 @@ PUGI__NS_BEGIN
 	private:
 		unsigned char _data;
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 #endif
 
 #ifdef PUGIXML_COMPACT
@@ -1053,7 +1066,7 @@ namespace pugi
 	{
 		xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
 		{
-			PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
+			PUGI_IMPL_STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
 		}
 
 		impl::compact_header header;
@@ -1071,7 +1084,7 @@ namespace pugi
 	{
 		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0)
 		{
-			PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12);
+			PUGI_IMPL_STATIC_ASSERT(sizeof(xml_node_struct) == 12);
 		}
 
 		impl::compact_header header;
@@ -1098,7 +1111,7 @@ namespace pugi
 	{
 		xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0)
 		{
-			header = PUGI__GETHEADER_IMPL(this, page, 0);
+			header = PUGI_IMPL_GETHEADER_IMPL(this, page, 0);
 		}
 
 		uintptr_t header;
@@ -1114,7 +1127,7 @@ namespace pugi
 	{
 		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
 		{
-			header = PUGI__GETHEADER_IMPL(this, page, type);
+			header = PUGI_IMPL_GETHEADER_IMPL(this, page, type);
 		}
 
 		uintptr_t header;
@@ -1134,7 +1147,7 @@ namespace pugi
 }
 #endif
 
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	struct xml_extra_buffer
 	{
 		char_t* buffer;
@@ -1160,19 +1173,19 @@ PUGI__NS_BEGIN
 	{
 		assert(object);
 
-		return *PUGI__GETPAGE(object)->allocator;
+		return *PUGI_IMPL_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);
+		return *static_cast<xml_document_struct*>(PUGI_IMPL_GETPAGE(object)->allocator);
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // Low-level DOM operations
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
 	{
 		xml_memory_page* page;
@@ -1199,7 +1212,7 @@ PUGI__NS_BEGIN
 		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));
+		alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI_IMPL_GETPAGE(a));
 	}
 
 	inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
@@ -1228,7 +1241,7 @@ PUGI__NS_BEGIN
 			child = next;
 		}
 
-		alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));
+		alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI_IMPL_GETPAGE(n));
 	}
 
 	inline void append_node(xml_node_struct* child, xml_node_struct* node)
@@ -1276,12 +1289,14 @@ PUGI__NS_BEGIN
 
 		child->parent = parent;
 
-		if (node->next_sibling)
-			node->next_sibling->prev_sibling_c = child;
+		xml_node_struct* next = node->next_sibling;
+
+		if (next)
+			next->prev_sibling_c = child;
 		else
 			parent->first_child->prev_sibling_c = child;
 
-		child->next_sibling = node->next_sibling;
+		child->next_sibling = next;
 		child->prev_sibling_c = node;
 
 		node->next_sibling = child;
@@ -1293,12 +1308,14 @@ PUGI__NS_BEGIN
 
 		child->parent = parent;
 
-		if (node->prev_sibling_c->next_sibling)
-			node->prev_sibling_c->next_sibling = child;
+		xml_node_struct* prev = node->prev_sibling_c;
+
+		if (prev->next_sibling)
+			prev->next_sibling = child;
 		else
 			parent->first_child = child;
 
-		child->prev_sibling_c = node->prev_sibling_c;
+		child->prev_sibling_c = prev;
 		child->next_sibling = node;
 
 		node->prev_sibling_c = child;
@@ -1308,15 +1325,18 @@ PUGI__NS_BEGIN
 	{
 		xml_node_struct* parent = node->parent;
 
-		if (node->next_sibling)
-			node->next_sibling->prev_sibling_c = node->prev_sibling_c;
+		xml_node_struct* next = node->next_sibling;
+		xml_node_struct* prev = node->prev_sibling_c;
+
+		if (next)
+			next->prev_sibling_c = prev;
 		else
-			parent->first_child->prev_sibling_c = node->prev_sibling_c;
+			parent->first_child->prev_sibling_c = prev;
 
-		if (node->prev_sibling_c->next_sibling)
-			node->prev_sibling_c->next_sibling = node->next_sibling;
+		if (prev->next_sibling)
+			prev->next_sibling = next;
 		else
-			parent->first_child = node->next_sibling;
+			parent->first_child = next;
 
 		node->parent = 0;
 		node->prev_sibling_c = 0;
@@ -1360,45 +1380,52 @@ PUGI__NS_BEGIN
 
 	inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
 	{
-		if (place->next_attribute)
-			place->next_attribute->prev_attribute_c = attr;
+		xml_attribute_struct* next = place->next_attribute;
+
+		if (next)
+			next->prev_attribute_c = attr;
 		else
 			node->first_attribute->prev_attribute_c = attr;
 
-		attr->next_attribute = place->next_attribute;
+		attr->next_attribute = next;
 		attr->prev_attribute_c = place;
 		place->next_attribute = attr;
 	}
 
 	inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
 	{
-		if (place->prev_attribute_c->next_attribute)
-			place->prev_attribute_c->next_attribute = attr;
+		xml_attribute_struct* prev = place->prev_attribute_c;
+
+		if (prev->next_attribute)
+			prev->next_attribute = attr;
 		else
 			node->first_attribute = attr;
 
-		attr->prev_attribute_c = place->prev_attribute_c;
+		attr->prev_attribute_c = prev;
 		attr->next_attribute = place;
 		place->prev_attribute_c = attr;
 	}
 
 	inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)
 	{
-		if (attr->next_attribute)
-			attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
+		xml_attribute_struct* next = attr->next_attribute;
+		xml_attribute_struct* prev = attr->prev_attribute_c;
+
+		if (next)
+			next->prev_attribute_c = prev;
 		else
-			node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
+			node->first_attribute->prev_attribute_c = prev;
 
-		if (attr->prev_attribute_c->next_attribute)
-			attr->prev_attribute_c->next_attribute = attr->next_attribute;
+		if (prev->next_attribute)
+			prev->next_attribute = next;
 		else
-			node->first_attribute = attr->next_attribute;
+			node->first_attribute = next;
 
 		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)
+	PUGI_IMPL_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;
 
@@ -1410,7 +1437,7 @@ PUGI__NS_BEGIN
 		return child;
 	}
 
-	PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
+	PUGI_IMPL_FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
 	{
 		if (!alloc.reserve()) return 0;
 
@@ -1421,10 +1448,10 @@ PUGI__NS_BEGIN
 
 		return attr;
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // Helper classes for code generation
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	struct opt_false
 	{
 		enum { value = 0 };
@@ -1434,10 +1461,10 @@ PUGI__NS_BEGIN
 	{
 		enum { value = 1 };
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // Unicode utilities
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	inline uint16_t endian_swap(uint16_t value)
 	{
 		return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));
@@ -1819,15 +1846,15 @@ PUGI__NS_BEGIN
 	};
 
 #ifdef PUGIXML_WCHAR_MODE
-	PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
+	PUGI_IMPL_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])));
 	}
 #endif
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	enum chartype_t
 	{
 		ct_parse_pcdata = 1,	// \0, &, \r, <
@@ -1893,24 +1920,24 @@ PUGI__NS_BEGIN
 	};
 
 #ifdef PUGIXML_WCHAR_MODE
-	#define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
+	#define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
 #else
-	#define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))
+	#define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))
 #endif
 
-	#define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)
-	#define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)
+	#define PUGI_IMPL_IS_CHARTYPE(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartype_table)
+	#define PUGI_IMPL_IS_CHARTYPEX(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartypex_table)
 
-	PUGI__FN bool is_little_endian()
+	PUGI_IMPL_FN bool is_little_endian()
 	{
 		unsigned int ui = 1;
 
 		return *reinterpret_cast<unsigned char*>(&ui) == 1;
 	}
 
-	PUGI__FN xml_encoding get_wchar_encoding()
+	PUGI_IMPL_FN xml_encoding get_wchar_encoding()
 	{
-		PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
+		PUGI_IMPL_STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
 
 		if (sizeof(wchar_t) == 2)
 			return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
@@ -1918,13 +1945,13 @@ PUGI__NS_BEGIN
 			return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
 	}
 
-	PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)
+	PUGI_IMPL_FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)
 	{
-	#define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }
-	#define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; }
+	#define PUGI_IMPL_SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }
+	#define PUGI_IMPL_SCANCHARTYPE(ct) { while (offset < size && PUGI_IMPL_IS_CHARTYPE(data[offset], ct)) offset++; }
 
 		// check if we have a non-empty XML declaration
-		if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space)))
+		if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI_IMPL_IS_CHARTYPE(data[5], ct_space)))
 			return false;
 
 		// scan XML declaration until the encoding field
@@ -1939,28 +1966,28 @@ PUGI__NS_BEGIN
 				size_t offset = i;
 
 				// encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed
-				PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o');
-				PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g');
+				PUGI_IMPL_SCANCHAR('e'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('c'); PUGI_IMPL_SCANCHAR('o');
+				PUGI_IMPL_SCANCHAR('d'); PUGI_IMPL_SCANCHAR('i'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('g');
 
 				// S? = S?
-				PUGI__SCANCHARTYPE(ct_space);
-				PUGI__SCANCHAR('=');
-				PUGI__SCANCHARTYPE(ct_space);
+				PUGI_IMPL_SCANCHARTYPE(ct_space);
+				PUGI_IMPL_SCANCHAR('=');
+				PUGI_IMPL_SCANCHARTYPE(ct_space);
 
 				// the only two valid delimiters are ' and "
 				uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\'';
 
-				PUGI__SCANCHAR(delimiter);
+				PUGI_IMPL_SCANCHAR(delimiter);
 
 				size_t start = offset;
 
 				out_encoding = data + offset;
 
-				PUGI__SCANCHARTYPE(ct_symbol);
+				PUGI_IMPL_SCANCHARTYPE(ct_symbol);
 
 				out_length = offset - start;
 
-				PUGI__SCANCHAR(delimiter);
+				PUGI_IMPL_SCANCHAR(delimiter);
 
 				return true;
 			}
@@ -1968,11 +1995,11 @@ PUGI__NS_BEGIN
 
 		return false;
 
-	#undef PUGI__SCANCHAR
-	#undef PUGI__SCANCHARTYPE
+	#undef PUGI_IMPL_SCANCHAR
+	#undef PUGI_IMPL_SCANCHARTYPE
 	}
 
-	PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)
+	PUGI_IMPL_FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)
 	{
 		// skip encoding autodetection if input buffer is too small
 		if (size < 4) return encoding_utf8;
@@ -2020,7 +2047,7 @@ PUGI__NS_BEGIN
 		return encoding_utf8;
 	}
 
-	PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
+	PUGI_IMPL_FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
 	{
 		// replace wchar encoding with utf implementation
 		if (encoding == encoding_wchar) return get_wchar_encoding();
@@ -2040,7 +2067,7 @@ PUGI__NS_BEGIN
 		return guess_buffer_encoding(data, size);
 	}
 
-	PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+	PUGI_IMPL_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);
 
@@ -2069,13 +2096,13 @@ PUGI__NS_BEGIN
 	}
 
 #ifdef PUGIXML_WCHAR_MODE
-	PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
+	PUGI_IMPL_FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
 	{
 		return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||
 			   (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);
 	}
 
-	PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+	PUGI_IMPL_FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
 	{
 		const char_t* data = static_cast<const char_t*>(contents);
 		size_t length = size / sizeof(char_t);
@@ -2104,7 +2131,7 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+	template <typename D> PUGI_IMPL_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);
@@ -2129,7 +2156,7 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+	PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
 	{
 		// get native encoding
 		xml_encoding wchar_encoding = get_wchar_encoding();
@@ -2174,7 +2201,7 @@ PUGI__NS_BEGIN
 		return false;
 	}
 #else
-	template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+	template <typename D> PUGI_IMPL_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);
@@ -2199,7 +2226,7 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
+	PUGI_IMPL_FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
 	{
 		for (size_t i = 0; i < size; ++i)
 			if (data[i] > 127)
@@ -2208,7 +2235,7 @@ PUGI__NS_BEGIN
 		return size;
 	}
 
-	PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+	PUGI_IMPL_FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
 	{
 		const uint8_t* data = static_cast<const uint8_t*>(contents);
 		size_t data_length = size;
@@ -2245,7 +2272,7 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+	PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
 	{
 		// fast path: no conversion required
 		if (encoding == encoding_utf8)
@@ -2280,13 +2307,13 @@ PUGI__NS_BEGIN
 	}
 #endif
 
-	PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
+	PUGI_IMPL_FN size_t as_utf8_begin(const wchar_t* str, size_t length)
 	{
 		// get length in utf8 characters
 		return wchar_decoder::process(str, length, 0, utf8_counter());
 	}
 
-	PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
+	PUGI_IMPL_FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
 	{
 		// convert to utf8
 		uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
@@ -2298,7 +2325,7 @@ PUGI__NS_BEGIN
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
+	PUGI_IMPL_FN std::string as_utf8_impl(const wchar_t* str, size_t length)
 	{
 		// first pass: get length in utf8 characters
 		size_t size = as_utf8_begin(str, length);
@@ -2313,7 +2340,7 @@ PUGI__NS_BEGIN
 		return result;
 	}
 
-	PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
+	PUGI_IMPL_FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
 	{
 		const uint8_t* data = reinterpret_cast<const uint8_t*>(str);
 
@@ -2356,12 +2383,14 @@ PUGI__NS_BEGIN
 	}
 
 	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)
+	PUGI_IMPL_FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)
 	{
+		assert((header & header_mask) == 0 || dest); // header bit indicates whether dest was previously allocated
+
 		if (source_length == 0)
 		{
 			// empty string and null pointer are equivalent, so just deallocate old memory
-			xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+			xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator;
 
 			if (header & header_mask) alloc->deallocate_string(dest);
 
@@ -2381,7 +2410,7 @@ PUGI__NS_BEGIN
 		}
 		else
 		{
-			xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+			xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator;
 
 			if (!alloc->reserve()) return false;
 
@@ -2446,7 +2475,7 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
+	PUGI_IMPL_FN char_t* strconv_escape(char_t* s, gap& g)
 	{
 		char_t* stre = s + 1;
 
@@ -2587,25 +2616,25 @@ PUGI__NS_BEGIN
 	}
 
 	// 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)
+	#define PUGI_IMPL_ENDSWITH(c, e)        ((c) == (e) || ((c) == 0 && endch == (e)))
+	#define PUGI_IMPL_SKIPWS()              { while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) ++s; }
+	#define PUGI_IMPL_OPTSET(OPT)           ( optmsk & (OPT) )
+	#define PUGI_IMPL_PUSHNODE(TYPE)        { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); }
+	#define PUGI_IMPL_POPNODE()             { cursor = cursor->parent; }
+	#define PUGI_IMPL_SCANFOR(X)            { while (*s != 0 && !(X)) ++s; }
+	#define PUGI_IMPL_SCANWHILE(X)          { while (X) ++s; }
+	#define PUGI_IMPL_SCANWHILE_UNROLL(X)   { for (;;) { char_t ss = s[0]; if (PUGI_IMPL_UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 3; break; } s += 4; } }
+	#define PUGI_IMPL_ENDSEG()              { ch = *s; *s = 0; ++s; }
+	#define PUGI_IMPL_THROW_ERROR(err, m)   return error_offset = m, error_status = err, static_cast<char_t*>(0)
+	#define PUGI_IMPL_CHECK_ERROR(err, m)   { if (*s == 0) PUGI_IMPL_THROW_ERROR(err, m); }
+
+	PUGI_IMPL_FN char_t* strconv_comment(char_t* s, char_t endch)
 	{
 		gap g;
 
 		while (true)
 		{
-			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment));
+			PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_comment));
 
 			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
 			{
@@ -2613,7 +2642,7 @@ PUGI__NS_BEGIN
 
 				if (*s == '\n') g.push(s, 1);
 			}
-			else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
+			else if (s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')) // comment ends here
 			{
 				*g.flush(s) = 0;
 
@@ -2627,13 +2656,13 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
+	PUGI_IMPL_FN char_t* strconv_cdata(char_t* s, char_t endch)
 	{
 		gap g;
 
 		while (true)
 		{
-			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata));
+			PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_cdata));
 
 			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
 			{
@@ -2641,7 +2670,7 @@ PUGI__NS_BEGIN
 
 				if (*s == '\n') g.push(s, 1);
 			}
-			else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
+			else if (s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')) // CDATA ends here
 			{
 				*g.flush(s) = 0;
 
@@ -2667,14 +2696,14 @@ PUGI__NS_BEGIN
 
 			while (true)
 			{
-				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata));
+				PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata));
 
 				if (*s == '<') // PCDATA ends here
 				{
 					char_t* end = g.flush(s);
 
 					if (opt_trim::value)
-						while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+						while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space))
 							--end;
 
 					*end = 0;
@@ -2696,7 +2725,7 @@ PUGI__NS_BEGIN
 					char_t* end = g.flush(s);
 
 					if (opt_trim::value)
-						while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+						while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space))
 							--end;
 
 					*end = 0;
@@ -2708,9 +2737,9 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
+	PUGI_IMPL_FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
 	{
-		PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
+		PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
 
 		switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above
 		{
@@ -2735,37 +2764,37 @@ PUGI__NS_BEGIN
 			gap g;
 
 			// trim leading whitespaces
-			if (PUGI__IS_CHARTYPE(*s, ct_space))
+			if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))
 			{
 				char_t* str = s;
 
 				do ++str;
-				while (PUGI__IS_CHARTYPE(*str, ct_space));
+				while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space));
 
 				g.push(s, str - s);
 			}
 
 			while (true)
 			{
-				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
+				PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
 
 				if (*s == end_quote)
 				{
 					char_t* str = g.flush(s);
 
 					do *str-- = 0;
-					while (PUGI__IS_CHARTYPE(*str, ct_space));
+					while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space));
 
 					return s + 1;
 				}
-				else if (PUGI__IS_CHARTYPE(*s, ct_space))
+				else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))
 				{
 					*s++ = ' ';
 
-					if (PUGI__IS_CHARTYPE(*s, ct_space))
+					if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))
 					{
 						char_t* str = s + 1;
-						while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
+						while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)) ++str;
 
 						g.push(s, str - s);
 					}
@@ -2788,7 +2817,7 @@ PUGI__NS_BEGIN
 
 			while (true)
 			{
-				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws));
+				PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws));
 
 				if (*s == end_quote)
 				{
@@ -2796,7 +2825,7 @@ PUGI__NS_BEGIN
 
 					return s + 1;
 				}
-				else if (PUGI__IS_CHARTYPE(*s, ct_space))
+				else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))
 				{
 					if (*s == '\r')
 					{
@@ -2824,7 +2853,7 @@ PUGI__NS_BEGIN
 
 			while (true)
 			{
-				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
+				PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr));
 
 				if (*s == end_quote)
 				{
@@ -2856,7 +2885,7 @@ PUGI__NS_BEGIN
 
 			while (true)
 			{
-				PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
+				PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr));
 
 				if (*s == end_quote)
 				{
@@ -2877,9 +2906,9 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
+	PUGI_IMPL_FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
 	{
-		PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
+		PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
 
 		switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above
 		{
@@ -2935,8 +2964,8 @@ PUGI__NS_BEGIN
 			{
 				// quoted string
 				char_t ch = *s++;
-				PUGI__SCANFOR(*s == ch);
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+				PUGI_IMPL_SCANFOR(*s == ch);
+				if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 				s++;
 			}
@@ -2944,20 +2973,20 @@ PUGI__NS_BEGIN
 			{
 				// <? ... ?>
 				s += 2;
-				PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+				PUGI_IMPL_SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
+				if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 				s += 2;
 			}
 			else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
 			{
 				s += 4;
-				PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+				PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
+				if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 				s += 3;
 			}
-			else PUGI__THROW_ERROR(status_bad_doctype, s);
+			else PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 			return s;
 		}
@@ -2990,7 +3019,7 @@ PUGI__NS_BEGIN
 				else s++;
 			}
 
-			PUGI__THROW_ERROR(status_bad_doctype, s);
+			PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 		}
 
 		char_t* parse_doctype_group(char_t* s, char_t endch)
@@ -3034,7 +3063,7 @@ PUGI__NS_BEGIN
 				else s++;
 			}
 
-			if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
+			if (depth != 0 || endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 			return s;
 		}
@@ -3052,31 +3081,31 @@ PUGI__NS_BEGIN
 				{
 					++s;
 
-					if (PUGI__OPTSET(parse_comments))
+					if (PUGI_IMPL_OPTSET(parse_comments))
 					{
-						PUGI__PUSHNODE(node_comment); // Append a new node on the tree.
+						PUGI_IMPL_PUSHNODE(node_comment); // Append a new node on the tree.
 						cursor->value = s; // Save the offset.
 					}
 
-					if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
+					if (PUGI_IMPL_OPTSET(parse_eol) && PUGI_IMPL_OPTSET(parse_comments))
 					{
 						s = strconv_comment(s, endch);
 
-						if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
+						if (!s) PUGI_IMPL_THROW_ERROR(status_bad_comment, cursor->value);
 					}
 					else
 					{
 						// Scan for terminating '-->'.
-						PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>'));
-						PUGI__CHECK_ERROR(status_bad_comment, s);
+						PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>'));
+						PUGI_IMPL_CHECK_ERROR(status_bad_comment, s);
 
-						if (PUGI__OPTSET(parse_comments))
+						if (PUGI_IMPL_OPTSET(parse_comments))
 							*s = 0; // Zero-terminate this segment at the first terminating '-'.
 
 						s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
 					}
 				}
-				else PUGI__THROW_ERROR(status_bad_comment, s);
+				else PUGI_IMPL_THROW_ERROR(status_bad_comment, s);
 			}
 			else if (*s == '[')
 			{
@@ -3085,22 +3114,22 @@ PUGI__NS_BEGIN
 				{
 					++s;
 
-					if (PUGI__OPTSET(parse_cdata))
+					if (PUGI_IMPL_OPTSET(parse_cdata))
 					{
-						PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.
+						PUGI_IMPL_PUSHNODE(node_cdata); // Append a new node on the tree.
 						cursor->value = s; // Save the offset.
 
-						if (PUGI__OPTSET(parse_eol))
+						if (PUGI_IMPL_OPTSET(parse_eol))
 						{
 							s = strconv_cdata(s, endch);
 
-							if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
+							if (!s) PUGI_IMPL_THROW_ERROR(status_bad_cdata, cursor->value);
 						}
 						else
 						{
 							// Scan for terminating ']]>'.
-							PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
-							PUGI__CHECK_ERROR(status_bad_cdata, s);
+							PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>'));
+							PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s);
 
 							*s++ = 0; // Zero-terminate this segment.
 						}
@@ -3108,21 +3137,21 @@ PUGI__NS_BEGIN
 					else // Flagged for discard, but we still have to scan for the terminator.
 					{
 						// Scan for terminating ']]>'.
-						PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
-						PUGI__CHECK_ERROR(status_bad_cdata, s);
+						PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>'));
+						PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s);
 
 						++s;
 					}
 
 					s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
 				}
-				else PUGI__THROW_ERROR(status_bad_cdata, s);
+				else PUGI_IMPL_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' && PUGI__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_IMPL_ENDSWITH(s[6], 'E'))
 			{
 				s -= 2;
 
-				if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
+				if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);
 
 				char_t* mark = s + 9;
 
@@ -3132,18 +3161,18 @@ PUGI__NS_BEGIN
 				assert((*s == 0 && endch == '>') || *s == '>');
 				if (*s) *s++ = 0;
 
-				if (PUGI__OPTSET(parse_doctype))
+				if (PUGI_IMPL_OPTSET(parse_doctype))
 				{
-					while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;
+					while (PUGI_IMPL_IS_CHARTYPE(*mark, ct_space)) ++mark;
 
-					PUGI__PUSHNODE(node_doctype);
+					PUGI_IMPL_PUSHNODE(node_doctype);
 
 					cursor->value = mark;
 				}
 			}
-			else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
-			else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
-			else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+			else if (*s == 0 && endch == '-') PUGI_IMPL_THROW_ERROR(status_bad_comment, s);
+			else if (*s == 0 && endch == '[') PUGI_IMPL_THROW_ERROR(status_bad_cdata, s);
+			else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s);
 
 			return s;
 		}
@@ -3160,50 +3189,50 @@ PUGI__NS_BEGIN
 			// read PI target
 			char_t* target = s;
 
-			if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
+			if (!PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);
 
-			PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));
-			PUGI__CHECK_ERROR(status_bad_pi, s);
+			PUGI_IMPL_SCANWHILE(PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol));
+			PUGI_IMPL_CHECK_ERROR(status_bad_pi, s);
 
 			// determine node type; stricmp / strcasecmp is not portable
 			bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
 
-			if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
+			if (declaration ? PUGI_IMPL_OPTSET(parse_declaration) : PUGI_IMPL_OPTSET(parse_pi))
 			{
 				if (declaration)
 				{
 					// disallow non top-level declarations
-					if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
+					if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);
 
-					PUGI__PUSHNODE(node_declaration);
+					PUGI_IMPL_PUSHNODE(node_declaration);
 				}
 				else
 				{
-					PUGI__PUSHNODE(node_pi);
+					PUGI_IMPL_PUSHNODE(node_pi);
 				}
 
 				cursor->name = target;
 
-				PUGI__ENDSEG();
+				PUGI_IMPL_ENDSEG();
 
 				// parse value/attributes
 				if (ch == '?')
 				{
 					// empty node
-					if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
+					if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);
 					s += (*s == '>');
 
-					PUGI__POPNODE();
+					PUGI_IMPL_POPNODE();
 				}
-				else if (PUGI__IS_CHARTYPE(ch, ct_space))
+				else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))
 				{
-					PUGI__SKIPWS();
+					PUGI_IMPL_SKIPWS();
 
 					// scan for tag end
 					char_t* value = s;
 
-					PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
-					PUGI__CHECK_ERROR(status_bad_pi, s);
+					PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>'));
+					PUGI_IMPL_CHECK_ERROR(status_bad_pi, s);
 
 					if (declaration)
 					{
@@ -3218,20 +3247,20 @@ PUGI__NS_BEGIN
 						// store value and step over >
 						cursor->value = value;
 
-						PUGI__POPNODE();
+						PUGI_IMPL_POPNODE();
 
-						PUGI__ENDSEG();
+						PUGI_IMPL_ENDSEG();
 
 						s += (*s == '>');
 					}
 				}
-				else PUGI__THROW_ERROR(status_bad_pi, s);
+				else PUGI_IMPL_THROW_ERROR(status_bad_pi, s);
 			}
 			else
 			{
 				// scan for tag end
-				PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
-				PUGI__CHECK_ERROR(status_bad_pi, s);
+				PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>'));
+				PUGI_IMPL_CHECK_ERROR(status_bad_pi, s);
 
 				s += (s[1] == '>' ? 2 : 1);
 			}
@@ -3250,6 +3279,7 @@ PUGI__NS_BEGIN
 			char_t ch = 0;
 			xml_node_struct* cursor = root;
 			char_t* mark = s;
+			char_t* merged_pcdata = s;
 
 			while (*s != 0)
 			{
@@ -3258,39 +3288,39 @@ PUGI__NS_BEGIN
 					++s;
 
 				LOC_TAG:
-					if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
+					if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
 					{
-						PUGI__PUSHNODE(node_element); // Append a new node to the tree.
+						PUGI_IMPL_PUSHNODE(node_element); // Append a new node to the tree.
 
 						cursor->name = s;
 
-						PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
-						PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+						PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
+						PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over.
 
 						if (ch == '>')
 						{
 							// end of tag
 						}
-						else if (PUGI__IS_CHARTYPE(ch, ct_space))
+						else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))
 						{
 						LOC_ATTRIBUTES:
 							while (true)
 							{
-								PUGI__SKIPWS(); // Eat any whitespace.
+								PUGI_IMPL_SKIPWS(); // Eat any whitespace.
 
-								if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
+								if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
 								{
 									xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.
-									if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
+									if (!a) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s);
 
 									a->name = s; // Save the offset.
 
-									PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
-									PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+									PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
+									PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over.
 
-									if (PUGI__IS_CHARTYPE(ch, ct_space))
+									if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))
 									{
-										PUGI__SKIPWS(); // Eat any whitespace.
+										PUGI_IMPL_SKIPWS(); // Eat any whitespace.
 
 										ch = *s;
 										++s;
@@ -3298,7 +3328,7 @@ PUGI__NS_BEGIN
 
 									if (ch == '=') // '<... #=...'
 									{
-										PUGI__SKIPWS(); // Eat any whitespace.
+										PUGI_IMPL_SKIPWS(); // Eat any whitespace.
 
 										if (*s == '"' || *s == '\'') // '<... #="...'
 										{
@@ -3308,16 +3338,16 @@ PUGI__NS_BEGIN
 
 											s = strconv_attribute(s, ch);
 
-											if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
+											if (!s) PUGI_IMPL_THROW_ERROR(status_bad_attribute, a->value);
 
 											// After this line the loop continues from the start;
 											// Whitespaces, / and > are ok, symbols and EOF are wrong,
 											// everything else will be detected
-											if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
+											if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);
 										}
-										else PUGI__THROW_ERROR(status_bad_attribute, s);
+										else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);
 									}
-									else PUGI__THROW_ERROR(status_bad_attribute, s);
+									else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);
 								}
 								else if (*s == '/')
 								{
@@ -3325,16 +3355,16 @@ PUGI__NS_BEGIN
 
 									if (*s == '>')
 									{
-										PUGI__POPNODE();
+										PUGI_IMPL_POPNODE();
 										s++;
 										break;
 									}
 									else if (*s == 0 && endch == '>')
 									{
-										PUGI__POPNODE();
+										PUGI_IMPL_POPNODE();
 										break;
 									}
-									else PUGI__THROW_ERROR(status_bad_start_element, s);
+									else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);
 								}
 								else if (*s == '>')
 								{
@@ -3346,16 +3376,16 @@ PUGI__NS_BEGIN
 								{
 									break;
 								}
-								else PUGI__THROW_ERROR(status_bad_start_element, s);
+								else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);
 							}
 
 							// !!!
 						}
 						else if (ch == '/') // '<#.../'
 						{
-							if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
+							if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);
 
-							PUGI__POPNODE(); // Pop.
+							PUGI_IMPL_POPNODE(); // Pop.
 
 							s += (*s == '>');
 						}
@@ -3364,9 +3394,9 @@ PUGI__NS_BEGIN
 							// we stepped over null terminator, backtrack & handle closing tag
 							--s;
 
-							if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
+							if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);
 						}
-						else PUGI__THROW_ERROR(status_bad_start_element, s);
+						else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);
 					}
 					else if (*s == '/')
 					{
@@ -3375,30 +3405,30 @@ PUGI__NS_BEGIN
 						mark = s;
 
 						char_t* name = cursor->name;
-						if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+						if (!name) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);
 
-						while (PUGI__IS_CHARTYPE(*s, ct_symbol))
+						while (PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol))
 						{
-							if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+							if (*s++ != *name++) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);
 						}
 
 						if (*name)
 						{
-							if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
-							else PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+							if (*s == 0 && name[0] == endch && name[1] == 0) PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);
+							else PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);
 						}
 
-						PUGI__POPNODE(); // Pop.
+						PUGI_IMPL_POPNODE(); // Pop.
 
-						PUGI__SKIPWS();
+						PUGI_IMPL_SKIPWS();
 
 						if (*s == 0)
 						{
-							if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+							if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);
 						}
 						else
 						{
-							if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+							if (*s != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);
 							++s;
 						}
 					}
@@ -3408,62 +3438,79 @@ PUGI__NS_BEGIN
 						if (!s) return s;
 
 						assert(cursor);
-						if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
+						if (PUGI_IMPL_NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
 					}
 					else if (*s == '!') // '<!...'
 					{
 						s = parse_exclamation(s, cursor, optmsk, endch);
 						if (!s) return s;
 					}
-					else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);
-					else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+					else if (*s == 0 && endch == '?') PUGI_IMPL_THROW_ERROR(status_bad_pi, s);
+					else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s);
 				}
 				else
 				{
 					mark = s; // Save this offset while searching for a terminator.
 
-					PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
+					PUGI_IMPL_SKIPWS(); // Eat whitespace if no genuine PCDATA here.
 
 					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) || PUGI__OPTSET(parse_trim_pcdata))
+						if (!PUGI_IMPL_OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI_IMPL_OPTSET(parse_trim_pcdata))
 						{
 							continue;
 						}
-						else if (PUGI__OPTSET(parse_ws_pcdata_single))
+						else if (PUGI_IMPL_OPTSET(parse_ws_pcdata_single))
 						{
 							if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
 						}
 					}
 
-					if (!PUGI__OPTSET(parse_trim_pcdata))
+					if (!PUGI_IMPL_OPTSET(parse_trim_pcdata))
 						s = mark;
 
-					if (cursor->parent || PUGI__OPTSET(parse_fragment))
+					if (cursor->parent || PUGI_IMPL_OPTSET(parse_fragment))
 					{
-						if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)
+						char_t* parsed_pcdata = s;
+
+						s = strconv_pcdata(s);
+
+						if (PUGI_IMPL_OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)
 						{
-							cursor->value = s; // Save the offset.
+							cursor->value = parsed_pcdata; // Save the offset.
 						}
-						else
+						else if (PUGI_IMPL_OPTSET(parse_merge_pcdata) && cursor->first_child && PUGI_IMPL_NODETYPE(cursor->first_child->prev_sibling_c) == node_pcdata)
 						{
-							PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
+							assert(merged_pcdata >= cursor->first_child->prev_sibling_c->value);
+
+							// Catch up to the end of last parsed value; only needed for the first fragment.
+							merged_pcdata += strlength(merged_pcdata);
 
-							cursor->value = s; // Save the offset.
+							size_t length = strlength(parsed_pcdata);
 
-							PUGI__POPNODE(); // Pop since this is a standalone.
+							// Must use memmove instead of memcpy as this move may overlap
+							memmove(merged_pcdata, parsed_pcdata, (length + 1) * sizeof(char_t));
+							merged_pcdata += length;
 						}
+						else
+						{
+							xml_node_struct* prev_cursor = cursor;
+							PUGI_IMPL_PUSHNODE(node_pcdata); // Append a new node on the tree.
 
-						s = strconv_pcdata(s);
+							cursor->value = parsed_pcdata; // Save the offset.
+							merged_pcdata = parsed_pcdata; // Used for parse_merge_pcdata above, cheaper to save unconditionally
+
+							cursor = prev_cursor; // Pop since this is a standalone.
+						}
 
 						if (!*s) break;
 					}
 					else
 					{
-						PUGI__SCANFOR(*s == '<'); // '...<'
+						PUGI_IMPL_SCANFOR(*s == '<'); // '...<'
 						if (!*s) break;
 
 						++s;
@@ -3475,7 +3522,7 @@ PUGI__NS_BEGIN
 			}
 
 			// check that last tag is closed
-			if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+			if (cursor != root) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, s);
 
 			return s;
 		}
@@ -3497,7 +3544,7 @@ PUGI__NS_BEGIN
 		{
 			while (node)
 			{
-				if (PUGI__NODETYPE(node) == node_element) return true;
+				if (PUGI_IMPL_NODETYPE(node) == node_element) return true;
 
 				node = node->next_sibling;
 			}
@@ -3509,7 +3556,7 @@ PUGI__NS_BEGIN
 		{
 			// early-out for empty documents
 			if (length == 0)
-				return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
+				return make_parse_result(PUGI_IMPL_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;
@@ -3537,9 +3584,9 @@ PUGI__NS_BEGIN
 					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;
+				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))
+				if (!PUGI_IMPL_OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
 					return make_parse_result(status_no_document_element, length - 1);
 			}
 			else
@@ -3554,7 +3601,7 @@ PUGI__NS_BEGIN
 	};
 
 	// Output facilities
-	PUGI__FN xml_encoding get_write_native_encoding()
+	PUGI_IMPL_FN xml_encoding get_write_native_encoding()
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return get_wchar_encoding();
@@ -3563,7 +3610,7 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
+	PUGI_IMPL_FN xml_encoding get_write_encoding(xml_encoding encoding)
 	{
 		// replace wchar encoding with utf implementation
 		if (encoding == encoding_wchar) return get_wchar_encoding();
@@ -3581,18 +3628,18 @@ PUGI__NS_BEGIN
 		return encoding_utf8;
 	}
 
-	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)
+	template <typename D, typename T> PUGI_IMPL_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));
+		PUGI_IMPL_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)
+	template <typename D, typename T> PUGI_IMPL_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));
+		PUGI_IMPL_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());
 
@@ -3606,7 +3653,7 @@ PUGI__NS_BEGIN
 	}
 
 #ifdef PUGIXML_WCHAR_MODE
-	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+	PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length)
 	{
 		if (length < 1) return 0;
 
@@ -3614,7 +3661,7 @@ PUGI__NS_BEGIN
 		return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;
 	}
 
-	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)
+	PUGI_IMPL_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
 		if (need_endian_swap_utf(encoding, get_wchar_encoding()))
@@ -3652,7 +3699,7 @@ PUGI__NS_BEGIN
 		return 0;
 	}
 #else
-	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+	PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length)
 	{
 		if (length < 5) return 0;
 
@@ -3668,7 +3715,7 @@ PUGI__NS_BEGIN
 		return length;
 	}
 
-	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)
+	PUGI_IMPL_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)
 		{
@@ -3700,7 +3747,7 @@ PUGI__NS_BEGIN
 	public:
 		xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
 		{
-			PUGI__STATIC_ASSERT(bufcapacity >= 8);
+			PUGI_IMPL_STATIC_ASSERT(bufcapacity >= 8);
 		}
 
 		size_t flush()
@@ -3906,14 +3953,14 @@ PUGI__NS_BEGIN
 		xml_encoding encoding;
 	};
 
-	PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
+	PUGI_IMPL_FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
 	{
 		while (*s)
 		{
 			const char_t* prev = s;
 
 			// While *s is a usual symbol
-			PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type));
+			PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPEX(ss, type));
 
 			writer.write_buffer(prev, static_cast<size_t>(s - prev));
 
@@ -3958,7 +4005,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
+	PUGI_IMPL_FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
 	{
 		if (flags & format_no_escapes)
 			writer.write_string(s);
@@ -3966,7 +4013,7 @@ PUGI__NS_BEGIN
 			text_output_escaped(writer, s, type, flags);
 	}
 
-	PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
+	PUGI_IMPL_FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
 	{
 		do
 		{
@@ -3988,7 +4035,7 @@ PUGI__NS_BEGIN
 		while (*s);
 	}
 
-	PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
+	PUGI_IMPL_FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
 	{
 		switch (indent_length)
 		{
@@ -4028,7 +4075,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
+	PUGI_IMPL_FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
 	{
 		writer.write('<', '!', '-', '-');
 
@@ -4053,7 +4100,7 @@ PUGI__NS_BEGIN
 		writer.write('-', '-', '>');
 	}
 
-	PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
+	PUGI_IMPL_FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
 	{
 		while (*s)
 		{
@@ -4074,7 +4121,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
+	PUGI_IMPL_FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
 		const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"';
@@ -4102,7 +4149,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
+	PUGI_IMPL_FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
 		const char_t* name = node->name ? node->name + 0 : default_name;
@@ -4164,7 +4211,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node)
+	PUGI_IMPL_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;
@@ -4174,11 +4221,11 @@ PUGI__NS_BEGIN
 		writer.write('>');
 	}
 
-	PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+	PUGI_IMPL_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))
+		switch (PUGI_IMPL_NODETYPE(node))
 		{
 			case node_pcdata:
 				text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
@@ -4236,7 +4283,7 @@ PUGI__NS_BEGIN
 		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)
+	PUGI_IMPL_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;
@@ -4248,7 +4295,7 @@ PUGI__NS_BEGIN
 			assert(node);
 
 			// begin writing current node
-			if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
+			if (PUGI_IMPL_NODETYPE(node) == node_pcdata || PUGI_IMPL_NODETYPE(node) == node_cdata)
 			{
 				node_output_simple(writer, node, flags);
 
@@ -4262,7 +4309,7 @@ PUGI__NS_BEGIN
 				if ((indent_flags & indent_indent) && indent_length)
 					text_output_indent(writer, indent, indent_length, depth);
 
-				if (PUGI__NODETYPE(node) == node_element)
+				if (PUGI_IMPL_NODETYPE(node) == node_element)
 				{
 					indent_flags = indent_newline | indent_indent;
 
@@ -4277,7 +4324,7 @@ PUGI__NS_BEGIN
 						continue;
 					}
 				}
-				else if (PUGI__NODETYPE(node) == node_document)
+				else if (PUGI_IMPL_NODETYPE(node) == node_document)
 				{
 					indent_flags = indent_indent;
 
@@ -4307,7 +4354,7 @@ PUGI__NS_BEGIN
 				node = node->parent;
 
 				// write closing node
-				if (PUGI__NODETYPE(node) == node_element)
+				if (PUGI_IMPL_NODETYPE(node) == node_element)
 				{
 					depth--;
 
@@ -4329,11 +4376,11 @@ PUGI__NS_BEGIN
 			writer.write('\n');
 	}
 
-	PUGI__FN bool has_declaration(xml_node_struct* node)
+	PUGI_IMPL_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);
+			xml_node_type type = PUGI_IMPL_NODETYPE(child);
 
 			if (type == node_declaration) return true;
 			if (type == node_element) return false;
@@ -4342,7 +4389,7 @@ PUGI__NS_BEGIN
 		return false;
 	}
 
-	PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)
+	PUGI_IMPL_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)
@@ -4351,12 +4398,12 @@ PUGI__NS_BEGIN
 		return false;
 	}
 
-	PUGI__FN bool allow_insert_attribute(xml_node_type parent)
+	PUGI_IMPL_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)
+	PUGI_IMPL_FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
 	{
 		if (parent != node_document && parent != node_element) return false;
 		if (child == node_document || child == node_null) return false;
@@ -4365,7 +4412,7 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	PUGI__FN bool allow_move(xml_node parent, xml_node child)
+	PUGI_IMPL_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()))
@@ -4390,9 +4437,9 @@ PUGI__NS_BEGIN
 	}
 
 	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)
+	PUGI_IMPL_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);
+		assert(!dest && (header & header_mask) == 0); // copies are performed into fresh nodes
 
 		if (source)
 		{
@@ -4409,7 +4456,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc)
+	PUGI_IMPL_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);
@@ -4426,7 +4473,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)
+	PUGI_IMPL_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;
@@ -4444,7 +4491,7 @@ PUGI__NS_BEGIN
 			// when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop
 			if (sit != dn)
 			{
-				xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
+				xml_node_struct* copy = append_new_node(dit, alloc, PUGI_IMPL_NODETYPE(sit));
 
 				if (copy)
 				{
@@ -4480,7 +4527,7 @@ PUGI__NS_BEGIN
 		assert(!sit || dit == dn->parent);
 	}
 
-	PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)
+	PUGI_IMPL_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;
@@ -4491,18 +4538,18 @@ PUGI__NS_BEGIN
 
 	inline bool is_text_node(xml_node_struct* node)
 	{
-		xml_node_type type = PUGI__NODETYPE(node);
+		xml_node_type type = PUGI_IMPL_NODETYPE(node);
 
 		return type == node_pcdata || type == node_cdata;
 	}
 
 	// get value with conversion functions
-	template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
+	template <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
 	{
 		U result = 0;
 		const char_t* s = value;
 
-		while (PUGI__IS_CHARTYPE(*s, ct_space))
+		while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))
 			s++;
 
 		bool negative = (*s == '-');
@@ -4557,7 +4604,7 @@ PUGI__NS_BEGIN
 
 			size_t digits = static_cast<size_t>(s - start);
 
-			PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);
+			PUGI_IMPL_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';
@@ -4579,17 +4626,17 @@ PUGI__NS_BEGIN
 			return (overflow || result > maxv) ? maxv : result;
 	}
 
-	PUGI__FN int get_value_int(const char_t* value)
+	PUGI_IMPL_FN int get_value_int(const char_t* value)
 	{
 		return string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX);
 	}
 
-	PUGI__FN unsigned int get_value_uint(const char_t* value)
+	PUGI_IMPL_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)
+	PUGI_IMPL_FN double get_value_double(const char_t* value)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return wcstod(value, 0);
@@ -4598,7 +4645,7 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN float get_value_float(const char_t* value)
+	PUGI_IMPL_FN float get_value_float(const char_t* value)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return static_cast<float>(wcstod(value, 0));
@@ -4607,7 +4654,7 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN bool get_value_bool(const char_t* value)
+	PUGI_IMPL_FN bool get_value_bool(const char_t* value)
 	{
 		// only look at first char
 		char_t first = *value;
@@ -4617,18 +4664,18 @@ PUGI__NS_BEGIN
 	}
 
 #ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long get_value_llong(const char_t* value)
+	PUGI_IMPL_FN long long get_value_llong(const char_t* value)
 	{
 		return string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
 	}
 
-	PUGI__FN unsigned long long get_value_ullong(const char_t* value)
+	PUGI_IMPL_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 PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
+	template <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
 	{
 		char_t* result = end - 1;
 		U rest = negative ? 0 - value : value;
@@ -4650,7 +4697,7 @@ PUGI__NS_BEGIN
 
 	// set value with conversion functions
 	template <typename String, typename Header>
-	PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
+	PUGI_IMPL_FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		char_t wbuf[128];
@@ -4666,7 +4713,7 @@ PUGI__NS_BEGIN
 	}
 
 	template <typename U, typename String, typename Header>
-	PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
+	PUGI_IMPL_FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
 	{
 		char_t buf[64];
 		char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
@@ -4676,30 +4723,30 @@ PUGI__NS_BEGIN
 	}
 
 	template <typename String, typename Header>
-	PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision)
+	PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision)
 	{
 		char buf[128];
-		PUGI__SNPRINTF(buf, "%.*g", precision, double(value));
+		PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, double(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, int precision)
+	PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision)
 	{
 		char buf[128];
-		PUGI__SNPRINTF(buf, "%.*g", precision, value);
+		PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, value);
 
 		return set_value_ascii(dest, header, header_mask, buf);
 	}
 
 	template <typename String, typename Header>
-	PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)
+	PUGI_IMPL_FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)
 	{
 		return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
 	}
 
-	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)
+	PUGI_IMPL_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);
@@ -4707,6 +4754,9 @@ PUGI__NS_BEGIN
 		// get actual encoding
 		xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
 
+		// if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it
+		auto_deleter<void> contents_guard(own ? contents : 0, xml_memory::deallocate);
+
 		// get private buffer
 		char_t* buffer = 0;
 		size_t length = 0;
@@ -4714,6 +4764,9 @@ PUGI__NS_BEGIN
 		// coverity[var_deref_model]
 		if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
 
+		// after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it
+		contents_guard.release();
+
 		// delete original buffer if we performed a conversion
 		if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
 
@@ -4733,9 +4786,19 @@ PUGI__NS_BEGIN
 	}
 
 	// we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
-	PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
+	PUGI_IMPL_FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
 	{
-	#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+	#if defined(__linux__) || defined(__APPLE__)
+		// this simultaneously retrieves the file size and file mode (to guard against loading non-files)
+		struct stat st;
+		if (fstat(fileno(file), &st) != 0) return status_io_error;
+
+		// anything that's not a regular file doesn't have a coherent length
+		if (!S_ISREG(st.st_mode)) return status_io_error;
+
+		typedef off_t length_type;
+		length_type length = st.st_size;
+	#elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400
 		// there are 64-bit versions of fseek/ftell, let's use them
 		typedef __int64 length_type;
 
@@ -4773,7 +4836,7 @@ PUGI__NS_BEGIN
 	}
 
 	// 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)
+	PUGI_IMPL_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
@@ -4797,7 +4860,7 @@ PUGI__NS_BEGIN
 		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)
+	PUGI_IMPL_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);
 
@@ -4826,7 +4889,7 @@ PUGI__NS_BEGIN
 		return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);
 	}
 
-	PUGI__FN void close_file(FILE* file)
+	PUGI_IMPL_FN void close_file(FILE* file)
 	{
 		fclose(file);
 	}
@@ -4865,7 +4928,7 @@ PUGI__NS_BEGIN
 		T data[xml_memory_page_size / sizeof(T)];
 	};
 
-	template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+	template <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
 	{
 		auto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);
 
@@ -4919,7 +4982,7 @@ PUGI__NS_BEGIN
 		return status_ok;
 	}
 
-	template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+	template <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
 	{
 		// get length of remaining data in stream
 		typename std::basic_istream<T>::pos_type pos = stream.tellg();
@@ -4955,7 +5018,7 @@ PUGI__NS_BEGIN
 		return status_ok;
 	}
 
-	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)
+	template <typename T> PUGI_IMPL_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;
@@ -4981,10 +5044,10 @@ PUGI__NS_BEGIN
 	}
 #endif
 
-#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)
+#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
+	PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
 	{
-#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400
 		FILE* file = 0;
 		return _wfopen_s(&file, path, mode) == 0 ? file : 0;
 #else
@@ -4992,7 +5055,7 @@ PUGI__NS_BEGIN
 #endif
 	}
 #else
-	PUGI__FN char* convert_path_heap(const wchar_t* str)
+	PUGI_IMPL_FN char* convert_path_heap(const wchar_t* str)
 	{
 		assert(str);
 
@@ -5013,7 +5076,7 @@ PUGI__NS_BEGIN
 		return result;
 	}
 
-	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
+	PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
 	{
 		// there is no standard function to open wide paths, so our best bet is to try utf8 path
 		char* path_utf8 = convert_path_heap(path);
@@ -5033,9 +5096,9 @@ PUGI__NS_BEGIN
 	}
 #endif
 
-	PUGI__FN FILE* open_file(const char* path, const char* mode)
+	PUGI_IMPL_FN FILE* open_file(const char* path, const char* mode)
 	{
-#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400
 		FILE* file = 0;
 		return fopen_s(&file, path, mode) == 0 ? file : 0;
 #else
@@ -5043,14 +5106,14 @@ PUGI__NS_BEGIN
 #endif
 	}
 
-	PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
+	PUGI_IMPL_FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
 	{
 		if (!file) return false;
 
 		xml_writer_file writer(file);
 		doc.save(writer, indent, flags, encoding);
 
-		return ferror(file) == 0;
+		return fflush(file) == 0 && ferror(file) == 0;
 	}
 
 	struct name_null_sentry
@@ -5068,30 +5131,34 @@ PUGI__NS_BEGIN
 			node->name = name;
 		}
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 namespace pugi
 {
-	PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)
+	PUGI_IMPL_FN xml_writer::~xml_writer()
 	{
 	}
 
-	PUGI__FN void xml_writer_file::write(const void* data, size_t size)
+	PUGI_IMPL_FN xml_writer_file::xml_writer_file(void* file_): file(file_)
+	{
+	}
+
+	PUGI_IMPL_FN void xml_writer_file::write(const void* data, size_t size)
 	{
 		size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));
 		(void)!result; // unfortunately we can't do proper error handling here
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
+	PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
 	{
 	}
 
-	PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
+	PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
 	{
 	}
 
-	PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
+	PUGI_IMPL_FN void xml_writer_stream::write(const void* data, size_t size)
 	{
 		if (narrow_stream)
 		{
@@ -5108,291 +5175,328 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)
+	PUGI_IMPL_FN xml_tree_walker::xml_tree_walker(): _depth(0)
 	{
 	}
 
-	PUGI__FN xml_tree_walker::~xml_tree_walker()
+	PUGI_IMPL_FN xml_tree_walker::~xml_tree_walker()
 	{
 	}
 
-	PUGI__FN int xml_tree_walker::depth() const
+	PUGI_IMPL_FN int xml_tree_walker::depth() const
 	{
 		return _depth;
 	}
 
-	PUGI__FN bool xml_tree_walker::begin(xml_node&)
+	PUGI_IMPL_FN bool xml_tree_walker::begin(xml_node&)
 	{
 		return true;
 	}
 
-	PUGI__FN bool xml_tree_walker::end(xml_node&)
+	PUGI_IMPL_FN bool xml_tree_walker::end(xml_node&)
 	{
 		return true;
 	}
 
-	PUGI__FN xml_attribute::xml_attribute(): _attr(0)
+	PUGI_IMPL_FN xml_attribute::xml_attribute(): _attr(0)
 	{
 	}
 
-	PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)
+	PUGI_IMPL_FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)
 	{
 	}
 
-	PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***)
+	PUGI_IMPL_FN static void unspecified_bool_xml_attribute(xml_attribute***)
 	{
 	}
 
-	PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
+	PUGI_IMPL_FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
 	{
 		return _attr ? unspecified_bool_xml_attribute : 0;
 	}
 
-	PUGI__FN bool xml_attribute::operator!() const
+	PUGI_IMPL_FN bool xml_attribute::operator!() const
 	{
 		return !_attr;
 	}
 
-	PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator==(const xml_attribute& r) const
 	{
 		return (_attr == r._attr);
 	}
 
-	PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator!=(const xml_attribute& r) const
 	{
 		return (_attr != r._attr);
 	}
 
-	PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator<(const xml_attribute& r) const
 	{
 		return (_attr < r._attr);
 	}
 
-	PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator>(const xml_attribute& r) const
 	{
 		return (_attr > r._attr);
 	}
 
-	PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator<=(const xml_attribute& r) const
 	{
 		return (_attr <= r._attr);
 	}
 
-	PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const
+	PUGI_IMPL_FN bool xml_attribute::operator>=(const xml_attribute& r) const
 	{
 		return (_attr >= r._attr);
 	}
 
-	PUGI__FN xml_attribute xml_attribute::next_attribute() const
+	PUGI_IMPL_FN xml_attribute xml_attribute::next_attribute() const
 	{
-		return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
+		if (!_attr) return xml_attribute();
+		return xml_attribute(_attr->next_attribute);
 	}
 
-	PUGI__FN xml_attribute xml_attribute::previous_attribute() const
+	PUGI_IMPL_FN xml_attribute xml_attribute::previous_attribute() const
 	{
-		return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();
+		if (!_attr) return xml_attribute();
+		xml_attribute_struct* prev = _attr->prev_attribute_c;
+		return prev->next_attribute ? xml_attribute(prev) : xml_attribute();
 	}
 
-	PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
+	PUGI_IMPL_FN const char_t* xml_attribute::as_string(const char_t* def) const
 	{
-		return (_attr && _attr->value) ? _attr->value + 0 : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? value : def;
 	}
 
-	PUGI__FN int xml_attribute::as_int(int def) const
+	PUGI_IMPL_FN int xml_attribute::as_int(int def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_int(value) : def;
 	}
 
-	PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
+	PUGI_IMPL_FN unsigned int xml_attribute::as_uint(unsigned int def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_uint(value) : def;
 	}
 
-	PUGI__FN double xml_attribute::as_double(double def) const
+	PUGI_IMPL_FN double xml_attribute::as_double(double def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_double(value) : def;
 	}
 
-	PUGI__FN float xml_attribute::as_float(float def) const
+	PUGI_IMPL_FN float xml_attribute::as_float(float def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_float(value) : def;
 	}
 
-	PUGI__FN bool xml_attribute::as_bool(bool def) const
+	PUGI_IMPL_FN bool xml_attribute::as_bool(bool def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_bool(value) : def;
 	}
 
 #ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long xml_attribute::as_llong(long long def) const
+	PUGI_IMPL_FN long long xml_attribute::as_llong(long long def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_llong(value) : def;
 	}
 
-	PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
+	PUGI_IMPL_FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
 	{
-		return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
+		if (!_attr) return def;
+		const char_t* value = _attr->value;
+		return value ? impl::get_value_ullong(value) : def;
 	}
 #endif
 
-	PUGI__FN bool xml_attribute::empty() const
+	PUGI_IMPL_FN bool xml_attribute::empty() const
 	{
 		return !_attr;
 	}
 
-	PUGI__FN const char_t* xml_attribute::name() const
+	PUGI_IMPL_FN const char_t* xml_attribute::name() const
 	{
-		return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
+		if (!_attr) return PUGIXML_TEXT("");
+		const char_t* name = _attr->name;
+		return name ? name : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const char_t* xml_attribute::value() const
+	PUGI_IMPL_FN const char_t* xml_attribute::value() const
 	{
-		return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
+		if (!_attr) return PUGIXML_TEXT("");
+		const char_t* value = _attr->value;
+		return value ? value : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN size_t xml_attribute::hash_value() const
+	PUGI_IMPL_FN size_t xml_attribute::hash_value() const
 	{
 		return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));
 	}
 
-	PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const
+	PUGI_IMPL_FN xml_attribute_struct* xml_attribute::internal_object() const
 	{
 		return _attr;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(const char_t* rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(int rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned int rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(long rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(double rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(float rhs)
+	PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(float rhs)
 	{
 		set_value(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
+	PUGI_IMPL_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)
+	PUGI_IMPL_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)
+	PUGI_IMPL_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)
+	PUGI_IMPL_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, impl::strlength(rhs));
 	}
 
-	PUGI__FN bool xml_attribute::set_value(const char_t* rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs, size_t size)
+	{
+		if (!_attr) return false;
+
+		return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size);
+	}
+
+	PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
 	}
 
-	PUGI__FN bool xml_attribute::set_value(int rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs, size_t size)
+	{
+		if (!_attr) return false;
+
+		return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size);
+	}
+
+	PUGI_IMPL_FN bool xml_attribute::set_value(int rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(unsigned int rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(long rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(long rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(unsigned long rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(double rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(double rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(double rhs, int precision)
+	PUGI_IMPL_FN bool xml_attribute::set_value(double rhs, int precision)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(float rhs)
+	PUGI_IMPL_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, default_float_precision);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(float rhs, int precision)
+	PUGI_IMPL_FN bool xml_attribute::set_value(float rhs, int precision)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(bool rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(bool rhs)
 	{
 		if (!_attr) return false;
 
@@ -5400,14 +5504,14 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN bool xml_attribute::set_value(long long rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(long long rhs)
 	{
 		if (!_attr) return false;
 
 		return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
 	}
 
-	PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
+	PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long long rhs)
 	{
 		if (!_attr) return false;
 
@@ -5416,171 +5520,190 @@ namespace pugi
 #endif
 
 #ifdef __BORLANDC__
-	PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator&&(const xml_attribute& lhs, bool rhs)
 	{
 		return (bool)lhs && rhs;
 	}
 
-	PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator||(const xml_attribute& lhs, bool rhs)
 	{
 		return (bool)lhs || rhs;
 	}
 #endif
 
-	PUGI__FN xml_node::xml_node(): _root(0)
+	PUGI_IMPL_FN xml_node::xml_node(): _root(0)
 	{
 	}
 
-	PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)
+	PUGI_IMPL_FN xml_node::xml_node(xml_node_struct* p): _root(p)
 	{
 	}
 
-	PUGI__FN static void unspecified_bool_xml_node(xml_node***)
+	PUGI_IMPL_FN static void unspecified_bool_xml_node(xml_node***)
 	{
 	}
 
-	PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const
+	PUGI_IMPL_FN xml_node::operator xml_node::unspecified_bool_type() const
 	{
 		return _root ? unspecified_bool_xml_node : 0;
 	}
 
-	PUGI__FN bool xml_node::operator!() const
+	PUGI_IMPL_FN bool xml_node::operator!() const
 	{
 		return !_root;
 	}
 
-	PUGI__FN xml_node::iterator xml_node::begin() const
+	PUGI_IMPL_FN xml_node::iterator xml_node::begin() const
 	{
 		return iterator(_root ? _root->first_child + 0 : 0, _root);
 	}
 
-	PUGI__FN xml_node::iterator xml_node::end() const
+	PUGI_IMPL_FN xml_node::iterator xml_node::end() const
 	{
 		return iterator(0, _root);
 	}
 
-	PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
+	PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_begin() const
 	{
 		return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
 	}
 
-	PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
+	PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_end() const
 	{
 		return attribute_iterator(0, _root);
 	}
 
-	PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const
+	PUGI_IMPL_FN xml_object_range<xml_node_iterator> xml_node::children() const
 	{
 		return xml_object_range<xml_node_iterator>(begin(), end());
 	}
 
-	PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
+	PUGI_IMPL_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_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
 	}
 
-	PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
+	PUGI_IMPL_FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
 	{
 		return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());
 	}
 
-	PUGI__FN bool xml_node::operator==(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator==(const xml_node& r) const
 	{
 		return (_root == r._root);
 	}
 
-	PUGI__FN bool xml_node::operator!=(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator!=(const xml_node& r) const
 	{
 		return (_root != r._root);
 	}
 
-	PUGI__FN bool xml_node::operator<(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator<(const xml_node& r) const
 	{
 		return (_root < r._root);
 	}
 
-	PUGI__FN bool xml_node::operator>(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator>(const xml_node& r) const
 	{
 		return (_root > r._root);
 	}
 
-	PUGI__FN bool xml_node::operator<=(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator<=(const xml_node& r) const
 	{
 		return (_root <= r._root);
 	}
 
-	PUGI__FN bool xml_node::operator>=(const xml_node& r) const
+	PUGI_IMPL_FN bool xml_node::operator>=(const xml_node& r) const
 	{
 		return (_root >= r._root);
 	}
 
-	PUGI__FN bool xml_node::empty() const
+	PUGI_IMPL_FN bool xml_node::empty() const
 	{
 		return !_root;
 	}
 
-	PUGI__FN const char_t* xml_node::name() const
+	PUGI_IMPL_FN const char_t* xml_node::name() const
 	{
-		return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
+		if (!_root) return PUGIXML_TEXT("");
+		const char_t* name = _root->name;
+		return name ? name : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN xml_node_type xml_node::type() const
+	PUGI_IMPL_FN xml_node_type xml_node::type() const
 	{
-		return _root ? PUGI__NODETYPE(_root) : node_null;
+		return _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
 	}
 
-	PUGI__FN const char_t* xml_node::value() const
+	PUGI_IMPL_FN const char_t* xml_node::value() const
 	{
-		return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
+		if (!_root) return PUGIXML_TEXT("");
+		const char_t* value = _root->value;
+		return value ? value : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN xml_node xml_node::child(const char_t* name_) const
+	PUGI_IMPL_FN xml_node xml_node::child(const char_t* name_) const
 	{
 		if (!_root) return xml_node();
 
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
+				return xml_node(i);
+		}
 
 		return xml_node();
 	}
 
-	PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const
+	PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_) const
 	{
 		if (!_root) return xml_attribute();
 
 		for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
-			if (i->name && impl::strequal(name_, i->name))
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
 				return xml_attribute(i);
+		}
 
 		return xml_attribute();
 	}
 
-	PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const
+	PUGI_IMPL_FN xml_node xml_node::next_sibling(const char_t* name_) const
 	{
 		if (!_root) return xml_node();
 
 		for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
-			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
+				return xml_node(i);
+		}
 
 		return xml_node();
 	}
 
-	PUGI__FN xml_node xml_node::next_sibling() const
+	PUGI_IMPL_FN xml_node xml_node::next_sibling() const
 	{
 		return _root ? xml_node(_root->next_sibling) : xml_node();
 	}
 
-	PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const
+	PUGI_IMPL_FN xml_node xml_node::previous_sibling(const char_t* name_) const
 	{
 		if (!_root) return xml_node();
 
 		for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
-			if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
+				return xml_node(i);
+		}
 
 		return xml_node();
 	}
 
-	PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
+	PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
 	{
 		xml_attribute_struct* hint = hint_._attr;
 
@@ -5591,94 +5714,108 @@ namespace pugi
 
 		// optimistically search from hint up until the end
 		for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
-			if (i->name && impl::strequal(name_, i->name))
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
 			{
 				// update hint to maximize efficiency of searching for consecutive attributes
 				hint_._attr = i->next_attribute;
 
 				return xml_attribute(i);
 			}
+		}
 
 		// wrap around and search from the first attribute until the hint
 		// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
 		for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
-			if (j->name && impl::strequal(name_, j->name))
+		{
+			const char_t* jname = j->name;
+			if (jname && impl::strequal(name_, jname))
 			{
 				// update hint to maximize efficiency of searching for consecutive attributes
 				hint_._attr = j->next_attribute;
 
 				return xml_attribute(j);
 			}
+		}
 
 		return xml_attribute();
 	}
 
-	PUGI__FN xml_node xml_node::previous_sibling() const
+	PUGI_IMPL_FN xml_node xml_node::previous_sibling() const
 	{
 		if (!_root) return xml_node();
-
-		if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
-		else return xml_node();
+		xml_node_struct* prev = _root->prev_sibling_c;
+		return prev->next_sibling ? xml_node(prev) : xml_node();
 	}
 
-	PUGI__FN xml_node xml_node::parent() const
+	PUGI_IMPL_FN xml_node xml_node::parent() const
 	{
 		return _root ? xml_node(_root->parent) : xml_node();
 	}
 
-	PUGI__FN xml_node xml_node::root() const
+	PUGI_IMPL_FN xml_node xml_node::root() const
 	{
 		return _root ? xml_node(&impl::get_document(_root)) : xml_node();
 	}
 
-	PUGI__FN xml_text xml_node::text() const
+	PUGI_IMPL_FN xml_text xml_node::text() const
 	{
 		return xml_text(_root);
 	}
 
-	PUGI__FN const char_t* xml_node::child_value() const
+	PUGI_IMPL_FN const char_t* xml_node::child_value() const
 	{
 		if (!_root) return PUGIXML_TEXT("");
 
 		// element nodes can have value if parse_embed_pcdata was used
-		if (PUGI__NODETYPE(_root) == node_element && _root->value)
+		if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value)
 			return _root->value;
 
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (impl::is_text_node(i) && i->value)
-				return i->value;
+		{
+			const char_t* ivalue = i->value;
+			if (impl::is_text_node(i) && ivalue)
+				return ivalue;
+		}
 
 		return PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const
+	PUGI_IMPL_FN const char_t* xml_node::child_value(const char_t* name_) const
 	{
 		return child(name_).child_value();
 	}
 
-	PUGI__FN xml_attribute xml_node::first_attribute() const
+	PUGI_IMPL_FN xml_attribute xml_node::first_attribute() const
 	{
-		return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
+		if (!_root) return xml_attribute();
+		return xml_attribute(_root->first_attribute);
 	}
 
-	PUGI__FN xml_attribute xml_node::last_attribute() const
+	PUGI_IMPL_FN xml_attribute xml_node::last_attribute() const
 	{
-		return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();
+		if (!_root) return xml_attribute();
+		xml_attribute_struct* first = _root->first_attribute;
+		return first ? xml_attribute(first->prev_attribute_c) : xml_attribute();
 	}
 
-	PUGI__FN xml_node xml_node::first_child() const
+	PUGI_IMPL_FN xml_node xml_node::first_child() const
 	{
-		return _root ? xml_node(_root->first_child) : xml_node();
+		if (!_root) return xml_node();
+		return xml_node(_root->first_child);
 	}
 
-	PUGI__FN xml_node xml_node::last_child() const
+	PUGI_IMPL_FN xml_node xml_node::last_child() const
 	{
-		return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();
+		if (!_root) return xml_node();
+		xml_node_struct* first = _root->first_child;
+		return first ? xml_node(first->prev_sibling_c) : xml_node();
 	}
 
-	PUGI__FN bool xml_node::set_name(const char_t* rhs)
+	PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs)
 	{
-		xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+		xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
 
 		if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
 			return false;
@@ -5686,9 +5823,19 @@ namespace pugi
 		return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
 	}
 
-	PUGI__FN bool xml_node::set_value(const char_t* rhs)
+	PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs, size_t size)
 	{
-		xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+		xml_node_type type_ = _root ? PUGI_IMPL_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, size);
+	}
+
+	PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs)
+	{
+		xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;
 
 		if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
 			return false;
@@ -5696,7 +5843,17 @@ namespace pugi
 		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_)
+	PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs, size_t size)
+	{
+		xml_node_type type_ = _root ? PUGI_IMPL_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, size);
+	}
+
+	PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_)
 	{
 		if (!impl::allow_insert_attribute(type())) return xml_attribute();
 
@@ -5713,7 +5870,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
+	PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
 	{
 		if (!impl::allow_insert_attribute(type())) return xml_attribute();
 
@@ -5730,7 +5887,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
+	PUGI_IMPL_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();
@@ -5748,7 +5905,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
+	PUGI_IMPL_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();
@@ -5766,7 +5923,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
+	PUGI_IMPL_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();
@@ -5783,7 +5940,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
+	PUGI_IMPL_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();
@@ -5800,7 +5957,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
+	PUGI_IMPL_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();
@@ -5818,7 +5975,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
+	PUGI_IMPL_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();
@@ -5836,7 +5993,7 @@ namespace pugi
 		return a;
 	}
 
-	PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
+	PUGI_IMPL_FN xml_node xml_node::append_child(xml_node_type type_)
 	{
 		if (!impl::allow_insert_child(type(), type_)) return xml_node();
 
@@ -5853,7 +6010,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
+	PUGI_IMPL_FN xml_node xml_node::prepend_child(xml_node_type type_)
 	{
 		if (!impl::allow_insert_child(type(), type_)) return xml_node();
 
@@ -5870,7 +6027,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
+	PUGI_IMPL_FN xml_node xml_node::insert_child_before(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();
@@ -5888,7 +6045,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
+	PUGI_IMPL_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();
@@ -5906,7 +6063,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::append_child(const char_t* name_)
+	PUGI_IMPL_FN xml_node xml_node::append_child(const char_t* name_)
 	{
 		xml_node result = append_child(node_element);
 
@@ -5915,7 +6072,7 @@ namespace pugi
 		return result;
 	}
 
-	PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
+	PUGI_IMPL_FN xml_node xml_node::prepend_child(const char_t* name_)
 	{
 		xml_node result = prepend_child(node_element);
 
@@ -5924,7 +6081,7 @@ namespace pugi
 		return result;
 	}
 
-	PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
+	PUGI_IMPL_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);
 
@@ -5933,7 +6090,7 @@ namespace pugi
 		return result;
 	}
 
-	PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
+	PUGI_IMPL_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);
 
@@ -5942,7 +6099,7 @@ namespace pugi
 		return result;
 	}
 
-	PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
+	PUGI_IMPL_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();
@@ -5959,7 +6116,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
+	PUGI_IMPL_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();
@@ -5976,7 +6133,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
+	PUGI_IMPL_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();
@@ -5994,7 +6151,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
+	PUGI_IMPL_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();
@@ -6012,7 +6169,7 @@ namespace pugi
 		return n;
 	}
 
-	PUGI__FN xml_node xml_node::append_move(const xml_node& moved)
+	PUGI_IMPL_FN xml_node xml_node::append_move(const xml_node& moved)
 	{
 		if (!impl::allow_move(*this, moved)) return xml_node();
 
@@ -6028,7 +6185,7 @@ namespace pugi
 		return moved;
 	}
 
-	PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved)
+	PUGI_IMPL_FN xml_node xml_node::prepend_move(const xml_node& moved)
 	{
 		if (!impl::allow_move(*this, moved)) return xml_node();
 
@@ -6044,7 +6201,7 @@ namespace pugi
 		return moved;
 	}
 
-	PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node)
+	PUGI_IMPL_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();
@@ -6062,7 +6219,7 @@ namespace pugi
 		return moved;
 	}
 
-	PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node)
+	PUGI_IMPL_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();
@@ -6080,12 +6237,12 @@ namespace pugi
 		return moved;
 	}
 
-	PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
+	PUGI_IMPL_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)
+	PUGI_IMPL_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;
@@ -6099,7 +6256,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xml_node::remove_attributes()
+	PUGI_IMPL_FN bool xml_node::remove_attributes()
 	{
 		if (!_root) return false;
 
@@ -6120,12 +6277,12 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xml_node::remove_child(const char_t* name_)
+	PUGI_IMPL_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)
+	PUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n)
 	{
 		if (!_root || !n._root || n._root->parent != _root) return false;
 
@@ -6138,7 +6295,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xml_node::remove_children()
+	PUGI_IMPL_FN bool xml_node::remove_children()
 	{
 		if (!_root) return false;
 
@@ -6159,11 +6316,14 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
+	PUGI_IMPL_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);
 
+		// append buffer can not merge PCDATA into existing PCDATA nodes
+		if ((options & parse_merge_pcdata) != 0 && last_child().type() == node_pcdata) return impl::make_parse_result(status_append_invalid_root);
+
 		// get document node
 		impl::xml_document_struct* doc = &impl::get_document(_root);
 
@@ -6194,35 +6354,52 @@ namespace pugi
 		return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
 	}
 
-	PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
+	PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
 	{
 		if (!_root) return xml_node();
 
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (i->name && impl::strequal(name_, i->name))
+		{
+			const char_t* iname = i->name;
+			if (iname && impl::strequal(name_, iname))
 			{
 				for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
-					if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
-						return xml_node(i);
+				{
+					const char_t* aname = a->name;
+					if (aname && impl::strequal(attr_name, aname))
+					{
+						const char_t* avalue = a->value;
+						if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT("")))
+							return xml_node(i);
+					}
+				}
 			}
+		}
 
 		return xml_node();
 	}
 
-	PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
+	PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
 	{
 		if (!_root) return xml_node();
 
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 			for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
-				if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
-					return xml_node(i);
+			{
+				const char_t* aname = a->name;
+				if (aname && impl::strequal(attr_name, aname))
+				{
+					const char_t* avalue = a->value;
+					if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT("")))
+						return xml_node(i);
+				}
+			}
 
 		return xml_node();
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN string_t xml_node::path(char_t delimiter) const
+	PUGI_IMPL_FN string_t xml_node::path(char_t delimiter) const
 	{
 		if (!_root) return string_t();
 
@@ -6230,8 +6407,9 @@ namespace pugi
 
 		for (xml_node_struct* i = _root; i; i = i->parent)
 		{
+			const char_t* iname = i->name;
 			offset += (i != _root);
-			offset += i->name ? impl::strlength(i->name) : 0;
+			offset += iname ? impl::strlength(iname) : 0;
 		}
 
 		string_t result;
@@ -6242,12 +6420,13 @@ namespace pugi
 			if (j != _root)
 				result[--offset] = delimiter;
 
-			if (j->name)
+			const char_t* jname = j->name;
+			if (jname)
 			{
-				size_t length = impl::strlength(j->name);
+				size_t length = impl::strlength(jname);
 
 				offset -= length;
-				memcpy(&result[offset], j->name, length * sizeof(char_t));
+				memcpy(&result[offset], jname, length * sizeof(char_t));
 			}
 		}
 
@@ -6257,7 +6436,7 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const
+	PUGI_IMPL_FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const
 	{
 		xml_node context = path_[0] == delimiter ? root() : *this;
 
@@ -6285,7 +6464,8 @@ namespace pugi
 		{
 			for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling)
 			{
-				if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
+				const char_t* jname = j->name;
+				if (jname && impl::strequalrange(jname, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
 				{
 					xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
 
@@ -6297,7 +6477,7 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN bool xml_node::traverse(xml_tree_walker& walker)
+	PUGI_IMPL_FN bool xml_node::traverse(xml_tree_walker& walker)
 	{
 		walker._depth = -1;
 
@@ -6344,17 +6524,17 @@ namespace pugi
 		return walker.end(arg_end);
 	}
 
-	PUGI__FN size_t xml_node::hash_value() const
+	PUGI_IMPL_FN size_t xml_node::hash_value() const
 	{
 		return static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));
 	}
 
-	PUGI__FN xml_node_struct* xml_node::internal_object() const
+	PUGI_IMPL_FN xml_node_struct* xml_node::internal_object() const
 	{
 		return _root;
 	}
 
-	PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+	PUGI_IMPL_FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
 	{
 		if (!_root) return;
 
@@ -6366,14 +6546,14 @@ namespace pugi
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+	PUGI_IMPL_FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
 	{
 		xml_writer_stream writer(stream);
 
 		print(writer, indent, flags, encoding, depth);
 	}
 
-	PUGI__FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const
+	PUGI_IMPL_FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const
 	{
 		xml_writer_stream writer(stream);
 
@@ -6381,7 +6561,7 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN ptrdiff_t xml_node::offset_debug() const
+	PUGI_IMPL_FN ptrdiff_t xml_node::offset_debug() const
 	{
 		if (!_root) return -1;
 
@@ -6413,27 +6593,27 @@ namespace pugi
 	}
 
 #ifdef __BORLANDC__
-	PUGI__FN bool operator&&(const xml_node& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator&&(const xml_node& lhs, bool rhs)
 	{
 		return (bool)lhs && rhs;
 	}
 
-	PUGI__FN bool operator||(const xml_node& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator||(const xml_node& lhs, bool rhs)
 	{
 		return (bool)lhs || rhs;
 	}
 #endif
 
-	PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root)
+	PUGI_IMPL_FN xml_text::xml_text(xml_node_struct* root): _root(root)
 	{
 	}
 
-	PUGI__FN xml_node_struct* xml_text::_data() const
+	PUGI_IMPL_FN xml_node_struct* xml_text::_data() const
 	{
 		if (!_root || impl::is_text_node(_root)) return _root;
 
 		// element nodes can have value if parse_embed_pcdata was used
-		if (PUGI__NODETYPE(_root) == node_element && _root->value)
+		if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value)
 			return _root;
 
 		for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)
@@ -6443,7 +6623,7 @@ namespace pugi
 		return 0;
 	}
 
-	PUGI__FN xml_node_struct* xml_text::_data_new()
+	PUGI_IMPL_FN xml_node_struct* xml_text::_data_new()
 	{
 		xml_node_struct* d = _data();
 		if (d) return d;
@@ -6451,158 +6631,174 @@ namespace pugi
 		return xml_node(_root).append_child(node_pcdata).internal_object();
 	}
 
-	PUGI__FN xml_text::xml_text(): _root(0)
+	PUGI_IMPL_FN xml_text::xml_text(): _root(0)
 	{
 	}
 
-	PUGI__FN static void unspecified_bool_xml_text(xml_text***)
+	PUGI_IMPL_FN static void unspecified_bool_xml_text(xml_text***)
 	{
 	}
 
-	PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const
+	PUGI_IMPL_FN xml_text::operator xml_text::unspecified_bool_type() const
 	{
 		return _data() ? unspecified_bool_xml_text : 0;
 	}
 
-	PUGI__FN bool xml_text::operator!() const
+	PUGI_IMPL_FN bool xml_text::operator!() const
 	{
 		return !_data();
 	}
 
-	PUGI__FN bool xml_text::empty() const
+	PUGI_IMPL_FN bool xml_text::empty() const
 	{
 		return _data() == 0;
 	}
 
-	PUGI__FN const char_t* xml_text::get() const
+	PUGI_IMPL_FN const char_t* xml_text::get() const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
+		if (!d) return PUGIXML_TEXT("");
+		const char_t* value = d->value;
+		return value ? value : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const char_t* xml_text::as_string(const char_t* def) const
+	PUGI_IMPL_FN const char_t* xml_text::as_string(const char_t* def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? d->value + 0 : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? value : def;
 	}
 
-	PUGI__FN int xml_text::as_int(int def) const
+	PUGI_IMPL_FN int xml_text::as_int(int def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_int(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_int(value) : def;
 	}
 
-	PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
+	PUGI_IMPL_FN unsigned int xml_text::as_uint(unsigned int def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_uint(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_uint(value) : def;
 	}
 
-	PUGI__FN double xml_text::as_double(double def) const
+	PUGI_IMPL_FN double xml_text::as_double(double def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_double(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_double(value) : def;
 	}
 
-	PUGI__FN float xml_text::as_float(float def) const
+	PUGI_IMPL_FN float xml_text::as_float(float def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_float(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_float(value) : def;
 	}
 
-	PUGI__FN bool xml_text::as_bool(bool def) const
+	PUGI_IMPL_FN bool xml_text::as_bool(bool def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_bool(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_bool(value) : def;
 	}
 
 #ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long xml_text::as_llong(long long def) const
+	PUGI_IMPL_FN long long xml_text::as_llong(long long def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_llong(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_llong(value) : def;
 	}
 
-	PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
+	PUGI_IMPL_FN unsigned long long xml_text::as_ullong(unsigned long long def) const
 	{
 		xml_node_struct* d = _data();
-
-		return (d && d->value) ? impl::get_value_ullong(d->value) : def;
+		if (!d) return def;
+		const char_t* value = d->value;
+		return value ? impl::get_value_ullong(value) : def;
 	}
 #endif
 
-	PUGI__FN bool xml_text::set(const char_t* rhs)
+	PUGI_IMPL_FN bool xml_text::set(const char_t* rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;
 	}
 
-	PUGI__FN bool xml_text::set(int rhs)
+	PUGI_IMPL_FN bool xml_text::set(const char_t* rhs, size_t size)
+	{
+		xml_node_struct* dn = _data_new();
+
+		return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false;
+	}
+
+	PUGI_IMPL_FN bool xml_text::set(int rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
 	}
 
-	PUGI__FN bool xml_text::set(unsigned int rhs)
+	PUGI_IMPL_FN bool xml_text::set(unsigned int rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
 	}
 
-	PUGI__FN bool xml_text::set(long rhs)
+	PUGI_IMPL_FN bool xml_text::set(long rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
 	}
 
-	PUGI__FN bool xml_text::set(unsigned long rhs)
+	PUGI_IMPL_FN bool xml_text::set(unsigned long rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
 	}
 
-	PUGI__FN bool xml_text::set(float rhs)
+	PUGI_IMPL_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, default_float_precision) : false;
 	}
 
-	PUGI__FN bool xml_text::set(float rhs, int precision)
+	PUGI_IMPL_FN bool xml_text::set(float rhs, int precision)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false;
 	}
 
-	PUGI__FN bool xml_text::set(double rhs)
+	PUGI_IMPL_FN bool xml_text::set(double 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, default_double_precision) : false;
 	}
 
-	PUGI__FN bool xml_text::set(double rhs, int precision)
+	PUGI_IMPL_FN bool xml_text::set(double rhs, int precision)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false;
 	}
 
-	PUGI__FN bool xml_text::set(bool rhs)
+	PUGI_IMPL_FN bool xml_text::set(bool rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
@@ -6610,14 +6806,14 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN bool xml_text::set(long long rhs)
+	PUGI_IMPL_FN bool xml_text::set(long long rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
 		return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
 	}
 
-	PUGI__FN bool xml_text::set(unsigned long long rhs)
+	PUGI_IMPL_FN bool xml_text::set(unsigned long long rhs)
 	{
 		xml_node_struct* dn = _data_new();
 
@@ -6625,256 +6821,256 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(const char_t* rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(int rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(int rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(unsigned int rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned int rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(long rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(long rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(unsigned long rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(double rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(double rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(float rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(float rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(bool rhs)
+	PUGI_IMPL_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)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 
-	PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)
+	PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long long rhs)
 	{
 		set(rhs);
 		return *this;
 	}
 #endif
 
-	PUGI__FN xml_node xml_text::data() const
+	PUGI_IMPL_FN xml_node xml_text::data() const
 	{
 		return xml_node(_data());
 	}
 
 #ifdef __BORLANDC__
-	PUGI__FN bool operator&&(const xml_text& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator&&(const xml_text& lhs, bool rhs)
 	{
 		return (bool)lhs && rhs;
 	}
 
-	PUGI__FN bool operator||(const xml_text& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator||(const xml_text& lhs, bool rhs)
 	{
 		return (bool)lhs || rhs;
 	}
 #endif
 
-	PUGI__FN xml_node_iterator::xml_node_iterator()
+	PUGI_IMPL_FN xml_node_iterator::xml_node_iterator()
 	{
 	}
 
-	PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())
+	PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())
 	{
 	}
 
-	PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+	PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
 	{
 	}
 
-	PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
+	PUGI_IMPL_FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
 	{
 		return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
 	}
 
-	PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
+	PUGI_IMPL_FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
 	{
 		return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
 	}
 
-	PUGI__FN xml_node& xml_node_iterator::operator*() const
+	PUGI_IMPL_FN xml_node& xml_node_iterator::operator*() const
 	{
 		assert(_wrap._root);
 		return _wrap;
 	}
 
-	PUGI__FN xml_node* xml_node_iterator::operator->() const
+	PUGI_IMPL_FN xml_node* xml_node_iterator::operator->() const
 	{
 		assert(_wrap._root);
 		return const_cast<xml_node*>(&_wrap); // BCC5 workaround
 	}
 
-	PUGI__FN xml_node_iterator& xml_node_iterator::operator++()
+	PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator++()
 	{
 		assert(_wrap._root);
 		_wrap._root = _wrap._root->next_sibling;
 		return *this;
 	}
 
-	PUGI__FN xml_node_iterator xml_node_iterator::operator++(int)
+	PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator++(int)
 	{
 		xml_node_iterator temp = *this;
 		++*this;
 		return temp;
 	}
 
-	PUGI__FN xml_node_iterator& xml_node_iterator::operator--()
+	PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator--()
 	{
 		_wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();
 		return *this;
 	}
 
-	PUGI__FN xml_node_iterator xml_node_iterator::operator--(int)
+	PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator--(int)
 	{
 		xml_node_iterator temp = *this;
 		--*this;
 		return temp;
 	}
 
-	PUGI__FN xml_attribute_iterator::xml_attribute_iterator()
+	PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator()
 	{
 	}
 
-	PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)
+	PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)
 	{
 	}
 
-	PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+	PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
 	{
 	}
 
-	PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
+	PUGI_IMPL_FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
 	{
 		return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;
 	}
 
-	PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
+	PUGI_IMPL_FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
 	{
 		return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;
 	}
 
-	PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const
+	PUGI_IMPL_FN xml_attribute& xml_attribute_iterator::operator*() const
 	{
 		assert(_wrap._attr);
 		return _wrap;
 	}
 
-	PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const
+	PUGI_IMPL_FN xml_attribute* xml_attribute_iterator::operator->() const
 	{
 		assert(_wrap._attr);
 		return const_cast<xml_attribute*>(&_wrap); // BCC5 workaround
 	}
 
-	PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator++()
+	PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator++()
 	{
 		assert(_wrap._attr);
 		_wrap._attr = _wrap._attr->next_attribute;
 		return *this;
 	}
 
-	PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int)
+	PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator++(int)
 	{
 		xml_attribute_iterator temp = *this;
 		++*this;
 		return temp;
 	}
 
-	PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator--()
+	PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator--()
 	{
 		_wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();
 		return *this;
 	}
 
-	PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int)
+	PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator--(int)
 	{
 		xml_attribute_iterator temp = *this;
 		--*this;
 		return temp;
 	}
 
-	PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)
+	PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)
 	{
 	}
 
-	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_IMPL_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)
+	PUGI_IMPL_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)
 	{
 	}
 
-	PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
+	PUGI_IMPL_FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
 	{
 		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
+	PUGI_IMPL_FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const
 	{
 		return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
 	}
 
-	PUGI__FN xml_node& xml_named_node_iterator::operator*() const
+	PUGI_IMPL_FN xml_node& xml_named_node_iterator::operator*() const
 	{
 		assert(_wrap._root);
 		return _wrap;
 	}
 
-	PUGI__FN xml_node* xml_named_node_iterator::operator->() const
+	PUGI_IMPL_FN xml_node* xml_named_node_iterator::operator->() const
 	{
 		assert(_wrap._root);
 		return const_cast<xml_node*>(&_wrap); // BCC5 workaround
 	}
 
-	PUGI__FN xml_named_node_iterator& xml_named_node_iterator::operator++()
+	PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator++()
 	{
 		assert(_wrap._root);
 		_wrap = _wrap.next_sibling(_name);
 		return *this;
 	}
 
-	PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int)
+	PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator++(int)
 	{
 		xml_named_node_iterator temp = *this;
 		++*this;
 		return temp;
 	}
 
-	PUGI__FN xml_named_node_iterator& xml_named_node_iterator::operator--()
+	PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator--()
 	{
 		if (_wrap._root)
 			_wrap = _wrap.previous_sibling(_name);
@@ -6889,23 +7085,23 @@ namespace pugi
 		return *this;
 	}
 
-	PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int)
+	PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator--(int)
 	{
 		xml_named_node_iterator temp = *this;
 		--*this;
 		return temp;
 	}
 
-	PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)
+	PUGI_IMPL_FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)
 	{
 	}
 
-	PUGI__FN xml_parse_result::operator bool() const
+	PUGI_IMPL_FN xml_parse_result::operator bool() const
 	{
 		return status == status_ok;
 	}
 
-	PUGI__FN const char* xml_parse_result::description() const
+	PUGI_IMPL_FN const char* xml_parse_result::description() const
 	{
 		switch (status)
 		{
@@ -6936,24 +7132,24 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xml_document::xml_document(): _buffer(0)
+	PUGI_IMPL_FN xml_document::xml_document(): _buffer(0)
 	{
 		_create();
 	}
 
-	PUGI__FN xml_document::~xml_document()
+	PUGI_IMPL_FN xml_document::~xml_document()
 	{
 		_destroy();
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)
+	PUGI_IMPL_FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)
 	{
 		_create();
 		_move(rhs);
 	}
 
-	PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+	PUGI_IMPL_FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
 	{
 		if (this == &rhs) return *this;
 
@@ -6965,20 +7161,20 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN void xml_document::reset()
+	PUGI_IMPL_FN void xml_document::reset()
 	{
 		_destroy();
 		_create();
 	}
 
-	PUGI__FN void xml_document::reset(const xml_document& proto)
+	PUGI_IMPL_FN void xml_document::reset(const xml_document& proto)
 	{
 		reset();
 
 		impl::node_copy_tree(_root, proto._root);
 	}
 
-	PUGI__FN void xml_document::_create()
+	PUGI_IMPL_FN void xml_document::_create()
 	{
 		assert(!_root);
 
@@ -6990,7 +7186,7 @@ namespace pugi
 	#endif
 
 		// initialize sentinel page
-		PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory));
+		PUGI_IMPL_STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory));
 
 		// prepare page structure
 		impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory);
@@ -7021,7 +7217,7 @@ namespace pugi
 		assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
 	}
 
-	PUGI__FN void xml_document::_destroy()
+	PUGI_IMPL_FN void xml_document::_destroy()
 	{
 		assert(_root);
 
@@ -7039,7 +7235,7 @@ namespace pugi
 		}
 
 		// destroy dynamic storage, leave sentinel page (it's in static memory)
-		impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
+		impl::xml_memory_page* root_page = PUGI_IMPL_GETPAGE(_root);
 		assert(root_page && !root_page->prev);
 		assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
 
@@ -7061,7 +7257,7 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+	PUGI_IMPL_FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
 	{
 		impl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(_root);
 		impl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(rhs._root);
@@ -7095,7 +7291,7 @@ namespace pugi
 
 		// move allocation state
 		// note that other->_root may point to the embedded document page, in which case we should keep original (empty) state
-		if (other->_root != PUGI__GETPAGE(other))
+		if (other->_root != PUGI_IMPL_GETPAGE(other))
 		{
 			doc->_root = other->_root;
 			doc->_busy_size = other->_busy_size;
@@ -7116,10 +7312,10 @@ namespace pugi
 	#endif
 
 		// move page structure
-		impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc);
+		impl::xml_memory_page* doc_page = PUGI_IMPL_GETPAGE(doc);
 		assert(doc_page && !doc_page->prev && !doc_page->next);
 
-		impl::xml_memory_page* other_page = PUGI__GETPAGE(other);
+		impl::xml_memory_page* other_page = PUGI_IMPL_GETPAGE(other);
 		assert(other_page && !other_page->prev);
 
 		// relink pages since root page is embedded into xml_document
@@ -7166,20 +7362,20 @@ namespace pugi
 		}
 
 		// reset other document
-		new (other) impl::xml_document_struct(PUGI__GETPAGE(other));
+		new (other) impl::xml_document_struct(PUGI_IMPL_GETPAGE(other));
 		rhs._buffer = 0;
 	}
 #endif
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
+	PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
 		return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);
 	}
 
-	PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
+	PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
 	{
 		reset();
 
@@ -7187,7 +7383,7 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
 	{
 		// Force native encoding (skip autodetection)
 	#ifdef PUGIXML_WCHAR_MODE
@@ -7199,12 +7395,12 @@ namespace pugi
 		return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);
 	}
 
-	PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
+	PUGI_IMPL_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)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
@@ -7214,7 +7410,7 @@ namespace pugi
 		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_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
@@ -7224,28 +7420,28 @@ namespace pugi
 		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)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
 		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)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
 		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)
+	PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
 	{
 		reset();
 
 		return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);
 	}
 
-	PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+	PUGI_IMPL_FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		impl::xml_buffered_writer buffered_writer(writer, encoding);
 
@@ -7274,14 +7470,14 @@ namespace pugi
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+	PUGI_IMPL_FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		xml_writer_stream writer(stream);
 
 		save(writer, indent, flags, encoding);
 	}
 
-	PUGI__FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const
+	PUGI_IMPL_FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const
 	{
 		xml_writer_stream writer(stream);
 
@@ -7289,71 +7485,71 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+	PUGI_IMPL_FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		using impl::auto_deleter; // MSVC7 workaround
 		auto_deleter<FILE> file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
 
-		return impl::save_file_impl(*this, file.data, indent, flags, encoding);
+		return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0;
 	}
 
-	PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+	PUGI_IMPL_FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		using impl::auto_deleter; // MSVC7 workaround
 		auto_deleter<FILE> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file);
 
-		return impl::save_file_impl(*this, file.data, indent, flags, encoding);
+		return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0;
 	}
 
-	PUGI__FN xml_node xml_document::document_element() const
+	PUGI_IMPL_FN xml_node xml_document::document_element() const
 	{
 		assert(_root);
 
 		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (PUGI__NODETYPE(i) == node_element)
+			if (PUGI_IMPL_NODETYPE(i) == node_element)
 				return xml_node(i);
 
 		return xml_node();
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)
+	PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)
 	{
 		assert(str);
 
 		return impl::as_utf8_impl(str, impl::strlength_wide(str));
 	}
 
-	PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)
+	PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)
 	{
 		return impl::as_utf8_impl(str.c_str(), str.size());
 	}
 
-	PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
+	PUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
 	{
 		assert(str);
 
 		return impl::as_wide_impl(str, strlen(str));
 	}
 
-	PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)
+	PUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)
 	{
 		return impl::as_wide_impl(str.c_str(), str.size());
 	}
 #endif
 
-	PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)
+	PUGI_IMPL_FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)
 	{
 		impl::xml_memory::allocate = allocate;
 		impl::xml_memory::deallocate = deallocate;
 	}
 
-	PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()
+	PUGI_IMPL_FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()
 	{
 		return impl::xml_memory::allocate;
 	}
 
-	PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()
+	PUGI_IMPL_FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()
 	{
 		return impl::xml_memory::deallocate;
 	}
@@ -7363,17 +7559,17 @@ namespace pugi
 namespace std
 {
 	// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
-	PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
 
-	PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
 
-	PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
@@ -7384,17 +7580,17 @@ namespace std
 namespace std
 {
 	// Workarounds for (non-standard) iterator category detection
-	PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
 
-	PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
 
-	PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
+	PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
 	{
 		return std::bidirectional_iterator_tag();
 	}
@@ -7403,7 +7599,7 @@ namespace std
 
 #ifndef PUGIXML_NO_XPATH
 // STL replacements
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	struct equal_to
 	{
 		template <typename T> bool operator()(const T& lhs, const T& rhs) const
@@ -7443,7 +7639,7 @@ PUGI__NS_BEGIN
 		rhs = temp;
 	}
 
-	template <typename I, typename Pred> PUGI__FN I min_element(I begin, I end, const Pred& pred)
+	template <typename I, typename Pred> PUGI_IMPL_FN I min_element(I begin, I end, const Pred& pred)
 	{
 		I result = begin;
 
@@ -7454,13 +7650,13 @@ PUGI__NS_BEGIN
 		return result;
 	}
 
-	template <typename I> PUGI__FN void reverse(I begin, I end)
+	template <typename I> PUGI_IMPL_FN void reverse(I begin, I end)
 	{
 		while (end - begin > 1)
 			swap(*begin++, *--end);
 	}
 
-	template <typename I> PUGI__FN I unique(I begin, I end)
+	template <typename I> PUGI_IMPL_FN I unique(I begin, I end)
 	{
 		// fast skip head
 		while (end - begin > 1 && *begin != *(begin + 1))
@@ -7485,7 +7681,7 @@ PUGI__NS_BEGIN
 		return write + 1;
 	}
 
-	template <typename T, typename Pred> PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred)
+	template <typename T, typename Pred> PUGI_IMPL_FN void insertion_sort(T* begin, T* end, const Pred& pred)
 	{
 		if (begin == end)
 			return;
@@ -7519,7 +7715,7 @@ PUGI__NS_BEGIN
 		return middle;
 	}
 
-	template <typename T, typename Pred> PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
+	template <typename T, typename Pred> PUGI_IMPL_FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
 	{
 		// invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
 		T* eq = begin;
@@ -7546,7 +7742,7 @@ PUGI__NS_BEGIN
 		*out_eqend = gt;
 	}
 
-	template <typename I, typename Pred> PUGI__FN void sort(I begin, I end, const Pred& pred)
+	template <typename I, typename Pred> PUGI_IMPL_FN void sort(I begin, I end, const Pred& pred)
 	{
 		// sort large chunks
 		while (end - begin > 16)
@@ -7576,7 +7772,7 @@ PUGI__NS_BEGIN
 		insertion_sort(begin, end, pred);
 	}
 
-	PUGI__FN bool hash_insert(const void** table, size_t size, const void* key)
+	PUGI_IMPL_FN bool hash_insert(const void** table, size_t size, const void* key)
 	{
 		assert(key);
 
@@ -7610,10 +7806,10 @@ PUGI__NS_BEGIN
 		assert(false && "Hash table is full"); // unreachable
 		return false;
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // Allocator used for AST and evaluation stacks
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	static const size_t xpath_memory_page_size =
 	#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
 		PUGIXML_MEMORY_XPATH_PAGE_SIZE
@@ -7809,10 +8005,10 @@ PUGI__NS_BEGIN
 			temp.release();
 		}
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // String class
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	class xpath_string
 	{
 		const char_t* _buffer;
@@ -7947,10 +8143,10 @@ PUGI__NS_BEGIN
 			return _uses_heap;
 		}
 	};
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
-PUGI__NS_BEGIN
-	PUGI__FN bool starts_with(const char_t* string, const char_t* pattern)
+PUGI_IMPL_NS_BEGIN
+	PUGI_IMPL_FN bool starts_with(const char_t* string, const char_t* pattern)
 	{
 		while (*pattern && *string == *pattern)
 		{
@@ -7961,7 +8157,7 @@ PUGI__NS_BEGIN
 		return *pattern == 0;
 	}
 
-	PUGI__FN const char_t* find_char(const char_t* s, char_t c)
+	PUGI_IMPL_FN const char_t* find_char(const char_t* s, char_t c)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		return wcschr(s, c);
@@ -7970,7 +8166,7 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p)
+	PUGI_IMPL_FN const char_t* find_substring(const char_t* s, const char_t* p)
 	{
 	#ifdef PUGIXML_WCHAR_MODE
 		// MSVC6 wcsstr bug workaround (if s is empty it always returns 0)
@@ -7981,12 +8177,12 @@ PUGI__NS_BEGIN
 	}
 
 	// Converts symbol to lower case, if it is an ASCII one
-	PUGI__FN char_t tolower_ascii(char_t ch)
+	PUGI_IMPL_FN char_t tolower_ascii(char_t ch)
 	{
 		return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;
 	}
 
-	PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)
+	PUGI_IMPL_FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)
 	{
 		if (na.attribute())
 			return xpath_string::from_const(na.attribute().value());
@@ -8040,7 +8236,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
+	PUGI_IMPL_FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
 	{
 		assert(ln->parent == rn->parent);
 
@@ -8064,7 +8260,7 @@ PUGI__NS_BEGIN
 		return !rs;
 	}
 
-	PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)
+	PUGI_IMPL_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;
@@ -8107,14 +8303,14 @@ PUGI__NS_BEGIN
 		return node_is_before_sibling(ln, rn);
 	}
 
-	PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)
+	PUGI_IMPL_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_buffer_order(const xpath_node& xnode)
+	PUGI_IMPL_FN const void* document_buffer_order(const xpath_node& xnode)
 	{
 		xml_node_struct* node = xnode.node().internal_object();
 
@@ -8199,10 +8395,10 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN double gen_nan()
+	PUGI_IMPL_FN double gen_nan()
 	{
 	#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
-		PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
+		PUGI_IMPL_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
 		typedef uint32_t UI; // BCC5 workaround
 		union { float f; UI i; } u;
 		u.i = 0x7fc00000;
@@ -8214,9 +8410,9 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN bool is_nan(double value)
+	PUGI_IMPL_FN bool is_nan(double value)
 	{
-	#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+	#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__)
 		return !!_isnan(value);
 	#elif defined(fpclassify) && defined(FP_NAN)
 		return fpclassify(value) == FP_NAN;
@@ -8227,9 +8423,9 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN const char_t* convert_number_to_string_special(double value)
+	PUGI_IMPL_FN const char_t* convert_number_to_string_special(double value)
 	{
-	#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+	#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__)
 		if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0;
 		if (_isnan(value)) return PUGIXML_TEXT("NaN");
 		return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
@@ -8259,12 +8455,12 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN bool convert_number_to_boolean(double value)
+	PUGI_IMPL_FN bool convert_number_to_boolean(double value)
 	{
 		return (value != 0 && !is_nan(value));
 	}
 
-	PUGI__FN void truncate_zeros(char* begin, char* end)
+	PUGI_IMPL_FN void truncate_zeros(char* begin, char* end)
 	{
 		while (begin != end && end[-1] == '0') end--;
 
@@ -8272,8 +8468,8 @@ PUGI__NS_BEGIN
 	}
 
 	// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
-#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
-	PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
+#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400
+	PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
 	{
 		// get base values
 		int sign, exponent;
@@ -8287,10 +8483,10 @@ PUGI__NS_BEGIN
 		*out_exponent = exponent;
 	}
 #else
-	PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
+	PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
 	{
 		// get a scientific notation value with IEEE DBL_DIG decimals
-		PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
+		PUGI_IMPL_SNPRINTF(buffer, "%.*e", DBL_DIG, value);
 
 		// get the exponent (possibly negative)
 		char* exponent_string = strchr(buffer, 'e');
@@ -8300,7 +8496,7 @@ PUGI__NS_BEGIN
 
 		// extract mantissa string: skip sign
 		char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;
-		assert(mantissa[0] != '0' && mantissa[1] == '.');
+		assert(mantissa[0] != '0' && (mantissa[1] == '.' || mantissa[1] == ','));
 
 		// divide mantissa by 10 to eliminate integer part
 		mantissa[1] = mantissa[0];
@@ -8316,7 +8512,7 @@ PUGI__NS_BEGIN
 	}
 #endif
 
-	PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)
+	PUGI_IMPL_FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)
 	{
 		// try special number conversion
 		const char_t* special = convert_number_to_string_special(value);
@@ -8383,10 +8579,10 @@ PUGI__NS_BEGIN
 		return xpath_string::from_heap_preallocated(result, s);
 	}
 
-	PUGI__FN bool check_string_to_number_format(const char_t* string)
+	PUGI_IMPL_FN bool check_string_to_number_format(const char_t* string)
 	{
 		// parse leading whitespace
-		while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+		while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string;
 
 		// parse sign
 		if (*string == '-') ++string;
@@ -8394,26 +8590,26 @@ PUGI__NS_BEGIN
 		if (!*string) return false;
 
 		// if there is no integer part, there should be a decimal part with at least one digit
-		if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false;
+		if (!PUGI_IMPL_IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI_IMPL_IS_CHARTYPEX(string[1], ctx_digit))) return false;
 
 		// parse integer part
-		while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+		while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string;
 
 		// parse decimal part
 		if (*string == '.')
 		{
 			++string;
 
-			while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+			while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string;
 		}
 
 		// parse trailing whitespace
-		while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+		while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string;
 
 		return *string == 0;
 	}
 
-	PUGI__FN double convert_string_to_number(const char_t* string)
+	PUGI_IMPL_FN double convert_string_to_number(const char_t* string)
 	{
 		// check string format
 		if (!check_string_to_number_format(string)) return gen_nan();
@@ -8426,7 +8622,7 @@ PUGI__NS_BEGIN
 	#endif
 	}
 
-	PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)
+	PUGI_IMPL_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;
@@ -8450,24 +8646,24 @@ PUGI__NS_BEGIN
 		return true;
 	}
 
-	PUGI__FN double round_nearest(double value)
+	PUGI_IMPL_FN double round_nearest(double value)
 	{
 		return floor(value + 0.5);
 	}
 
-	PUGI__FN double round_nearest_nzero(double value)
+	PUGI_IMPL_FN double round_nearest_nzero(double value)
 	{
 		// same as round_nearest, but returns -0 for [-0.5, -0]
 		// ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)
 		return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);
 	}
 
-	PUGI__FN const char_t* qualified_name(const xpath_node& node)
+	PUGI_IMPL_FN const char_t* qualified_name(const xpath_node& node)
 	{
 		return node.attribute() ? node.attribute().name() : node.node().name();
 	}
 
-	PUGI__FN const char_t* local_name(const xpath_node& node)
+	PUGI_IMPL_FN const char_t* local_name(const xpath_node& node)
 	{
 		const char_t* name = qualified_name(node);
 		const char_t* p = find_char(name, ':');
@@ -8498,7 +8694,7 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN const char_t* namespace_uri(xml_node node)
+	PUGI_IMPL_FN const char_t* namespace_uri(xml_node node)
 	{
 		namespace_uri_predicate pred = node.name();
 
@@ -8516,7 +8712,7 @@ PUGI__NS_BEGIN
 		return PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
+	PUGI_IMPL_FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
 	{
 		namespace_uri_predicate pred = attr.name();
 
@@ -8537,12 +8733,12 @@ PUGI__NS_BEGIN
 		return PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const char_t* namespace_uri(const xpath_node& node)
+	PUGI_IMPL_FN const char_t* namespace_uri(const xpath_node& node)
 	{
 		return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());
 	}
 
-	PUGI__FN char_t* normalize_space(char_t* buffer)
+	PUGI_IMPL_FN char_t* normalize_space(char_t* buffer)
 	{
 		char_t* write = buffer;
 
@@ -8550,10 +8746,10 @@ PUGI__NS_BEGIN
 		{
 			char_t ch = *it++;
 
-			if (PUGI__IS_CHARTYPE(ch, ct_space))
+			if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))
 			{
 				// replace whitespace sequence with single space
-				while (PUGI__IS_CHARTYPE(*it, ct_space)) it++;
+				while (PUGI_IMPL_IS_CHARTYPE(*it, ct_space)) it++;
 
 				// avoid leading spaces
 				if (write != buffer) *write++ = ' ';
@@ -8562,7 +8758,7 @@ PUGI__NS_BEGIN
 		}
 
 		// remove trailing space
-		if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--;
+		if (write != buffer && PUGI_IMPL_IS_CHARTYPE(write[-1], ct_space)) write--;
 
 		// zero-terminate
 		*write = 0;
@@ -8570,13 +8766,13 @@ PUGI__NS_BEGIN
 		return write;
 	}
 
-	PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
+	PUGI_IMPL_FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
 	{
 		char_t* write = buffer;
 
 		while (*buffer)
 		{
-			PUGI__DMC_VOLATILE char_t ch = *buffer++;
+			PUGI_IMPL_DMC_VOLATILE char_t ch = *buffer++;
 
 			const char_t* pos = find_char(from, ch);
 
@@ -8592,7 +8788,7 @@ PUGI__NS_BEGIN
 		return write;
 	}
 
-	PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
+	PUGI_IMPL_FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
 	{
 		unsigned char table[128] = {0};
 
@@ -8624,7 +8820,7 @@ PUGI__NS_BEGIN
 		return static_cast<unsigned char*>(result);
 	}
 
-	PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
+	PUGI_IMPL_FN char_t* translate_table(char_t* buffer, const unsigned char* table)
 	{
 		char_t* write = buffer;
 
@@ -8706,7 +8902,7 @@ PUGI__NS_BEGIN
 
 	static const xpath_node_set dummy_node_set;
 
-	PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)
+	PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)
 	{
 		// Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
 		unsigned int result = 0;
@@ -8725,7 +8921,7 @@ PUGI__NS_BEGIN
 		return result;
 	}
 
-	template <typename T> PUGI__FN T* new_xpath_variable(const char_t* name)
+	template <typename T> PUGI_IMPL_FN T* new_xpath_variable(const char_t* name)
 	{
 		size_t length = strlength(name);
 		if (length == 0) return 0; // empty variable names are invalid
@@ -8741,7 +8937,7 @@ PUGI__NS_BEGIN
 		return result;
 	}
 
-	PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
+	PUGI_IMPL_FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
 	{
 		switch (type)
 		{
@@ -8762,13 +8958,13 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	template <typename T> PUGI__FN void delete_xpath_variable(T* var)
+	template <typename T> PUGI_IMPL_FN void delete_xpath_variable(T* var)
 	{
 		var->~T();
 		xml_memory::deallocate(var);
 	}
 
-	PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
+	PUGI_IMPL_FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
 	{
 		switch (type)
 		{
@@ -8793,7 +8989,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
+	PUGI_IMPL_FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
 	{
 		switch (rhs->type())
 		{
@@ -8815,7 +9011,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	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)
+	PUGI_IMPL_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;
@@ -8838,11 +9034,11 @@ PUGI__NS_BEGIN
 
 		return true;
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 // 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)
+PUGI_IMPL_NS_BEGIN
+	PUGI_IMPL_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;
@@ -8858,7 +9054,7 @@ PUGI__NS_BEGIN
 		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)
+	PUGI_IMPL_FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
 	{
 		xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
 
@@ -8881,7 +9077,7 @@ PUGI__NS_BEGIN
 		return order;
 	}
 
-	PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)
+	PUGI_IMPL_FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)
 	{
 		if (begin == end) return xpath_node();
 
@@ -9035,7 +9231,7 @@ PUGI__NS_BEGIN
 		}
 	};
 
-	PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc)
+	PUGI_IMPL_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);
 
@@ -9054,9 +9250,9 @@ PUGI__NS_BEGIN
 		// push
 		*_end++ = node;
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
-PUGI__NS_BEGIN
+PUGI_IMPL_NS_BEGIN
 	struct xpath_context
 	{
 		xpath_node n;
@@ -9138,7 +9334,7 @@ PUGI__NS_BEGIN
 		{
 			const char_t* cur = _cur;
 
-			while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur;
+			while (PUGI_IMPL_IS_CHARTYPE(*cur, ct_space)) ++cur;
 
 			// save lexeme position for error reporting
 			_cur_lexeme_pos = cur;
@@ -9220,17 +9416,17 @@ PUGI__NS_BEGIN
 			case '$':
 				cur += 1;
 
-				if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+				if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol))
 				{
 					_cur_lexeme_contents.begin = cur;
 
-					while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+					while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
 
-					if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname
+					if (cur[0] == ':' && PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // qname
 					{
 						cur++; // :
 
-						while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+						while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
 					}
 
 					_cur_lexeme_contents.end = cur;
@@ -9293,13 +9489,13 @@ PUGI__NS_BEGIN
 					cur += 2;
 					_cur_lexeme = lex_double_dot;
 				}
-				else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit))
+				else if (PUGI_IMPL_IS_CHARTYPEX(*(cur+1), ctx_digit))
 				{
 					_cur_lexeme_contents.begin = cur; // .
 
 					++cur;
 
-					while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+					while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;
 
 					_cur_lexeme_contents.end = cur;
 
@@ -9353,28 +9549,28 @@ PUGI__NS_BEGIN
 				break;
 
 			default:
-				if (PUGI__IS_CHARTYPEX(*cur, ctx_digit))
+				if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit))
 				{
 					_cur_lexeme_contents.begin = cur;
 
-					while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+					while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;
 
 					if (*cur == '.')
 					{
 						cur++;
 
-						while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+						while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;
 					}
 
 					_cur_lexeme_contents.end = cur;
 
 					_cur_lexeme = lex_number;
 				}
-				else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+				else if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol))
 				{
 					_cur_lexeme_contents.begin = cur;
 
-					while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+					while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
 
 					if (cur[0] == ':')
 					{
@@ -9382,11 +9578,11 @@ PUGI__NS_BEGIN
 						{
 							cur += 2; // :*
 						}
-						else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
+						else if (PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
 						{
 							cur++; // :
 
-							while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+							while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
 						}
 					}
 
@@ -9798,7 +9994,8 @@ PUGI__NS_BEGIN
 
 			xpath_node* last = ns.begin() + first;
 
-			xpath_context c(xpath_node(), 1, size);
+			xpath_node cn;
+			xpath_context c(cn, 1, size);
 
 			double er = expr->eval_number(c, stack);
 
@@ -9885,7 +10082,7 @@ PUGI__NS_BEGIN
 		{
 			assert(n);
 
-			xml_node_type type = PUGI__NODETYPE(n);
+			xml_node_type type = PUGI_IMPL_NODETYPE(n);
 
 			switch (_test)
 			{
@@ -11908,7 +12105,7 @@ PUGI__NS_BEGIN
 					// This is either a function call, or not - if not, we shall proceed with location path
 					const char_t* state = _lexer.state();
 
-					while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;
+					while (PUGI_IMPL_IS_CHARTYPE(*state, ct_space)) ++state;
 
 					if (*state != '(')
 						return parse_location_path();
@@ -12151,7 +12348,7 @@ PUGI__NS_BEGIN
 		bool oom;
 	};
 
-	PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
+	PUGI_IMPL_FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
 	{
 		if (!impl) return 0;
 
@@ -12169,91 +12366,91 @@ PUGI__NS_BEGIN
 
 		return impl->root;
 	}
-PUGI__NS_END
+PUGI_IMPL_NS_END
 
 namespace pugi
 {
 #ifndef PUGIXML_NO_EXCEPTIONS
-	PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)
+	PUGI_IMPL_FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)
 	{
 		assert(_result.error);
 	}
 
-	PUGI__FN const char* xpath_exception::what() const throw()
+	PUGI_IMPL_FN const char* xpath_exception::what() const throw()
 	{
 		return _result.error;
 	}
 
-	PUGI__FN const xpath_parse_result& xpath_exception::result() const
+	PUGI_IMPL_FN const xpath_parse_result& xpath_exception::result() const
 	{
 		return _result;
 	}
 #endif
 
-	PUGI__FN xpath_node::xpath_node()
+	PUGI_IMPL_FN xpath_node::xpath_node()
 	{
 	}
 
-	PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_)
+	PUGI_IMPL_FN xpath_node::xpath_node(const xml_node& node_): _node(node_)
 	{
 	}
 
-	PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)
+	PUGI_IMPL_FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)
 	{
 	}
 
-	PUGI__FN xml_node xpath_node::node() const
+	PUGI_IMPL_FN xml_node xpath_node::node() const
 	{
 		return _attribute ? xml_node() : _node;
 	}
 
-	PUGI__FN xml_attribute xpath_node::attribute() const
+	PUGI_IMPL_FN xml_attribute xpath_node::attribute() const
 	{
 		return _attribute;
 	}
 
-	PUGI__FN xml_node xpath_node::parent() const
+	PUGI_IMPL_FN xml_node xpath_node::parent() const
 	{
 		return _attribute ? _node : _node.parent();
 	}
 
-	PUGI__FN static void unspecified_bool_xpath_node(xpath_node***)
+	PUGI_IMPL_FN static void unspecified_bool_xpath_node(xpath_node***)
 	{
 	}
 
-	PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const
+	PUGI_IMPL_FN xpath_node::operator xpath_node::unspecified_bool_type() const
 	{
 		return (_node || _attribute) ? unspecified_bool_xpath_node : 0;
 	}
 
-	PUGI__FN bool xpath_node::operator!() const
+	PUGI_IMPL_FN bool xpath_node::operator!() const
 	{
 		return !(_node || _attribute);
 	}
 
-	PUGI__FN bool xpath_node::operator==(const xpath_node& n) const
+	PUGI_IMPL_FN bool xpath_node::operator==(const xpath_node& n) const
 	{
 		return _node == n._node && _attribute == n._attribute;
 	}
 
-	PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const
+	PUGI_IMPL_FN bool xpath_node::operator!=(const xpath_node& n) const
 	{
 		return _node != n._node || _attribute != n._attribute;
 	}
 
 #ifdef __BORLANDC__
-	PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator&&(const xpath_node& lhs, bool rhs)
 	{
 		return (bool)lhs && rhs;
 	}
 
-	PUGI__FN bool operator||(const xpath_node& lhs, bool rhs)
+	PUGI_IMPL_FN bool operator||(const xpath_node& lhs, bool rhs)
 	{
 		return (bool)lhs || rhs;
 	}
 #endif
 
-	PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)
+	PUGI_IMPL_FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)
 	{
 		assert(begin_ <= end_);
 
@@ -12285,7 +12482,7 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT
 	{
 		_type = rhs._type;
 		_storage[0] = rhs._storage[0];
@@ -12298,27 +12495,27 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage)
+	PUGI_IMPL_FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage)
 	{
 	}
 
-	PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage)
+	PUGI_IMPL_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()
+	PUGI_IMPL_FN xpath_node_set::~xpath_node_set()
 	{
 		if (_begin != _storage)
 			impl::xml_memory::deallocate(_begin);
 	}
 
-	PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage)
+	PUGI_IMPL_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);
 	}
 
-	PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
+	PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
 	{
 		if (this == &ns) return *this;
 
@@ -12328,12 +12525,12 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage)
+	PUGI_IMPL_FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage)
 	{
 		_move(rhs);
 	}
 
-	PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT
 	{
 		if (this == &rhs) return *this;
 
@@ -12346,66 +12543,66 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xpath_node_set::type_t xpath_node_set::type() const
+	PUGI_IMPL_FN xpath_node_set::type_t xpath_node_set::type() const
 	{
 		return _type;
 	}
 
-	PUGI__FN size_t xpath_node_set::size() const
+	PUGI_IMPL_FN size_t xpath_node_set::size() const
 	{
 		return _end - _begin;
 	}
 
-	PUGI__FN bool xpath_node_set::empty() const
+	PUGI_IMPL_FN bool xpath_node_set::empty() const
 	{
 		return _begin == _end;
 	}
 
-	PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const
+	PUGI_IMPL_FN const xpath_node& xpath_node_set::operator[](size_t index) const
 	{
 		assert(index < size());
 		return _begin[index];
 	}
 
-	PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const
+	PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::begin() const
 	{
 		return _begin;
 	}
 
-	PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const
+	PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::end() const
 	{
 		return _end;
 	}
 
-	PUGI__FN void xpath_node_set::sort(bool reverse)
+	PUGI_IMPL_FN void xpath_node_set::sort(bool reverse)
 	{
 		_type = impl::xpath_sort(_begin, _end, _type, reverse);
 	}
 
-	PUGI__FN xpath_node xpath_node_set::first() const
+	PUGI_IMPL_FN xpath_node xpath_node_set::first() const
 	{
 		return impl::xpath_first(_begin, _end, _type);
 	}
 
-	PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)
+	PUGI_IMPL_FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)
 	{
 	}
 
-	PUGI__FN xpath_parse_result::operator bool() const
+	PUGI_IMPL_FN xpath_parse_result::operator bool() const
 	{
 		return error == 0;
 	}
 
-	PUGI__FN const char* xpath_parse_result::description() const
+	PUGI_IMPL_FN const char* xpath_parse_result::description() const
 	{
 		return error ? error : "No error";
 	}
 
-	PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)
+	PUGI_IMPL_FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)
 	{
 	}
 
-	PUGI__FN const char_t* xpath_variable::name() const
+	PUGI_IMPL_FN const char_t* xpath_variable::name() const
 	{
 		switch (_type)
 		{
@@ -12427,33 +12624,33 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xpath_value_type xpath_variable::type() const
+	PUGI_IMPL_FN xpath_value_type xpath_variable::type() const
 	{
 		return _type;
 	}
 
-	PUGI__FN bool xpath_variable::get_boolean() const
+	PUGI_IMPL_FN bool xpath_variable::get_boolean() const
 	{
 		return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;
 	}
 
-	PUGI__FN double xpath_variable::get_number() const
+	PUGI_IMPL_FN double xpath_variable::get_number() const
 	{
 		return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();
 	}
 
-	PUGI__FN const char_t* xpath_variable::get_string() const
+	PUGI_IMPL_FN const char_t* xpath_variable::get_string() const
 	{
 		const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;
 		return value ? value : PUGIXML_TEXT("");
 	}
 
-	PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const
+	PUGI_IMPL_FN const xpath_node_set& xpath_variable::get_node_set() const
 	{
 		return (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;
 	}
 
-	PUGI__FN bool xpath_variable::set(bool value)
+	PUGI_IMPL_FN bool xpath_variable::set(bool value)
 	{
 		if (_type != xpath_type_boolean) return false;
 
@@ -12461,7 +12658,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xpath_variable::set(double value)
+	PUGI_IMPL_FN bool xpath_variable::set(double value)
 	{
 		if (_type != xpath_type_number) return false;
 
@@ -12469,7 +12666,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xpath_variable::set(const char_t* value)
+	PUGI_IMPL_FN bool xpath_variable::set(const char_t* value)
 	{
 		if (_type != xpath_type_string) return false;
 
@@ -12490,7 +12687,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN bool xpath_variable::set(const xpath_node_set& value)
+	PUGI_IMPL_FN bool xpath_variable::set(const xpath_node_set& value)
 	{
 		if (_type != xpath_type_node_set) return false;
 
@@ -12498,19 +12695,19 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN xpath_variable_set::xpath_variable_set()
+	PUGI_IMPL_FN xpath_variable_set::xpath_variable_set()
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
 			_data[i] = 0;
 	}
 
-	PUGI__FN xpath_variable_set::~xpath_variable_set()
+	PUGI_IMPL_FN xpath_variable_set::~xpath_variable_set()
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
 			_destroy(_data[i]);
 	}
 
-	PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)
+	PUGI_IMPL_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;
@@ -12518,7 +12715,7 @@ namespace pugi
 		_assign(rhs);
 	}
 
-	PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
+	PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
 	{
 		if (this == &rhs) return *this;
 
@@ -12528,7 +12725,7 @@ namespace pugi
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
 		{
@@ -12537,7 +12734,7 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
 		{
@@ -12551,7 +12748,7 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
+	PUGI_IMPL_FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
 	{
 		xpath_variable_set temp;
 
@@ -12562,7 +12759,7 @@ namespace pugi
 		_swap(temp);
 	}
 
-	PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
+	PUGI_IMPL_FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
 	{
 		for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
 		{
@@ -12573,7 +12770,7 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
+	PUGI_IMPL_FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
 	{
 		const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
 		size_t hash = impl::hash_string(name) % hash_size;
@@ -12586,7 +12783,7 @@ namespace pugi
 		return 0;
 	}
 
-	PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
+	PUGI_IMPL_FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
 	{
 		xpath_variable* last = 0;
 
@@ -12613,7 +12810,7 @@ namespace pugi
 		return true;
 	}
 
-	PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var)
+	PUGI_IMPL_FN void xpath_variable_set::_destroy(xpath_variable* var)
 	{
 		while (var)
 		{
@@ -12625,7 +12822,7 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
+	PUGI_IMPL_FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
 	{
 		const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
 		size_t hash = impl::hash_string(name) % hash_size;
@@ -12648,41 +12845,41 @@ namespace pugi
 		return result;
 	}
 
-	PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value)
+	PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, bool value)
 	{
 		xpath_variable* var = add(name, xpath_type_boolean);
 		return var ? var->set(value) : false;
 	}
 
-	PUGI__FN bool xpath_variable_set::set(const char_t* name, double value)
+	PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, double value)
 	{
 		xpath_variable* var = add(name, xpath_type_number);
 		return var ? var->set(value) : false;
 	}
 
-	PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value)
+	PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const char_t* value)
 	{
 		xpath_variable* var = add(name, xpath_type_string);
 		return var ? var->set(value) : false;
 	}
 
-	PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)
+	PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)
 	{
 		xpath_variable* var = add(name, xpath_type_node_set);
 		return var ? var->set(value) : false;
 	}
 
-	PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
+	PUGI_IMPL_FN xpath_variable* xpath_variable_set::get(const char_t* name)
 	{
 		return _find(name);
 	}
 
-	PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
+	PUGI_IMPL_FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
 	{
 		return _find(name);
 	}
 
-	PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)
+	PUGI_IMPL_FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)
 	{
 		impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();
 
@@ -12720,18 +12917,18 @@ namespace pugi
 		}
 	}
 
-	PUGI__FN xpath_query::xpath_query(): _impl(0)
+	PUGI_IMPL_FN xpath_query::xpath_query(): _impl(0)
 	{
 	}
 
-	PUGI__FN xpath_query::~xpath_query()
+	PUGI_IMPL_FN xpath_query::~xpath_query()
 	{
 		if (_impl)
 			impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
 	}
 
 #ifdef PUGIXML_HAS_MOVE
-	PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT
 	{
 		_impl = rhs._impl;
 		_result = rhs._result;
@@ -12739,7 +12936,7 @@ namespace pugi
 		rhs._result = xpath_parse_result();
 	}
 
-	PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT
+	PUGI_IMPL_FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT
 	{
 		if (this == &rhs) return *this;
 
@@ -12755,14 +12952,14 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN xpath_value_type xpath_query::return_type() const
+	PUGI_IMPL_FN xpath_value_type xpath_query::return_type() const
 	{
 		if (!_impl) return xpath_type_none;
 
 		return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();
 	}
 
-	PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const
+	PUGI_IMPL_FN bool xpath_query::evaluate_boolean(const xpath_node& n) const
 	{
 		if (!_impl) return false;
 
@@ -12783,7 +12980,7 @@ namespace pugi
 		return r;
 	}
 
-	PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const
+	PUGI_IMPL_FN double xpath_query::evaluate_number(const xpath_node& n) const
 	{
 		if (!_impl) return impl::gen_nan();
 
@@ -12805,7 +13002,7 @@ namespace pugi
 	}
 
 #ifndef PUGIXML_NO_STL
-	PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const
+	PUGI_IMPL_FN string_t xpath_query::evaluate_string(const xpath_node& n) const
 	{
 		if (!_impl) return string_t();
 
@@ -12827,7 +13024,7 @@ namespace pugi
 	}
 #endif
 
-	PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
+	PUGI_IMPL_FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
 	{
 		impl::xpath_context c(n, 1, 1);
 		impl::xpath_stack_data sd;
@@ -12857,7 +13054,7 @@ namespace pugi
 		return full_size;
 	}
 
-	PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
+	PUGI_IMPL_FN xpath_node_set xpath_query::evaluate_node_set(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_set();
@@ -12879,7 +13076,7 @@ namespace pugi
 		return xpath_node_set(r.begin(), r.end(), r.type());
 	}
 
-	PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const
+	PUGI_IMPL_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();
@@ -12901,54 +13098,54 @@ namespace pugi
 		return r.first();
 	}
 
-	PUGI__FN const xpath_parse_result& xpath_query::result() const
+	PUGI_IMPL_FN const xpath_parse_result& xpath_query::result() const
 	{
 		return _result;
 	}
 
-	PUGI__FN static void unspecified_bool_xpath_query(xpath_query***)
+	PUGI_IMPL_FN static void unspecified_bool_xpath_query(xpath_query***)
 	{
 	}
 
-	PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const
+	PUGI_IMPL_FN xpath_query::operator xpath_query::unspecified_bool_type() const
 	{
 		return _impl ? unspecified_bool_xpath_query : 0;
 	}
 
-	PUGI__FN bool xpath_query::operator!() const
+	PUGI_IMPL_FN bool xpath_query::operator!() const
 	{
 		return !_impl;
 	}
 
-	PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
+	PUGI_IMPL_FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
 	{
 		xpath_query q(query, variables);
 		return q.evaluate_node(*this);
 	}
 
-	PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
+	PUGI_IMPL_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
+	PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
 	{
 		xpath_query q(query, variables);
 		return q.evaluate_node_set(*this);
 	}
 
-	PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
+	PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
 	{
 		return query.evaluate_node_set(*this);
 	}
 
-	PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
+	PUGI_IMPL_FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
 	{
 		xpath_query q(query, variables);
 		return q.evaluate_node(*this);
 	}
 
-	PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
+	PUGI_IMPL_FN xpath_node xml_node::select_single_node(const xpath_query& query) const
 	{
 		return query.evaluate_node(*this);
 	}
@@ -12971,40 +13168,40 @@ namespace pugi
 #endif
 
 // Undefine all local macros (makes sure we're not leaking macros in header-only mode)
-#undef PUGI__NO_INLINE
-#undef PUGI__UNLIKELY
-#undef PUGI__STATIC_ASSERT
-#undef PUGI__DMC_VOLATILE
-#undef PUGI__UNSIGNED_OVERFLOW
-#undef PUGI__MSVC_CRT_VERSION
-#undef PUGI__SNPRINTF
-#undef PUGI__NS_BEGIN
-#undef PUGI__NS_END
-#undef PUGI__FN
-#undef PUGI__FN_NO_INLINE
-#undef PUGI__GETHEADER_IMPL
-#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
-#undef PUGI__PUSHNODE
-#undef PUGI__POPNODE
-#undef PUGI__SCANFOR
-#undef PUGI__SCANWHILE
-#undef PUGI__SCANWHILE_UNROLL
-#undef PUGI__ENDSEG
-#undef PUGI__THROW_ERROR
-#undef PUGI__CHECK_ERROR
+#undef PUGI_IMPL_NO_INLINE
+#undef PUGI_IMPL_UNLIKELY
+#undef PUGI_IMPL_STATIC_ASSERT
+#undef PUGI_IMPL_DMC_VOLATILE
+#undef PUGI_IMPL_UNSIGNED_OVERFLOW
+#undef PUGI_IMPL_MSVC_CRT_VERSION
+#undef PUGI_IMPL_SNPRINTF
+#undef PUGI_IMPL_NS_BEGIN
+#undef PUGI_IMPL_NS_END
+#undef PUGI_IMPL_FN
+#undef PUGI_IMPL_FN_NO_INLINE
+#undef PUGI_IMPL_GETHEADER_IMPL
+#undef PUGI_IMPL_GETPAGE_IMPL
+#undef PUGI_IMPL_GETPAGE
+#undef PUGI_IMPL_NODETYPE
+#undef PUGI_IMPL_IS_CHARTYPE_IMPL
+#undef PUGI_IMPL_IS_CHARTYPE
+#undef PUGI_IMPL_IS_CHARTYPEX
+#undef PUGI_IMPL_ENDSWITH
+#undef PUGI_IMPL_SKIPWS
+#undef PUGI_IMPL_OPTSET
+#undef PUGI_IMPL_PUSHNODE
+#undef PUGI_IMPL_POPNODE
+#undef PUGI_IMPL_SCANFOR
+#undef PUGI_IMPL_SCANWHILE
+#undef PUGI_IMPL_SCANWHILE_UNROLL
+#undef PUGI_IMPL_ENDSEG
+#undef PUGI_IMPL_THROW_ERROR
+#undef PUGI_IMPL_CHECK_ERROR
 
 #endif
 
 /**
- * Copyright (c) 2006-2022 Arseny Kapoulkine
+ * Copyright (c) 2006-2023 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
  * obtaining a copy of this software and associated documentation
diff --git a/thirdparty/pugixml/pugixml/src/pugixml.hpp b/thirdparty/pugixml/pugixml/src/pugixml.hpp
index 579f143990..d17a7e6930 100644
--- a/thirdparty/pugixml/pugixml/src/pugixml.hpp
+++ b/thirdparty/pugixml/pugixml/src/pugixml.hpp
@@ -1,7 +1,7 @@
 /**
- * pugixml parser - version 1.12
+ * pugixml parser - version 1.14
  * --------------------------------------------------------
- * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
  * Report bugs and download new versions at https://pugixml.org/
  *
  * This library is distributed under the MIT License. See notice at the end
@@ -14,7 +14,7 @@
 // Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons
 // Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits
 #ifndef PUGIXML_VERSION
-#	define PUGIXML_VERSION 1120 // 1.12
+#	define PUGIXML_VERSION 1140 // 1.14
 #endif
 
 // Include user configuration file (this can define various configuration macros)
@@ -115,6 +115,8 @@
 #ifndef PUGIXML_NULL
 #	if __cplusplus >= 201103
 #		define PUGIXML_NULL nullptr
+#	elif defined(_MSC_VER) && _MSC_VER >= 1600
+#		define PUGIXML_NULL nullptr
 #	else
 #		define PUGIXML_NULL 0
 #	endif
@@ -210,6 +212,10 @@ namespace pugi
 	// the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments.
 	// This flag is off by default.
 	const unsigned int parse_embed_pcdata = 0x2000;
+	
+	// This flag determines whether determines whether the the two pcdata should be merged or not, if no intermediatory data are parsed in the document.
+	// This flag is off by default.
+	const unsigned int parse_merge_pcdata = 0x4000;
 
 	// The default parsing mode.
 	// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
@@ -322,7 +328,7 @@ namespace pugi
 	class PUGIXML_CLASS xml_writer
 	{
 	public:
-		virtual ~xml_writer() {}
+		virtual ~xml_writer();
 
 		// Write memory chunk into stream/file/whatever
 		virtual void write(const void* data, size_t size) = 0;
@@ -416,7 +422,9 @@ namespace pugi
 
 		// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
 		bool set_name(const char_t* rhs);
+		bool set_name(const char_t* rhs, size_t size);
 		bool set_value(const char_t* rhs);
+		bool set_value(const char_t* rhs, size_t size);
 
 		// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
 		bool set_value(int rhs);
@@ -550,7 +558,9 @@ namespace pugi
 
 		// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
 		bool set_name(const char_t* rhs);
+		bool set_name(const char_t* rhs, size_t size);
 		bool set_value(const char_t* rhs);
+		bool set_value(const char_t* rhs, size_t size);
 
 		// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
 		xml_attribute append_attribute(const char_t* name);
@@ -708,9 +718,12 @@ namespace pugi
 
 		// Range-based for support
 		xml_object_range<xml_node_iterator> children() const;
-		xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
 		xml_object_range<xml_attribute_iterator> attributes() const;
 
+		// Range-based for support for all children with the specified name
+		// Note: name pointer must have a longer lifetime than the returned object; be careful with passing temporaries!
+		xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
+
 		// Get node offset in parsed file/string (in char_t units) for debugging purposes
 		ptrdiff_t offset_debug() const;
 
@@ -776,6 +789,7 @@ namespace pugi
 
 		// Set text (returns false if object is empty or there is not enough memory)
 		bool set(const char_t* rhs);
+		bool set(const char_t* rhs, size_t size);
 
 		// Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
 		bool set(int rhs);
@@ -922,6 +936,7 @@ namespace pugi
 		xml_named_node_iterator();
 
 		// Construct an iterator which points to the specified node
+		// Note: name pointer is stored in the iterator and must have a longer lifetime than iterator itself
 		xml_named_node_iterator(const xml_node& node, const char_t* name);
 
 		// Iterator operators
@@ -1476,7 +1491,7 @@ namespace std
 #endif
 
 /**
- * Copyright (c) 2006-2022 Arseny Kapoulkine
+ * Copyright (c) 2006-2023 Arseny Kapoulkine
  *
  * Permission is hereby granted, free of charge, to any person
  * obtaining a copy of this software and associated documentation