summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArseny Kapoulkine <arseny.kapoulkine@gmail.com>2015-05-14 08:01:03 -0700
committerArseny Kapoulkine <arseny.kapoulkine@gmail.com>2015-05-14 08:04:06 -0700
commitf828eae3eaca02c9659d63cbcab65c9dd8e13869 (patch)
tree71347971f8e7c857f49f2f7a2d4ecaef390e939f
parent01f627a4d75ae25850bc29df74112696da571326 (diff)
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.
-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