Implement non-recursive node copying

This makes node copying 6% faster, prevents it from ever running out of
stack space and makes the profiling results more actionable for profilers
that can't merge information from recursive calls.

git-svn-id: https://pugixml.googlecode.com/svn/trunk@1027 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
Arseny Kapoulkine 2014-09-28 23:23:28 +00:00
parent 8e2aeb6f60
commit ddf6db3078

View File

@ -3602,29 +3602,19 @@ PUGI__NS_BEGIN
return true; return true;
} }
PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip) PUGI__FN void node_copy_contents(xml_node dest, xml_node source)
{ {
assert(dest.type() == source.type()); assert(dest.type() == source.type());
switch (source.type()) switch (source.type())
{ {
case node_element: case node_element:
case node_declaration:
{ {
dest.set_name(source.name()); dest.set_name(source.name());
for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
dest.append_attribute(a.name()).set_value(a.value()); dest.append_attribute(a.name()).set_value(a.value());
for (xml_node c = source.first_child(); c; c = c.next_sibling())
{
if (c == skip) continue;
xml_node cc = dest.append_child(c.type());
assert(cc);
recursive_copy_skip(cc, c, skip);
}
break; break;
} }
@ -3640,21 +3630,50 @@ PUGI__NS_BEGIN
dest.set_value(source.value()); dest.set_value(source.value());
break; break;
case node_declaration:
{
dest.set_name(source.name());
for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
dest.append_attribute(a.name()).set_value(a.value());
break;
}
default: default:
assert(!"Invalid node type"); assert(!"Invalid node type");
} }
} }
PUGI__FN void node_copy_tree(xml_node dest, xml_node source)
{
node_copy_contents(dest, source);
xml_node destit = dest;
xml_node sourceit = source.first_child();
while (sourceit && sourceit != source)
{
if (sourceit != dest)
{
xml_node copy = destit.append_child(sourceit.type());
node_copy_contents(copy, sourceit);
if (sourceit.first_child())
{
destit = copy;
sourceit = sourceit.first_child();
continue;
}
}
// continue to the next node
do
{
if (sourceit.next_sibling())
{
sourceit = sourceit.next_sibling();
break;
}
sourceit = sourceit.parent();
destit = destit.parent();
}
while (sourceit != source);
}
}
inline bool is_text_node(xml_node_struct* node) inline bool is_text_node(xml_node_struct* node)
{ {
xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1); xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
@ -4965,7 +4984,7 @@ namespace pugi
{ {
xml_node result = append_child(proto.type()); xml_node result = append_child(proto.type());
if (result) impl::recursive_copy_skip(result, proto, result); if (result) impl::node_copy_tree(result, proto);
return result; return result;
} }
@ -4974,7 +4993,7 @@ namespace pugi
{ {
xml_node result = prepend_child(proto.type()); xml_node result = prepend_child(proto.type());
if (result) impl::recursive_copy_skip(result, proto, result); if (result) impl::node_copy_tree(result, proto);
return result; return result;
} }
@ -4983,7 +5002,7 @@ namespace pugi
{ {
xml_node result = insert_child_after(proto.type(), node); xml_node result = insert_child_after(proto.type(), node);
if (result) impl::recursive_copy_skip(result, proto, result); if (result) impl::node_copy_tree(result, proto);
return result; return result;
} }
@ -4992,7 +5011,7 @@ namespace pugi
{ {
xml_node result = insert_child_before(proto.type(), node); xml_node result = insert_child_before(proto.type(), node);
if (result) impl::recursive_copy_skip(result, proto, result); if (result) impl::node_copy_tree(result, proto);
return result; return result;
} }