diff options
| -rw-r--r-- | src/pugixml.cpp | 94 | ||||
| -rw-r--r-- | src/pugixml.hpp | 13 | ||||
| -rw-r--r-- | tests/test_xpath_variables.cpp | 60 | 
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
  | 
