summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pugixml.cpp94
-rw-r--r--src/pugixml.hpp13
-rw-r--r--tests/test_xpath_variables.cpp60
3 files changed, 160 insertions, 7 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 078a39c..ce47fce 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -7758,6 +7758,28 @@ PUGI__NS_BEGIN
}
}
+ PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
+ {
+ switch (rhs->type())
+ {
+ case xpath_type_node_set:
+ return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);
+
+ case xpath_type_number:
+ return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);
+
+ case xpath_type_string:
+ return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);
+
+ case xpath_type_boolean:
+ return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
+
+ default:
+ assert(!"Invalid variable type");
+ return false;
+ }
+ }
+
PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result)
{
size_t length = static_cast<size_t>(end - begin);
@@ -11277,7 +11299,46 @@ namespace pugi
}
}
- PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const
+ PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)
+ {
+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+ _data[i] = 0;
+
+ _assign(rhs);
+ }
+
+ PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
+ {
+ if (this == &rhs) return *this;
+
+ _assign(rhs);
+
+ return *this;
+ }
+
+ PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
+ {
+ xpath_variable_set temp;
+
+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+ if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))
+ return;
+
+ _swap(temp);
+ }
+
+ PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
+ {
+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+ {
+ xpath_variable* chain = _data[i];
+
+ _data[i] = rhs._data[i];
+ rhs._data[i] = chain;
+ }
+ }
+
+ PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
{
const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
size_t hash = impl::hash_string(name) % hash_size;
@@ -11290,6 +11351,33 @@ namespace pugi
return 0;
}
+ PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
+ {
+ xpath_variable* last = 0;
+
+ while (var)
+ {
+ // allocate storage for new variable
+ xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());
+ if (!nvar) return false;
+
+ // link the variable to the result immediately to handle failures gracefully
+ if (last)
+ last->_next = nvar;
+ else
+ *out_result = nvar;
+
+ last = nvar;
+
+ // copy the value; this can fail due to out-of-memory conditions
+ if (!impl::copy_xpath_variable(nvar, var)) return false;
+
+ var = var->_next;
+ }
+
+ return true;
+ }
+
PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
{
const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
@@ -11339,12 +11427,12 @@ namespace pugi
PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
{
- return find(name);
+ return _find(name);
}
PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
{
- return find(name);
+ return _find(name);
}
PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index a7154f1..b8b8946 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -1075,17 +1075,22 @@ namespace pugi
private:
xpath_variable* _data[64];
- // Non-copyable semantics
- xpath_variable_set(const xpath_variable_set&);
- xpath_variable_set& operator=(const xpath_variable_set&);
+ void _assign(const xpath_variable_set& rhs);
+ void _swap(xpath_variable_set& rhs);
+
+ xpath_variable* _find(const char_t* name) const;
- xpath_variable* find(const char_t* name) const;
+ static bool _clone(xpath_variable* var, xpath_variable** out_result);
public:
// Default constructor/destructor
xpath_variable_set();
~xpath_variable_set();
+ // Copy constructor/assignment operator
+ xpath_variable_set(const xpath_variable_set& rhs);
+ xpath_variable_set& operator=(const xpath_variable_set& rhs);
+
// Add a new variable or get the existing one, if the types match
xpath_variable* add(const char_t* name, xpath_value_type type);
diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp
index 0d33312..b2ebc47 100644
--- a/tests/test_xpath_variables.cpp
+++ b/tests/test_xpath_variables.cpp
@@ -413,4 +413,64 @@ TEST_XML(xpath_variables_count_sum, "<node><c1>12</c1><c2>23</c2><c3>34</c3></no
CHECK_XPATH_NUMBER_VAR(xml_node(), STR("sum($c12) * count($c) - sum($c3)"), &set, 71);
}
+
+TEST_XML(xpath_variables_copy, "<node />")
+{
+ xpath_variable_set set1;
+ set1.set(STR("a"), true);
+ set1.set(STR("b"), 2.0);
+ set1.set(STR("c"), STR("string"));
+ set1.set(STR("d"), doc.select_nodes(STR("//*")));
+
+ CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set1, STR("ring"));
+
+ xpath_variable_set set2 = set1;
+
+ CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring"));
+
+ xpath_variable_set set3;
+
+ CHECK(!set3.get(STR("a")));
+
+ set3 = set1;
+
+ CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring"));
+
+ set3 = set3;
+
+ CHECK_XPATH_STRING_VAR(xml_node(), STR("substring($c, count($d[$a]) + $b)"), &set2, STR("ring"));
+
+ set3 = xpath_variable_set();
+
+ CHECK(!set3.get(STR("a")));
+}
+
+TEST_XML(xpath_variables_copy_out_of_memory, "<node />")
+{
+ xpath_variable_set set1;
+ set1.set(STR("a"), true);
+ set1.set(STR("b"), 2.0);
+ set1.set(STR("c"), STR("string"));
+ set1.set(STR("d"), doc.select_nodes(STR("//*")));
+
+ xpath_variable_set set2 = set1;
+
+ test_runner::_memory_fail_threshold = 32768 + 75 * sizeof(void*);
+
+ CHECK_ALLOC_FAIL(xpath_variable_set set3 = set1);
+
+ xpath_variable_set set4;
+
+ CHECK_ALLOC_FAIL(set4 = set1);
+ CHECK(!set4.get(STR("a")) && !set4.get(STR("b")) && !set4.get(STR("c")) && !set4.get(STR("d")));
+
+ CHECK_ALLOC_FAIL(set2 = set1);
+
+ CHECK(set2.get(STR("a")) && set2.get(STR("b")) && set2.get(STR("c")) && set2.get(STR("d")));
+
+ CHECK(set2.get(STR("a"))->get_boolean() == true);
+ CHECK(set2.get(STR("b"))->get_number() == 2.0);
+ CHECK_STRING(set2.get(STR("c"))->get_string(), STR("string"));
+ CHECK(set2.get(STR("d"))->get_node_set().size() == 1);
+}
#endif