diff options
| -rw-r--r-- | src/pugixml.cpp | 33 | ||||
| -rw-r--r-- | src/pugixml.hpp | 3 | ||||
| -rw-r--r-- | tests/test_dom_traverse.cpp | 21 | 
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 | 
