From d6f7766172bd3dcd6b286888f5bdfdcb1953f3ba Mon Sep 17 00:00:00 2001
From: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Date: Sun, 12 Apr 2015 02:05:59 -0700
Subject: [PATCH] Optimize xml_node::path() to use 1 allocation

Instead of reallocating the string for every tree level just do two passes
over the ancestor chain.
---
 src/pugixml.cpp | 38 ++++++++++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 6b3e87e4..619cc7b7 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -4056,6 +4056,7 @@ PUGI__NS_BEGIN
 		return status_ok;
 	}
 
+	// This function assumes that buffer has extra sizeof(char_t) writable bytes after size
 	PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) 
 	{
 		// We only need to zero-terminate if encoding conversion does not do it for us
@@ -5328,20 +5329,35 @@ namespace pugi
 #ifndef PUGIXML_NO_STL
 	PUGI__FN string_t xml_node::path(char_t delimiter) const
 	{
-		xml_node cursor = *this; // Make a copy.
-		
-		string_t result = cursor.name();
+		if (!_root) return string_t();
+
+		size_t offset = 0;
 
-		while (cursor.parent())
+		for (xml_node_struct* i = _root; i; i = i->parent)
 		{
-			cursor = cursor.parent();
-			
-			string_t temp = cursor.name();
-			temp += delimiter;
-			temp += result;
-			result.swap(temp);
+			offset += (i != _root);
+			offset += i->name ? impl::strlength(i->name) : 0;
+		}
+
+		string_t result;
+		result.resize(offset);
+
+		for (xml_node_struct* j = _root; j; j = j->parent)
+		{
+			if (j != _root)
+				result[--offset] = delimiter;
+
+			if (j->name && *j->name)
+			{
+				size_t length = impl::strlength(j->name);
+
+				offset -= length;
+				memcpy(&result[offset], j->name, length * sizeof(char_t));
+			}
 		}
 
+		assert(offset == 0);
+
 		return result;
 	}
 #endif
@@ -6188,12 +6204,14 @@ namespace pugi
 	PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
+
 		return impl::save_file_impl(*this, file, indent, flags, encoding);
 	}
 
 	PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
 	{
 		FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
+
 		return impl::save_file_impl(*this, file, indent, flags, encoding);
 	}
 
-- 
GitLab