Reworked DOM memory allocation scheme (name/value allocations use the same pages as node/attribute structures, pages are now deallocated when completely free)

git-svn-id: http://pugixml.googlecode.com/svn/trunk@401 99668b35-9821-0410-8761-19e4c4f06640
This commit is contained in:
arseny.kapoulkine 2010-05-10 08:59:48 +00:00
parent 5ff56a6d68
commit 47c23efe62
5 changed files with 329 additions and 143 deletions

View File

@ -162,7 +162,7 @@ else if ( $(toolset) = "ic8" )
actions ObjectAction actions ObjectAction
{ {
"%$(toolset)_PATH%\bin\icl.exe" /W4 /WX /Wport /Qwd981,444,280,383,909,304,167,177,1419 /I"%$(msvc)_PATH%\include" /I"%$(msvc)_PATH%\PlatformSDK\Include" /I"%$(toolset)_PATH%\include" /c $(>) /Fo$(<) /nologo $(CCFLAGS) "%$(toolset)_PATH%\bin\icl.exe" /W4 /WX /Wport /Qwd981,444,280,383,909,304,167,171,177,1419 /I"%$(msvc)_PATH%\include" /I"%$(msvc)_PATH%\PlatformSDK\Include" /I"%$(toolset)_PATH%\include" /c $(>) /Fo$(<) /nologo $(CCFLAGS)
} }
actions LibraryAction actions LibraryAction

View File

@ -48,6 +48,13 @@ using std::memmove;
using std::memcpy; using std::memcpy;
#endif #endif
// uintptr_t
#if defined(__BORLANDC__) || defined(__MWERKS__) || defined(__DMC__)
# include <stdint.h>
#elif defined(_MSC_VER) && _MSC_VER < 1300
typedef size_t uintptr_t;
#endif
#define STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } #define STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
// Memory allocation // Memory allocation
@ -222,71 +229,184 @@ namespace pugi
{ {
struct xml_document_struct; struct xml_document_struct;
class xml_allocator static const uintptr_t xml_memory_page_alignment = 32;
static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
static const uintptr_t xml_memory_page_name_allocated_mask = 16;
static const uintptr_t xml_memory_page_value_allocated_mask = 8;
static const uintptr_t xml_memory_page_type_mask = 7;
struct xml_memory_string_header
{ {
public: xml_memory_page* page;
xml_allocator(xml_memory_block* root): _root(root) size_t full_size;
};
struct xml_allocator
{
xml_allocator(xml_memory_page* root): _root(root)
{ {
} }
xml_memory_page* allocate_page(size_t data_size)
{
size_t size = sizeof(xml_memory_page) + data_size; // $$$ slightly incorrect
// allocate block with some alignment, leaving memory for worst-case padding
void* memory = global_allocate(size + xml_memory_page_alignment);
// align upwards to page boundary
void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
// prepare page structure
xml_memory_page* page = new (page_memory) xml_memory_page();
page->memory = memory;
page->allocator = this;
return page;
}
static void deallocate_page(xml_memory_page* page)
{
global_deallocate(page->memory);
}
void* allocate_memory_oob(size_t size, xml_memory_page*& out_page)
{
const size_t large_allocation_threshold = xml_memory_page_size / 4;
xml_memory_page* page;
if (size <= large_allocation_threshold)
{
page = allocate_page(xml_memory_page_size);
// insert page at the end of linked list
page->prev = _root;
_root->next = page;
_root = page;
}
else
{
// standalone page
page = allocate_page(size);
// insert page before the end of linked list
assert(_root->prev);
page->prev = _root->prev;
page->next = _root;
_root->prev->next = page;
_root->prev = page;
}
// allocate inside page
page->busy_size = size;
out_page = page;
return page->data;
}
void* allocate_memory(size_t size, xml_memory_page*& out_page)
{
if (_root->busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
void* buf = _root->data + _root->busy_size;
_root->busy_size += size;
out_page = _root;
return buf;
}
void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
{
assert(ptr >= page->data && ptr < page->data + xml_memory_page_size);
(void)!ptr;
page->freed_size += size;
assert(page->freed_size <= page->busy_size);
if (page->freed_size == page->busy_size)
{
if (page->next == 0)
{
assert(_root == page);
// top page freed, just reset sizes
page->busy_size = page->freed_size = 0;
}
else
{
assert(_root != page);
assert(page->prev);
// remove from the list
page->prev->next = page->next;
page->next->prev = page->prev;
// deallocate
deallocate_page(page);
}
}
}
xml_document_struct* allocate_document(); xml_document_struct* allocate_document();
xml_node_struct* allocate_node(xml_node_type type); xml_node_struct* allocate_node(xml_node_type type);
xml_attribute_struct* allocate_attribute(); xml_attribute_struct* allocate_attribute();
private: char_t* allocate_string(size_t length)
xml_memory_block* _root;
void* memalloc(size_t size)
{ {
if (_root->size + size <= memory_block_size) // get actual size, rounded up to pointer alignment boundary
{ size_t size = ((length * sizeof(char_t)) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
void* buf = _root->data + _root->size;
_root->size += size;
return buf;
}
else
{
void* new_block = global_allocate(sizeof(xml_memory_block));
_root->next = new (new_block) xml_memory_block(); // allocate memory for string and header block
_root = _root->next; size_t full_size = sizeof(xml_memory_string_header) + size;
_root->size = size; xml_memory_page* page;
xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
return _root->data; // setup header
} header->page = page;
header->full_size = full_size;
return reinterpret_cast<char_t*>(header + 1);
} }
void deallocate_string(char_t* string)
{
// get header
xml_memory_string_header* header = reinterpret_cast<xml_memory_string_header*>(string) - 1;
// deallocate
deallocate_memory(header, header->full_size, header->page);
}
xml_memory_page* _root;
}; };
/// A 'name=value' XML attribute structure. /// A 'name=value' XML attribute structure.
struct xml_attribute_struct struct xml_attribute_struct
{ {
/// Default ctor /// Default ctor
xml_attribute_struct(): padding(0), name_allocated(false), value_allocated(false), name(0), value(0), prev_attribute(0), next_attribute(0) xml_attribute_struct(xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute(0), next_attribute(0)
{ {
} }
void destroy() void destroy(xml_allocator& alloc)
{ {
if (name_allocated) if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(name);
{ if (header & xml_memory_page_value_allocated_mask) alloc.deallocate_string(value);
global_deallocate(name);
name = 0;
}
if (value_allocated) alloc.deallocate_memory(this, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
{
global_deallocate(value);
value = 0;
}
} }
unsigned int padding : 30; uintptr_t header;
unsigned int name_allocated : 1;
unsigned int value_allocated : 1;
char_t* name; ///< Pointer to attribute name. char_t* name; ///< Pointer to attribute name.
char_t* value; ///< Pointer to attribute value. char_t* value; ///< Pointer to attribute value.
xml_attribute_struct* prev_attribute; ///< Previous attribute xml_attribute_struct* prev_attribute; ///< Previous attribute
xml_attribute_struct* next_attribute; ///< Next attribute xml_attribute_struct* next_attribute; ///< Next attribute
@ -297,31 +417,39 @@ namespace pugi
{ {
/// Default ctor /// Default ctor
/// \param type - node type /// \param type - node type
xml_node_struct(xml_node_type type = node_element): name_allocated(false), value_allocated(false), padding(0), type(type), parent(0), name(0), value(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), first_attribute(0), last_attribute(0) xml_node_struct(xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | type), parent(0), name(0), value(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), first_attribute(0), last_attribute(0)
{ {
} }
void destroy() void destroy(xml_allocator& alloc)
{ {
parent = 0; destroy(alloc, sizeof(xml_node_struct));
}
if (name_allocated)
void destroy(xml_allocator& alloc, size_t size)
{
if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(name);
if (header & xml_memory_page_value_allocated_mask) alloc.deallocate_string(value);
for (xml_attribute_struct* attr = first_attribute; attr; )
{ {
global_deallocate(name); xml_attribute_struct* next = attr->next_attribute;
name = 0;
attr->destroy(alloc);
attr = next;
} }
if (value_allocated) for (xml_node_struct* node = first_child; node; )
{ {
global_deallocate(value); xml_node_struct* next = node->next_sibling;
value = 0;
}
for (xml_attribute_struct* attr = first_attribute; attr; attr = attr->next_attribute) node->destroy(alloc);
attr->destroy();
for (xml_node_struct* node = first_child; node; node = node->next_sibling) node = next;
node->destroy(); }
alloc.deallocate_memory(this, size, reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
} }
xml_node_struct* append_node(xml_allocator& alloc, xml_node_type type = node_element) xml_node_struct* append_node(xml_allocator& alloc, xml_node_type type = node_element)
@ -355,10 +483,7 @@ namespace pugi
return a; return a;
} }
unsigned int name_allocated : 1; uintptr_t header;
unsigned int value_allocated : 1;
unsigned int padding : 27;
unsigned int type : 3; ///< Node type; see xml_node_type.
xml_node_struct* parent; ///< Pointer to parent xml_node_struct* parent; ///< Pointer to parent
@ -377,7 +502,7 @@ namespace pugi
struct xml_document_struct: public xml_node_struct struct xml_document_struct: public xml_node_struct
{ {
xml_document_struct(): xml_node_struct(node_document), allocator(0), buffer(0) xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), allocator(0), buffer(0)
{ {
} }
@ -387,17 +512,26 @@ namespace pugi
xml_document_struct* xml_allocator::allocate_document() xml_document_struct* xml_allocator::allocate_document()
{ {
return new (memalloc(sizeof(xml_document_struct))) xml_document_struct; xml_memory_page* page;
void* memory = allocate_memory(sizeof(xml_document_struct), page);
return new (memory) xml_document_struct(page);
} }
xml_node_struct* xml_allocator::allocate_node(xml_node_type type) xml_node_struct* xml_allocator::allocate_node(xml_node_type type)
{ {
return new (memalloc(sizeof(xml_node_struct))) xml_node_struct(type); xml_memory_page* page;
void* memory = allocate_memory(sizeof(xml_node_struct), page);
return new (memory) xml_node_struct(page, type);
} }
xml_attribute_struct* xml_allocator::allocate_attribute() xml_attribute_struct* xml_allocator::allocate_attribute()
{ {
return new (memalloc(sizeof(xml_attribute_struct))) xml_attribute_struct; xml_memory_page* page;
void* memory = allocate_memory(sizeof(xml_attribute_struct), page);
return new (memory) xml_attribute_struct(page);
} }
} }
@ -1120,7 +1254,7 @@ namespace
} }
#endif #endif
bool strcpy_insitu(char_t*& dest, bool& allocated, const char_t* source) bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
{ {
size_t source_length = impl::strlen(source); size_t source_length = impl::strlen(source);
@ -1132,15 +1266,17 @@ namespace
} }
else else
{ {
char_t* buf = static_cast<char_t*>(global_allocate((source_length + 1) * sizeof(char_t))); xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
char_t* buf = alloc->allocate_string(source_length + 1);
if (!buf) return false; if (!buf) return false;
impl::strcpy(buf, source); impl::strcpy(buf, source);
if (allocated) global_deallocate(dest); if (header & header_mask) alloc->deallocate_string(dest);
dest = buf; dest = buf;
allocated = true; header |= header_mask;
return true; return true;
} }
@ -1997,7 +2133,7 @@ namespace
if (!quest_result) return quest_result; if (!quest_result) return quest_result;
if (cursor && cursor->type == node_declaration) goto LOC_ATTRIBUTES; if (cursor && (cursor->header & xml_memory_page_type_mask) == node_declaration) goto LOC_ATTRIBUTES;
} }
else if (*s == '!') // '<!...' else if (*s == '!') // '<!...'
{ {
@ -2021,7 +2157,7 @@ namespace
s = mark; s = mark;
if (static_cast<xml_node_type>(cursor->type) != node_document) if (cursor->parent)
{ {
PUSHNODE(node_pcdata); // Append a new node on the tree. PUSHNODE(node_pcdata); // Append a new node on the tree.
cursor->value = s; // Save the offset. cursor->value = s; // Save the offset.
@ -2071,6 +2207,9 @@ namespace
// perform actual parsing // perform actual parsing
xml_parse_result result = parser.parse(buffer, xmldoc, optmsk, endch); xml_parse_result result = parser.parse(buffer, xmldoc, optmsk, endch);
// fixup page live_sizes $$$
// for (xml_memory_page* page = alloc._root; page; page = page->prev) page->live_size = page->busy_size;
// since we removed last character, we have to handle the only possible false positive // since we removed last character, we have to handle the only possible false positive
if (result && endch == '<') if (result && endch == '<')
{ {
@ -2658,6 +2797,7 @@ namespace
} }
} }
#ifndef PUGIXML_NO_STL
template <typename T> xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T, std::char_traits<T> >& stream, unsigned int options, encoding_t encoding) template <typename T> xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T, std::char_traits<T> >& stream, unsigned int options, encoding_t encoding)
{ {
if (!stream.good()) return MAKE_PARSE_RESULT(status_io_error); if (!stream.good()) return MAKE_PARSE_RESULT(status_io_error);
@ -2691,7 +2831,7 @@ namespace
// load data from buffer // load data from buffer
return doc.load_buffer_inplace_own(s, actual_length * sizeof(T), options, encoding); return doc.load_buffer_inplace_own(s, actual_length * sizeof(T), options, encoding);
} }
#endif
} }
namespace pugi namespace pugi
@ -2927,22 +3067,14 @@ namespace pugi
{ {
if (!_attr) return false; if (!_attr) return false;
bool allocated = _attr->name_allocated; return strcpy_insitu(_attr->name, _attr->header, xml_memory_page_name_allocated_mask, rhs);
bool res = strcpy_insitu(_attr->name, allocated, rhs);
_attr->name_allocated = allocated;
return res;
} }
bool xml_attribute::set_value(const char_t* rhs) bool xml_attribute::set_value(const char_t* rhs)
{ {
if (!_attr) return false; if (!_attr) return false;
bool allocated = _attr->value_allocated; return strcpy_insitu(_attr->value, _attr->header, xml_memory_page_value_allocated_mask, rhs);
bool res = strcpy_insitu(_attr->value, allocated, rhs);
_attr->value_allocated = allocated;
return res;
} }
bool xml_attribute::set_value(int rhs) bool xml_attribute::set_value(int rhs)
@ -3086,9 +3218,9 @@ namespace pugi
xml_allocator& xml_node::get_allocator() const xml_allocator& xml_node::get_allocator() const
{ {
xml_node_struct* r = root()._root; assert(_root);
return static_cast<xml_document_struct*>(r)->allocator; return *reinterpret_cast<xml_memory_page*>(_root->header & xml_memory_page_pointer_mask)->allocator;
} }
const char_t* xml_node::name() const const char_t* xml_node::name() const
@ -3098,7 +3230,7 @@ namespace pugi
xml_node_type xml_node::type() const xml_node_type xml_node::type() const
{ {
return _root ? static_cast<xml_node_type>(_root->type) : node_null; return _root ? static_cast<xml_node_type>(_root->header & xml_memory_page_type_mask) : node_null;
} }
const char_t* xml_node::value() const const char_t* xml_node::value() const
@ -3221,8 +3353,12 @@ namespace pugi
if (!_root) return PUGIXML_TEXT(""); if (!_root) return PUGIXML_TEXT("");
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
if ((static_cast<xml_node_type>(i->type) == node_pcdata || static_cast<xml_node_type>(i->type) == node_cdata) && i->value) {
xml_node_type type = static_cast<xml_node_type>(i->header & xml_memory_page_type_mask);
if (i->value && (type == node_pcdata || type == node_cdata))
return i->value; return i->value;
}
return PUGIXML_TEXT(""); return PUGIXML_TEXT("");
} }
@ -3270,11 +3406,7 @@ namespace pugi
case node_declaration: case node_declaration:
case node_element: case node_element:
{ {
bool allocated = _root->name_allocated; return strcpy_insitu(_root->name, _root->header, xml_memory_page_name_allocated_mask, rhs);
bool res = strcpy_insitu(_root->name, allocated, rhs);
_root->name_allocated = allocated;
return res;
} }
default: default:
@ -3291,11 +3423,7 @@ namespace pugi
case node_pcdata: case node_pcdata:
case node_comment: case node_comment:
{ {
bool allocated = _root->value_allocated; return strcpy_insitu(_root->value, _root->header, xml_memory_page_value_allocated_mask, rhs);
bool res = strcpy_insitu(_root->value, allocated, rhs);
_root->value_allocated = allocated;
return res;
} }
default: default:
@ -3491,7 +3619,7 @@ namespace pugi
if (a._attr->prev_attribute) a._attr->prev_attribute->next_attribute = a._attr->next_attribute; if (a._attr->prev_attribute) a._attr->prev_attribute->next_attribute = a._attr->next_attribute;
else _root->first_attribute = a._attr->next_attribute; else _root->first_attribute = a._attr->next_attribute;
a._attr->destroy(); a._attr->destroy(get_allocator());
} }
void xml_node::remove_child(const char_t* name) void xml_node::remove_child(const char_t* name)
@ -3509,7 +3637,7 @@ namespace pugi
if (n._root->prev_sibling) n._root->prev_sibling->next_sibling = n._root->next_sibling; if (n._root->prev_sibling) n._root->prev_sibling->next_sibling = n._root->next_sibling;
else _root->first_child = n._root->next_sibling; else _root->first_child = n._root->next_sibling;
n._root->destroy(); n._root->destroy(get_allocator());
} }
xml_node xml_node::find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const xml_node xml_node::find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const
@ -3733,12 +3861,12 @@ namespace pugi
case node_element: case node_element:
case node_declaration: case node_declaration:
case node_pi: case node_pi:
return _root->name_allocated ? -1 : _root->name - buffer; return (_root->header & xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;
case node_pcdata: case node_pcdata:
case node_cdata: case node_cdata:
case node_comment: case node_comment:
return _root->value_allocated ? -1 : _root->value - buffer; return (_root->header & xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;
default: default:
return -1; return -1;
@ -3885,7 +4013,7 @@ namespace pugi
return temp; return temp;
} }
xml_memory_block::xml_memory_block(): next(0), size(0) xml_memory_page::xml_memory_page(): allocator(0), memory(0), prev(0), next(0), busy_size(0), freed_size(0)
{ {
} }
@ -3924,15 +4052,34 @@ namespace pugi
xml_document::~xml_document() xml_document::~xml_document()
{ {
destroy(); destroy();
if (_memory.next)
{
assert(!_memory.next->next);
xml_allocator::deallocate_page(_memory.next);
}
} }
void xml_document::create() void xml_document::create()
{ {
xml_allocator alloc(&_memory); destroy();
// initialize sentinel page
_memory.busy_size = xml_memory_page_size;
// allocate new root
xml_allocator alloc(_memory.next ? _memory.next : &_memory);
_root = alloc.allocate_document(); // Allocate a new root. _root = alloc.allocate_document();
// setup allocator
xml_allocator& a = static_cast<xml_document_struct*>(_root)->allocator; xml_allocator& a = static_cast<xml_document_struct*>(_root)->allocator;
a = alloc; a = alloc;
// setup page
assert(_memory.next);
_memory.next->allocator = &a;
} }
void xml_document::destroy() void xml_document::destroy()
@ -3943,34 +4090,32 @@ namespace pugi
_buffer = 0; _buffer = 0;
} }
if (_root) _root->destroy(); // unoptimized deallocation, for verification purposes
if (_root)
xml_memory_block* current = _memory.next;
while (current)
{ {
xml_memory_block* next = current->next; _root->destroy(get_allocator(), sizeof(xml_document_struct));
global_deallocate(current);
current = next;
}
_memory.next = 0;
_memory.size = 0;
create(); assert(_memory.next);
assert(!_memory.next->next);
assert(_memory.next->busy_size == 0 && _memory.next->freed_size == 0);
}
else
{
assert(!_memory.next);
}
} }
#ifndef PUGIXML_NO_STL #ifndef PUGIXML_NO_STL
xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, encoding_t encoding) xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, encoding_t encoding)
{ {
destroy(); create();
return load_stream_impl(*this, stream, options, encoding); return load_stream_impl(*this, stream, options, encoding);
} }
xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options) xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
{ {
destroy(); create();
return load_stream_impl(*this, stream, options, encoding_wchar); return load_stream_impl(*this, stream, options, encoding_wchar);
} }
@ -3978,7 +4123,7 @@ namespace pugi
xml_parse_result xml_document::load(const char_t* contents, unsigned int options) xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
{ {
destroy(); create();
// Force native encoding (skip autodetection) // Force native encoding (skip autodetection)
#ifdef PUGIXML_WCHAR_MODE #ifdef PUGIXML_WCHAR_MODE
@ -4002,7 +4147,7 @@ namespace pugi
xml_parse_result xml_document::load_file(const char* name, unsigned int options, encoding_t encoding) xml_parse_result xml_document::load_file(const char* name, unsigned int options, encoding_t encoding)
{ {
destroy(); create();
FILE* file = fopen(name, "rb"); FILE* file = fopen(name, "rb");
if (!file) return MAKE_PARSE_RESULT(status_file_not_found); if (!file) return MAKE_PARSE_RESULT(status_file_not_found);
@ -4039,14 +4184,14 @@ namespace pugi
xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, encoding_t encoding, bool is_mutable, bool own) xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, encoding_t encoding, bool is_mutable, bool own)
{ {
destroy(); create();
// get actual encoding // get actual encoding
encoding_t buffer_encoding = get_buffer_encoding(encoding, contents, size); encoding_t buffer_encoding = get_buffer_encoding(encoding, contents, size);
// get private buffer // get private buffer
char_t* buffer; char_t* buffer = 0;
size_t length; size_t length = 0;
if (!convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return MAKE_PARSE_RESULT(status_out_of_memory); if (!convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return MAKE_PARSE_RESULT(status_out_of_memory);

View File

@ -131,11 +131,11 @@ namespace pugi
// Parsing options // Parsing options
/** /**
* Memory block size, used for fast allocator. Memory for DOM tree is allocated in blocks of * Memory page size, used for fast allocator. Memory for DOM tree is allocated in pages of
* memory_block_size + 4. * xml_memory_page_size + size of header (approximately 64 bytes)
* This value affects size of xml_memory class. * This value affects size of xml_memory_page class.
*/ */
const size_t memory_block_size = 32768; const size_t xml_memory_page_size = 32768;
/** /**
* Minimal parsing mode. Equivalent to turning all other flags off. This set of flags means * Minimal parsing mode. Equivalent to turning all other flags off. This set of flags means
@ -329,7 +329,7 @@ namespace pugi
struct xml_attribute_struct; struct xml_attribute_struct;
struct xml_node_struct; struct xml_node_struct;
class xml_allocator; struct xml_allocator;
class xml_node_iterator; class xml_node_iterator;
class xml_attribute_iterator; class xml_attribute_iterator;
@ -1772,15 +1772,22 @@ namespace pugi
virtual bool end(xml_node&); virtual bool end(xml_node&);
}; };
/// \internal Memory block /// \internal Memory page
struct PUGIXML_CLASS xml_memory_block struct PUGIXML_CLASS xml_memory_page
{ {
xml_memory_block(); xml_memory_page();
xml_memory_block* next; xml_allocator* allocator;
size_t size;
char data[memory_block_size]; void* memory;
xml_memory_page* prev;
xml_memory_page* next;
size_t busy_size;
size_t freed_size;
char data[1];
}; };
/** /**
@ -1848,9 +1855,9 @@ namespace pugi
class PUGIXML_CLASS xml_document: public xml_node class PUGIXML_CLASS xml_document: public xml_node
{ {
private: private:
char_t* _buffer; char_t* _buffer;
xml_memory_block _memory; xml_memory_page _memory;
xml_document(const xml_document&); xml_document(const xml_document&);
const xml_document& operator=(const xml_document&); const xml_document& operator=(const xml_document&);

View File

@ -494,7 +494,7 @@ TEST_XML_FLAGS(dom_node_copy_types, "<root><?xml version='1.0'?><?pi value?><!--
CHECK_NODE(doc, STR("<root><?xml version=\"1.0\"?><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?xml version=\"1.0\"?><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>")); CHECK_NODE(doc, STR("<root><?xml version=\"1.0\"?><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?xml version=\"1.0\"?><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>"));
} }
TEST_XML(dom_attr_assign_large, "<node attr1='' attr2='' />") TEST_XML(dom_attr_assign_large_number, "<node attr1='' attr2='' />")
{ {
xml_node node = doc.child(STR("node")); xml_node node = doc.child(STR("node"));

View File

@ -1,22 +1,22 @@
#include "common.hpp" #include "common.hpp"
#include <string>
namespace namespace
{ {
pugi::char_t buffer[8];
int allocate_count = 0; int allocate_count = 0;
int deallocate_count = 0; int deallocate_count = 0;
void* allocate(size_t size) void* allocate(size_t size)
{ {
CHECK(size == sizeof(pugi::char_t) * 8);
++allocate_count; ++allocate_count;
return buffer; return new char[size];
} }
void deallocate(void* ptr) void deallocate(void* ptr)
{ {
CHECK(ptr == buffer);
++deallocate_count; ++deallocate_count;
delete[] reinterpret_cast<char*>(ptr);
} }
} }
@ -34,22 +34,56 @@ TEST(custom_memory_management)
xml_document doc; xml_document doc;
CHECK(doc.load(STR("<node />"))); CHECK(doc.load(STR("<node />")));
CHECK(allocate_count == 1); CHECK(allocate_count == 2);
CHECK(deallocate_count == 0); CHECK(deallocate_count == 0);
CHECK_STRING(buffer, STR("<node"));
// modify document // modify document
doc.child(STR("node")).set_name(STR("foobars")); doc.child(STR("node")).set_name(STR("foobars"));
CHECK(allocate_count == 2); CHECK(allocate_count == 2);
CHECK(deallocate_count == 0); CHECK(deallocate_count == 0);
CHECK_STRING(buffer, STR("foobars"));
} }
CHECK(allocate_count == 2); CHECK(allocate_count == 2);
CHECK(deallocate_count == 2); CHECK(deallocate_count == 2);
CHECK_STRING(buffer, STR("foobars"));
// restore old functions // restore old functions
set_memory_management_functions(old_allocate, old_deallocate); set_memory_management_functions(old_allocate, old_deallocate);
} }
TEST(large_allocations)
{
xml_document doc;
// initial fill
for (size_t i = 0; i < 128; ++i)
{
std::basic_string<pugi::char_t> s(i * 128, 'x');
CHECK(doc.append_child(node_pcdata).set_value(s.c_str()));
}
// grow-prune loop
while (doc.first_child())
{
pugi::xml_node node;
// grow
for (node = doc.first_child(); node; node = node.next_sibling())
{
std::basic_string<pugi::char_t> s = node.value();
CHECK(node.set_value((s + s).c_str()));
}
// prune
for (node = doc.first_child(); node; )
{
pugi::xml_node next = node.next_sibling().next_sibling();
node.parent().remove_child(node);
node = next;
}
}
}