Merge branch 'master' into compact

This commit is contained in:
Arseny Kapoulkine 2015-03-10 20:44:06 -07:00
commit f81d7cc018
17 changed files with 230 additions and 51 deletions

View file

@ -30,7 +30,7 @@ all: $(EXECUTABLE)
ifeq ($(config),coverage)
test: $(EXECUTABLE)
@find $(BUILD) -name '*.gcda' | xargs -r rm
-@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

View file

@ -30,11 +30,11 @@ This is the complete manual for pugixml, which describes all features of the lib
[section:feedback Feedback]
If you believe you've found a bug in pugixml (bugs include compilation problems (errors/warnings), crashes, performance degradation and incorrect behavior), please file an issue via [@http://code.google.com/p/pugixml/issues/entry issue submission form]. Be sure to include the relevant information so that the bug can be reproduced: the version of pugixml, compiler version and target architecture, the code that uses pugixml and exhibits the bug, etc.
If you believe you've found a bug in pugixml (bugs include compilation problems (errors/warnings), crashes, performance degradation and incorrect behavior), please file an issue via [@https://github.com/zeux/pugixml/issues/new issue submission form]. Be sure to include the relevant information so that the bug can be reproduced: the version of pugixml, compiler version and target architecture, the code that uses pugixml and exhibits the bug, etc.
Feature requests can be reported the same way as bugs, so if you're missing some functionality in pugixml or if the API is rough in some places and you can suggest an improvement, [@http://code.google.com/p/pugixml/issues/entry?template=Feature%20request file an issue]. However please note that there are many factors when considering API changes (compatibility with previous versions, API redundancy, etc.), so generally features that can be implemented via a small function without pugixml modification are not accepted. However, all rules have exceptions.
Feature requests can be reported the same way as bugs, so if you're missing some functionality in pugixml or if the API is rough in some places and you can suggest an improvement, [@https://github.com/zeux/pugixml/issues/new file an issue]. However please note that there are many factors when considering API changes (compatibility with previous versions, API redundancy, etc.), so generally features that can be implemented via a small function without pugixml modification are not accepted. However, all rules have exceptions.
If you have a contribution to pugixml, such as build script for some build system/IDE, or a well-designed set of helper functions, or a binding to some language other than C++, please [@http://code.google.com/p/pugixml/issues/entry?template=Feature%20request file an issue]. You can include the relevant patches as issue attachments. Your contribution has to be distributed under the terms of a license that's compatible with pugixml license; i.e. GPL/LGPL licensed code is not accepted.
If you have a contribution to pugixml, such as build script for some build system/IDE, or a well-designed set of helper functions, or a binding to some language other than C++, please [@https://github.com/zeux/pugixml/issues/new file an issue]. You can include the relevant patches as issue attachments. Your contribution has to be distributed under the terms of a license that's compatible with pugixml license; i.e. GPL/LGPL licensed code is not accepted.
[#email]
If filing an issue is not possible due to privacy or other concerns, you can contact pugixml author by e-mail directly: [@mailto:arseny.kapoulkine@gmail.com arseny.kapoulkine@gmail.com].

View file

@ -219,7 +219,7 @@ While the previously described functions save the whole document to the destinat
[section:feedback Feedback]
If you believe you've found a bug in pugixml, please file an issue via [@http://code.google.com/p/pugixml/issues/entry issue submission form]. Be sure to include the relevant information so that the bug can be reproduced: the version of pugixml, compiler version and target architecture, the code that uses pugixml and exhibits the bug, etc. Feature requests and contributions can be filed as issues, too.
If you believe you've found a bug in pugixml, please file an issue via [@https://github.com/zeux/pugixml/issues/new issue submission form]. Be sure to include the relevant information so that the bug can be reproduced: the version of pugixml, compiler version and target architecture, the code that uses pugixml and exhibits the bug, etc. Feature requests and contributions can be filed as issues, too.
[#email]
If filing an issue is not possible due to privacy or other concerns, you can contact pugixml author by e-mail directly: [@mailto:arseny.kapoulkine@gmail.com arseny.kapoulkine@gmail.com].

View file

@ -11,7 +11,7 @@ struct xml_string_writer: pugi::xml_writer
virtual void write(const void* data, size_t size)
{
result += std::string(static_cast<const char*>(data), size);
result.append(static_cast<const char*>(data), size);
}
};
//]

View file

@ -559,7 +559,9 @@ PUGI__NS_BEGIN
char_t* allocate_string(size_t length)
{
PUGI__STATIC_ASSERT(xml_memory_page_size <= (1 << 16));
static const size_t max_encoded_offset = (1 << 16) * sizeof(void*);
PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
// allocate memory for string and header block
size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
@ -575,12 +577,14 @@ PUGI__NS_BEGIN
// setup header
ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
assert(page_offset >= 0 && page_offset < (1 << 16));
header->page_offset = static_cast<uint16_t>(page_offset);
assert(page_offset % sizeof(void*) == 0);
assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);
header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / sizeof(void*));
// full_size == 0 for large strings that occupy the whole page
assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);
assert(full_size % sizeof(void*) == 0);
assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));
header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / sizeof(void*) : 0);
// round-trip through void* to avoid 'cast increases required alignment of target type' warning
// header is guaranteed a pointer-sized alignment, which should be enough for char_t
@ -597,11 +601,11 @@ PUGI__NS_BEGIN
assert(header);
// deallocate
size_t page_offset = sizeof(xml_memory_page) + header->page_offset;
size_t page_offset = sizeof(xml_memory_page) + header->page_offset * sizeof(void*);
xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
// if full_size == 0 then this string occupies the whole page
size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * sizeof(void*);
deallocate_memory(header, full_size, page);
}
@ -2894,23 +2898,28 @@ PUGI__NS_BEGIN
char_t* parse_doctype_ignore(char_t* s)
{
size_t depth = 0;
assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
s++;
s += 3;
while (*s)
{
if (s[0] == '<' && s[1] == '!' && s[2] == '[')
{
// nested ignore section
s = parse_doctype_ignore(s);
if (!s) return s;
s += 3;
depth++;
}
else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
{
// ignore section end
s += 3;
return s;
if (depth == 0)
return s;
depth--;
}
else s++;
}
@ -2918,10 +2927,12 @@ PUGI__NS_BEGIN
PUGI__THROW_ERROR(status_bad_doctype, s);
}
char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
char_t* parse_doctype_group(char_t* s, char_t endch)
{
size_t depth = 0;
assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
s++;
s += 2;
while (*s)
{
@ -2936,12 +2947,8 @@ PUGI__NS_BEGIN
else
{
// some control group
s = parse_doctype_group(s, endch, false);
if (!s) return s;
// skip >
assert(*s == '>');
s++;
s += 2;
depth++;
}
}
else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
@ -2952,12 +2959,16 @@ PUGI__NS_BEGIN
}
else if (*s == '>')
{
return s;
if (depth == 0)
return s;
depth--;
s++;
}
else s++;
}
if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
return s;
}
@ -3049,7 +3060,7 @@ PUGI__NS_BEGIN
char_t* mark = s + 9;
s = parse_doctype_group(s, endch, true);
s = parse_doctype_group(s, endch);
if (!s) return s;
assert((*s == 0 && endch == '>') || *s == '>');
@ -3989,6 +4000,27 @@ PUGI__NS_BEGIN
writer.write('-', '-', '>');
}
PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
{
while (*s)
{
const char_t* prev = s;
// look for ?> sequence - we can't output it since ?> terminates PI
while (*s && !(s[0] == '?' && s[1] == '>')) ++s;
writer.write_buffer(prev, static_cast<size_t>(s - prev));
if (*s)
{
assert(s[0] == '?' && s[1] == '>');
writer.write('?', ' ', '>');
s += 2;
}
}
}
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");
@ -4102,7 +4134,7 @@ PUGI__NS_BEGIN
if (static_cast<xml_node_pi_struct*>(node)->pi_value)
{
writer.write(' ');
writer.write_string(static_cast<xml_node_pi_struct*>(node)->pi_value);
node_output_pi_value(writer, static_cast<xml_node_pi_struct*>(node)->pi_value);
}
writer.write('?', '>');
@ -4863,7 +4895,7 @@ PUGI__NS_BEGIN
PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)
{
// check input buffer
assert(contents || size == 0);
if (!contents && size) return make_parse_result(status_io_error);
// get actual encoding
xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
@ -8066,7 +8098,7 @@ PUGI__NS_BEGIN
return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());
}
PUGI__FN void normalize_space(char_t* buffer)
PUGI__FN char_t* normalize_space(char_t* buffer)
{
char_t* write = buffer;
@ -8090,9 +8122,11 @@ PUGI__NS_BEGIN
// zero-terminate
*write = 0;
return write;
}
PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
{
char_t* write = buffer;
@ -8110,6 +8144,8 @@ PUGI__NS_BEGIN
// zero-terminate
*write = 0;
return write;
}
PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
@ -8146,7 +8182,7 @@ PUGI__NS_BEGIN
return static_cast<unsigned char*>(result);
}
PUGI__FN void translate_table(char_t* buffer, const unsigned char* table)
PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
{
char_t* write = buffer;
@ -8172,6 +8208,8 @@ PUGI__NS_BEGIN
// zero-terminate
*write = 0;
return write;
}
inline bool is_xpath_attribute(const char_t* name)
@ -10278,18 +10316,20 @@ PUGI__NS_BEGIN
{
xpath_string s = string_value(c.n, stack.result);
normalize_space(s.data(stack.result));
char_t* begin = s.data(stack.result);
char_t* end = normalize_space(begin);
return s;
return xpath_string::from_heap_preallocated(begin, end);
}
case ast_func_normalize_space_1:
{
xpath_string s = _left->eval_string(c, stack);
normalize_space(s.data(stack.result));
char_t* begin = s.data(stack.result);
char_t* end = normalize_space(begin);
return s;
return xpath_string::from_heap_preallocated(begin, end);
}
case ast_func_translate:
@ -10302,18 +10342,20 @@ PUGI__NS_BEGIN
xpath_string from = _right->eval_string(c, swapped_stack);
xpath_string to = _right->_next->eval_string(c, swapped_stack);
translate(s.data(stack.result), from.c_str(), to.c_str(), to.length());
char_t* begin = s.data(stack.result);
char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
return s;
return xpath_string::from_heap_preallocated(begin, end);
}
case ast_opt_translate_table:
{
xpath_string s = _left->eval_string(c, stack);
translate_table(s.data(stack.result), _data.table);
char_t* begin = s.data(stack.result);
char_t* end = translate_table(begin, _data.table);
return s;
return xpath_string::from_heap_preallocated(begin, end);
}
case ast_variable:

View file

@ -66,6 +66,50 @@ namespace
VirtualProtect(rptr, aligned_size + PAGE_SIZE, PAGE_NOACCESS, &old_flags);
}
}
#elif defined(__APPLE__) || defined(__linux__)
# include <sys/mman.h>
namespace
{
const size_t PAGE_SIZE = 4096;
size_t align_to_page(size_t value)
{
return (value + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
}
void* allocate_page_aligned(size_t size)
{
return mmap(0, size + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
}
void* allocate(size_t size)
{
size_t aligned_size = align_to_page(size);
void* ptr = allocate_page_aligned(aligned_size + PAGE_SIZE);
if (!ptr) return 0;
char* end = static_cast<char*>(ptr) + aligned_size;
int res = mprotect(end, PAGE_SIZE, PROT_NONE);
assert(res == 0);
(void)!res;
return end - size;
}
void deallocate(void* ptr, size_t size)
{
size_t aligned_size = align_to_page(size);
void* rptr = static_cast<char*>(ptr) + size - aligned_size;
int res = mprotect(rptr, aligned_size + PAGE_SIZE, PROT_NONE);
assert(res == 0);
(void)!res;
}
}
#else
# include <stdlib.h>

View file

@ -65,8 +65,8 @@ if ($fast)
print "### autotest begin " . scalar localtime() . "\n";
# print SVN revision info
print "### autotest revision $1\n" if (`svn info` =~ /Revision:\s+(\d+)/);
# print Git revision info
print "### autotest revision $1\n" if (`git rev-parse HEAD` =~ /(.+)/);
# get CPU info
$cpucount = &getcpucount();

View file

@ -32,6 +32,6 @@ exit unless $client;
select $client;
&execprint('svn up') == 0 || die "error updating from repo\n";
&execprint('git pull') == 0 || die "error updating from repo\n";
&execprint('perl tests/autotest-local.pl') == 0 || die "error launching tests\n";
system($exitcmd);

View file

@ -128,7 +128,7 @@ while (<>)
$defines{$_} = 1 foreach (split /,/, $defineset);
&insertindex(\%configurations, $fullconf);
}
elsif (/^### autotest revision (\d+)/)
elsif (/^### autotest revision (.+)/)
{
if (defined $revision && $revision != $1)
{
@ -224,6 +224,6 @@ $date = localtime;
print <<END;
</table><br>
Generated on $date from Subversion r$revision
Generated on $date from Git $revision
</body></html>
END

View file

@ -71,6 +71,15 @@ bool test_double_nan(double value)
}
#ifndef PUGIXML_NO_XPATH
static size_t strlength(const pugi::char_t* s)
{
#ifdef PUGIXML_WCHAR_MODE
return wcslen(s);
#else
return strlen(s);
#endif
}
bool test_xpath_string(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, const pugi::char_t* expected)
{
pugi::xpath_query q(query, variables);
@ -81,7 +90,11 @@ bool test_xpath_string(const pugi::xpath_node& node, const pugi::char_t* query,
size_t size = q.evaluate_string(result, capacity, node);
if (size <= capacity) return test_string_equal(result, expected);
if (size != strlength(expected) + 1)
return false;
if (size <= capacity)
return test_string_equal(result, expected);
std::basic_string<pugi::char_t> buffer(size, ' ');

View file

@ -990,6 +990,17 @@ TEST(document_load_buffer_empty_fragment)
}
}
TEST(document_load_buffer_null)
{
xml_document doc;
CHECK(doc.load_buffer(0, 12).status == status_io_error && !doc.first_child());
CHECK(doc.load_buffer(0, 12, parse_fragment).status == status_io_error && !doc.first_child());
CHECK(doc.load_buffer_inplace(0, 12).status == status_io_error && !doc.first_child());
CHECK(doc.load_buffer_inplace_own(0, 12).status == status_io_error && !doc.first_child());
}
TEST(document_progressive_truncation)
{
char* original_data;

View file

@ -2,7 +2,8 @@
#include <limits>
#include <string>
#include <cmath>
#include <math.h>
TEST_XML(dom_attr_assign, "<node/>")
{
@ -1488,7 +1489,7 @@ TEST(dom_fp_roundtrip_float)
{
for (size_t i = 0; i < sizeof(fp_roundtrip_base) / sizeof(fp_roundtrip_base[0]); ++i)
{
float value = ldexpf(fp_roundtrip_base[i], e);
float value = ldexpf(static_cast<float>(fp_roundtrip_base[i]), e);
doc.text().set(value);
CHECK(doc.text().as_float() == value);

View file

@ -863,6 +863,7 @@ TEST(parse_declaration_error)
TEST(parse_empty)
{
xml_document doc;
CHECK(doc.load_string(STR("")).status == status_no_document_element && !doc.first_child());
CHECK(doc.load_string(STR(""), parse_fragment) && !doc.first_child());
}

View file

@ -322,3 +322,43 @@ TEST(parse_doctype_error_ignore)
CHECK(doc.load_string(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE[")).status == status_bad_doctype);
CHECK(doc.load_string(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE["), parse_doctype).status == status_bad_doctype);
}
TEST(parse_doctype_stackless_group)
{
std::basic_string<char_t> str;
int count = 100000;
str += STR("<!DOCTYPE ");
for (int i = 0; i < count; ++i)
str += STR("<!G ");
for (int j = 0; j < count; ++j)
str += STR(">");
str += STR(">");
xml_document doc;
CHECK(doc.load_string(str.c_str(), parse_fragment));
}
TEST(parse_doctype_stackless_ignore)
{
std::basic_string<char_t> str;
int count = 100000;
str += STR("<!DOCTYPE ");
for (int i = 0; i < count; ++i)
str += STR("<![IGNORE[ ");
for (int j = 0; j < count; ++j)
str += STR("]]>");
str += STR(">");
xml_document doc;
CHECK(doc.load_string(str.c_str(), parse_fragment));
}

View file

@ -115,6 +115,25 @@ TEST(write_pi_null)
CHECK_NODE(doc, STR("<?:anonymous value?>"));
}
TEST(write_pi_invalid)
{
xml_document doc;
xml_node node = doc.append_child(node_pi);
node.set_name(STR("test"));
node.set_value(STR("?"));
CHECK_NODE(doc, STR("<?test ?" "?>"));
node.set_value(STR("?>"));
CHECK_NODE(doc, STR("<?test ? >?>"));
node.set_value(STR("<?foo?>"));
CHECK_NODE(doc, STR("<?test <?foo? >?>"));
}
TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_declaration | parse_fragment)
{
CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>"));
@ -171,7 +190,7 @@ struct test_writer: xml_writer
virtual void write(const void* data, size_t size)
{
CHECK(size % sizeof(pugi::char_t) == 0);
contents += std::basic_string<pugi::char_t>(static_cast<const pugi::char_t*>(data), static_cast<const pugi::char_t*>(data) + size / sizeof(pugi::char_t));
contents.append(static_cast<const pugi::char_t*>(data), size / sizeof(pugi::char_t));
}
};

View file

@ -570,6 +570,14 @@ TEST(xpath_string_translate_table)
CHECK_XPATH_STRING(c, STR("translate('abcde', 'abcd', concat('ABC', 'D'))"), STR("ABCDe"));
}
TEST(xpath_string_translate_remove)
{
xml_node c;
CHECK_XPATH_STRING(c, STR("translate('000000755', '0', '')"), STR("755"));
CHECK_XPATH_STRING(c, STR("translate('000000755', concat('0', ''), '')"), STR("755"));
}
TEST_XML(xpath_nodeset_last, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>")
{
xml_node n = doc.child(STR("node"));

View file

@ -15,7 +15,7 @@ static bool test_narrow(const std::string& result, const char* expected, size_t
void xml_writer_string::write(const void* data, size_t size)
{
contents += std::string(static_cast<const char*>(data), size);
contents.append(static_cast<const char*>(data), size);
}
std::string xml_writer_string::as_narrow() const