summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--src/pugixml.cpp948
-rw-r--r--tests/autotest-appveyor.ps12
-rw-r--r--tests/test_dom_modify.cpp38
-rw-r--r--tests/test_memory.cpp103
-rw-r--r--tests/test_xpath_variables.cpp4
6 files changed, 935 insertions, 161 deletions
diff --git a/.travis.yml b/.travis.yml
index e52453e..e30a179 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,4 +5,5 @@ compiler:
env:
- DEFINES=standard
- DEFINES=PUGIXML_WCHAR_MODE
+ - DEFINES=PUGIXML_COMPACT
script: make test defines=$DEFINES -j2
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 37c61bb..caf4ad3 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -133,16 +133,16 @@ using std::memmove;
#if !defined(_MSC_VER) || _MSC_VER >= 1600
# include <stdint.h>
#else
+namespace pugi
+{
# ifndef _UINTPTR_T_DEFINED
-// No native uintptr_t in MSVC6 and in some WinCE versions
-typedef size_t uintptr_t;
-#define _UINTPTR_T_DEFINED
+ typedef size_t uintptr_t;
# endif
-PUGI__NS_BEGIN
+
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
-PUGI__NS_END
+}
#endif
// Memory allocation
@@ -259,6 +259,147 @@ PUGI__NS_BEGIN
PUGI__NS_END
#endif
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+ class compact_hash_table
+ {
+ public:
+ compact_hash_table(): _items(0), _capacity(0), _count(0)
+ {
+ }
+
+ void clear()
+ {
+ if (_items)
+ {
+ xml_memory::deallocate(_items);
+ _items = 0;
+ _capacity = 0;
+ _count = 0;
+ }
+ }
+
+ void** find(const void* key)
+ {
+ assert(key);
+
+ if (_capacity == 0) return 0;
+
+ size_t hashmod = _capacity - 1;
+ size_t bucket = hash(key) & hashmod;
+
+ for (size_t probe = 0; probe <= hashmod; ++probe)
+ {
+ item_t& probe_item = _items[bucket];
+
+ if (probe_item.key == key)
+ return &probe_item.value;
+
+ if (probe_item.key == 0)
+ return 0;
+
+ // hash collision, quadratic probing
+ bucket = (bucket + probe + 1) & hashmod;
+ }
+
+ assert(!"Hash table is full");
+ return 0;
+ }
+
+ void** insert(const void* key)
+ {
+ assert(key);
+ assert(_count < _capacity * 3 / 4);
+
+ size_t hashmod = _capacity - 1;
+ size_t bucket = hash(key) & hashmod;
+
+ for (size_t probe = 0; probe <= hashmod; ++probe)
+ {
+ item_t& probe_item = _items[bucket];
+
+ if (probe_item.key == 0)
+ {
+ probe_item.key = key;
+ _count++;
+ return &probe_item.value;
+ }
+
+ if (probe_item.key == key)
+ return &probe_item.value;
+
+ // hash collision, quadratic probing
+ bucket = (bucket + probe + 1) & hashmod;
+ }
+
+ assert(!"Hash table is full");
+ return 0;
+ }
+
+ bool reserve()
+ {
+ if (_count + 16 >= _capacity - _capacity / 4)
+ return rehash();
+
+ return true;
+ }
+
+ private:
+ struct item_t
+ {
+ const void* key;
+ void* value;
+ };
+
+ item_t* _items;
+ size_t _capacity;
+
+ size_t _count;
+
+ bool rehash();
+
+ static unsigned int hash(const void* key)
+ {
+ unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));
+
+ // MurmurHash3 32-bit finalizer
+ h ^= h >> 16;
+ h *= 0x85ebca6bu;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35u;
+ h ^= h >> 16;
+
+ return h;
+ }
+ };
+
+ PUGI__FN_NO_INLINE bool compact_hash_table::rehash()
+ {
+ compact_hash_table rt;
+ rt._capacity = (_capacity == 0) ? 32 : _capacity * 2;
+ rt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * rt._capacity));
+
+ if (!rt._items)
+ return false;
+
+ memset(rt._items, 0, sizeof(item_t) * rt._capacity);
+
+ for (size_t i = 0; i < _capacity; ++i)
+ if (_items[i].key)
+ *rt.insert(_items[i].key) = _items[i].value;
+
+ if (_items)
+ xml_memory::deallocate(_items);
+
+ _capacity = rt._capacity;
+ _items = rt._items;
+
+ return true;
+ }
+
+PUGI__NS_END
+#endif
+
PUGI__NS_BEGIN
static const size_t xml_memory_page_size =
#ifdef PUGIXML_MEMORY_PAGE_SIZE
@@ -268,15 +409,34 @@ PUGI__NS_BEGIN
#endif
;
+#ifdef PUGIXML_COMPACT
+ static const uintptr_t xml_memory_block_alignment = 4;
+
+ static const uintptr_t xml_memory_page_alignment = sizeof(void*);
+#else
+ static const uintptr_t xml_memory_block_alignment = sizeof(void*);
+
static const uintptr_t xml_memory_page_alignment = 64;
static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
+#endif
+
+ // extra metadata bits
static const uintptr_t xml_memory_page_contents_shared_mask = 32;
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;
+
+ // combined masks for string uniqueness
static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask;
static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask;
+#ifdef PUGIXML_COMPACT
+ #define PUGI__GETPAGE_IMPL(header) (header).get_page()
+#else
+ #define PUGI__GETPAGE_IMPL(header) reinterpret_cast<impl::xml_memory_page*>((header) & impl::xml_memory_page_pointer_mask)
+#endif
+
+ #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
#define PUGI__NODETYPE(n) static_cast<xml_node_type>(((n)->header & impl::xml_memory_page_type_mask) + 1)
struct xml_allocator;
@@ -293,6 +453,12 @@ PUGI__NS_BEGIN
result->busy_size = 0;
result->freed_size = 0;
+ #ifdef PUGIXML_COMPACT
+ result->compact_string_base = 0;
+ result->compact_shared_parent = 0;
+ result->compact_page_marker = 0;
+ #endif
+
return result;
}
@@ -303,6 +469,12 @@ PUGI__NS_BEGIN
size_t busy_size;
size_t freed_size;
+
+ #ifdef PUGIXML_COMPACT
+ char_t* compact_string_base;
+ void* compact_shared_parent;
+ uint32_t* compact_page_marker;
+ #endif
};
struct xml_memory_string_header
@@ -315,6 +487,9 @@ PUGI__NS_BEGIN
{
xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
{
+ #ifdef PUGIXML_COMPACT
+ _hash = 0;
+ #endif
}
xml_memory_page* allocate_page(size_t data_size)
@@ -352,7 +527,8 @@ PUGI__NS_BEGIN
void* allocate_memory(size_t size, xml_memory_page*& out_page)
{
- if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
+ if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size))
+ return allocate_memory_oob(size, out_page);
void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;
@@ -363,6 +539,44 @@ PUGI__NS_BEGIN
return buf;
}
+ #ifdef PUGIXML_COMPACT
+ void* allocate_object(size_t size, xml_memory_page*& out_page)
+ {
+ void* result = allocate_memory(size + sizeof(uint32_t), out_page);
+ if (!result) return 0;
+
+ // adjust for marker
+ ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);
+
+ if (PUGI__UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
+ {
+ // insert new marker
+ uint32_t* marker = static_cast<uint32_t*>(result);
+
+ *marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));
+ out_page->compact_page_marker = marker;
+
+ // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block
+ // this will make sure deallocate_memory correctly tracks the size
+ out_page->freed_size += sizeof(uint32_t);
+
+ return marker + 1;
+ }
+ else
+ {
+ // roll back uint32_t part
+ _busy_size -= sizeof(uint32_t);
+
+ return result;
+ }
+ }
+ #else
+ void* allocate_object(size_t size, xml_memory_page*& out_page)
+ {
+ return allocate_memory(size, out_page);
+ }
+ #endif
+
void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
{
if (page == _root) page->busy_size = _busy_size;
@@ -380,7 +594,16 @@ PUGI__NS_BEGIN
assert(_root == page);
// top page freed, just reset sizes
- page->busy_size = page->freed_size = 0;
+ page->busy_size = 0;
+ page->freed_size = 0;
+
+ #ifdef PUGIXML_COMPACT
+ // reset compact state to maximize efficiency
+ page->compact_string_base = 0;
+ page->compact_shared_parent = 0;
+ page->compact_page_marker = 0;
+ #endif
+
_busy_size = 0;
}
else
@@ -400,15 +623,15 @@ PUGI__NS_BEGIN
char_t* allocate_string(size_t length)
{
- static const size_t max_encoded_offset = (1 << 16) * sizeof(void*);
+ static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
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);
- // round size up to pointer alignment boundary
- size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
+ // round size up to block alignment boundary
+ size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);
xml_memory_page* page;
xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
@@ -418,14 +641,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 % sizeof(void*) == 0);
+ assert(page_offset % xml_memory_block_alignment == 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*));
+ header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);
// full_size == 0 for large strings that occupy the whole page
- assert(full_size % sizeof(void*) == 0);
+ assert(full_size % xml_memory_block_alignment == 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);
+ header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 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
@@ -442,17 +665,30 @@ PUGI__NS_BEGIN
assert(header);
// deallocate
- size_t page_offset = sizeof(xml_memory_page) + header->page_offset * sizeof(void*);
+ size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
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 * sizeof(void*);
+ size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;
deallocate_memory(header, full_size, page);
}
+ bool reserve()
+ {
+ #ifdef PUGIXML_COMPACT
+ return _hash->reserve();
+ #else
+ return true;
+ #endif
+ }
+
xml_memory_page* _root;
size_t _busy_size;
+
+ #ifdef PUGIXML_COMPACT
+ compact_hash_table* _hash;
+ #endif
};
PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
@@ -486,58 +722,396 @@ PUGI__NS_BEGIN
_root->prev->next = page;
_root->prev = page;
- }
- // allocate inside page
- page->busy_size = size;
+ page->busy_size = size;
+ }
return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
}
PUGI__NS_END
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+ static const uintptr_t compact_alignment_log2 = 2;
+ static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
+
+ class compact_header
+ {
+ public:
+ compact_header(xml_memory_page* page, unsigned int flags)
+ {
+ PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);
+
+ ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));
+ assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);
+
+ _page = static_cast<unsigned char>(offset >> compact_alignment_log2);
+ _flags = static_cast<unsigned char>(flags);
+ }
+
+ void operator&=(uintptr_t mod)
+ {
+ _flags &= mod;
+ }
+
+ void operator|=(uintptr_t mod)
+ {
+ _flags |= mod;
+ }
+
+ uintptr_t operator&(uintptr_t mod) const
+ {
+ return _flags & mod;
+ }
+
+ xml_memory_page* get_page() const
+ {
+ const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);
+ const char* page = page_marker - *reinterpret_cast<const uint32_t*>(page_marker);
+
+ return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(page));
+ }
+
+ private:
+ unsigned char _page;
+ unsigned char _flags;
+ };
+
+ PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
+ {
+ const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);
+
+ return header->get_page();
+ }
+
+ template <int header_offset, typename T> PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
+ {
+ return static_cast<T*>(*compact_get_page(object, header_offset)->allocator->_hash->find(object));
+ }
+
+ template <int header_offset, typename T> PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
+ {
+ *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value;
+ }
+
+ template <typename T, int header_offset, int start = -126> class compact_pointer
+ {
+ public:
+ compact_pointer(): _data(0)
+ {
+ }
+
+ void operator=(const compact_pointer& rhs)
+ {
+ *this = rhs + 0;
+ }
+
+ void operator=(T* value)
+ {
+ if (value)
+ {
+ // value is guaranteed to be compact-aligned; 'this' is not
+ // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+ // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+ // compensate for arithmetic shift rounding for negative values
+ ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+ ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;
+
+ if (static_cast<uintptr_t>(offset) <= 253)
+ _data = static_cast<unsigned char>(offset + 1);
+ else
+ {
+ compact_set_value<header_offset>(this, value);
+
+ _data = 255;
+ }
+ }
+ else
+ _data = 0;
+ }
+
+ operator T*() const
+ {
+ if (_data)
+ {
+ if (_data < 255)
+ {
+ uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+ return reinterpret_cast<T*>(base + ((_data - 1 + start) << compact_alignment_log2));
+ }
+ else
+ return compact_get_value<header_offset, T>(this);
+ }
+ else
+ return 0;
+ }
+
+ T* operator->() const
+ {
+ return operator T*();
+ }
+
+ private:
+ unsigned char _data;
+ };
+
+ template <typename T, int header_offset> class compact_pointer_parent
+ {
+ public:
+ compact_pointer_parent(): _data(0)
+ {
+ }
+
+ void operator=(const compact_pointer_parent& rhs)
+ {
+ *this = rhs + 0;
+ }
+
+ void operator=(T* value)
+ {
+ if (value)
+ {
+ // value is guaranteed to be compact-aligned; 'this' is not
+ // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+ // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+ // compensate for arithmetic shift behavior for negative values
+ ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+ ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;
+
+ if (static_cast<uintptr_t>(offset) <= 65533)
+ {
+ _data = static_cast<unsigned short>(offset + 1);
+ }
+ else
+ {
+ xml_memory_page* page = compact_get_page(this, header_offset);
+
+ if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
+ page->compact_shared_parent = value;
+
+ if (page->compact_shared_parent == value)
+ {
+ _data = 65534;
+ }
+ else
+ {
+ compact_set_value<header_offset>(this, value);
+
+ _data = 65535;
+ }
+ }
+ }
+ else
+ {
+ _data = 0;
+ }
+ }
+
+ operator T*() const
+ {
+ if (_data)
+ {
+ if (_data < 65534)
+ {
+ uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+ return reinterpret_cast<T*>(base + ((_data - 1 - 65533) << compact_alignment_log2));
+ }
+ else if (_data == 65534)
+ return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
+ else
+ return compact_get_value<header_offset, T>(this);
+ }
+ else
+ return 0;
+ }
+
+ T* operator->() const
+ {
+ return operator T*();
+ }
+
+ private:
+ uint16_t _data;
+ };
+
+ template <int header_offset, int base_offset> class compact_string
+ {
+ public:
+ compact_string(): _data(0)
+ {
+ }
+
+ void operator=(const compact_string& rhs)
+ {
+ *this = rhs + 0;
+ }
+
+ void operator=(char_t* value)
+ {
+ if (value)
+ {
+ xml_memory_page* page = compact_get_page(this, header_offset);
+
+ if (PUGI__UNLIKELY(page->compact_string_base == 0))
+ page->compact_string_base = value;
+
+ ptrdiff_t offset = value - page->compact_string_base;
+
+ if (static_cast<uintptr_t>(offset) < (65535 << 7))
+ {
+ uint16_t* base = reinterpret_cast<uint16_t*>(reinterpret_cast<char*>(this) - base_offset);
+
+ if (*base == 0)
+ {
+ *base = static_cast<uint16_t>((offset >> 7) + 1);
+ _data = static_cast<unsigned char>((offset & 127) + 1);
+ }
+ else
+ {
+ ptrdiff_t remainder = offset - ((*base - 1) << 7);
+
+ if (static_cast<uintptr_t>(remainder) <= 253)
+ {
+ _data = static_cast<unsigned char>(remainder + 1);
+ }
+ else
+ {
+ compact_set_value<header_offset>(this, value);
+
+ _data = 255;
+ }
+ }
+ }
+ else
+ {
+ compact_set_value<header_offset>(this, value);
+
+ _data = 255;
+ }
+ }
+ else
+ {
+ _data = 0;
+ }
+ }
+
+ operator char_t*() const
+ {
+ if (_data)
+ {
+ if (_data < 255)
+ {
+ xml_memory_page* page = compact_get_page(this, header_offset);
+
+ const uint16_t* base = reinterpret_cast<const uint16_t*>(reinterpret_cast<const char*>(this) - base_offset);
+ assert(*base);
+
+ ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);
+
+ return page->compact_string_base + offset;
+ }
+ else
+ {
+ return compact_get_value<header_offset, char_t>(this);
+ }
+ }
+ else
+ return 0;
+ }
+
+ private:
+ unsigned char _data;
+ };
+PUGI__NS_END
+#endif
+
+#ifdef PUGIXML_COMPACT
+namespace pugi
+{
+ struct xml_attribute_struct
+ {
+ xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
+ {
+ PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
+ }
+
+ impl::compact_header header;
+
+ uint16_t namevalue_base;
+
+ impl::compact_string<4, 2> name;
+ impl::compact_string<5, 3> value;
+
+ impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c;
+ impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute;
+ };
+
+ struct xml_node_struct
+ {
+ xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type - 1), namevalue_base(0)
+ {
+ PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12);
+ }
+
+ impl::compact_header header;
+
+ uint16_t namevalue_base;
+
+ impl::compact_string<4, 2> name;
+ impl::compact_string<5, 3> value;
+
+ impl::compact_pointer_parent<xml_node_struct, 6> parent;
+
+ impl::compact_pointer<xml_node_struct, 8, 0> first_child;
+
+ impl::compact_pointer<xml_node_struct, 9> prev_sibling_c;
+ impl::compact_pointer<xml_node_struct, 10, 0> next_sibling;
+
+ impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute;
+ };
+}
+#else
namespace pugi
{
- /// A 'name=value' XML attribute structure.
struct xml_attribute_struct
{
- /// Default ctor
xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
{
}
uintptr_t header;
- char_t* name; ///< Pointer to attribute name.
- char_t* value; ///< Pointer to attribute value.
+ char_t* name;
+ char_t* value;
- xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list)
- xml_attribute_struct* next_attribute; ///< Next attribute
+ xml_attribute_struct* prev_attribute_c;
+ xml_attribute_struct* next_attribute;
};
- /// An XML document tree node.
struct xml_node_struct
{
- /// Default ctor
- /// \param type - node type
- xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
+ xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
{
}
uintptr_t header;
- xml_node_struct* parent; ///< Pointer to parent
+ char_t* name;
+ char_t* value;
+
+ xml_node_struct* parent;
- char_t* name; ///< Pointer to element name.
- char_t* value; ///< Pointer to any associated string data.
+ xml_node_struct* first_child;
- xml_node_struct* first_child; ///< First child
-
- xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list)
- xml_node_struct* next_sibling; ///< Right brother
-
- xml_attribute_struct* first_attribute; ///< First attribute
+ xml_node_struct* prev_sibling_c;
+ xml_node_struct* next_sibling;
+
+ xml_attribute_struct* first_attribute;
};
}
+#endif
PUGI__NS_BEGIN
struct xml_extra_buffer
@@ -550,25 +1124,32 @@ PUGI__NS_BEGIN
{
xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)
{
+ #ifdef PUGIXML_COMPACT
+ _hash = &hash;
+ #endif
}
const char_t* buffer;
xml_extra_buffer* extra_buffers;
+
+ #ifdef PUGIXML_COMPACT
+ compact_hash_table hash;
+ #endif
};
template <typename Object> inline xml_allocator& get_allocator(const Object* object)
{
assert(object);
- return *reinterpret_cast<xml_memory_page*>(object->header & xml_memory_page_pointer_mask)->allocator;
+ return *PUGI__GETPAGE(object)->allocator;
}
template <typename Object> inline xml_document_struct& get_document(const Object* object)
{
assert(object);
- return *static_cast<xml_document_struct*>(reinterpret_cast<xml_memory_page*>(object->header & xml_memory_page_pointer_mask)->allocator);
+ return *static_cast<xml_document_struct*>(PUGI__GETPAGE(object)->allocator);
}
PUGI__NS_END
@@ -577,7 +1158,7 @@ PUGI__NS_BEGIN
inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
{
xml_memory_page* page;
- void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
+ void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);
return new (memory) xml_attribute_struct(page);
}
@@ -585,27 +1166,29 @@ PUGI__NS_BEGIN
inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)
{
xml_memory_page* page;
- void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
+ void* memory = alloc.allocate_object(sizeof(xml_node_struct), page);
return new (memory) xml_node_struct(page, type);
}
inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
{
- uintptr_t header = a->header;
+ if (a->header & impl::xml_memory_page_name_allocated_mask)
+ alloc.deallocate_string(a->name);
- if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
- if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
+ if (a->header & impl::xml_memory_page_value_allocated_mask)
+ alloc.deallocate_string(a->value);
- alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
+ alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a));
}
inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
{
- uintptr_t header = n->header;
+ if (n->header & impl::xml_memory_page_name_allocated_mask)
+ alloc.deallocate_string(n->name);
- if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
- if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
+ if (n->header & impl::xml_memory_page_value_allocated_mask)
+ alloc.deallocate_string(n->value);
for (xml_attribute_struct* attr = n->first_attribute; attr; )
{
@@ -625,7 +1208,7 @@ PUGI__NS_BEGIN
child = next;
}
- alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
+ alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));
}
inline void append_node(xml_node_struct* child, xml_node_struct* node)
@@ -797,6 +1380,8 @@ PUGI__NS_BEGIN
PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
{
+ if (!alloc.reserve()) return 0;
+
xml_node_struct* child = allocate_node(alloc, type);
if (!child) return 0;
@@ -807,6 +1392,8 @@ PUGI__NS_BEGIN
PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
{
+ if (!alloc.reserve()) return 0;
+
xml_attribute_struct* attr = allocate_attribute(alloc);
if (!attr) return 0;
@@ -1735,7 +2322,8 @@ PUGI__NS_BEGIN
}
#endif
- inline bool strcpy_insitu_allow(size_t length, uintptr_t header, uintptr_t header_mask, char_t* target)
+ template <typename Header>
+ inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)
{
// never reuse shared memory
if (header & xml_memory_page_contents_shared_mask) return false;
@@ -1751,16 +2339,15 @@ PUGI__NS_BEGIN
return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
}
- PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
+ template <typename String, typename Header>
+ PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source)
{
- assert(header);
-
size_t source_length = strlength(source);
if (source_length == 0)
{
// empty string and null pointer are equivalent, so just deallocate old memory
- xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+ xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
if (header & header_mask) alloc->deallocate_string(dest);
@@ -1779,7 +2366,9 @@ PUGI__NS_BEGIN
}
else
{
- xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+ xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+
+ if (!alloc->reserve()) return false;
// allocate new buffer
char_t* buf = alloc->allocate_string(source_length + 1);
@@ -2618,6 +3207,7 @@ PUGI__NS_BEGIN
{
// store value and step over >
cursor->value = value;
+
PUGI__POPNODE();
PUGI__ENDSEG();
@@ -2902,7 +3492,7 @@ PUGI__NS_BEGIN
return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
// get last child of the root before parsing
- xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0;
+ xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
// create parser on stack
xml_parser parser(static_cast<xml_allocator*>(xmldoc));
@@ -2927,7 +3517,7 @@ PUGI__NS_BEGIN
return make_parse_result(status_unrecognized_tag, length - 1);
// check if there are any element nodes parsed
- xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling : root->first_child;
+ xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child;
if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
return make_parse_result(status_no_document_element, length - 1);
@@ -3545,15 +4135,15 @@ PUGI__NS_BEGIN
switch (PUGI__NODETYPE(node))
{
case node_pcdata:
- text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
+ text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
break;
case node_cdata:
- text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT(""));
+ text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
break;
case node_comment:
- node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT(""));
+ node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
break;
case node_pi:
@@ -3749,7 +4339,8 @@ PUGI__NS_BEGIN
return true;
}
- PUGI__FN void node_copy_string(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char_t* source, uintptr_t& source_header, xml_allocator* alloc)
+ template <typename String, typename Header>
+ PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)
{
assert(!dest && (header & header_mask) == 0);
@@ -3964,7 +4555,8 @@ PUGI__NS_BEGIN
#endif
// set value with conversion functions
- PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_buffer(String& dest, Header& header, uintptr_t header_mask, char (&buf)[128])
{
#ifdef PUGIXML_WCHAR_MODE
char_t wbuf[128];
@@ -3976,7 +4568,8 @@ PUGI__NS_BEGIN
#endif
}
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value)
{
char buf[128];
sprintf(buf, "%d", value);
@@ -3984,7 +4577,8 @@ PUGI__NS_BEGIN
return set_value_buffer(dest, header, header_mask, buf);
}
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned int value)
{
char buf[128];
sprintf(buf, "%u", value);
@@ -3992,15 +4586,17 @@ PUGI__NS_BEGIN
return set_value_buffer(dest, header, header_mask, buf);
}
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, float value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
{
char buf[128];
sprintf(buf, "%.9g", value);
return set_value_buffer(dest, header, header_mask, buf);
}
-
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
+
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
{
char buf[128];
sprintf(buf, "%.17g", value);
@@ -4008,13 +4604,15 @@ PUGI__NS_BEGIN
return set_value_buffer(dest, header, header_mask, buf);
}
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, bool value)
{
return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
}
#ifdef PUGIXML_HAS_LONG_LONG
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, long long value)
{
char buf[128];
sprintf(buf, "%lld", value);
@@ -4022,7 +4620,8 @@ PUGI__NS_BEGIN
return set_value_buffer(dest, header, header_mask, buf);
}
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value)
+ template <typename String, typename Header>
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned long long value)
{
char buf[128];
sprintf(buf, "%llu", value);
@@ -4494,38 +5093,38 @@ namespace pugi
PUGI__FN int xml_attribute::as_int(int def) const
{
- return impl::get_value_int(_attr ? _attr->value : 0, def);
+ return impl::get_value_int(_attr ? _attr->value + 0 : 0, def);
}
PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
{
- return impl::get_value_uint(_attr ? _attr->value : 0, def);
+ return impl::get_value_uint(_attr ? _attr->value + 0 : 0, def);
}
PUGI__FN double xml_attribute::as_double(double def) const
{
- return impl::get_value_double(_attr ? _attr->value : 0, def);
+ return impl::get_value_double(_attr ? _attr->value + 0 : 0, def);
}
PUGI__FN float xml_attribute::as_float(float def) const
{
- return impl::get_value_float(_attr ? _attr->value : 0, def);
+ return impl::get_value_float(_attr ? _attr->value + 0 : 0, def);
}
PUGI__FN bool xml_attribute::as_bool(bool def) const
{
- return impl::get_value_bool(_attr ? _attr->value : 0, def);
+ return impl::get_value_bool(_attr ? _attr->value + 0 : 0, def);
}
#ifdef PUGIXML_HAS_LONG_LONG
PUGI__FN long long xml_attribute::as_llong(long long def) const
{
- return impl::get_value_llong(_attr ? _attr->value : 0, def);
+ return impl::get_value_llong(_attr ? _attr->value + 0 : 0, def);
}
PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
{
- return impl::get_value_ullong(_attr ? _attr->value : 0, def);
+ return impl::get_value_ullong(_attr ? _attr->value + 0 : 0, def);
}
#endif
@@ -4536,12 +5135,12 @@ namespace pugi
PUGI__FN const char_t* xml_attribute::name() const
{
- return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");
+ return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
}
PUGI__FN const char_t* xml_attribute::value() const
{
- return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
+ return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
}
PUGI__FN size_t xml_attribute::hash_value() const
@@ -4583,7 +5182,7 @@ namespace pugi
set_value(rhs);
return *this;
}
-
+
PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
{
set_value(rhs);
@@ -4645,7 +5244,7 @@ namespace pugi
return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
}
-
+
PUGI__FN bool xml_attribute::set_value(bool rhs)
{
if (!_attr) return false;
@@ -4705,7 +5304,7 @@ namespace pugi
PUGI__FN xml_node::iterator xml_node::begin() const
{
- return iterator(_root ? _root->first_child : 0, _root);
+ return iterator(_root ? _root->first_child + 0 : 0, _root);
}
PUGI__FN xml_node::iterator xml_node::end() const
@@ -4715,7 +5314,7 @@ namespace pugi
PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
{
- return attribute_iterator(_root ? _root->first_attribute : 0, _root);
+ return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
}
PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
@@ -4775,7 +5374,7 @@ namespace pugi
PUGI__FN const char_t* xml_node::name() const
{
- return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");
+ return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
}
PUGI__FN xml_node_type xml_node::type() const
@@ -4785,7 +5384,7 @@ namespace pugi
PUGI__FN const char_t* xml_node::value() const
{
- return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");
+ return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
}
PUGI__FN xml_node xml_node::child(const char_t* name_) const
@@ -4862,7 +5461,7 @@ namespace pugi
if (!_root) return PUGIXML_TEXT("");
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
- if (i->value && impl::is_text_node(i))
+ if (impl::is_text_node(i) && i->value)
return i->value;
return PUGIXML_TEXT("");
@@ -4895,39 +5494,32 @@ namespace pugi
PUGI__FN bool xml_node::set_name(const char_t* rhs)
{
- switch (type())
- {
- case node_pi:
- case node_declaration:
- case node_element:
- return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
+ static const bool has_name[] = { false, false, true, false, false, false, true, true, false };
- default:
+ if (!_root || !has_name[PUGI__NODETYPE(_root)])
return false;
- }
+
+ return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
}
PUGI__FN bool xml_node::set_value(const char_t* rhs)
{
- switch (type())
- {
- case node_pi:
- case node_cdata:
- case node_pcdata:
- case node_comment:
- case node_doctype:
- return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ static const bool has_value[] = { false, false, false, true, true, true, true, false, true };
- default:
+ if (!_root || !has_value[PUGI__NODETYPE(_root)])
return false;
- }
+
+ return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
}
PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::append_attribute(a._attr, _root);
@@ -4941,7 +5533,10 @@ namespace pugi
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::prepend_attribute(a._attr, _root);
@@ -4956,7 +5551,10 @@ namespace pugi
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::insert_attribute_after(a._attr, attr._attr, _root);
@@ -4971,7 +5569,10 @@ namespace pugi
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::insert_attribute_before(a._attr, attr._attr, _root);
@@ -4986,7 +5587,10 @@ namespace pugi
if (!proto) return xml_attribute();
if (!impl::allow_insert_attribute(type())) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::append_attribute(a._attr, _root);
@@ -5000,7 +5604,10 @@ namespace pugi
if (!proto) return xml_attribute();
if (!impl::allow_insert_attribute(type())) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::prepend_attribute(a._attr, _root);
@@ -5015,7 +5622,10 @@ namespace pugi
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::insert_attribute_after(a._attr, attr._attr, _root);
@@ -5030,7 +5640,10 @@ namespace pugi
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
- xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();
impl::insert_attribute_before(a._attr, attr._attr, _root);
@@ -5043,7 +5656,10 @@ namespace pugi
{
if (!impl::allow_insert_child(type(), type_)) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::append_node(n._root, _root);
@@ -5056,8 +5672,11 @@ namespace pugi
PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
{
if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::prepend_node(n._root, _root);
@@ -5071,8 +5690,11 @@ namespace pugi
{
if (!impl::allow_insert_child(type(), type_)) return xml_node();
if (!node._root || node._root->parent != _root) return xml_node();
+
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::insert_node_before(n._root, node._root);
@@ -5086,8 +5708,11 @@ namespace pugi
{
if (!impl::allow_insert_child(type(), type_)) return xml_node();
if (!node._root || node._root->parent != _root) return xml_node();
+
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::insert_node_after(n._root, node._root);
@@ -5138,7 +5763,10 @@ namespace pugi
xml_node_type type_ = proto.type();
if (!impl::allow_insert_child(type(), type_)) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::append_node(n._root, _root);
@@ -5152,7 +5780,10 @@ namespace pugi
xml_node_type type_ = proto.type();
if (!impl::allow_insert_child(type(), type_)) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::prepend_node(n._root, _root);
@@ -5167,7 +5798,10 @@ namespace pugi
if (!impl::allow_insert_child(type(), type_)) return xml_node();
if (!node._root || node._root->parent != _root) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::insert_node_after(n._root, node._root);
@@ -5182,7 +5816,10 @@ namespace pugi
if (!impl::allow_insert_child(type(), type_)) return xml_node();
if (!node._root || node._root->parent != _root) return xml_node();
- xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
+ xml_node n(impl::allocate_node(alloc, type_));
if (!n) return xml_node();
impl::insert_node_before(n._root, node._root);
@@ -5195,6 +5832,9 @@ namespace pugi
{
if (!impl::allow_move(*this, moved)) return xml_node();
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
@@ -5208,6 +5848,9 @@ namespace pugi
{
if (!impl::allow_move(*this, moved)) return xml_node();
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
@@ -5223,6 +5866,9 @@ namespace pugi
if (!node._root || node._root->parent != _root) return xml_node();
if (moved._root == node._root) return xml_node();
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
@@ -5238,6 +5884,9 @@ namespace pugi
if (!node._root || node._root->parent != _root) return xml_node();
if (moved._root == node._root) return xml_node();
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return xml_node();
+
// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
@@ -5257,8 +5906,11 @@ namespace pugi
if (!_root || !a._attr) return false;
if (!impl::is_attribute_of(a._attr, _root)) return false;
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return false;
+
impl::remove_attribute(a._attr, _root);
- impl::destroy_attribute(a._attr, impl::get_allocator(_root));
+ impl::destroy_attribute(a._attr, alloc);
return true;
}
@@ -5272,8 +5924,11 @@ namespace pugi
{
if (!_root || !n._root || n._root->parent != _root) return false;
+ impl::xml_allocator& alloc = impl::get_allocator(_root);
+ if (!alloc.reserve()) return false;
+
impl::remove_node(n._root);
- impl::destroy_node(n._root, impl::get_allocator(_root));
+ impl::destroy_node(n._root, alloc);
return true;
}
@@ -5325,7 +5980,7 @@ namespace pugi
if (i->name && impl::strequal(name_, i->name))
{
for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
- if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT("")))
+ if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
return xml_node(i);
}
@@ -5338,7 +5993,7 @@ namespace pugi
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
- if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT("")))
+ if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
return xml_node(i);
return xml_node();
@@ -5604,7 +6259,7 @@ namespace pugi
{
xml_node_struct* d = _data();
- return (d && d->value) ? d->value : PUGIXML_TEXT("");
+ return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
}
PUGI__FN const char_t* xml_text::as_string(const char_t* def) const
@@ -5618,35 +6273,35 @@ namespace pugi
{
xml_node_struct* d = _data();
- return impl::get_value_int(d ? d->value : 0, def);
+ return impl::get_value_int(d ? d->value + 0 : 0, def);
}
PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
{
xml_node_struct* d = _data();
- return impl::get_value_uint(d ? d->value : 0, def);
+ return impl::get_value_uint(d ? d->value + 0 : 0, def);
}
PUGI__FN double xml_text::as_double(double def) const
{
xml_node_struct* d = _data();
- return impl::get_value_double(d ? d->value : 0, def);
+ return impl::get_value_double(d ? d->value + 0 : 0, def);
}
PUGI__FN float xml_text::as_float(float def) const
{
xml_node_struct* d = _data();
- return impl::get_value_float(d ? d->value : 0, def);
+ return impl::get_value_float(d ? d->value + 0 : 0, def);
}
PUGI__FN bool xml_text::as_bool(bool def) const
{
xml_node_struct* d = _data();
- return impl::get_value_bool(d ? d->value : 0, def);
+ return impl::get_value_bool(d ? d->value + 0 : 0, def);
}
#ifdef PUGIXML_HAS_LONG_LONG
@@ -5654,14 +6309,14 @@ namespace pugi
{
xml_node_struct* d = _data();
- return impl::get_value_llong(d ? d->value : 0, def);
+ return impl::get_value_llong(d ? d->value + 0 : 0, def);
}
PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
{
xml_node_struct* d = _data();
- return impl::get_value_ullong(d ? d->value : 0, def);
+ return impl::get_value_ullong(d ? d->value + 0 : 0, def);
}
#endif
@@ -6050,8 +6705,14 @@ namespace pugi
{
assert(!_root);
+ #ifdef PUGIXML_COMPACT
+ const size_t page_offset = sizeof(uint32_t);
+ #else
+ const size_t page_offset = 0;
+ #endif
+
// initialize sentinel page
- PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) <= sizeof(_memory));
+ PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) + page_offset <= sizeof(_memory));
// align upwards to page boundary
void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1));
@@ -6062,8 +6723,14 @@ namespace pugi
page->busy_size = impl::xml_memory_page_size;
+ // setup first page marker
+ #ifdef PUGIXML_COMPACT
+ page->compact_page_marker = reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page));
+ *page->compact_page_marker = sizeof(impl::xml_memory_page);
+ #endif
+
// allocate new root
- _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)) impl::xml_document_struct(page);
+ _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);
_root->prev_sibling_c = _root;
// setup sentinel page
@@ -6091,7 +6758,7 @@ namespace pugi
}
// destroy dynamic storage, leave sentinel page (it's in static memory)
- impl::xml_memory_page* root_page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
+ impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
assert(root_page && !root_page->prev);
assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
@@ -6104,6 +6771,11 @@ namespace pugi
page = next;
}
+ #ifdef PUGIXML_COMPACT
+ // destroy hash table
+ static_cast<impl::xml_document_struct*>(_root)->hash.clear();
+ #endif
+
_root = 0;
}
@@ -8781,7 +9453,7 @@ PUGI__NS_BEGIN
{
assert(a);
- const char_t* name = a->name ? a->name : PUGIXML_TEXT("");
+ const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT("");
switch (_test)
{
@@ -11728,6 +12400,8 @@ namespace pugi
#undef PUGI__NS_END
#undef PUGI__FN
#undef PUGI__FN_NO_INLINE
+#undef PUGI__GETPAGE_IMPL
+#undef PUGI__GETPAGE
#undef PUGI__NODETYPE
#undef PUGI__IS_CHARTYPE_IMPL
#undef PUGI__IS_CHARTYPE
diff --git a/tests/autotest-appveyor.ps1 b/tests/autotest-appveyor.ps1
index 8b7a24c..6b88766 100644
--- a/tests/autotest-appveyor.ps1
+++ b/tests/autotest-appveyor.ps1
@@ -21,7 +21,7 @@ foreach ($vs in 9,10,11,12)
Invoke-CmdScript "C:\Program Files (x86)\Microsoft Visual Studio $vs.0\VC\vcvarsall.bat" $arch
if (! $?) { throw "Error setting up VS$vs $arch" }
- foreach ($defines in "standard", "PUGIXML_WCHAR_MODE")
+ foreach ($defines in "standard", "PUGIXML_WCHAR_MODE", "PUGIXML_COMPACT")
{
$target = "tests_vs${vs}_${arch}_${defines}"
$deflist = if ($defines -eq "standard") { "" } else { "/D$defines" }
diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp
index f2877ff..365561f 100644
--- a/tests/test_dom_modify.cpp
+++ b/tests/test_dom_modify.cpp
@@ -948,6 +948,25 @@ TEST(dom_node_memory_limit)
}
}
+TEST(dom_node_memory_limit_pi)
+{
+ const unsigned int length = 65536;
+ static char_t string[length + 1];
+
+ for (unsigned int i = 0; i < length; ++i) string[i] = 'a';
+ string[length] = 0;
+
+ test_runner::_memory_fail_threshold = 32768 * 2 + sizeof(string);
+
+ xml_document doc;
+
+ for (int j = 0; j < 32; ++j)
+ {
+ CHECK(doc.append_child(node_pi).set_value(string));
+ CHECK(doc.remove_child(doc.first_child()));
+ }
+}
+
TEST(dom_node_doctype_top_level)
{
xml_document doc;
@@ -1116,6 +1135,11 @@ TEST(dom_node_append_buffer_out_of_memory_nodes)
test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(char_t) + 32;
+#ifdef PUGIXML_COMPACT
+ // ... and some space for hash table
+ test_runner::_memory_fail_threshold += 2048;
+#endif
+
xml_document doc;
CHECK_ALLOC_FAIL(CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(char_t), parse_fragment).status == status_out_of_memory));
@@ -1132,9 +1156,9 @@ TEST(dom_node_append_buffer_out_of_memory_nodes)
TEST(dom_node_append_buffer_out_of_memory_name)
{
- test_runner::_memory_fail_threshold = 32768 + 128;
+ test_runner::_memory_fail_threshold = 32768 + 4096;
- char data[128] = {0};
+ char data[4096] = {0};
xml_document doc;
CHECK(doc.append_child(STR("root")));
@@ -1378,6 +1402,11 @@ TEST(dom_node_copy_copyless)
// the document is parsed in-place so there should only be 1 page worth of allocations
test_runner::_memory_fail_threshold = 32768 + 128;
+#ifdef PUGIXML_COMPACT
+ // ... and some space for hash table
+ test_runner::_memory_fail_threshold += 2048;
+#endif
+
xml_document doc;
CHECK(doc.load_buffer_inplace(&datacopy[0], datacopy.size() * sizeof(char_t), parse_full));
@@ -1455,6 +1484,11 @@ TEST(dom_node_copy_attribute_copyless)
// the document is parsed in-place so there should only be 1 page worth of allocations
test_runner::_memory_fail_threshold = 32768 + 128;
+#ifdef PUGIXML_COMPACT
+ // ... and some space for hash table
+ test_runner::_memory_fail_threshold += 2048;
+#endif
+
xml_document doc;
CHECK(doc.load_buffer_inplace(&datacopy[0], datacopy.size() * sizeof(char_t), parse_full));
diff --git a/tests/test_memory.cpp b/tests/test_memory.cpp
index bd80ca1..85d6e86 100644
--- a/tests/test_memory.cpp
+++ b/tests/test_memory.cpp
@@ -1,30 +1,38 @@
#include "common.hpp"
#include "writer_string.hpp"
+#include "allocator.hpp"
#include <string>
+#include <vector>
namespace
{
- int allocate_count = 0;
- int deallocate_count = 0;
+ int page_allocs = 0;
+ int page_deallocs = 0;
+
+ bool is_page(size_t size)
+ {
+ return size >= 16384;
+ }
void* allocate(size_t size)
{
- ++allocate_count;
- return new char[size];
+ void* ptr = memory_allocate(size);
+ page_allocs += is_page(memory_size(ptr));
+ return ptr;
}
void deallocate(void* ptr)
{
- ++deallocate_count;
- delete[] reinterpret_cast<char*>(ptr);
+ page_deallocs += is_page(memory_size(ptr));
+ memory_deallocate(ptr);
}
}
TEST(memory_custom_memory_management)
{
- allocate_count = deallocate_count = 0;
+ page_allocs = page_deallocs = 0;
// remember old functions
allocation_function old_allocate = get_memory_allocation_function();
@@ -37,30 +45,30 @@ TEST(memory_custom_memory_management)
// parse document
xml_document doc;
- CHECK(allocate_count == 0 && deallocate_count == 0);
+ CHECK(page_allocs == 0 && page_deallocs == 0);
CHECK(doc.load_string(STR("<node />")));
- CHECK(allocate_count == 2 && deallocate_count == 0);
+ CHECK(page_allocs == 1 && page_deallocs == 0);
// modify document (no new page)
CHECK(doc.first_child().set_name(STR("foobars")));
- CHECK(allocate_count == 2 && deallocate_count == 0);
+ CHECK(page_allocs == 1 && page_deallocs == 0);
// modify document (new page)
std::basic_string<pugi::char_t> s(65536, 'x');
CHECK(doc.first_child().set_name(s.c_str()));
- CHECK(allocate_count == 3 && deallocate_count == 0);
+ CHECK(page_allocs == 2 && page_deallocs == 0);
// modify document (new page, old one should die)
s += s;
CHECK(doc.first_child().set_name(s.c_str()));
- CHECK(allocate_count == 4 && deallocate_count == 1);
+ CHECK(page_allocs == 3 && page_deallocs == 1);
}
- CHECK(allocate_count == 4 && deallocate_count == 4);
+ CHECK(page_allocs == 3 && page_deallocs == 3);
// restore old functions
set_memory_management_functions(old_allocate, old_deallocate);
@@ -68,7 +76,7 @@ TEST(memory_custom_memory_management)
TEST(memory_large_allocations)
{
- allocate_count = deallocate_count = 0;
+ page_allocs = page_deallocs = 0;
// remember old functions
allocation_function old_allocate = get_memory_allocation_function();
@@ -80,7 +88,7 @@ TEST(memory_large_allocations)
{
xml_document doc;
- CHECK(allocate_count == 0 && deallocate_count == 0);
+ CHECK(page_allocs == 0 && page_deallocs == 0);
// initial fill
for (size_t i = 0; i < 128; ++i)
@@ -90,7 +98,7 @@ TEST(memory_large_allocations)
CHECK(doc.append_child(node_pcdata).set_value(s.c_str()));
}
- CHECK(allocate_count > 0 && deallocate_count == 0);
+ CHECK(page_allocs > 0 && page_deallocs == 0);
// grow-prune loop
while (doc.first_child())
@@ -116,15 +124,72 @@ TEST(memory_large_allocations)
}
}
- CHECK(allocate_count == deallocate_count + 1); // only one live page left (it waits for new allocations)
+ CHECK(page_allocs == page_deallocs + 1); // only one live page left (it waits for new allocations)
+
+ char buffer;
+ CHECK(doc.load_buffer_inplace(&buffer, 0, parse_fragment, get_native_encoding()));
+
+ CHECK(page_allocs == page_deallocs); // no live pages left
+ }
+
+ CHECK(page_allocs == page_deallocs); // everything is freed
+
+ // restore old functions
+ set_memory_management_functions(old_allocate, old_deallocate);
+}
+
+TEST(memory_page_management)
+{
+ page_allocs = page_deallocs = 0;
+
+ // remember old functions
+ allocation_function old_allocate = get_memory_allocation_function();
+ deallocation_function old_deallocate = get_memory_deallocation_function();
+
+ // replace functions
+ set_memory_management_functions(allocate, deallocate);
+
+ {
+ xml_document doc;
+
+ CHECK(page_allocs == 0 && page_deallocs == 0);
+
+ // initial fill
+ std::vector<xml_node> nodes;
+
+ for (size_t i = 0; i < 4000; ++i)
+ {
+ xml_node node = doc.append_child(STR("node"));
+ CHECK(node);
+
+ nodes.push_back(node);
+ }
+
+ CHECK(page_allocs > 0 && page_deallocs == 0);
+
+ // grow-prune loop
+ size_t offset = 0;
+ size_t prime = 15485863;
+
+ while (nodes.size() > 0)
+ {
+ offset = (offset + prime) % nodes.size();
+
+ doc.remove_child(nodes[offset]);
+
+ nodes[offset] = nodes.back();
+ nodes.pop_back();
+ }
+
+ CHECK(page_allocs == page_deallocs + 1); // only one live page left (it waits for new allocations)
char buffer;
CHECK(doc.load_buffer_inplace(&buffer, 0, parse_fragment, get_native_encoding()));
- CHECK(allocate_count == deallocate_count); // no live pages left
+ CHECK(page_allocs == page_deallocs); // no live pages left
}
- CHECK(allocate_count == deallocate_count); // everything is freed
+ CHECK(page_allocs == page_deallocs); // everything is freed
// restore old functions
set_memory_management_functions(old_allocate, old_deallocate);
diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp
index f72d6ff..c4a3b7f 100644
--- a/tests/test_xpath_variables.cpp
+++ b/tests/test_xpath_variables.cpp
@@ -445,7 +445,7 @@ TEST_XML(xpath_variables_copy, "<node />")
CHECK(!set3.get(STR("a")));
}
-TEST_XML(xpath_variables_copy_out_of_memory, "<node />")
+TEST_XML(xpath_variables_copy_out_of_memory, "<node1 /><node2 />")
{
xpath_variable_set set1;
set1.set(STR("a"), true);
@@ -471,7 +471,7 @@ TEST_XML(xpath_variables_copy_out_of_memory, "<node />")
CHECK(set2.get(STR("a"))->get_boolean() == true);
CHECK(set2.get(STR("b"))->get_number() == 2.0);
CHECK_STRING(set2.get(STR("c"))->get_string(), STR("string"));
- CHECK(set2.get(STR("d"))->get_node_set().size() == 1);
+ CHECK(set2.get(STR("d"))->get_node_set().size() == 2);
}
#if __cplusplus >= 201103