summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pugixml.cpp33
-rw-r--r--src/pugixml.hpp3
-rw-r--r--tests/test_dom_traverse.cpp21
3 files changed, 57 insertions, 0 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index b1e81ad..1f23b44 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -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();
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index 0f79fb7..cdd24b6 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -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;
diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp
index e4b8c44..8f96d93 100644
--- a/tests/test_dom_traverse.cpp
+++ b/tests/test_dom_traverse.cpp
@@ -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);
+} \ No newline at end of file