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.
@ -41,4 +41,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
OTHER DEALINGS IN THE SOFTWARE.

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,8 +4237,11 @@ PUGI__NS_BEGIN
{
xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
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);
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);
}
}
}
@ -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
@ -9041,7 +9059,7 @@ PUGI__NS_BEGIN
size_t size = ns.size() - first;
xpath_node* last = ns.begin() + first;
// remove_if... or well, sort of
for (xpath_node* it = last; it != ns.end(); ++it, ++i)
{
@ -9056,17 +9074,48 @@ PUGI__NS_BEGIN
if (once) break;
}
}
else if (expr->eval_boolean(c, stack))
else
{
*last++ = *it;
if (expr->eval_boolean(c, stack))
{
*last++ = *it;
if (once) break;
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);
}
void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
{
if (ns.size() == first) return;
@ -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;
@ -10130,27 +10191,33 @@ PUGI__NS_BEGIN
xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
// we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
rs.set_type(xpath_node_set::type_unsorted);
rs.append(ls.begin(), ls.end(), stack.result);
rs.remove_duplicates();
return rs;
}
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();
bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval);
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);
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)
copy.append_copy(doc.first_child());
xml_document copy;
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;