mirror of
https://github.com/zeux/pugixml.git
synced 2025-01-14 01:47:55 +08:00
Implement copy ctor/assignment for xpath_variable_set
xpath_variable_set is essentially an associative container; it's about time it became copyable. Implementation is slightly tricky due to out of memory handling. Both copy ctor and assignment operator have strong exception guarantee (even if exceptions are disabled! which translates to "roll back on allocation errors").
This commit is contained in:
parent
70a78b2fa5
commit
cbf3807ad4
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user