Implement non-recursive node output

This makes node output 3% 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@1014 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
Arseny Kapoulkine 2014-09-21 21:52:07 +00:00
parent 2f1fed4b9f
commit 4ed5972d4f

View File

@ -3269,6 +3269,12 @@ PUGI__NS_BEGIN
while (*s); while (*s);
} }
PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, unsigned int depth)
{
for (unsigned int i = 0; i < depth; ++i)
writer.write(indent);
}
PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags) PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
{ {
const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* default_name = PUGIXML_TEXT(":anonymous");
@ -3285,133 +3291,183 @@ PUGI__NS_BEGIN
} }
} }
PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth) PUGI__FN bool node_output_start(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
{ {
const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* default_name = PUGIXML_TEXT(":anonymous");
const char_t* name = node.name()[0] ? node.name() : default_name;
if ((flags & format_indent) != 0 && (flags & format_raw) == 0) writer.write('<');
for (unsigned int i = 0; i < depth; ++i) writer.write(indent); writer.write(name);
switch (node.type()) node_output_attributes(writer, node, flags);
if (flags & format_raw)
{ {
case node_document: if (!node.first_child())
{ writer.write(' ', '/', '>');
for (xml_node n = node.first_child(); n; n = n.next_sibling()) else
node_output(writer, n, indent, flags, depth);
break;
}
case node_element:
{
const char_t* name = node.name()[0] ? node.name() : default_name;
writer.write('<');
writer.write(name);
node_output_attributes(writer, node, flags);
if (flags & format_raw)
{
if (!node.first_child())
writer.write(' ', '/', '>');
else
{
writer.write('>');
for (xml_node n = node.first_child(); n; n = n.next_sibling())
node_output(writer, n, indent, flags, depth + 1);
writer.write('<', '/');
writer.write(name);
writer.write('>');
}
}
else if (!node.first_child())
writer.write(' ', '/', '>', '\n');
else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
{ {
writer.write('>'); writer.write('>');
if (node.first_child().type() == node_pcdata) return true;
text_output(writer, node.first_child().value(), ctx_special_pcdata, flags); }
else }
text_output_cdata(writer, node.first_child().value()); else if (!node.first_child())
writer.write(' ', '/', '>', '\n');
else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
{
writer.write('>');
writer.write('<', '/'); if (node.first_child().type() == node_pcdata)
writer.write(name); text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
writer.write('>', '\n'); else
text_output_cdata(writer, node.first_child().value());
writer.write('<', '/');
writer.write(name);
writer.write('>', '\n');
}
else
{
writer.write('>', '\n');
return true;
}
return false;
}
PUGI__FN void node_output_end(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
const char_t* name = node.name()[0] ? node.name() : default_name;
writer.write('<', '/');
writer.write(name);
writer.write('>');
if ((flags & format_raw) == 0)
writer.write('\n');
}
PUGI__FN void node_output_simple(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
switch (node.type())
{
case node_pcdata:
text_output(writer, node.value(), ctx_special_pcdata, flags);
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_cdata:
text_output_cdata(writer, node.value());
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_comment:
writer.write('<', '!', '-', '-');
writer.write(node.value());
writer.write('-', '-', '>');
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_pi:
case node_declaration:
writer.write('<', '?');
writer.write(node.name()[0] ? node.name() : default_name);
if (node.type() == node_declaration)
{
node_output_attributes(writer, node, flags);
}
else if (node.value()[0])
{
writer.write(' ');
writer.write(node.value());
}
writer.write('?', '>');
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_doctype:
writer.write('<', '!', 'D', 'O', 'C');
writer.write('T', 'Y', 'P', 'E');
if (node.value()[0])
{
writer.write(' ');
writer.write(node.value());
}
writer.write('>');
if ((flags & format_raw) == 0) writer.write('\n');
break;
default:
assert(!"Invalid node type");
}
}
PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& root, const char_t* indent, unsigned int flags, unsigned int depth)
{
xml_node node = root;
do
{
assert(node);
// begin writing current node
if ((flags & (format_indent | format_raw)) == format_indent)
text_output_indent(writer, indent, depth);
if (node.type() == node_element)
{
if (node_output_start(writer, node, flags))
{
node = node.first_child();
depth++;
continue;
}
}
else if (node.type() == node_document)
{
if (node.first_child())
{
node = node.first_child();
continue;
}
} }
else else
{ {
writer.write('>', '\n'); node_output_simple(writer, node, flags);
for (xml_node n = node.first_child(); n; n = n.next_sibling())
node_output(writer, n, indent, flags, depth + 1);
if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
writer.write('<', '/');
writer.write(name);
writer.write('>', '\n');
} }
break; // continue to the next node
} while (node != root)
{
case node_pcdata: if (node.next_sibling())
text_output(writer, node.value(), ctx_special_pcdata, flags); {
if ((flags & format_raw) == 0) writer.write('\n'); node = node.next_sibling();
break; break;
}
case node_cdata:
text_output_cdata(writer, node.value()); node = node.parent();
if ((flags & format_raw) == 0) writer.write('\n'); depth--;
break;
// write closing node
case node_comment: if (node.type() == node_element)
writer.write('<', '!', '-', '-'); {
writer.write(node.value()); if ((flags & (format_indent | format_raw)) == format_indent)
writer.write('-', '-', '>'); text_output_indent(writer, indent, depth);
if ((flags & format_raw) == 0) writer.write('\n');
break; node_output_end(writer, node, flags);
}
case node_pi: }
case node_declaration:
writer.write('<', '?');
writer.write(node.name()[0] ? node.name() : default_name);
if (node.type() == node_declaration)
{
node_output_attributes(writer, node, flags);
}
else if (node.value()[0])
{
writer.write(' ');
writer.write(node.value());
}
writer.write('?', '>');
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_doctype:
writer.write('<', '!', 'D', 'O', 'C');
writer.write('T', 'Y', 'P', 'E');
if (node.value()[0])
{
writer.write(' ');
writer.write(node.value());
}
writer.write('>');
if ((flags & format_raw) == 0) writer.write('\n');
break;
default:
assert(!"Invalid node type");
} }
while (node != root);
} }
inline bool has_declaration(const xml_node& node) inline bool has_declaration(const xml_node& node)