Newer
Older
* --------------------------------------------------------
* Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at http://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
arseny.kapoulkine@gmail.com
committed
#ifndef SOURCE_PUGIXML_CPP
#define SOURCE_PUGIXML_CPP
#include "pugixml.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
arseny.kapoulkine@gmail.com
committed
#ifdef PUGIXML_WCHAR_MODE
# include <wchar.h>
#endif
arseny.kapoulkine
committed
#ifndef PUGIXML_NO_XPATH
# include <math.h>
# include <float.h>
# ifdef PUGIXML_NO_EXCEPTIONS
# include <setjmp.h>
# endif
arseny.kapoulkine
committed
#endif
#ifndef PUGIXML_NO_STL
# include <istream>
# include <ostream>
# include <string>
#endif
// For placement new
#include <new>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4324) // structure was padded due to __declspec(align())
arseny.kapoulkine
committed
# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // this function or variable may be unsafe
# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
#endif
#ifdef __INTEL_COMPILER
# pragma warning(disable: 177) // function was declared but never referenced
# pragma warning(disable: 279) // controlling expression is constant
# pragma warning(disable: 1478 1786) // function was declared "deprecated"
# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type
arseny.kapoulkine@gmail.com
committed
#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away
#endif
# pragma option push
# pragma warn -8008 // condition is always false
# pragma warn -8066 // unreachable code
#endif
#ifdef __SNC__
arseny.kapoulkine@gmail.com
committed
// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug
# pragma diag_suppress=178 // function was declared but never referenced
arseny.kapoulkine
committed
# pragma diag_suppress=237 // controlling expression is constant
#endif
// Inlining controls
#if defined(_MSC_VER) && _MSC_VER >= 1300
arseny.kapoulkine@gmail.com
committed
# define PUGI__NO_INLINE __declspec(noinline)
#elif defined(__GNUC__)
arseny.kapoulkine@gmail.com
committed
# define PUGI__NO_INLINE __attribute__((noinline))
arseny.kapoulkine@gmail.com
committed
# define PUGI__NO_INLINE
// Branch weight controls
#if defined(__GNUC__)
# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define PUGI__UNLIKELY(cond) (cond)
#endif
// Simple static assertion
arseny.kapoulkine@gmail.com
committed
#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
arseny.kapoulkine
committed
// Digital Mars C++ bug workaround for passing char loaded from memory via stack
#ifdef __DMC__
arseny.kapoulkine@gmail.com
committed
# define PUGI__DMC_VOLATILE volatile
arseny.kapoulkine
committed
#else
arseny.kapoulkine@gmail.com
committed
# define PUGI__DMC_VOLATILE
arseny.kapoulkine
committed
#endif
arseny.kapoulkine@gmail.com
committed
// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
using std::memcpy;
using std::memmove;
#endif
arseny.kapoulkine
committed
// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
#if defined(_MSC_VER) && !defined(__S3E__)
arseny.kapoulkine@gmail.com
committed
# define PUGI__MSVC_CRT_VERSION _MSC_VER
arseny.kapoulkine
committed
#endif
arseny.kapoulkine@gmail.com
committed
#ifdef PUGIXML_HEADER_ONLY
# define PUGI__NS_BEGIN namespace pugi { namespace impl {
# define PUGI__NS_END } }
# define PUGI__FN inline
# define PUGI__FN_NO_INLINE inline
arseny.kapoulkine@gmail.com
committed
#else
# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
# define PUGI__NS_BEGIN namespace pugi { namespace impl {
# define PUGI__NS_END } }
# else
# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
# define PUGI__NS_END } } }
# endif
# define PUGI__FN
# define PUGI__FN_NO_INLINE PUGI__NO_INLINE
arseny.kapoulkine@gmail.com
committed
#endif
// uintptr_t
#if !defined(_MSC_VER) || _MSC_VER >= 1600
# include <stdint.h>
#else
# ifndef _UINTPTR_T_DEFINED
// No native uintptr_t in MSVC6 and in some WinCE versions
typedef size_t uintptr_t;
#define _UINTPTR_T_DEFINED
# endif
PUGI__NS_BEGIN
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
arseny.kapoulkine@gmail.com
committed
PUGI__NS_END
#endif
arseny.kapoulkine@gmail.com
committed
PUGI__NS_BEGIN
PUGI__FN void* default_allocate(size_t size)
{
return malloc(size);
}
arseny.kapoulkine@gmail.com
committed
PUGI__FN void default_deallocate(void* ptr)
{
free(ptr);
}
template <typename T>
struct xml_memory_management_function_storage
{
static allocation_function allocate;
static deallocation_function deallocate;
};
arseny.kapoulkine@gmail.com
committed
// Global allocation functions are stored in class statics so that in header mode linker deduplicates them
// Without a template<> we'll get multiple definitions of the same static
template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
arseny.kapoulkine@gmail.com
committed
typedef xml_memory_management_function_storage<int> xml_memory;
arseny.kapoulkine@gmail.com
committed
PUGI__NS_END
arseny.kapoulkine
committed
// String utilities
arseny.kapoulkine@gmail.com
committed
PUGI__NS_BEGIN
arseny.kapoulkine
committed
// Get string length
arseny.kapoulkine@gmail.com
committed
PUGI__FN size_t strlength(const char_t* s)
arseny.kapoulkine
committed
#ifdef PUGIXML_WCHAR_MODE
return wcslen(s);
#else
return strlen(s);
#endif
arseny.kapoulkine
committed
// Compare two strings
arseny.kapoulkine@gmail.com
committed
PUGI__FN bool strequal(const char_t* src, const char_t* dst)
arseny.kapoulkine
committed
#ifdef PUGIXML_WCHAR_MODE
return wcscmp(src, dst) == 0;
#else
return strcmp(src, dst) == 0;
#endif
}
arseny.kapoulkine
committed
// Compare lhs with [rhs_begin, rhs_end)
arseny.kapoulkine@gmail.com
committed
PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
arseny.kapoulkine
committed
{
for (size_t i = 0; i < count; ++i)
if (lhs[i] != rhs[i])
return false;
return lhs[count] == 0;
}
arseny.kapoulkine@gmail.com
committed
// Get length of wide string, even if CRT lacks wide character support
PUGI__FN size_t strlength_wide(const wchar_t* s)
{
assert(s);
#ifdef PUGIXML_WCHAR_MODE
return wcslen(s);
#else
const wchar_t* end = s;
while (*end) end++;
return static_cast<size_t>(end - s);
#endif
}
#ifdef PUGIXML_WCHAR_MODE
arseny.kapoulkine
committed
// Convert string to wide string, assuming all symbols are ASCII
arseny.kapoulkine@gmail.com
committed
PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
arseny.kapoulkine
committed
{
for (const char* i = source; *i; ++i) *dest++ = *i;
*dest = 0;
arseny.kapoulkine
committed
#endif
arseny.kapoulkine@gmail.com
committed
PUGI__NS_END
arseny.kapoulkine
committed
#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
// auto_ptr-like object for exception recovery
arseny.kapoulkine@gmail.com
committed
PUGI__NS_BEGIN
template <typename T, typename D = void(*)(T*)> struct auto_deleter
arseny.kapoulkine
committed
{
arseny.kapoulkine
committed
auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)
arseny.kapoulkine
committed
{
}
arseny.kapoulkine
committed
{
if (data) deleter(data);
}
arseny.kapoulkine
committed
{
arseny.kapoulkine
committed
data = 0;
return result;
}
};
arseny.kapoulkine@gmail.com
committed
PUGI__NS_END
arseny.kapoulkine
committed
#endif
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#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;
}
assert(_count < _capacity * 3 / 4);
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
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;
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;
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
arseny.kapoulkine@gmail.com
committed
PUGI__NS_BEGIN
arseny.kapoulkine@gmail.com
committed
static const size_t xml_memory_page_size =
#ifdef PUGIXML_MEMORY_PAGE_SIZE
PUGIXML_MEMORY_PAGE_SIZE
#else
32768
#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);
// 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;
struct xml_memory_page
{
static xml_memory_page* construct(void* memory)
{
xml_memory_page* result = static_cast<xml_memory_page*>(memory);
result->allocator = 0;
result->prev = 0;
result->next = 0;
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;
return result;
}
xml_allocator* allocator;
xml_memory_page* prev;
xml_memory_page* next;
size_t busy_size;
size_t freed_size;
#ifdef PUGIXML_COMPACT
char_t* compact_string_base;
void* compact_shared_parent;
};
struct xml_memory_string_header
{
uint16_t page_offset; // offset from page->data
uint16_t full_size; // 0 if string occupies whole page
};
struct xml_allocator
{
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)
{
size_t size = sizeof(xml_memory_page) + data_size;
// allocate block with some alignment, leaving memory for worst-case padding
arseny.kapoulkine@gmail.com
committed
void* memory = xml_memory::allocate(size + xml_memory_page_alignment);
if (!memory) return 0;
// align to next page boundary (note: this guarantees at least 1 usable byte before the page)
char* page_memory = reinterpret_cast<char*>((reinterpret_cast<uintptr_t>(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1));
// prepare page structure
xml_memory_page* page = xml_memory_page::construct(page_memory);
arseny.kapoulkine@gmail.com
committed
assert(page);
page->allocator = _root->allocator;
// record the offset for freeing the memory block
assert(page_memory > memory && page_memory - static_cast<char*>(memory) <= 127);
page_memory[-1] = static_cast<char>(page_memory - static_cast<char*>(memory));
return page;
}
static void deallocate_page(xml_memory_page* page)
{
char* page_memory = reinterpret_cast<char*>(page);
xml_memory::deallocate(page_memory - page_memory[-1]);
}
void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
void* allocate_memory(size_t size, xml_memory_page*& 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;
_busy_size += size;
out_page = _root;
return buf;
}
void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
{
if (page == _root) page->busy_size = _busy_size;
assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);
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 = 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
{
assert(_root != page);
assert(page->prev);
// remove from the list
page->prev->next = page->next;
page->next->prev = page->prev;
// deallocate
deallocate_page(page);
}
}
}
char_t* allocate_string(size_t length)
{
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 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));
if (!header) return 0;
// setup header
ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
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) / xml_memory_block_alignment);
// full_size == 0 for large strings that occupy the whole page
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 / 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
arseny.kapoulkine@gmail.com
committed
return static_cast<char_t*>(static_cast<void*>(header + 1));
}
void deallocate_string(char_t* string)
{
// this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
// we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string
arseny.kapoulkine@gmail.com
committed
arseny.kapoulkine@gmail.com
committed
xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
arseny.kapoulkine@gmail.com
committed
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 * 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;
compact_hash_table* _hash;
arseny.kapoulkine@gmail.com
committed
PUGI__FN_NO_INLINE void* xml_allocator::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 = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
out_page = page;
arseny.kapoulkine@gmail.com
committed
if (!page) return 0;
if (size <= large_allocation_threshold)
{
_root->busy_size = _busy_size;
// insert page at the end of linked list
page->prev = _root;
_root->next = page;
_root = page;
_busy_size = size;
}
else
{
arseny.kapoulkine
committed
// insert page before the end of linked list, so that it is deleted as soon as possible
// the last page is not deleted even if it's empty (see deallocate_memory)
assert(_root->prev);
page->prev = _root->prev;
page->next = _root;
_root->prev->next = page;
_root->prev = page;
return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
arseny.kapoulkine@gmail.com
committed
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(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)
void operator|=(uintptr_t mod)
uintptr_t operator&(uintptr_t mod) const
}
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));
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)
{
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;
}
{
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
{
}
private:
unsigned char _data;
};
template <typename T, int header_offset> class compact_pointer_parent
compact_pointer_parent(): _data(0)
{
}
void operator=(const compact_pointer_parent& rhs)
{
*this = rhs + 0;
}
// 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);
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;
compact_set_value<header_offset>(this, value);
_data = 65535;
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
{
uint16_t _data;
template <int header_offset, int base_offset> class compact_string
compact_string(): _data(0)
void operator=(const compact_string& rhs)
}
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) < 254)
_data = static_cast<unsigned char>(remainder + 1);
compact_set_value<header_offset>(this, value);
_data = 255;
}
else
{
compact_set_value<header_offset>(this, value);
_data = 255;
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:
};
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);