Merge branch 'master' into compact
This commit is contained in:
commit
054b0b447e
@ -497,7 +497,7 @@ allocation_function get_memory_allocation_function();
|
|||||||
deallocation_function get_memory_deallocation_function();
|
deallocation_function get_memory_deallocation_function();
|
||||||
----
|
----
|
||||||
|
|
||||||
Allocation function is called with the size (in bytes) as an argument and should return a pointer to a memory block with alignment that is suitable for storage of primitive types (usually a maximum of `void*` and `double` types alignment is sufficient) and size that is greater than or equal to the requested one. If the allocation fails, the function has to return null pointer (throwing an exception from allocation function results in undefined behavior).
|
Allocation function is called with the size (in bytes) as an argument and should return a pointer to a memory block with alignment that is suitable for storage of primitive types (usually a maximum of `void*` and `double` types alignment is sufficient) and size that is greater than or equal to the requested one. If the allocation fails, the function has to either return null pointer or to throw an exception.
|
||||||
|
|
||||||
Deallocation function is called with the pointer that was returned by some call to allocation function; it is never called with a null pointer. If memory management functions are not thread-safe, library thread safety is not guaranteed.
|
Deallocation function is called with the pointer that was returned by some call to allocation function; it is never called with a null pointer. If memory management functions are not thread-safe, library thread safety is not guaranteed.
|
||||||
|
|
||||||
|
128
src/pugixml.cpp
128
src/pugixml.cpp
@ -233,25 +233,25 @@ PUGI__NS_BEGIN
|
|||||||
PUGI__NS_END
|
PUGI__NS_END
|
||||||
|
|
||||||
#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
|
#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
|
||||||
// auto_ptr-like buffer holder for exception recovery
|
// auto_ptr-like object for exception recovery
|
||||||
PUGI__NS_BEGIN
|
PUGI__NS_BEGIN
|
||||||
struct buffer_holder
|
template <typename T, typename D = void(*)(T*)> struct auto_deleter
|
||||||
{
|
{
|
||||||
void* data;
|
T* data;
|
||||||
void (*deleter)(void*);
|
D deleter;
|
||||||
|
|
||||||
buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
|
auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~buffer_holder()
|
~auto_deleter()
|
||||||
{
|
{
|
||||||
if (data) deleter(data);
|
if (data) deleter(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* release()
|
T* release()
|
||||||
{
|
{
|
||||||
void* result = data;
|
T* result = data;
|
||||||
data = 0;
|
data = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -2856,13 +2856,19 @@ PUGI__NS_BEGIN
|
|||||||
struct xml_parser
|
struct xml_parser
|
||||||
{
|
{
|
||||||
xml_allocator alloc;
|
xml_allocator alloc;
|
||||||
|
xml_allocator* alloc_state;
|
||||||
char_t* error_offset;
|
char_t* error_offset;
|
||||||
xml_parse_status error_status;
|
xml_parse_status error_status;
|
||||||
|
|
||||||
xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
|
xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~xml_parser()
|
||||||
|
{
|
||||||
|
*alloc_state = alloc;
|
||||||
|
}
|
||||||
|
|
||||||
// DOCTYPE consists of nested sections of the following possible types:
|
// DOCTYPE consists of nested sections of the following possible types:
|
||||||
// <!-- ... -->, <? ... ?>, "...", '...'
|
// <!-- ... -->, <? ... ?>, "...", '...'
|
||||||
// <![...]]>
|
// <![...]]>
|
||||||
@ -3438,9 +3444,6 @@ PUGI__NS_BEGIN
|
|||||||
|
|
||||||
static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
|
static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
|
||||||
{
|
{
|
||||||
// allocator object is a part of document object
|
|
||||||
xml_allocator& alloc_ = *static_cast<xml_allocator*>(xmldoc);
|
|
||||||
|
|
||||||
// early-out for empty documents
|
// early-out for empty documents
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
|
return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
|
||||||
@ -3449,7 +3452,7 @@ PUGI__NS_BEGIN
|
|||||||
xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
|
xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
|
||||||
|
|
||||||
// create parser on stack
|
// create parser on stack
|
||||||
xml_parser parser(alloc_);
|
xml_parser parser(static_cast<xml_allocator*>(xmldoc));
|
||||||
|
|
||||||
// save last character and make buffer zero-terminated (speeds up parsing)
|
// save last character and make buffer zero-terminated (speeds up parsing)
|
||||||
char_t endch = buffer[length - 1];
|
char_t endch = buffer[length - 1];
|
||||||
@ -3461,9 +3464,6 @@ PUGI__NS_BEGIN
|
|||||||
// perform actual parsing
|
// perform actual parsing
|
||||||
parser.parse_tree(buffer_data, root, optmsk, endch);
|
parser.parse_tree(buffer_data, root, optmsk, endch);
|
||||||
|
|
||||||
// update allocator state
|
|
||||||
alloc_ = parser.alloc;
|
|
||||||
|
|
||||||
xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
|
xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
|
||||||
assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
|
assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
|
||||||
|
|
||||||
@ -4661,27 +4661,16 @@ PUGI__NS_BEGIN
|
|||||||
// get file size (can result in I/O errors)
|
// get file size (can result in I/O errors)
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
xml_parse_status size_status = get_file_size(file, size);
|
xml_parse_status size_status = get_file_size(file, size);
|
||||||
|
if (size_status != status_ok) return make_parse_result(size_status);
|
||||||
if (size_status != status_ok)
|
|
||||||
{
|
|
||||||
fclose(file);
|
|
||||||
return make_parse_result(size_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t max_suffix_size = sizeof(char_t);
|
size_t max_suffix_size = sizeof(char_t);
|
||||||
|
|
||||||
// allocate buffer for the whole file
|
// allocate buffer for the whole file
|
||||||
char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
|
char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
|
||||||
|
if (!contents) return make_parse_result(status_out_of_memory);
|
||||||
if (!contents)
|
|
||||||
{
|
|
||||||
fclose(file);
|
|
||||||
return make_parse_result(status_out_of_memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// read file in memory
|
// read file in memory
|
||||||
size_t read_size = fread(contents, 1, size, file);
|
size_t read_size = fread(contents, 1, size, file);
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
if (read_size != size)
|
if (read_size != size)
|
||||||
{
|
{
|
||||||
@ -4704,10 +4693,8 @@ PUGI__NS_BEGIN
|
|||||||
return new (memory) xml_stream_chunk();
|
return new (memory) xml_stream_chunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy(void* ptr)
|
static void destroy(xml_stream_chunk* chunk)
|
||||||
{
|
{
|
||||||
xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);
|
|
||||||
|
|
||||||
// free chunk chain
|
// free chunk chain
|
||||||
while (chunk)
|
while (chunk)
|
||||||
{
|
{
|
||||||
@ -4731,7 +4718,7 @@ PUGI__NS_BEGIN
|
|||||||
|
|
||||||
template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
|
template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
|
||||||
{
|
{
|
||||||
buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
|
auto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);
|
||||||
|
|
||||||
// read file to a chunk list
|
// read file to a chunk list
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
@ -4767,7 +4754,7 @@ PUGI__NS_BEGIN
|
|||||||
|
|
||||||
char* write = buffer;
|
char* write = buffer;
|
||||||
|
|
||||||
for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)
|
for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)
|
||||||
{
|
{
|
||||||
assert(write + chunk->size <= buffer + total);
|
assert(write + chunk->size <= buffer + total);
|
||||||
memcpy(write, chunk->data, chunk->size);
|
memcpy(write, chunk->data, chunk->size);
|
||||||
@ -4801,7 +4788,7 @@ PUGI__NS_BEGIN
|
|||||||
size_t max_suffix_size = sizeof(char_t);
|
size_t max_suffix_size = sizeof(char_t);
|
||||||
|
|
||||||
// read stream data into memory (guard against stream exceptions with buffer holder)
|
// read stream data into memory (guard against stream exceptions with buffer holder)
|
||||||
buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
|
auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
|
||||||
if (!buffer.data) return status_out_of_memory;
|
if (!buffer.data) return status_out_of_memory;
|
||||||
|
|
||||||
stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
|
stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
|
||||||
@ -4899,11 +4886,7 @@ PUGI__NS_BEGIN
|
|||||||
xml_writer_file writer(file);
|
xml_writer_file writer(file);
|
||||||
doc.save(writer, indent, flags, encoding);
|
doc.save(writer, indent, flags, encoding);
|
||||||
|
|
||||||
int result = ferror(file);
|
return ferror(file) == 0;
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return result == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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)
|
||||||
@ -4923,6 +4906,9 @@ PUGI__NS_BEGIN
|
|||||||
// delete original buffer if we performed a conversion
|
// delete original buffer if we performed a conversion
|
||||||
if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
|
if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
|
||||||
|
|
||||||
|
// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
|
||||||
|
if (own || buffer != contents) *out_buffer = buffer;
|
||||||
|
|
||||||
// store buffer for offset_debug
|
// store buffer for offset_debug
|
||||||
doc->buffer = buffer;
|
doc->buffer = buffer;
|
||||||
|
|
||||||
@ -4932,9 +4918,6 @@ PUGI__NS_BEGIN
|
|||||||
// remember encoding
|
// remember encoding
|
||||||
res.encoding = buffer_encoding;
|
res.encoding = buffer_encoding;
|
||||||
|
|
||||||
// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
|
|
||||||
if (own || buffer != contents) *out_buffer = buffer;
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5966,23 +5949,25 @@ namespace pugi
|
|||||||
|
|
||||||
if (!extra) return impl::make_parse_result(status_out_of_memory);
|
if (!extra) return impl::make_parse_result(status_out_of_memory);
|
||||||
|
|
||||||
// save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
|
|
||||||
char_t* rootname = _root->contents;
|
|
||||||
_root->contents = 0;
|
|
||||||
|
|
||||||
// parse
|
|
||||||
char_t* buffer = 0;
|
|
||||||
xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &buffer);
|
|
||||||
|
|
||||||
// restore name
|
|
||||||
_root->contents = rootname;
|
|
||||||
|
|
||||||
// add extra buffer to the list
|
// add extra buffer to the list
|
||||||
extra->buffer = buffer;
|
extra->buffer = 0;
|
||||||
extra->next = doc->extra_buffers;
|
extra->next = doc->extra_buffers;
|
||||||
doc->extra_buffers = extra;
|
doc->extra_buffers = extra;
|
||||||
|
|
||||||
return res;
|
// name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
|
||||||
|
struct name_sentry
|
||||||
|
{
|
||||||
|
xml_node_struct* node;
|
||||||
|
char_t* name;
|
||||||
|
|
||||||
|
~name_sentry() { node->contents = name; }
|
||||||
|
};
|
||||||
|
|
||||||
|
name_sentry sentry = { _root, _root->contents };
|
||||||
|
|
||||||
|
sentry.node->contents = 0;
|
||||||
|
|
||||||
|
return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
|
PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
|
||||||
@ -6800,18 +6785,18 @@ namespace pugi
|
|||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
FILE* file = fopen(path_, "rb");
|
impl::auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, "rb"), fclose);
|
||||||
|
|
||||||
return impl::load_file_impl(*this, file, options, encoding);
|
return impl::load_file_impl(*this, file.data, options, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
|
PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
FILE* file = impl::open_file_wide(path_, L"rb");
|
impl::auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, L"rb"), fclose);
|
||||||
|
|
||||||
return impl::load_file_impl(*this, file, options, encoding);
|
return impl::load_file_impl(*this, file.data, options, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
|
PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
|
||||||
@ -6879,16 +6864,16 @@ namespace pugi
|
|||||||
|
|
||||||
PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
|
PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
|
||||||
{
|
{
|
||||||
FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
|
impl::auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), fclose);
|
||||||
|
|
||||||
return impl::save_file_impl(*this, file, indent, flags, encoding);
|
return impl::save_file_impl(*this, file.data, indent, flags, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
|
PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
|
||||||
{
|
{
|
||||||
FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
|
impl::auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), fclose);
|
||||||
|
|
||||||
return impl::save_file_impl(*this, file, indent, flags, encoding);
|
return impl::save_file_impl(*this, file.data, indent, flags, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN xml_node xml_document::document_element() const
|
PUGI__FN xml_node xml_document::document_element() const
|
||||||
@ -11544,15 +11529,13 @@ PUGI__NS_BEGIN
|
|||||||
return new (memory) xpath_query_impl();
|
return new (memory) xpath_query_impl();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy(void* ptr)
|
static void destroy(xpath_query_impl* impl)
|
||||||
{
|
{
|
||||||
if (!ptr) return;
|
|
||||||
|
|
||||||
// free all allocated pages
|
// free all allocated pages
|
||||||
static_cast<xpath_query_impl*>(ptr)->alloc.release();
|
impl->alloc.release();
|
||||||
|
|
||||||
// free allocator memory (with the first page)
|
// free allocator memory (with the first page)
|
||||||
xml_memory::deallocate(ptr);
|
xml_memory::deallocate(impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
xpath_query_impl(): root(0), alloc(&block)
|
xpath_query_impl(): root(0), alloc(&block)
|
||||||
@ -12011,7 +11994,7 @@ namespace pugi
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);
|
impl::auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);
|
||||||
|
|
||||||
qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
|
qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
|
||||||
|
|
||||||
@ -12019,7 +12002,7 @@ namespace pugi
|
|||||||
{
|
{
|
||||||
qimpl->root->optimize(&qimpl->alloc);
|
qimpl->root->optimize(&qimpl->alloc);
|
||||||
|
|
||||||
_impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());
|
_impl = impl.release();
|
||||||
_result.error = 0;
|
_result.error = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12027,7 +12010,8 @@ namespace pugi
|
|||||||
|
|
||||||
PUGI__FN xpath_query::~xpath_query()
|
PUGI__FN xpath_query::~xpath_query()
|
||||||
{
|
{
|
||||||
impl::xpath_query_impl::destroy(_impl);
|
if (_impl)
|
||||||
|
impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
|
||||||
}
|
}
|
||||||
|
|
||||||
PUGI__FN xpath_value_type xpath_query::return_type() const
|
PUGI__FN xpath_value_type xpath_query::return_type() const
|
||||||
|
@ -50,6 +50,18 @@ static void* custom_allocate(size_t size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PUGIXML_NO_EXCEPTIONS
|
||||||
|
static void* custom_allocate_throw(size_t size)
|
||||||
|
{
|
||||||
|
void* result = custom_allocate(size);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void custom_deallocate(void* ptr)
|
static void custom_deallocate(void* ptr)
|
||||||
{
|
{
|
||||||
assert(ptr);
|
assert(ptr);
|
||||||
@ -82,7 +94,7 @@ namespace std
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool run_test(test_runner* test)
|
static bool run_test(test_runner* test, const char* test_name, pugi::allocation_function allocate)
|
||||||
{
|
{
|
||||||
#ifndef PUGIXML_NO_EXCEPTIONS
|
#ifndef PUGIXML_NO_EXCEPTIONS
|
||||||
try
|
try
|
||||||
@ -94,7 +106,7 @@ static bool run_test(test_runner* test)
|
|||||||
test_runner::_memory_fail_threshold = 0;
|
test_runner::_memory_fail_threshold = 0;
|
||||||
test_runner::_memory_fail_triggered = false;
|
test_runner::_memory_fail_triggered = false;
|
||||||
|
|
||||||
pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
|
pugi::set_memory_management_functions(allocate, custom_deallocate);
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
@ -110,7 +122,7 @@ static bool run_test(test_runner* test)
|
|||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
printf("Test %s failed: %s\n", test->_name, test_runner::_failure_message);
|
printf("Test %s failed: %s\n", test_name, test_runner::_failure_message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,13 +130,13 @@ static bool run_test(test_runner* test)
|
|||||||
|
|
||||||
if (test_runner::_memory_fail_triggered)
|
if (test_runner::_memory_fail_triggered)
|
||||||
{
|
{
|
||||||
printf("Test %s failed: unguarded memory fail triggered\n", test->_name);
|
printf("Test %s failed: unguarded memory fail triggered\n", test_name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_memory_total_size != 0 || g_memory_total_count != 0)
|
if (g_memory_total_size != 0 || g_memory_total_count != 0)
|
||||||
{
|
{
|
||||||
printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test->_name, static_cast<unsigned int>(g_memory_total_size), static_cast<unsigned int>(g_memory_total_count));
|
printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test_name, static_cast<unsigned int>(g_memory_total_size), static_cast<unsigned int>(g_memory_total_count));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +145,12 @@ static bool run_test(test_runner* test)
|
|||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
printf("Test %s failed: exception %s\n", test->_name, e.what());
|
printf("Test %s failed: exception %s\n", test_name, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
printf("Test %s failed for unknown reason\n", test->_name);
|
printf("Test %s failed for unknown reason\n", test_name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -176,7 +188,15 @@ int main(int, char** argv)
|
|||||||
for (test = test_runner::_tests; test; test = test->_next)
|
for (test = test_runner::_tests; test; test = test->_next)
|
||||||
{
|
{
|
||||||
total++;
|
total++;
|
||||||
passed += run_test(test);
|
passed += run_test(test, test->_name, custom_allocate);
|
||||||
|
|
||||||
|
#ifndef PUGIXML_NO_EXCEPTIONS
|
||||||
|
if (g_memory_fail_triggered)
|
||||||
|
{
|
||||||
|
total++;
|
||||||
|
passed += run_test(test, (test->_name + std::string(" (throw)")).c_str(), custom_allocate_throw);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int failed = total - passed;
|
unsigned int failed = total - passed;
|
||||||
|
@ -143,9 +143,9 @@ struct dummy_fixture {};
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PUGIXML_NO_EXCEPTIONS
|
#ifdef PUGIXML_NO_EXCEPTIONS
|
||||||
#define CHECK_ALLOC_FAIL(code) CHECK(!test_runner::_memory_fail_triggered); code; CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false
|
#define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); code; CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered)
|
||||||
#else
|
#else
|
||||||
#define CHECK_ALLOC_FAIL(code) CHECK(!test_runner::_memory_fail_triggered); try { code; } catch (std::bad_alloc&) {} CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false
|
#define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); try { code; } catch (std::bad_alloc&) {} CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STR(text) PUGIXML_TEXT(text)
|
#define STR(text) PUGIXML_TEXT(text)
|
||||||
|
@ -319,9 +319,7 @@ TEST(document_load_file_out_of_memory_file_leak)
|
|||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
|
|
||||||
for (int i = 0; i < 256; ++i)
|
for (int i = 0; i < 256; ++i)
|
||||||
{
|
|
||||||
CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory));
|
CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory));
|
||||||
}
|
|
||||||
|
|
||||||
test_runner::_memory_fail_threshold = 0;
|
test_runner::_memory_fail_threshold = 0;
|
||||||
|
|
||||||
@ -329,6 +327,21 @@ TEST(document_load_file_out_of_memory_file_leak)
|
|||||||
CHECK_NODE(doc, STR("<node />"));
|
CHECK_NODE(doc, STR("<node />"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(document_load_file_wide_out_of_memory_file_leak)
|
||||||
|
{
|
||||||
|
test_runner::_memory_fail_threshold = 256;
|
||||||
|
|
||||||
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
CHECK_ALLOC_FAIL(CHECK(doc.load_file(L"tests/data/small.xml").status == status_out_of_memory));
|
||||||
|
|
||||||
|
test_runner::_memory_fail_threshold = 0;
|
||||||
|
|
||||||
|
CHECK(doc.load_file(L"tests/data/small.xml"));
|
||||||
|
CHECK_NODE(doc, STR("<node />"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(document_load_file_error_previous)
|
TEST(document_load_file_error_previous)
|
||||||
{
|
{
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
@ -556,6 +569,26 @@ TEST_XML(document_save_file_wide_text, "<node/>")
|
|||||||
CHECK(test_file_contents(f.path, "<node />\n", 9));
|
CHECK(test_file_contents(f.path, "<node />\n", 9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_XML(document_save_file_leak, "<node/>")
|
||||||
|
{
|
||||||
|
temp_file f;
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
CHECK(doc.save_file(f.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_XML(document_save_file_wide_leak, "<node/>")
|
||||||
|
{
|
||||||
|
temp_file f;
|
||||||
|
|
||||||
|
// widen the path
|
||||||
|
wchar_t wpath[sizeof(f.path)];
|
||||||
|
std::copy(f.path, f.path + strlen(f.path) + 1, wpath + 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
CHECK(doc.save_file(wpath));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(document_load_buffer)
|
TEST(document_load_buffer)
|
||||||
{
|
{
|
||||||
const pugi::char_t text[] = STR("<?xml?><node/>");
|
const pugi::char_t text[] = STR("<?xml?><node/>");
|
||||||
|
@ -905,7 +905,8 @@ TEST(dom_node_out_of_memory)
|
|||||||
xml_attribute a = n.append_attribute(STR("a"));
|
xml_attribute a = n.append_attribute(STR("a"));
|
||||||
CHECK(a);
|
CHECK(a);
|
||||||
|
|
||||||
CHECK_ALLOC_FAIL(while (n.append_child(node_comment) || n.append_attribute(STR("b"))) { /* nop */ });
|
CHECK_ALLOC_FAIL(while (n.append_child(node_comment)) { /* nop */ });
|
||||||
|
CHECK_ALLOC_FAIL(while (n.append_attribute(STR("b"))) { /* nop */ });
|
||||||
|
|
||||||
// verify all node modification operations
|
// verify all node modification operations
|
||||||
CHECK_ALLOC_FAIL(CHECK(!n.append_child()));
|
CHECK_ALLOC_FAIL(CHECK(!n.append_child()));
|
||||||
|
@ -935,6 +935,31 @@ TEST(parse_out_of_memory_conversion)
|
|||||||
CHECK(!doc.first_child());
|
CHECK(!doc.first_child());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(parse_out_of_memory_allocator_state_sync)
|
||||||
|
{
|
||||||
|
const unsigned int count = 10000;
|
||||||
|
static char_t text[count * 4];
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
text[4*i + 0] = '<';
|
||||||
|
text[4*i + 1] = 'n';
|
||||||
|
text[4*i + 2] = '/';
|
||||||
|
text[4*i + 3] = '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
test_runner::_memory_fail_threshold = 65536;
|
||||||
|
|
||||||
|
xml_document doc;
|
||||||
|
CHECK_ALLOC_FAIL(CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory));
|
||||||
|
CHECK_NODE(doc.first_child(), STR("<n />"));
|
||||||
|
|
||||||
|
test_runner::_memory_fail_threshold = 0;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; ++i)
|
||||||
|
CHECK(doc.append_child(STR("n")));
|
||||||
|
}
|
||||||
|
|
||||||
static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset)
|
static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset)
|
||||||
{
|
{
|
||||||
xml_document doc;
|
xml_document doc;
|
||||||
|
Loading…
Reference in New Issue
Block a user