Merge branch 'master' into compact
This commit is contained in:
commit
a49f932b61
14
Makefile
14
Makefile
@ -13,6 +13,12 @@ ifeq ($(config),release)
|
||||
CXXFLAGS+=-O3 -DNDEBUG
|
||||
endif
|
||||
|
||||
ifeq ($(config),coverage)
|
||||
CXXFLAGS+=-DNDEBUG
|
||||
CXXFLAGS+=-fprofile-arcs -ftest-coverage
|
||||
LDFLAGS+=-fprofile-arcs
|
||||
endif
|
||||
|
||||
ifneq ($(defines),standard)
|
||||
COMMA=,
|
||||
CXXFLAGS+=-D $(subst $(COMMA), -D ,$(defines))
|
||||
@ -22,8 +28,16 @@ OBJECTS=$(SOURCES:%=$(BUILD)/%.o)
|
||||
|
||||
all: $(EXECUTABLE)
|
||||
|
||||
ifeq ($(config),coverage)
|
||||
test: $(EXECUTABLE)
|
||||
@find $(BUILD) -name '*.gcda' | xargs rm
|
||||
./$(EXECUTABLE)
|
||||
@gcov -b -c $(BUILD)/src/pugixml.cpp.gcda | sed -e '/./{H;$!d;}' -e 'x;/pugixml.cpp/!d;'
|
||||
@ls *.gcov | grep -v pugixml.cpp.gcov | xargs rm
|
||||
else
|
||||
test: $(EXECUTABLE)
|
||||
./$(EXECUTABLE)
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
|
||||
@ -12,8 +12,8 @@ pugixml is used by a lot of projects, both open-source and proprietary, for perf
|
||||
|
||||
Documentation for the current release of pugixml is available on-line as two separate documents:
|
||||
|
||||
* [Quick-start guide](http://pugixml.googlecode.com/svn/tags/latest/docs/quickstart.html), that aims to provide enough information to start using the library;
|
||||
* [Complete reference manual](http://pugixml.googlecode.com/svn/tags/latest/docs/manual.html), that describes all features of the library in detail.
|
||||
* [Quick-start guide](http://cdn.rawgit.com/zeux/pugixml/v1.4/docs/quickstart.html), that aims to provide enough information to start using the library;
|
||||
* [Complete reference manual](http://cdn.rawgit.com/zeux/pugixml/v1.4/docs/manual.html), that describes all features of the library in detail.
|
||||
|
||||
You’re advised to start with the quick-start guide; however, many important library features are either not described in it at all or only mentioned briefly; if you require more information you should read the complete manual.
|
||||
|
||||
|
||||
212
src/pugixml.cpp
212
src/pugixml.cpp
@ -1,4 +1,5 @@
|
||||
/**
|
||||
{
|
||||
* pugixml parser - version 1.4
|
||||
* --------------------------------------------------------
|
||||
* Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
@ -164,6 +165,8 @@ PUGI__NS_BEGIN
|
||||
static deallocation_function deallocate;
|
||||
};
|
||||
|
||||
// Global allocation functions are stored in class statics so that in header mode linker deduplicates them
|
||||
// Without a template<> we'll get multiple definitions of the same static
|
||||
template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
|
||||
template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
|
||||
|
||||
@ -1189,7 +1192,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
if (node->next_sibling)
|
||||
node->next_sibling->prev_sibling_c = node->prev_sibling_c;
|
||||
else if (parent->first_child)
|
||||
else
|
||||
parent->first_child->prev_sibling_c = node->prev_sibling_c;
|
||||
|
||||
if (node->prev_sibling_c->next_sibling)
|
||||
@ -1264,7 +1267,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
if (attr->next_attribute)
|
||||
attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
|
||||
else if (node->first_attribute)
|
||||
else
|
||||
node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
|
||||
|
||||
if (attr->prev_attribute_c->next_attribute)
|
||||
@ -3945,36 +3948,37 @@ PUGI__NS_BEGIN
|
||||
writer.write('-', '-', '>');
|
||||
}
|
||||
|
||||
PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
|
||||
PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
|
||||
{
|
||||
const char_t* default_name = PUGIXML_TEXT(":anonymous");
|
||||
|
||||
for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
|
||||
for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
|
||||
{
|
||||
writer.write(' ');
|
||||
writer.write_string(a.name()[0] ? a.name() : default_name);
|
||||
writer.write_string(a->name ? a->name : default_name);
|
||||
writer.write('=', '"');
|
||||
|
||||
text_output(writer, a.value(), ctx_special_attr, flags);
|
||||
if (a->value)
|
||||
text_output(writer, a->value, ctx_special_attr, flags);
|
||||
|
||||
writer.write('"');
|
||||
}
|
||||
}
|
||||
|
||||
PUGI__FN bool node_output_start(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
|
||||
PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
|
||||
{
|
||||
const char_t* default_name = PUGIXML_TEXT(":anonymous");
|
||||
const char_t* name = node.name()[0] ? node.name() : default_name;
|
||||
const char_t* name = node->name ? node->name : default_name;
|
||||
|
||||
writer.write('<');
|
||||
writer.write_string(name);
|
||||
|
||||
if (node.first_attribute())
|
||||
if (node->first_attribute)
|
||||
node_output_attributes(writer, node, flags);
|
||||
|
||||
if (flags & format_raw)
|
||||
{
|
||||
if (!node.first_child())
|
||||
if (!node->first_child)
|
||||
writer.write(' ', '/', '>');
|
||||
else
|
||||
{
|
||||
@ -3985,18 +3989,20 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
else
|
||||
{
|
||||
xml_node first = node.first_child();
|
||||
xml_node_struct* first = node->first_child;
|
||||
|
||||
if (!first)
|
||||
writer.write(' ', '/', '>', '\n');
|
||||
else if (!first.next_sibling() && (first.type() == node_pcdata || first.type() == node_cdata))
|
||||
else if (!first->next_sibling && (PUGI__NODETYPE(first) == node_pcdata || PUGI__NODETYPE(first) == node_cdata))
|
||||
{
|
||||
writer.write('>');
|
||||
|
||||
if (first.type() == node_pcdata)
|
||||
text_output(writer, first.value(), ctx_special_pcdata, flags);
|
||||
const char_t* value = first->value ? first->value : PUGIXML_TEXT("");
|
||||
|
||||
if (PUGI__NODETYPE(first) == node_pcdata)
|
||||
text_output(writer, value, ctx_special_pcdata, flags);
|
||||
else
|
||||
text_output_cdata(writer, first.value());
|
||||
text_output_cdata(writer, value);
|
||||
|
||||
writer.write('<', '/');
|
||||
writer.write_string(name);
|
||||
@ -4013,10 +4019,10 @@ PUGI__NS_BEGIN
|
||||
return false;
|
||||
}
|
||||
|
||||
PUGI__FN void node_output_end(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
|
||||
PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
|
||||
{
|
||||
const char_t* default_name = PUGIXML_TEXT(":anonymous");
|
||||
const char_t* name = node.name()[0] ? node.name() : default_name;
|
||||
const char_t* name = node->name ? node->name : default_name;
|
||||
|
||||
writer.write('<', '/');
|
||||
writer.write_string(name);
|
||||
@ -4027,35 +4033,35 @@ PUGI__NS_BEGIN
|
||||
writer.write('>', '\n');
|
||||
}
|
||||
|
||||
PUGI__FN void node_output_simple(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
|
||||
PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
|
||||
{
|
||||
const char_t* default_name = PUGIXML_TEXT(":anonymous");
|
||||
|
||||
switch (node.type())
|
||||
switch (PUGI__NODETYPE(node))
|
||||
{
|
||||
case node_pcdata:
|
||||
text_output(writer, node.value(), ctx_special_pcdata, flags);
|
||||
text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_cdata:
|
||||
text_output_cdata(writer, node.value());
|
||||
text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT(""));
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_comment:
|
||||
node_output_comment(writer, node.value());
|
||||
node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT(""));
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
break;
|
||||
|
||||
case node_pi:
|
||||
writer.write('<', '?');
|
||||
writer.write_string(node.name()[0] ? node.name() : default_name);
|
||||
writer.write_string(node->name ? node->name : default_name);
|
||||
|
||||
if (node.value()[0])
|
||||
if (node->value)
|
||||
{
|
||||
writer.write(' ');
|
||||
writer.write_string(node.value());
|
||||
writer.write_string(node->value);
|
||||
}
|
||||
|
||||
writer.write('?', '>');
|
||||
@ -4064,7 +4070,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
case node_declaration:
|
||||
writer.write('<', '?');
|
||||
writer.write_string(node.name()[0] ? node.name() : default_name);
|
||||
writer.write_string(node->name ? node->name : default_name);
|
||||
node_output_attributes(writer, node, flags);
|
||||
writer.write('?', '>');
|
||||
if ((flags & format_raw) == 0) writer.write('\n');
|
||||
@ -4074,10 +4080,10 @@ PUGI__NS_BEGIN
|
||||
writer.write('<', '!', 'D', 'O', 'C');
|
||||
writer.write('T', 'Y', 'P', 'E');
|
||||
|
||||
if (node.value()[0])
|
||||
if (node->value)
|
||||
{
|
||||
writer.write(' ');
|
||||
writer.write_string(node.value());
|
||||
writer.write_string(node->value);
|
||||
}
|
||||
|
||||
writer.write('>');
|
||||
@ -4089,11 +4095,11 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node root, const char_t* indent, unsigned int flags, unsigned int depth)
|
||||
PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
|
||||
{
|
||||
size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0;
|
||||
|
||||
xml_node node = root;
|
||||
xml_node_struct* node = root;
|
||||
|
||||
do
|
||||
{
|
||||
@ -4103,20 +4109,20 @@ PUGI__NS_BEGIN
|
||||
if (indent_length)
|
||||
text_output_indent(writer, indent, indent_length, depth);
|
||||
|
||||
if (node.type() == node_element)
|
||||
if (PUGI__NODETYPE(node) == node_element)
|
||||
{
|
||||
if (node_output_start(writer, node, flags))
|
||||
{
|
||||
node = node.first_child();
|
||||
node = node->first_child;
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (node.type() == node_document)
|
||||
else if (PUGI__NODETYPE(node) == node_document)
|
||||
{
|
||||
if (node.first_child())
|
||||
if (node->first_child)
|
||||
{
|
||||
node = node.first_child();
|
||||
node = node->first_child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -4128,17 +4134,17 @@ PUGI__NS_BEGIN
|
||||
// continue to the next node
|
||||
while (node != root)
|
||||
{
|
||||
if (node.next_sibling())
|
||||
if (node->next_sibling)
|
||||
{
|
||||
node = node.next_sibling();
|
||||
node = node->next_sibling;
|
||||
break;
|
||||
}
|
||||
|
||||
node = node.parent();
|
||||
node = node->parent;
|
||||
depth--;
|
||||
|
||||
// write closing node
|
||||
if (node.type() == node_element)
|
||||
if (PUGI__NODETYPE(node) == node_element)
|
||||
{
|
||||
if (indent_length)
|
||||
text_output_indent(writer, indent, indent_length, depth);
|
||||
@ -4231,10 +4237,13 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
|
||||
|
||||
if (da)
|
||||
{
|
||||
node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
|
||||
node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)
|
||||
{
|
||||
@ -5936,7 +5945,7 @@ namespace pugi
|
||||
|
||||
impl::xml_buffered_writer buffered_writer(writer, encoding);
|
||||
|
||||
impl::node_output(buffered_writer, *this, indent, flags, depth);
|
||||
impl::node_output(buffered_writer, _root, indent, flags, depth);
|
||||
}
|
||||
|
||||
#ifndef PUGIXML_NO_STL
|
||||
@ -6621,7 +6630,7 @@ namespace pugi
|
||||
if (!(flags & format_raw)) buffered_writer.write('\n');
|
||||
}
|
||||
|
||||
impl::node_output(buffered_writer, *this, indent, flags, 0);
|
||||
impl::node_output(buffered_writer, _root, indent, flags, 0);
|
||||
}
|
||||
|
||||
#ifndef PUGIXML_NO_STL
|
||||
@ -8746,7 +8755,6 @@ PUGI__NS_BEGIN
|
||||
ast_op_union, // left | right
|
||||
ast_predicate, // apply predicate to set; next points to next predicate
|
||||
ast_filter, // select * from left where right
|
||||
ast_filter_posinv, // select * from left where right; proximity position invariant
|
||||
ast_string_constant, // string constant
|
||||
ast_number_constant, // number constant
|
||||
ast_variable, // variable
|
||||
@ -8822,6 +8830,14 @@ PUGI__NS_BEGIN
|
||||
nodetest_all_in_namespace
|
||||
};
|
||||
|
||||
enum predicate_t
|
||||
{
|
||||
predicate_default,
|
||||
predicate_posinv,
|
||||
predicate_constant,
|
||||
predicate_constant_one
|
||||
};
|
||||
|
||||
enum nodeset_eval_t
|
||||
{
|
||||
nodeset_eval_all,
|
||||
@ -8843,8 +8859,10 @@ PUGI__NS_BEGIN
|
||||
char _type;
|
||||
char _rettype;
|
||||
|
||||
// for ast_step / ast_predicate
|
||||
// for ast_step
|
||||
char _axis;
|
||||
|
||||
// for ast_step/ast_predicate/ast_filter
|
||||
char _test;
|
||||
|
||||
// tree node structure
|
||||
@ -9056,13 +9074,44 @@ PUGI__NS_BEGIN
|
||||
if (once) break;
|
||||
}
|
||||
}
|
||||
else if (expr->eval_boolean(c, stack))
|
||||
else
|
||||
{
|
||||
if (expr->eval_boolean(c, stack))
|
||||
{
|
||||
*last++ = *it;
|
||||
|
||||
if (once) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ns.truncate(last);
|
||||
}
|
||||
|
||||
static void apply_predicate_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
|
||||
{
|
||||
assert(ns.size() >= first);
|
||||
assert(expr->rettype() == xpath_type_number);
|
||||
|
||||
size_t size = ns.size() - first;
|
||||
|
||||
xpath_node* last = ns.begin() + first;
|
||||
|
||||
xpath_context c(xpath_node(), 1, size);
|
||||
|
||||
double er = expr->eval_number(c, stack);
|
||||
|
||||
if (er >= 1.0 && er <= size)
|
||||
{
|
||||
size_t eri = static_cast<size_t>(er);
|
||||
|
||||
if (er == eri)
|
||||
{
|
||||
xpath_node r = last[eri - 1];
|
||||
|
||||
*last++ = r;
|
||||
}
|
||||
}
|
||||
|
||||
ns.truncate(last);
|
||||
}
|
||||
@ -9075,7 +9124,12 @@ PUGI__NS_BEGIN
|
||||
|
||||
for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
|
||||
{
|
||||
apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once);
|
||||
assert(pred->_type == ast_predicate);
|
||||
|
||||
if (pred->_test == predicate_constant || pred->_test == predicate_constant_one)
|
||||
apply_predicate_const(ns, first, pred->_right, stack);
|
||||
else
|
||||
apply_predicate(ns, first, pred->_right, stack, !pred->_next && last_once);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9485,9 +9539,9 @@ PUGI__NS_BEGIN
|
||||
bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
|
||||
|
||||
bool once =
|
||||
(axis == axis_attribute && _test == nodetest_name)
|
||||
? true
|
||||
: !_right && eval_once(!axis_reverse, eval);
|
||||
(axis == axis_attribute && _test == nodetest_name) ||
|
||||
(!_right && eval_once(!axis_reverse, eval)) ||
|
||||
(_right && !_right->_next && _right->_test == predicate_constant_one);
|
||||
|
||||
xpath_node_set_raw ns;
|
||||
ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
|
||||
@ -9554,9 +9608,16 @@ PUGI__NS_BEGIN
|
||||
xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):
|
||||
_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
|
||||
{
|
||||
assert(type == ast_step);
|
||||
_data.nodetest = contents;
|
||||
}
|
||||
|
||||
xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):
|
||||
_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
|
||||
{
|
||||
assert(type == ast_filter || type == ast_predicate);
|
||||
}
|
||||
|
||||
void set_next(xpath_ast_node* value)
|
||||
{
|
||||
_next = value;
|
||||
@ -10141,16 +10202,22 @@ PUGI__NS_BEGIN
|
||||
}
|
||||
|
||||
case ast_filter:
|
||||
case ast_filter_posinv:
|
||||
{
|
||||
xpath_node_set_raw set = _left->eval_node_set(c, stack, nodeset_eval_all);
|
||||
xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);
|
||||
|
||||
// either expression is a number or it contains position() call; sort by document order
|
||||
if (_type == ast_filter) set.sort_do();
|
||||
if (_test != predicate_posinv) set.sort_do();
|
||||
|
||||
if (_test == predicate_constant || _test == predicate_constant_one)
|
||||
{
|
||||
apply_predicate_const(set, 0, _right, stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval);
|
||||
|
||||
apply_predicate(set, 0, _right, stack, once);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
@ -10253,10 +10320,31 @@ PUGI__NS_BEGIN
|
||||
if (_right) _right->optimize(alloc);
|
||||
if (_next) _next->optimize(alloc);
|
||||
|
||||
// Replace descendant-or-self::node()/child::foo with descendant::foo
|
||||
// Rewrite [position()=expr] with [expr]
|
||||
// Note that this step has to go before classification to recognize [position()=1]
|
||||
if ((_type == ast_filter || _type == ast_predicate) &&
|
||||
_right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
|
||||
{
|
||||
_right = _right->_right;
|
||||
}
|
||||
|
||||
// Classify filter/predicate ops to perform various optimizations during evaluation
|
||||
if (_type == ast_filter || _type == ast_predicate)
|
||||
{
|
||||
assert(_test == predicate_default);
|
||||
|
||||
if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
|
||||
_test = predicate_constant_one;
|
||||
else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
|
||||
_test = predicate_constant;
|
||||
else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
|
||||
_test = predicate_posinv;
|
||||
}
|
||||
|
||||
// Rewrite descendant-or-self::node()/child::foo with descendant::foo
|
||||
// The former is a full form of //foo, the latter is much faster since it executes the node test immediately
|
||||
// Do a similar kind of replacement for self/descendant/descendant-or-self axes
|
||||
// Note that we only replace positionally invariant steps (//foo[1] != /descendant::foo[1])
|
||||
// Do a similar kind of rewrite for self/descendant/descendant-or-self axes
|
||||
// Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
|
||||
if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
|
||||
_left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
|
||||
is_posinv_step())
|
||||
@ -10269,7 +10357,7 @@ PUGI__NS_BEGIN
|
||||
_left = _left->_left;
|
||||
}
|
||||
|
||||
// Replace translate() with constant arguments with a table
|
||||
// Use optimized lookup table implementation for translate() with constant arguments
|
||||
if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
|
||||
{
|
||||
unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
|
||||
@ -10284,7 +10372,7 @@ PUGI__NS_BEGIN
|
||||
// Use optimized path for @attr = 'value' or @attr = $value
|
||||
if (_type == ast_op_equal &&
|
||||
_left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
|
||||
(_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->rettype() == xpath_type_string)))
|
||||
(_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
|
||||
{
|
||||
_type = ast_opt_compare_attribute;
|
||||
}
|
||||
@ -10309,7 +10397,6 @@ PUGI__NS_BEGIN
|
||||
|
||||
case ast_predicate:
|
||||
case ast_filter:
|
||||
case ast_filter_posinv:
|
||||
return true;
|
||||
|
||||
default:
|
||||
@ -10330,10 +10417,7 @@ PUGI__NS_BEGIN
|
||||
{
|
||||
assert(n->_type == ast_predicate);
|
||||
|
||||
xpath_ast_node* expr = n->_left;
|
||||
bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr();
|
||||
|
||||
if (!posinv)
|
||||
if (n->_test != predicate_posinv)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -10753,9 +10837,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
|
||||
|
||||
bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr();
|
||||
|
||||
n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
|
||||
n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default);
|
||||
|
||||
if (_lexer.current() != lex_close_square_brace)
|
||||
throw_error("Unmatched square brace");
|
||||
@ -10899,7 +10981,7 @@ PUGI__NS_BEGIN
|
||||
|
||||
xpath_ast_node* expr = parse_expression();
|
||||
|
||||
xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);
|
||||
xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default);
|
||||
|
||||
if (_lexer.current() != lex_close_square_brace)
|
||||
throw_error("Unmatched square brace");
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
git svn init https://pugixml.googlecode.com/svn/trunk
|
||||
git update-ref refs/remotes/git-svn refs/remotes/origin/master
|
||||
git svn rebase
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
test_runner* test_runner::_tests = 0;
|
||||
size_t test_runner::_memory_fail_threshold = 0;
|
||||
bool test_runner::_memory_fail_triggered = false;
|
||||
jmp_buf test_runner::_failure_buffer;
|
||||
const char* test_runner::_failure_message;
|
||||
const char* test_runner::_temp_path;
|
||||
@ -30,7 +31,11 @@ static size_t g_memory_total_count = 0;
|
||||
static void* custom_allocate(size_t size)
|
||||
{
|
||||
if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size)
|
||||
{
|
||||
test_runner::_memory_fail_triggered = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* ptr = memory_allocate(size);
|
||||
@ -84,6 +89,7 @@ static bool run_test(test_runner* test)
|
||||
g_memory_total_size = 0;
|
||||
g_memory_total_count = 0;
|
||||
test_runner::_memory_fail_threshold = 0;
|
||||
test_runner::_memory_fail_triggered = false;
|
||||
|
||||
pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ struct test_runner
|
||||
|
||||
static test_runner* _tests;
|
||||
static size_t _memory_fail_threshold;
|
||||
static bool _memory_fail_triggered;
|
||||
static jmp_buf _failure_buffer;
|
||||
static const char* _failure_message;
|
||||
|
||||
|
||||
@ -227,6 +227,34 @@ TEST(document_load_stream_nonseekable_large)
|
||||
CHECK(doc.load(in));
|
||||
CHECK_NODE(doc, str.c_str());
|
||||
}
|
||||
|
||||
TEST(document_load_stream_nonseekable_out_of_memory)
|
||||
{
|
||||
char contents[] = "<node />";
|
||||
char_array_buffer<char> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
|
||||
std::istream in(&buffer);
|
||||
|
||||
test_runner::_memory_fail_threshold = 1;
|
||||
|
||||
pugi::xml_document doc;
|
||||
CHECK(doc.load(in).status == status_out_of_memory);
|
||||
}
|
||||
|
||||
TEST(document_load_stream_nonseekable_out_of_memory_large)
|
||||
{
|
||||
std::basic_string<pugi::char_t> str;
|
||||
str += STR("<node>");
|
||||
for (int i = 0; i < 10000; ++i) str += STR("<node />");
|
||||
str += STR("</node>");
|
||||
|
||||
char_array_buffer<pugi::char_t> buffer(&str[0], &str[0] + str.length());
|
||||
std::basic_istream<pugi::char_t> in(&buffer);
|
||||
|
||||
test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2;
|
||||
|
||||
pugi::xml_document doc;
|
||||
CHECK(doc.load(in).status == status_out_of_memory);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(document_load_string)
|
||||
@ -295,6 +323,17 @@ TEST(document_load_file_wide_ascii)
|
||||
CHECK_NODE(doc, STR("<node />"));
|
||||
}
|
||||
|
||||
TEST(document_load_file_wide_out_of_memory)
|
||||
{
|
||||
test_runner::_memory_fail_threshold = 1;
|
||||
|
||||
pugi::xml_document doc;
|
||||
|
||||
pugi::xml_parse_result result = doc.load_file(L"tests/data/small.xml");
|
||||
|
||||
CHECK(result.status == status_out_of_memory || result.status == status_file_not_found);
|
||||
}
|
||||
|
||||
TEST_XML(document_save, "<node/>")
|
||||
{
|
||||
xml_writer_string writer;
|
||||
@ -1235,3 +1274,37 @@ TEST(document_alignment)
|
||||
doc->~xml_document();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(document_convert_out_of_memory)
|
||||
{
|
||||
file_data_t files[] =
|
||||
{
|
||||
{"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0},
|
||||
{"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0},
|
||||
{"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0},
|
||||
{"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0},
|
||||
{"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0},
|
||||
{"tests/data/latintest_latin1.xml", encoding_latin1, 0, 0}
|
||||
};
|
||||
|
||||
// load files in memory
|
||||
for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
|
||||
{
|
||||
CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
|
||||
}
|
||||
|
||||
// disallow allocations
|
||||
test_runner::_memory_fail_threshold = 1;
|
||||
|
||||
for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
|
||||
{
|
||||
xml_document doc;
|
||||
CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default, files[src].encoding).status == status_out_of_memory);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
|
||||
{
|
||||
delete[] files[j].data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,16 @@ TEST_XML(dom_attr_assign, "<node/>")
|
||||
CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />"));
|
||||
}
|
||||
|
||||
TEST_XML(dom_attr_set_name, "<node attr='value' />")
|
||||
{
|
||||
xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));
|
||||
|
||||
CHECK(attr.set_name(STR("n")));
|
||||
CHECK(!xml_attribute().set_name(STR("n")));
|
||||
|
||||
CHECK_NODE(doc, STR("<node n=\"value\" />"));
|
||||
}
|
||||
|
||||
TEST_XML(dom_attr_set_value, "<node/>")
|
||||
{
|
||||
xml_node node = doc.child(STR("node"));
|
||||
@ -758,8 +768,9 @@ TEST(dom_node_declaration_name)
|
||||
|
||||
doc.insert_child_after(node_declaration, doc.first_child());
|
||||
doc.insert_child_before(node_declaration, doc.first_child());
|
||||
doc.prepend_child(node_declaration);
|
||||
|
||||
CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?>"));
|
||||
CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?><?xml?>"));
|
||||
}
|
||||
|
||||
TEST(dom_node_declaration_attributes)
|
||||
@ -867,17 +878,21 @@ TEST(dom_node_out_of_memory)
|
||||
|
||||
// verify all node modification operations
|
||||
CHECK(!n.append_child());
|
||||
CHECK(!n.prepend_child());
|
||||
CHECK(!n.insert_child_after(node_element, n.first_child()));
|
||||
CHECK(!n.insert_child_before(node_element, n.first_child()));
|
||||
CHECK(!n.append_attribute(STR("")));
|
||||
CHECK(!n.prepend_attribute(STR("")));
|
||||
CHECK(!n.insert_attribute_after(STR(""), a));
|
||||
CHECK(!n.insert_attribute_before(STR(""), a));
|
||||
|
||||
// verify node copy operations
|
||||
CHECK(!n.append_copy(n.first_child()));
|
||||
CHECK(!n.prepend_copy(n.first_child()));
|
||||
CHECK(!n.insert_copy_after(n.first_child(), n.first_child()));
|
||||
CHECK(!n.insert_copy_before(n.first_child(), n.first_child()));
|
||||
CHECK(!n.append_copy(a));
|
||||
CHECK(!n.prepend_copy(a));
|
||||
CHECK(!n.insert_copy_after(a, a));
|
||||
CHECK(!n.insert_copy_before(a, a));
|
||||
}
|
||||
@ -1346,11 +1361,55 @@ TEST_XML(dom_node_copyless_taint, "<node attr=\"value\" />")
|
||||
CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"valu2\" /><node att3=\"value\" />"));
|
||||
}
|
||||
|
||||
TEST_XML(dom_node_copy_out_of_memory, "<node><child1 attr1='value1' attr2='value2' /><child2 /><child3>text1<child4 />text2</child3></node>")
|
||||
TEST_XML(dom_node_copy_out_of_memory_node, "<node><child1 /><child2 /><child3>text1<child4 />text2</child3></node>")
|
||||
{
|
||||
test_runner::_memory_fail_threshold = 32768 * 2 + 4096;
|
||||
|
||||
xml_document copy;
|
||||
for (int i = 0; i < 100; ++i)
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
copy.append_copy(doc.first_child());
|
||||
}
|
||||
|
||||
TEST_XML(dom_node_copy_out_of_memory_attr, "<node attr1='' attr2='' attr3='' attr4='' attr5='' attr6='' attr7='' attr8='' attr9='' attr10='' attr11='' attr12='' attr13='' attr14='' attr15='' />")
|
||||
{
|
||||
test_runner::_memory_fail_threshold = 32768 * 2 + 4096;
|
||||
|
||||
xml_document copy;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
copy.append_copy(doc.first_child());
|
||||
}
|
||||
|
||||
TEST_XML(dom_node_remove_deallocate, "<node attr='value'>text</node>")
|
||||
{
|
||||
xml_node node = doc.child(STR("node"));
|
||||
|
||||
xml_attribute attr = node.attribute(STR("attr"));
|
||||
attr.set_name(STR("longattr"));
|
||||
attr.set_value(STR("longvalue"));
|
||||
|
||||
node.set_name(STR("longnode"));
|
||||
node.text().set(STR("longtext"));
|
||||
|
||||
node.remove_attribute(attr);
|
||||
doc.remove_child(node);
|
||||
|
||||
CHECK_NODE(doc, STR(""));
|
||||
}
|
||||
|
||||
TEST_XML(dom_node_set_deallocate, "<node attr='value'>text</node>")
|
||||
{
|
||||
xml_node node = doc.child(STR("node"));
|
||||
|
||||
xml_attribute attr = node.attribute(STR("attr"));
|
||||
|
||||
attr.set_name(STR("longattr"));
|
||||
attr.set_value(STR("longvalue"));
|
||||
node.set_name(STR("longnode"));
|
||||
|
||||
attr.set_name(STR(""));
|
||||
attr.set_value(STR(""));
|
||||
node.set_name(STR(""));
|
||||
node.text().set(STR(""));
|
||||
|
||||
CHECK_NODE(doc, STR("<:anonymous :anonymous=\"\"></:anonymous>"));
|
||||
}
|
||||
|
||||
@ -1048,14 +1048,14 @@ TEST_XML(dom_unspecified_bool_coverage, "<node attr='value'>text</node>")
|
||||
{
|
||||
xml_node node = doc.first_child();
|
||||
|
||||
node(0);
|
||||
node.first_attribute()(0);
|
||||
node.text()(0);
|
||||
static_cast<void (*)(xml_node***)>(node)(0);
|
||||
static_cast<void (*)(xml_attribute***)>(node.first_attribute())(0);
|
||||
static_cast<void (*)(xml_text***)>(node.text())(0);
|
||||
|
||||
#ifndef PUGIXML_NO_XPATH
|
||||
xpath_query q(STR("/node"));
|
||||
|
||||
q(0);
|
||||
q.evaluate_node(doc)(0);
|
||||
static_cast<void (*)(xpath_query***)>(q)(0);
|
||||
static_cast<void (*)(xpath_node***)>(q.evaluate_node(doc))(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -876,7 +876,7 @@ TEST(parse_out_of_memory)
|
||||
CHECK(!doc.first_child());
|
||||
}
|
||||
|
||||
TEST(parse_out_of_memory_halfway)
|
||||
TEST(parse_out_of_memory_halfway_node)
|
||||
{
|
||||
const unsigned int count = 10000;
|
||||
static char_t text[count * 4];
|
||||
@ -896,6 +896,35 @@ TEST(parse_out_of_memory_halfway)
|
||||
CHECK_NODE(doc.first_child(), STR("<n />"));
|
||||
}
|
||||
|
||||
TEST(parse_out_of_memory_halfway_attr)
|
||||
{
|
||||
const unsigned int count = 10000;
|
||||
static char_t text[count * 5 + 4];
|
||||
|
||||
text[0] = '<';
|
||||
text[1] = 'n';
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
text[5*i + 2] = ' ';
|
||||
text[5*i + 3] = 'a';
|
||||
text[5*i + 4] = '=';
|
||||
text[5*i + 5] = '"';
|
||||
text[5*i + 6] = '"';
|
||||
}
|
||||
|
||||
text[5 * count + 2] = '/';
|
||||
text[5 * count + 3] = '>';
|
||||
|
||||
test_runner::_memory_fail_threshold = 65536;
|
||||
|
||||
xml_document doc;
|
||||
CHECK(doc.load_buffer_inplace(text, count * 5 + 4).status == status_out_of_memory);
|
||||
CHECK_STRING(doc.first_child().name(), STR("n"));
|
||||
CHECK_STRING(doc.first_child().first_attribute().name(), STR("a"));
|
||||
CHECK_STRING(doc.first_child().last_attribute().name(), STR("a"));
|
||||
}
|
||||
|
||||
static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset)
|
||||
{
|
||||
xml_document doc;
|
||||
@ -1039,3 +1068,25 @@ TEST(parse_pcdata_gap_fragment)
|
||||
CHECK(doc.load(STR("a&b"), parse_fragment | parse_escapes));
|
||||
CHECK_STRING(doc.text().get(), STR("a&b"));
|
||||
}
|
||||
|
||||
TEST(parse_name_end_eof)
|
||||
{
|
||||
char_t test[] = STR("<node>");
|
||||
|
||||
xml_document doc;
|
||||
CHECK(doc.load_buffer_inplace(test, 6 * sizeof(char_t)).status == status_end_element_mismatch);
|
||||
CHECK_STRING(doc.first_child().name(), STR("node"));
|
||||
}
|
||||
|
||||
TEST(parse_close_tag_eof)
|
||||
{
|
||||
char_t test1[] = STR("<node></node");
|
||||
char_t test2[] = STR("<node></nodx");
|
||||
|
||||
xml_document doc;
|
||||
CHECK(doc.load_buffer_inplace(test1, 12 * sizeof(char_t)).status == status_bad_end_element);
|
||||
CHECK_STRING(doc.first_child().name(), STR("node"));
|
||||
|
||||
CHECK(doc.load_buffer_inplace(test2, 12 * sizeof(char_t)).status == status_end_element_mismatch);
|
||||
CHECK_STRING(doc.first_child().name(), STR("node"));
|
||||
}
|
||||
|
||||
@ -313,3 +313,12 @@ TEST(parse_doctype_error_toplevel)
|
||||
CHECK(doc.load(STR("<node><!DOCTYPE></node>")).status == status_bad_doctype);
|
||||
CHECK(doc.load(STR("<node><!DOCTYPE></node>"), parse_doctype).status == status_bad_doctype);
|
||||
}
|
||||
|
||||
TEST(parse_doctype_error_ignore)
|
||||
{
|
||||
xml_document doc;
|
||||
CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ ")).status == status_bad_doctype);
|
||||
CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ "), parse_doctype).status == status_bad_doctype);
|
||||
CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE[")).status == status_bad_doctype);
|
||||
CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE["), parse_doctype).status == status_bad_doctype);
|
||||
}
|
||||
|
||||
@ -51,6 +51,15 @@ TEST_XML(write_cdata_inner, "<node><![CDATA[value]]></node>")
|
||||
CHECK_NODE_EX(doc, STR("<node><![CDATA[value]]></node>\n"), STR(""), 0);
|
||||
}
|
||||
|
||||
TEST(write_cdata_null)
|
||||
{
|
||||
xml_document doc;
|
||||
doc.append_child(node_cdata);
|
||||
doc.append_child(STR("node")).append_child(node_cdata);
|
||||
|
||||
CHECK_NODE(doc, STR("<![CDATA[]]><node><![CDATA[]]></node>"));
|
||||
}
|
||||
|
||||
TEST_XML_FLAGS(write_comment, "<!--text-->", parse_comments | parse_fragment)
|
||||
{
|
||||
CHECK_NODE(doc, STR("<!--text-->"));
|
||||
@ -80,12 +89,32 @@ TEST(write_comment_invalid)
|
||||
CHECK_NODE(doc, STR("<!--- ->- -->"));
|
||||
}
|
||||
|
||||
TEST(write_comment_null)
|
||||
{
|
||||
xml_document doc;
|
||||
doc.append_child(node_comment);
|
||||
|
||||
CHECK_NODE(doc, STR("<!---->"));
|
||||
}
|
||||
|
||||
TEST_XML_FLAGS(write_pi, "<?name value?>", parse_pi | parse_fragment)
|
||||
{
|
||||
CHECK_NODE(doc, STR("<?name value?>"));
|
||||
CHECK_NODE_EX(doc, STR("<?name value?>\n"), STR(""), 0);
|
||||
}
|
||||
|
||||
TEST(write_pi_null)
|
||||
{
|
||||
xml_document doc;
|
||||
xml_node node = doc.append_child(node_pi);
|
||||
|
||||
CHECK_NODE(doc, STR("<?:anonymous?>"));
|
||||
|
||||
node.set_value(STR("value"));
|
||||
|
||||
CHECK_NODE(doc, STR("<?:anonymous value?>"));
|
||||
}
|
||||
|
||||
TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_declaration | parse_fragment)
|
||||
{
|
||||
CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>"));
|
||||
@ -98,6 +127,14 @@ TEST_XML_FLAGS(write_doctype, "<!DOCTYPE id [ foo ]>", parse_doctype | parse_fra
|
||||
CHECK_NODE_EX(doc, STR("<!DOCTYPE id [ foo ]>\n"), STR(""), 0);
|
||||
}
|
||||
|
||||
TEST(write_doctype_null)
|
||||
{
|
||||
xml_document doc;
|
||||
doc.append_child(node_doctype);
|
||||
|
||||
CHECK_NODE(doc, STR("<!DOCTYPE>"));
|
||||
}
|
||||
|
||||
TEST_XML(write_escape, "<node attr=''>text</node>")
|
||||
{
|
||||
doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t");
|
||||
@ -460,3 +497,16 @@ TEST_XML(write_indent_custom, "<node attr='1'><child><sub>text</sub></child></no
|
||||
CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCD<child>\nABCDABCD<sub>text</sub>\nABCD</child>\n</node>\n"), STR("ABCD"), format_indent);
|
||||
CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCDE<child>\nABCDEABCDE<sub>text</sub>\nABCDE</child>\n</node>\n"), STR("ABCDE"), format_indent);
|
||||
}
|
||||
|
||||
TEST(write_pcdata_null)
|
||||
{
|
||||
xml_document doc;
|
||||
doc.append_child(STR("node")).append_child(node_pcdata);
|
||||
|
||||
CHECK_NODE(doc, STR("<node></node>"));
|
||||
CHECK_NODE_EX(doc, STR("<node></node>\n"), STR("\t"), format_indent);
|
||||
|
||||
doc.first_child().append_child(node_pcdata);
|
||||
|
||||
CHECK_NODE_EX(doc, STR("<node>\n\t\n\t\n</node>\n"), STR("\t"), format_indent);
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ TEST_XML(xpath_sort_attributes, "<node/>")
|
||||
n.append_attribute(STR("attr3"));
|
||||
n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2")));
|
||||
|
||||
xpath_node_set ns = n.select_nodes(STR("@*"));
|
||||
xpath_node_set ns = n.select_nodes(STR("@* | @*"));
|
||||
|
||||
ns.sort(true);
|
||||
xpath_node_set reverse_sorted = ns;
|
||||
@ -122,6 +122,25 @@ TEST_XML(xpath_sort_attributes, "<node/>")
|
||||
xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_sort_attributes_docorder, "<node attr1='' attr2='value' attr4='value' />")
|
||||
{
|
||||
xml_node n = doc.child(STR("node"));
|
||||
|
||||
n.first_attribute().set_name(STR("attribute1"));
|
||||
n.insert_attribute_after(STR("attr3"), n.attribute(STR("attr2")));
|
||||
|
||||
xpath_node_set ns = n.select_nodes(STR("@* | @*"));
|
||||
|
||||
ns.sort(true);
|
||||
xpath_node_set reverse_sorted = ns;
|
||||
|
||||
ns.sort(false);
|
||||
xpath_node_set sorted = ns;
|
||||
|
||||
xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5 % 6;
|
||||
xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 6 % 5 % 4 % 3;
|
||||
}
|
||||
|
||||
TEST(xpath_sort_random_medium)
|
||||
{
|
||||
xml_document doc;
|
||||
@ -606,4 +625,49 @@ TEST(xpath_sort_crossdoc_different_depth)
|
||||
CHECK(ns.size() == 2);
|
||||
CHECK((ns[0] == ns1[0] && ns[1] == ns2[0]) || (ns[0] == ns2[0] && ns[1] == ns1[0]));
|
||||
}
|
||||
|
||||
TEST(xpath_allocate_string_out_of_memory)
|
||||
{
|
||||
std::basic_string<char_t> query;
|
||||
|
||||
for (int i = 0; i < 1024; ++i) query += STR("abcdefgh");
|
||||
|
||||
test_runner::_memory_fail_threshold = 8*1024;
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
CHECK(!xpath_query(query.c_str()));
|
||||
#else
|
||||
try
|
||||
{
|
||||
xpath_query q(query.c_str());
|
||||
|
||||
CHECK_FORCE_FAIL("Expected out of memory exception");
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(xpath_remove_duplicates)
|
||||
{
|
||||
xml_document doc;
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
doc.append_child(STR("node2"));
|
||||
doc.prepend_child(STR("node1"));
|
||||
}
|
||||
|
||||
xpath_node_set ns = doc.select_nodes(STR("/node2/preceding::* | //node1 | /node() | /* | /node1/following-sibling::*"));
|
||||
|
||||
ns.sort();
|
||||
|
||||
{
|
||||
xpath_node_set_tester tester(ns, "sorted order failed");
|
||||
|
||||
for (int i = 0; i < 40; ++i)
|
||||
tester % (2 + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -128,6 +128,11 @@ TEST_XML(xpath_api_nodeset_copy, "<node><foo/><foo/></node>")
|
||||
copy4 = copy1;
|
||||
CHECK(copy4.size() == 2);
|
||||
CHECK_STRING(copy4[0].node().name(), STR("foo"));
|
||||
|
||||
xpath_node_set copy5;
|
||||
copy5 = set;
|
||||
copy5 = xpath_node_set();
|
||||
CHECK(copy5.size() == 0);
|
||||
}
|
||||
|
||||
TEST(xpath_api_nodeset_copy_empty)
|
||||
|
||||
@ -216,7 +216,7 @@ TEST(xpath_boolean_false)
|
||||
CHECK_XPATH_FAIL(STR("false(1)"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subchild/></child></node><foo><bar/></foo>")
|
||||
TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subchild attr=''/></child></node><foo><bar/></foo>")
|
||||
{
|
||||
xml_node c;
|
||||
|
||||
@ -244,6 +244,9 @@ TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subch
|
||||
CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('r')"), false);
|
||||
CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('en')"), false);
|
||||
|
||||
// lang with 1 attribute argument
|
||||
CHECK_XPATH_NODESET(doc, STR("//@*[lang('en')]"));
|
||||
|
||||
// lang with 2 arguments
|
||||
CHECK_XPATH_FAIL(STR("lang(1, 2)"));
|
||||
}
|
||||
@ -773,6 +776,16 @@ TEST_XML_FLAGS(xpath_string_value, "<node><c1>pcdata</c1><c2><child/></c2><c3 at
|
||||
CHECK_XPATH_STRING(n, STR("string(c6/node())"), STR("cdata"));
|
||||
}
|
||||
|
||||
TEST(xpath_string_value_empty)
|
||||
{
|
||||
xml_document doc;
|
||||
doc.append_child(node_pcdata).set_value(STR("head"));
|
||||
doc.append_child(node_pcdata);
|
||||
doc.append_child(node_pcdata).set_value(STR("tail"));
|
||||
|
||||
CHECK_XPATH_STRING(doc, STR("string()"), STR("headtail"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_string_concat_translate, "<node>foobar</node>")
|
||||
{
|
||||
CHECK_XPATH_STRING(doc, STR("concat('a', 'b', 'c', translate(node, 'o', 'a'), 'd')"), STR("abcfaabard"));
|
||||
|
||||
@ -437,6 +437,52 @@ TEST_XML(xpath_paths_predicate_number, "<node><chapter/><chapter/><chapter/><cha
|
||||
CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[2]")) % 3;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_predicate_number_boundary, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[0.999999999999999]"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[1]")) % 3;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[1.000000000000001]"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[1.999999999999999]"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[2]")) % 4;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[2.000000000000001]"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[4.999999999999999]"));
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[5]")) % 7;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[5.000000000000001]"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_predicate_number_out_of_range, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
|
||||
{
|
||||
xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
|
||||
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1000000000000]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1 div 0]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1000000000000]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1 div 0]"));
|
||||
CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0 div 0]"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_predicate_constant_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
|
||||
{
|
||||
xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
|
||||
|
||||
xpath_variable_set set;
|
||||
set.set(STR("true"), true);
|
||||
set.set(STR("false"), false);
|
||||
|
||||
CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$false]"), &set);
|
||||
CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$true]"), &set) % 6 % 7;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_predicate_position_eq, "<node><chapter/><chapter/><chapter>3</chapter><chapter/><chapter/></node>")
|
||||
{
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=1]")) % 3;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=2+2]")) % 7;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=last()]")) % 8;
|
||||
CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=string()]")) % 5;
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_predicate_several, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
|
||||
{
|
||||
xml_node n = doc.child(STR("node"));
|
||||
@ -615,6 +661,9 @@ TEST_XML(xpath_paths_optimize_step_once, "<node><para1><para2/><para3/><para4><p
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//@attr5/following::para6"), true);
|
||||
CHECK_XPATH_STRING(doc, STR("name(//@attr5/following::para6)"), STR("para6"));
|
||||
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//para5/ancestor-or-self::*"), true);
|
||||
CHECK_XPATH_BOOLEAN(doc, STR("//para5/ancestor::*"), true);
|
||||
}
|
||||
|
||||
TEST_XML(xpath_paths_null_nodeset_entries, "<node attr='value'/>")
|
||||
|
||||
@ -88,6 +88,9 @@ TEST(xpath_variables_type_string)
|
||||
CHECK_DOUBLE_NAN(var->get_number());
|
||||
CHECK_STRING(var->get_string(), STR("abc"));
|
||||
CHECK(var->get_node_set().empty());
|
||||
|
||||
CHECK(var->set(STR("abcdef")));
|
||||
CHECK_STRING(var->get_string(), STR("abcdef"));
|
||||
}
|
||||
|
||||
TEST_XML(xpath_variables_type_node_set, "<node/>")
|
||||
@ -273,6 +276,29 @@ TEST(xpath_variables_long_name)
|
||||
CHECK_XPATH_BOOLEAN_VAR(xml_node(), STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set, true);
|
||||
}
|
||||
|
||||
TEST(xpath_variables_long_name_out_of_memory)
|
||||
{
|
||||
xpath_variable_set set;
|
||||
set.set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), true);
|
||||
|
||||
test_runner::_memory_fail_threshold = 4096 + 64 + 52 * sizeof(char_t);
|
||||
|
||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||
xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set);
|
||||
CHECK(!q);
|
||||
#else
|
||||
try
|
||||
{
|
||||
xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set);
|
||||
|
||||
CHECK_FORCE_FAIL("Expected exception");
|
||||
}
|
||||
catch (const xpath_exception&)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_XML(xpath_variables_select, "<node attr='1'/><node attr='2'/>")
|
||||
{
|
||||
xpath_variable_set set;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user