Merge branch 'master' into compact

This commit is contained in:
Arseny Kapoulkine 2014-10-28 20:11:06 -07:00
commit a49f932b61
17 changed files with 592 additions and 95 deletions

View File

@ -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)

View File

@ -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.
Youre 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.

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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>"));
}

View File

@ -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
}

View File

@ -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&amp;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"));
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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)

View File

@ -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"));

View File

@ -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'/>")

View File

@ -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;