Implement xml_node::attribute with a hint

Extra argument 'hint' is used to start the attribute lookup; if the attribute
is not found the lookup is restarted from the beginning of the attriubte list.

This allows to optimize attribute lookups if you need to get many attributes
from the node and can make assumptions about the likely ordering. The code is
correct regardless of the order, but it is faster than using vanilla lookups
if the order matches the calling order.

Fixes #30.
This commit is contained in:
Arseny Kapoulkine 2015-05-14 08:01:03 -07:00
parent 01f627a4d7
commit f828eae3ea
3 changed files with 57 additions and 0 deletions

View File

@ -5431,6 +5431,39 @@ namespace pugi
return xml_node();
}
PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
{
xml_attribute_struct* hint = hint_._attr;
// if hint is not an attribute of node, behavior is not defined
assert(!hint || (_root && impl::is_attribute_of(hint, _root)));
if (!_root) return xml_attribute();
// optimistically search from hint up until the end
for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
if (i->name && impl::strequal(name_, i->name))
{
// update hint to maximize efficiency of searching for consecutive attributes
hint_._attr = i->next_attribute;
return xml_attribute(i);
}
// wrap around and search from the first attribute until the hint
// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
if (j->name && impl::strequal(name_, j->name))
{
// update hint to maximize efficiency of searching for consecutive attributes
hint_._attr = j->next_attribute;
return xml_attribute(j);
}
return xml_attribute();
}
PUGI__FN xml_node xml_node::previous_sibling() const
{
if (!_root) return xml_node();

View File

@ -466,6 +466,9 @@ namespace pugi
xml_node next_sibling(const char_t* name) const;
xml_node previous_sibling(const char_t* name) const;
// Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast)
xml_attribute attribute(const char_t* name, xml_attribute& hint) const;
// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
const char_t* child_value() const;

View File

@ -1130,3 +1130,24 @@ TEST_XML(dom_ranged_for, "<node attr1='1' attr2='2'><test>3</test><fake>5</fake>
CHECK(index == 5);
}
#endif
TEST_XML(dom_node_attribute_hinted, "<node attr1='1' attr2='2' attr3='3' />")
{
xml_node node = doc.first_child();
xml_attribute attr1 = node.attribute(STR("attr1"));
xml_attribute attr2 = node.attribute(STR("attr2"));
xml_attribute attr3 = node.attribute(STR("attr3"));
xml_attribute hint;
CHECK(!xml_node().attribute(STR("test"), hint) && !hint);
CHECK(node.attribute(STR("attr2"), hint) == attr2 && hint == attr3);
CHECK(node.attribute(STR("attr3"), hint) == attr3 && !hint);
CHECK(node.attribute(STR("attr1"), hint) == attr1 && hint == attr2);
CHECK(node.attribute(STR("attr2"), hint) == attr2 && hint == attr3);
CHECK(node.attribute(STR("attr1"), hint) == attr1 && hint == attr2);
CHECK(node.attribute(STR("attr1"), hint) == attr1 && hint == attr2);
CHECK(!node.attribute(STR("attr"), hint) && hint == attr2);
}