diff --git a/GNUmakefile b/GNUmakefile index eaac008523a..2ac18fd3aba 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -101,7 +101,7 @@ tool_targets += tools/tracemerge endif # FVTest Helper Libraries -test_prereqs := third_party/pugixml-1.5 fvtest/util fvtest/omrGtestGlue +test_prereqs := third_party/pugixml-1.8 fvtest/util fvtest/omrGtestGlue test_targets += $(test_prereqs) # Utility Libraries diff --git a/longabout.html b/longabout.html index fd9b7fc5b02..153c323864a 100644 --- a/longabout.html +++ b/longabout.html @@ -66,8 +66,8 @@

Google C++ Testing Framework 1.7.0

The source is available at https://github.com/google/googletest.

-

pugixml 1.5

-

Copyright (c) 2006-2015 Arseny Kapoulkine

+

pugixml 1.8

+

Copyright (c) 2006-2016 Arseny Kapoulkine

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -88,7 +88,7 @@

pugixml 1.5

THE SOFTWARE.

-The source is available at http://pugixml.org/2014/11/27/pugixml-1.5-release.html. +The source is available at https://github.com/zeux/pugixml/releases/tag/v1.8.

config.sub and config.guess

diff --git a/omrmakefiles/configure.mk.in b/omrmakefiles/configure.mk.in index 0d85e1b915a..f163b145c24 100644 --- a/omrmakefiles/configure.mk.in +++ b/omrmakefiles/configure.mk.in @@ -216,7 +216,7 @@ GLOBAL_LIBPATH += . $(exe_output_dir) $(lib_output_dir) # Location of fvtest framework OMR_GTEST_DIR := $(top_srcdir)/third_party/gtest-1.8.0 OMR_GTEST_INCLUDES := $(OMR_GTEST_DIR) $(OMR_GTEST_DIR)/include $(top_srcdir)/fvtest/omrGtestGlue -OMR_PUGIXML_DIR := $(top_srcdir)/third_party/pugixml-1.5 +OMR_PUGIXML_DIR := $(top_srcdir)/third_party/pugixml-1.8 # googletest code requires exception handling ifeq (linux,$(OMR_HOST_OS)) diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 5fbb1fff868..e25e1211b53 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -20,5 +20,5 @@ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 ############################################################################### -add_subdirectory(pugixml-1.5) +add_subdirectory(pugixml-1.8) # TODO: gtest/gmock diff --git a/third_party/pugixml-1.5/makefile b/third_party/pugixml-1.5/makefile deleted file mode 100644 index a83b0d090a9..00000000000 --- a/third_party/pugixml-1.5/makefile +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# Copyright IBM Corp. and others 2015 -# -# This program and the accompanying materials are made available under -# the terms of the Eclipse Public License 2.0 which accompanies this -# distribution and is available at https://www.eclipse.org/legal/epl-2.0/ -# or the Apache License, Version 2.0 which accompanies this distribution and -# is available at https://www.apache.org/licenses/LICENSE-2.0. -# -# This Source Code may also be made available under the following -# Secondary Licenses when the conditions for such availability set -# forth in the Eclipse Public License, v. 2.0 are satisfied: GNU -# General Public License, version 2 with the GNU Classpath -# Exception [1] and GNU General Public License, version 2 with the -# OpenJDK Assembly Exception [2]. -# -# [1] https://www.gnu.org/software/classpath/license.html -# [2] https://openjdk.org/legal/assembly-exception.html -# -# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 -############################################################################### - -top_srcdir := ../.. -include $(top_srcdir)/omrmakefiles/configure.mk - -MODULE_NAME := pugixml -ARTIFACT_TYPE := archive -OBJECTS := pugixml$(OBJEXT) - -include $(top_srcdir)/omrmakefiles/rules.mk - diff --git a/third_party/pugixml-1.5/CMakeLists.txt b/third_party/pugixml-1.8/CMakeLists.txt similarity index 100% rename from third_party/pugixml-1.5/CMakeLists.txt rename to third_party/pugixml-1.8/CMakeLists.txt diff --git a/third_party/pugixml-1.5/pugiconfig.hpp b/third_party/pugixml-1.8/pugiconfig.hpp similarity index 92% rename from third_party/pugixml-1.5/pugiconfig.hpp rename to third_party/pugixml-1.8/pugiconfig.hpp index 26b1ef6dc97..52aa7e10c97 100644 --- a/third_party/pugixml-1.5/pugiconfig.hpp +++ b/third_party/pugixml-1.8/pugiconfig.hpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.5 + * pugixml parser - version 1.8 * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2016, 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 @@ -17,6 +17,9 @@ // Uncomment this to enable wchar_t mode // #define PUGIXML_WCHAR_MODE +// Uncomment this to enable compact mode +// #define PUGIXML_COMPACT + // Uncomment this to disable XPath // #define PUGIXML_NO_XPATH @@ -39,7 +42,6 @@ // Uncomment this to switch to header-only version // #define PUGIXML_HEADER_ONLY -// #include "pugixml.cpp" // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG @@ -47,7 +49,7 @@ #endif /** - * Copyright (c) 2006-2014 Arseny Kapoulkine + * Copyright (c) 2006-2016 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -60,7 +62,7 @@ * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND diff --git a/third_party/pugixml-1.5/pugixml.cpp b/third_party/pugixml-1.8/pugixml.cpp similarity index 80% rename from third_party/pugixml-1.5/pugixml.cpp rename to third_party/pugixml-1.8/pugixml.cpp index 59a2ec7b255..cac51a53426 100644 --- a/third_party/pugixml-1.5/pugixml.cpp +++ b/third_party/pugixml-1.8/pugixml.cpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.5 + * pugixml parser - version 1.8 * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2016, 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 @@ -11,12 +11,6 @@ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ -/* - * =========================================================================== - * Copyright IBM Corp. and others 2018 - * =========================================================================== - */ - #ifndef SOURCE_PUGIXML_CPP #define SOURCE_PUGIXML_CPP @@ -26,6 +20,7 @@ #include #include #include +#include #ifdef PUGIXML_WCHAR_MODE # include @@ -59,7 +54,7 @@ #endif #ifdef __INTEL_COMPILER -# pragma warning(disable: 177) // function was declared but never referenced +# 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 @@ -87,7 +82,7 @@ #elif defined(__GNUC__) # define PUGI__NO_INLINE __attribute__((noinline)) #else -# define PUGI__NO_INLINE +# define PUGI__NO_INLINE #endif // Branch weight controls @@ -111,6 +106,14 @@ #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; +using std::memset; +#endif + +// Some MinGW versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions in strict ANSI mode +#if defined(PUGIXML_HAS_LONG_LONG) && defined(__MINGW32__) && defined(__STRICT_ANSI__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) +# define LLONG_MAX 9223372036854775807LL +# define LLONG_MIN (-LLONG_MAX-1) +# define ULLONG_MAX (2ULL*LLONG_MAX+1) #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features @@ -136,25 +139,21 @@ using std::memmove; #endif // uintptr_t -#if !defined(_MSC_VER) || _MSC_VER >= 1600 -# include -#else +#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) +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 +} +#else +# include #endif -#if __cplusplus < 201103L -#define snprintf(buf, buf_size, format, ...) sprintf(buf, format, __VA_ARGS__) -#endif /* __cplusplus < 201103L */ - // Memory allocation PUGI__NS_BEGIN PUGI__FN void* default_allocate(size_t size) @@ -214,7 +213,7 @@ PUGI__NS_BEGIN for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; - + return lhs[count] == 0; } @@ -231,63 +230,206 @@ PUGI__NS_BEGIN return static_cast(end - s); #endif } - -#ifdef PUGIXML_WCHAR_MODE - // Convert string to wide string, assuming all symbols are ASCII - PUGI__FN void widen_ascii(wchar_t* dest, const char* source) - { - for (const char* i = source; *i; ++i) *dest++ = *i; - *dest = 0; - } -#endif PUGI__NS_END -#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) -// auto_ptr-like buffer holder for exception recovery +// auto_ptr-like object for exception recovery PUGI__NS_BEGIN - struct buffer_holder + template struct auto_deleter { - void* data; - void (*deleter)(void*); + typedef void (*D)(T*); - buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_) + T* data; + D deleter; + + auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) { } - ~buffer_holder() + ~auto_deleter() { if (data) deleter(data); } - void* release() + T* release() { - void* result = data; + T* result = data; data = 0; return result; } }; PUGI__NS_END + +#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(false && "Hash table is full"); + return 0; + } + + void** insert(const void* key) + { + assert(key); + assert(_capacity != 0 && _count < _capacity - _capacity / 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(false && "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(reinterpret_cast(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(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; + + assert(_count == rt._count); + + return true; + } + +PUGI__NS_END #endif PUGI__NS_BEGIN - 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; +#else + static const uintptr_t xml_memory_block_alignment = sizeof(void*); +#endif - static const uintptr_t xml_memory_page_alignment = 64; - static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); - 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; + // extra metadata bits + static const uintptr_t xml_memory_page_contents_shared_mask = 64; + static const uintptr_t xml_memory_page_name_allocated_mask = 32; + static const uintptr_t xml_memory_page_value_allocated_mask = 16; + static const uintptr_t xml_memory_page_type_mask = 15; + + // 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; - #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1) +#ifdef PUGIXML_COMPACT + #define PUGI__GETHEADER_IMPL(object, page, flags) // unused + #define PUGI__GETPAGE_IMPL(header) (header).get_page() +#else + #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) + // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + #define PUGI__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) +#endif + + #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) + #define PUGI__NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) struct xml_allocator; @@ -303,6 +445,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; } @@ -313,8 +461,22 @@ 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 }; + static const size_t xml_memory_page_size = + #ifdef PUGIXML_MEMORY_PAGE_SIZE + (PUGIXML_MEMORY_PAGE_SIZE) + #else + 32768 + #endif + - sizeof(xml_memory_page); + struct xml_memory_string_header { uint16_t page_offset; // offset from page->data @@ -325,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) @@ -332,37 +497,29 @@ PUGI__NS_BEGIN size_t size = sizeof(xml_memory_page) + data_size; // allocate block with some alignment, leaving memory for worst-case padding - void* memory = xml_memory::allocate(size + xml_memory_page_alignment); + void* memory = xml_memory::allocate(size); 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((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1)); - // prepare page structure - xml_memory_page* page = xml_memory_page::construct(page_memory); + xml_memory_page* page = xml_memory_page::construct(memory); assert(page); page->allocator = _root->allocator; - // record the offset for freeing the memory block - assert(page_memory > memory && page_memory - static_cast(memory) <= 127); - page_memory[-1] = static_cast(page_memory - static_cast(memory)); - return page; } static void deallocate_page(xml_memory_page* page) { - char* page_memory = reinterpret_cast(page); - - xml_memory::deallocate(page_memory - page_memory[-1]); + xml_memory::deallocate(page); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); 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(_root) + sizeof(xml_memory_page) + _busy_size; @@ -373,6 +530,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(result) - reinterpret_cast(out_page->compact_page_marker); + + if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) + { + // insert new marker + uint32_t* marker = static_cast(result); + + *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(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; @@ -390,7 +585,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 @@ -410,13 +614,15 @@ PUGI__NS_BEGIN char_t* allocate_string(size_t length) { - PUGI__STATIC_ASSERT(xml_memory_page_size <= (1 << 16)); + static const size_t max_encoded_offset = (1 << 16) * 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(allocate_memory(full_size, page)); @@ -426,12 +632,14 @@ PUGI__NS_BEGIN // setup header ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); - assert(page_offset >= 0 && page_offset < (1 << 16)); - header->page_offset = static_cast(page_offset); + assert(page_offset % xml_memory_block_alignment == 0); + assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); + header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); // full_size == 0 for large strings that occupy the whole page - assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); - header->full_size = static_cast(full_size < (1 << 16) ? full_size : 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(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 @@ -448,102 +656,458 @@ PUGI__NS_BEGIN assert(header); // deallocate - size_t page_offset = sizeof(xml_memory_page) + header->page_offset; + size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); // if full_size == 0 then this string occupies the whole page - size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size; + size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * 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) { 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; + xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); + out_page = page; + + 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 + { + // 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; + + page->busy_size = size; + } + + return reinterpret_cast(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(this) - reinterpret_cast(page->compact_page_marker)); + assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); + + _page = static_cast(offset >> compact_alignment_log2); + _flags = static_cast(flags); + } + + void operator&=(uintptr_t mod) + { + _flags &= static_cast(mod); + } + + void operator|=(uintptr_t mod) + { + _flags |= static_cast(mod); + } + + uintptr_t operator&(uintptr_t mod) const + { + return _flags & mod; + } + + xml_memory_page* get_page() const + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); + const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); + + return const_cast(reinterpret_cast(static_cast(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(static_cast(object) - header_offset); + + return header->get_page(); + } + + template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) + { + return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object)); + } + + template 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 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(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; + + if (static_cast(offset) <= 253) + _data = static_cast(offset + 1); + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + _data = 0; + } + + operator T*() const + { + if (_data) + { + if (_data < 255) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + ((_data - 1 + start) << compact_alignment_log2)); + } + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + unsigned char _data; + }; + + template 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(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; + + if (static_cast(offset) <= 65533) + { + _data = static_cast(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(this, value); + + _data = 65535; + } + } + } + else + { + _data = 0; + } + } + + operator T*() const + { + if (_data) + { + if (_data < 65534) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + ((_data - 1 - 65533) << compact_alignment_log2)); + } + else if (_data == 65534) + return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + uint16_t _data; + }; + + template 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(offset) < (65535 << 7)) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + + if (*base == 0) + { + *base = static_cast((offset >> 7) + 1); + _data = static_cast((offset & 127) + 1); + } + else + { + ptrdiff_t remainder = offset - ((*base - 1) << 7); + + if (static_cast(remainder) <= 253) + { + _data = static_cast(remainder + 1); + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + } + else + { + compact_set_value(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); + + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + assert(*base); + + ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); + + return page->compact_string_base + offset; + } + else + { + return compact_get_value(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; - if (!page) return 0; + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; - if (size <= large_allocation_threshold) + impl::compact_pointer prev_attribute_c; + impl::compact_pointer next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) { - _root->busy_size = _busy_size; + PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); + } - // insert page at the end of linked list - page->prev = _root; - _root->next = page; - _root = page; + impl::compact_header header; - _busy_size = size; - } - else - { - // 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); + uint16_t namevalue_base; - page->prev = _root->prev; - page->next = _root; + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; - _root->prev->next = page; - _root->prev = page; - } + impl::compact_pointer_parent parent; - // allocate inside page - page->busy_size = size; + impl::compact_pointer first_child; - return reinterpret_cast(page) + sizeof(xml_memory_page); - } -PUGI__NS_END + impl::compact_pointer prev_sibling_c; + impl::compact_pointer next_sibling; + impl::compact_pointer 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(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) + xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) { + header = PUGI__GETHEADER_IMPL(this, page, 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(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): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { + header = PUGI__GETHEADER_IMPL(this, page, type); } 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 @@ -561,20 +1125,24 @@ PUGI__NS_BEGIN const char_t* buffer; xml_extra_buffer* extra_buffers; + + #ifdef PUGIXML_COMPACT + compact_hash_table hash; + #endif }; - inline xml_allocator& get_allocator(const xml_node_struct* node) + template inline xml_allocator& get_allocator(const Object* object) { - assert(node); + assert(object); - return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; + return *PUGI__GETPAGE(object)->allocator; } template inline xml_document_struct& get_document(const Object* object) { assert(object); - return *static_cast(reinterpret_cast(object->header & xml_memory_page_pointer_mask)->allocator); + return *static_cast(PUGI__GETPAGE(object)->allocator); } PUGI__NS_END @@ -583,7 +1151,8 @@ 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); + if (!memory) return 0; return new (memory) xml_attribute_struct(page); } @@ -591,27 +1160,30 @@ 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); + if (!memory) return 0; 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(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; ) { @@ -631,7 +1203,7 @@ PUGI__NS_BEGIN child = next; } - alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(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) @@ -803,6 +1375,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; @@ -813,6 +1387,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; @@ -1018,28 +1594,11 @@ PUGI__NS_BEGIN } }; - template struct wchar_selector; - - template <> struct wchar_selector<2> - { - typedef uint16_t type; - typedef utf16_counter counter; - typedef utf16_writer writer; - }; - - template <> struct wchar_selector<4> + struct utf8_decoder { - typedef uint32_t type; - typedef utf32_counter counter; - typedef utf32_writer writer; - }; - - typedef wchar_selector::counter wchar_counter; - typedef wchar_selector::writer wchar_writer; + typedef uint8_t type; - template struct utf_decoder - { - static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { const uint8_t utf8_byte_mask = 0x3f; @@ -1100,29 +1659,34 @@ PUGI__NS_BEGIN return result; } + }; - static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) - { - const uint16_t* end = data + size; + template struct utf16_decoder + { + typedef uint16_t type; - while (data < end) + template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) { - unsigned int lead = opt_swap::value ? endian_swap(*data) : *data; + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+D7FF if (lead < 0xD800) { result = Traits::low(result, lead); data += 1; + size -= 1; } // U+E000..U+FFFF else if (static_cast(lead - 0xE000) < 0x2000) { result = Traits::low(result, lead); data += 1; + size -= 1; } // surrogate pair lead - else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) + else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) { uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; @@ -1130,26 +1694,32 @@ PUGI__NS_BEGIN { result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); data += 2; + size -= 2; } else { data += 1; + size -= 1; } } else { data += 1; + size -= 1; } } return result; } + }; - static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) - { - const uint32_t* end = data + size; + template struct utf32_decoder + { + typedef uint32_t type; - while (data < end) + template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) { uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; @@ -1158,53 +1728,76 @@ PUGI__NS_BEGIN { result = Traits::low(result, lead); data += 1; + size -= 1; } // U+10000..U+10FFFF else { result = Traits::high(result, lead); data += 1; + size -= 1; } } return result; } + }; + + struct latin1_decoder + { + typedef uint8_t type; - static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result) + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { - for (size_t i = 0; i < size; ++i) + while (size) { - result = Traits::low(result, data[i]); + result = Traits::low(result, *data); + data += 1; + size -= 1; } return result; } + }; - static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf16_block(data, size, result); - } + template struct wchar_selector; - static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf32_block(data, size, result); - } + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + typedef utf16_decoder decoder; + }; - static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result) - { - return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result); - } + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + typedef utf32_decoder decoder; }; - template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length) + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + struct wchar_decoder { - for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); - } + typedef wchar_t type; + + template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) + { + typedef wchar_selector::decoder decoder; + + return decoder::process(reinterpret_cast(data), size, result, traits); + } + }; #ifdef PUGIXML_WCHAR_MODE PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { - for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); + for (size_t i = 0; i < length; ++i) + result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif PUGI__NS_END @@ -1222,7 +1815,6 @@ PUGI__NS_BEGIN ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : }; -#if !defined(OMR_EBCDIC) static const unsigned char chartype_table[256] = { 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 @@ -1243,27 +1835,6 @@ PUGI__NS_BEGIN 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }; -#else /* !defined(OMR_EBCDIC) */ - static const unsigned char chartype_table[256] = - { - 55, 0, 0, 0, 192, 12, 192, 0, 192, 192, 192, 0, 0, 63, 0, 0, - 0, 0, 0, 0, 192, 12, 0, 192, 0, 0, 192, 192, 0, 0, 0, 0, - 192, 192, 192, 192, 192, 192, 0, 0, 192, 192, 192, 192, 192, 0, 0, 0, - 192, 192, 0, 192, 192, 192, 192, 0, 192, 192, 192, 192, 0, 0, 192, 0, - 8, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 64, 1, 0, 0, 0, - 7, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, 0, - 96, 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 192, 48, 0, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 192, 0, 0, 6, 0, 6, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 16, 192, 192, - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 192, 192, 192, 192, 192 - }; -#endif /* !defined(OMR_EBCDIC) */ enum chartypex_t { @@ -1273,8 +1844,7 @@ PUGI__NS_BEGIN ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . }; - -#if !defined(OMR_EBCDIC) + static const unsigned char chartypex_table[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 @@ -1296,28 +1866,6 @@ PUGI__NS_BEGIN 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 }; - -#else /* !defined(OMR_EBCDIC) */ - static const unsigned char chartypex_table[256] = - { - 3, 3, 3, 3, 20, 0, 20, 0, 20, 20, 20, 3, 3, 2, 3, 3, - 3, 3, 3, 3, 20, 2, 3, 20, 3, 3, 20, 20, 3, 3, 3, 3, - 20, 20, 20, 20, 20, 20, 3, 3, 20, 20, 20, 20, 20, 3, 3, 3, - 20, 20, 3, 20, 20, 20, 20, 3, 20, 20, 20, 20, 3, 3, 20, 3, - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 16, 3, 0, 0, 0, - 3, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, - 16, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 20, 3, 0, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 2, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 20, 20, - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 20, 20, 20 - }; -#endif /* !defined(OMR_EBCDIC) */ #ifdef PUGIXML_WCHAR_MODE #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) @@ -1341,12 +1889,71 @@ PUGI__NS_BEGIN if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - else + else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } - PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) + PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) + { + #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } + #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; } + + // check if we have a non-empty XML declaration + if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space))) + return false; + + // scan XML declaration until the encoding field + for (size_t i = 6; i + 1 < size; ++i) + { + // declaration can not contain ? in quoted values + if (data[i] == '?') + return false; + + if (data[i] == 'e' && data[i + 1] == 'n') + { + size_t offset = i; + + // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed + PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o'); + PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g'); + + // S? = S? + PUGI__SCANCHARTYPE(ct_space); + PUGI__SCANCHAR('='); + PUGI__SCANCHARTYPE(ct_space); + + // the only two valid delimiters are ' and " + uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; + + PUGI__SCANCHAR(delimiter); + + size_t start = offset; + + out_encoding = data + offset; + + PUGI__SCANCHARTYPE(ct_symbol); + + out_length = offset - start; + + PUGI__SCANCHAR(delimiter); + + return true; + } + } + + return false; + + #undef PUGI__SCANCHAR + #undef PUGI__SCANCHARTYPE + } + + PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) { + // skip encoding autodetection if input buffer is too small + if (size < 4) return encoding_utf8; + + uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + // look for BOM in first few bytes if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; @@ -1359,13 +1966,32 @@ PUGI__NS_BEGIN if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le; if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be; if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le; - if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8; // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early) if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be; if (d0 == 0x3c && d1 == 0) return encoding_utf16_le; - // no known BOM detected, assume utf8 + // no known BOM detected; parse declaration + const uint8_t* enc = 0; + size_t enc_length = 0; + + if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length)) + { + // iso-8859-1 (case-insensitive) + if (enc_length == 10 + && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o' + && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9' + && enc[8] == '-' && enc[9] == '1') + return encoding_latin1; + + // latin1 (case-insensitive) + if (enc_length == 6 + && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't' + && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n' + && enc[5] == '1') + return encoding_latin1; + } + return encoding_utf8; } @@ -1383,15 +2009,10 @@ PUGI__NS_BEGIN // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; - // skip encoding autodetection if input buffer is too small - if (size < 4) return encoding_utf8; - // try to guess encoding (based on XML specification, Appendix F.1) const uint8_t* data = static_cast(contents); - PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; - - return guess_buffer_encoding(d0, d1, d2, d3); + return guess_buffer_encoding(data, size); } PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) @@ -1458,38 +2079,13 @@ PUGI__NS_BEGIN return true; } - PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf8 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf8_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); + size_t length = D::process(data, data_length, 0, wchar_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); @@ -1497,57 +2093,7 @@ PUGI__NS_BEGIN // second pass: convert utf16 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // get length in wchar_t units - size_t length = data_length; - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // convert latin1 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_latin1_block(data, data_length, obegin); + wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); assert(oend == obegin + length); *oend = 0; @@ -1564,13 +2110,16 @@ PUGI__NS_BEGIN xml_encoding wchar_encoding = get_wchar_encoding(); // fast path: no conversion required - if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + if (encoding == wchar_encoding) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // only endian-swapping is required - if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); + if (need_endian_swap_utf(encoding, wchar_encoding)) + return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf8 - if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size); + if (encoding == encoding_utf8) + return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) @@ -1578,8 +2127,8 @@ PUGI__NS_BEGIN xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 @@ -1588,24 +2137,25 @@ PUGI__NS_BEGIN xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size); + if (encoding == encoding_latin1) + return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); return false; } #else - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); + size_t length = D::process(data, data_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); @@ -1613,32 +2163,7 @@ PUGI__NS_BEGIN // second pass: convert utf16 input to utf8 uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to utf8 - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf32_block(data, data_length, obegin); + uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); assert(oend == obegin + length); *oend = 0; @@ -1674,7 +2199,7 @@ PUGI__NS_BEGIN if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // first pass: get length in utf8 units - size_t length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); + size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); @@ -1684,7 +2209,7 @@ PUGI__NS_BEGIN memcpy(buffer, data, prefix_length); uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_latin1_block(postfix, postfix_length, obegin + prefix_length); + uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); assert(oend == obegin + length); *oend = 0; @@ -1698,7 +2223,8 @@ PUGI__NS_BEGIN PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required - if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + if (encoding == encoding_utf8) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) @@ -1706,8 +2232,8 @@ PUGI__NS_BEGIN xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 @@ -1716,14 +2242,15 @@ PUGI__NS_BEGIN xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); + if (encoding == encoding_latin1) + return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); return false; } #endif @@ -1731,22 +2258,20 @@ PUGI__NS_BEGIN PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters - return utf_decoder::decode_wchar_block(str, length, 0); + return wchar_decoder::process(str, length, 0, utf8_counter()); } PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); - uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin); - + uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); + assert(begin + size == end); (void)!end; - - // zero-terminate - buffer[size] = 0; + (void)!size; } - + #ifndef PUGIXML_NO_STL PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) { @@ -1768,7 +2293,7 @@ PUGI__NS_BEGIN const uint8_t* data = reinterpret_cast(str); // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, size, 0); + size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); // allocate resulting string std::basic_string result; @@ -1778,7 +2303,7 @@ PUGI__NS_BEGIN if (length > 0) { wchar_writer::value_type begin = reinterpret_cast(&result[0]); - wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); + wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); assert(begin + length == end); (void)!end; @@ -1788,7 +2313,8 @@ PUGI__NS_BEGIN } #endif - inline bool strcpy_insitu_allow(size_t length, uintptr_t header, uintptr_t header_mask, char_t* target) + template + 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; @@ -1804,19 +2330,16 @@ 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 + PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) { - 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(header & xml_memory_page_pointer_mask)->allocator; + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; if (header & header_mask) alloc->deallocate_string(dest); - + // mark the string as not allocated dest = 0; header &= ~header_mask; @@ -1826,24 +2349,28 @@ PUGI__NS_BEGIN else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) { // we can reuse old buffer, so just copy the new data (including zero terminator) - memcpy(dest, source, (source_length + 1) * sizeof(char_t)); - + memcpy(dest, source, source_length * sizeof(char_t)); + dest[source_length] = 0; + return true; } else { - xml_allocator* alloc = reinterpret_cast(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); if (!buf) return false; // copy the string (including zero terminator) - memcpy(buf, source, (source_length + 1) * sizeof(char_t)); + memcpy(buf, source, source_length * sizeof(char_t)); + buf[source_length] = 0; // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); - + // the string is now allocated, so set the flag dest = buf; header |= header_mask; @@ -1856,11 +2383,11 @@ PUGI__NS_BEGIN { char_t* end; size_t size; - + gap(): end(0), size(0) { } - + // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) @@ -1871,14 +2398,14 @@ PUGI__NS_BEGIN assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } - + s += count; // end of current gap - + // "merge" two gaps end = s; size += count; } - + // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { @@ -1893,7 +2420,7 @@ PUGI__NS_BEGIN else return s; } }; - + PUGI__FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; @@ -1925,7 +2452,7 @@ PUGI__NS_BEGIN ch = *++stre; } - + ++stre; } else // &#... (dec code) @@ -1945,7 +2472,7 @@ PUGI__NS_BEGIN ch = *++stre; } - + ++stre; } @@ -1954,7 +2481,7 @@ PUGI__NS_BEGIN #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif - + g.push(s, stre - s); return stre; } @@ -1969,7 +2496,7 @@ PUGI__NS_BEGIN { *s++ = '&'; ++stre; - + g.push(s, stre - s); return stre; } @@ -1994,7 +2521,7 @@ PUGI__NS_BEGIN { *s++ = '>'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2007,7 +2534,7 @@ PUGI__NS_BEGIN { *s++ = '<'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2020,7 +2547,7 @@ PUGI__NS_BEGIN { *s++ = '"'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2030,7 +2557,7 @@ PUGI__NS_BEGIN default: break; } - + return stre; } @@ -2038,7 +2565,7 @@ PUGI__NS_BEGIN #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) - #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } + #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } #define PUGI__POPNODE() { cursor = cursor->parent; } #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI__SCANWHILE(X) { while (X) ++s; } @@ -2050,21 +2577,21 @@ PUGI__NS_BEGIN PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; - + while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); - + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; - + return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) @@ -2078,21 +2605,21 @@ PUGI__NS_BEGIN PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; - + while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); - + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; - + return s + 1; } else if (*s == 0) @@ -2102,9 +2629,9 @@ PUGI__NS_BEGIN else ++s; } } - + typedef char_t* (*strconv_pcdata_t)(char_t*); - + template struct strconv_pcdata_impl { static char_t* parse(char_t* s) @@ -2126,13 +2653,13 @@ PUGI__NS_BEGIN --end; *end = 0; - + return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') @@ -2155,7 +2682,7 @@ PUGI__NS_BEGIN } } }; - + PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); @@ -2175,7 +2702,7 @@ PUGI__NS_BEGIN } typedef char_t* (*strconv_attribute_t)(char_t*, char_t); - + template struct strconv_attribute_impl { static char_t* parse_wnorm(char_t* s, char_t end_quote) @@ -2186,35 +2713,35 @@ PUGI__NS_BEGIN if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s; - + do ++str; while (PUGI__IS_CHARTYPE(*str, ct_space)); - + g.push(s, str - s); } while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); - + if (*s == end_quote) { char_t* str = g.flush(s); - + do *str-- = 0; while (PUGI__IS_CHARTYPE(*str, ct_space)); - + return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; - + if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; - + g.push(s, str - s); } } @@ -2237,11 +2764,11 @@ PUGI__NS_BEGIN while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); - + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) @@ -2249,7 +2776,7 @@ PUGI__NS_BEGIN if (*s == '\r') { *s++ = ' '; - + if (*s == '\n') g.push(s, 1); } else *s++ = ' '; @@ -2273,17 +2800,17 @@ PUGI__NS_BEGIN while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } else if (*s == '\r') { *s++ = '\n'; - + if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') @@ -2305,11 +2832,11 @@ PUGI__NS_BEGIN while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } else if (opt_escape::value && *s == '&') @@ -2328,7 +2855,7 @@ PUGI__NS_BEGIN PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) { case 0: return strconv_attribute_impl::parse_simple; @@ -2362,11 +2889,11 @@ PUGI__NS_BEGIN struct xml_parser { - xml_allocator alloc; + xml_allocator* alloc; char_t* error_offset; xml_parse_status error_status; - - xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) + + xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) { } @@ -2403,7 +2930,7 @@ PUGI__NS_BEGIN PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - s += 4; + s += 3; } else PUGI__THROW_ERROR(status_bad_doctype, s); @@ -2412,23 +2939,28 @@ PUGI__NS_BEGIN char_t* parse_doctype_ignore(char_t* s) { + size_t depth = 0; + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); - s++; + s += 3; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] == '[') { // nested ignore section - s = parse_doctype_ignore(s); - if (!s) return s; + s += 3; + depth++; } else if (s[0] == ']' && s[1] == ']' && s[2] == '>') { // ignore section end s += 3; - return s; + if (depth == 0) + return s; + + depth--; } else s++; } @@ -2436,10 +2968,12 @@ PUGI__NS_BEGIN PUGI__THROW_ERROR(status_bad_doctype, s); } - char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel) + char_t* parse_doctype_group(char_t* s, char_t endch) { + size_t depth = 0; + assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); - s++; + s += 2; while (*s) { @@ -2454,12 +2988,8 @@ PUGI__NS_BEGIN else { // some control group - s = parse_doctype_group(s, endch, false); - if (!s) return s; - - // skip > - assert(*s == '>'); - s++; + s += 2; + depth++; } } else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') @@ -2470,12 +3000,16 @@ PUGI__NS_BEGIN } else if (*s == '>') { - return s; + if (depth == 0) + return s; + + depth--; + s++; } else s++; } - if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); + if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); return s; } @@ -2567,7 +3101,7 @@ PUGI__NS_BEGIN char_t* mark = s + 9; - s = parse_doctype_group(s, endch, true); + s = parse_doctype_group(s, endch); if (!s) return s; assert((*s == 0 && endch == '>') || *s == '>'); @@ -2658,6 +3192,7 @@ PUGI__NS_BEGIN { // store value and step over > cursor->value = value; + PUGI__POPNODE(); PUGI__ENDSEG(); @@ -2686,7 +3221,7 @@ PUGI__NS_BEGIN { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); - + char_t ch = 0; xml_node_struct* cursor = root; char_t* mark = s; @@ -2717,10 +3252,10 @@ PUGI__NS_BEGIN while (true) { PUGI__SKIPWS(); // Eat any whitespace. - + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { - xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute. + xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. @@ -2735,7 +3270,7 @@ PUGI__NS_BEGIN ch = *s; ++s; } - + if (ch == '=') // '<... #=...' { PUGI__SKIPWS(); // Eat any whitespace. @@ -2747,7 +3282,7 @@ PUGI__NS_BEGIN a->value = s; // Save the offset. s = strconv_attribute(s, ch); - + if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; @@ -2762,7 +3297,7 @@ PUGI__NS_BEGIN else if (*s == '/') { ++s; - + if (*s == '>') { PUGI__POPNODE(); @@ -2803,7 +3338,7 @@ PUGI__NS_BEGIN { // we stepped over null terminator, backtrack & handle closing tag --s; - + if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); } else PUGI__THROW_ERROR(status_bad_start_element, s); @@ -2812,20 +3347,22 @@ PUGI__NS_BEGIN { ++s; + mark = s; + char_t* name = cursor->name; - if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); - + if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + while (PUGI__IS_CHARTYPE(*s, ct_symbol)) { - if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); + if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); - else PUGI__THROW_ERROR(status_end_element_mismatch, s); + else PUGI__THROW_ERROR(status_end_element_mismatch, mark); } - + PUGI__POPNODE(); // Pop. PUGI__SKIPWS(); @@ -2879,23 +3416,31 @@ PUGI__NS_BEGIN if (!PUGI__OPTSET(parse_trim_pcdata)) s = mark; - + if (cursor->parent || PUGI__OPTSET(parse_fragment)) { - PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. - cursor->value = s; // Save the offset. + if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) + { + cursor->value = s; // Save the offset. + } + else + { + PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. + + cursor->value = s; // Save the offset. + + PUGI__POPNODE(); // Pop since this is a standalone. + } s = strconv_pcdata(s); - - PUGI__POPNODE(); // Pop since this is a standalone. - + if (!*s) break; } else { PUGI__SCANFOR(*s == '<'); // '...<' if (!*s) break; - + ++s; } @@ -2937,32 +3482,26 @@ PUGI__NS_BEGIN static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) { - // allocator object is a part of document object - xml_allocator& alloc_ = *static_cast(xmldoc); - // early-out for empty documents if (length == 0) 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(alloc_); + xml_parser parser(static_cast(xmldoc)); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; - + // skip BOM to make sure it does not end up as part of parse output char_t* buffer_data = parse_skip_bom(buffer); // perform actual parsing parser.parse_tree(buffer_data, root, optmsk, endch); - // update allocator state - alloc_ = parser.alloc; - xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); assert(result.offset >= 0 && static_cast(result.offset) <= length); @@ -2973,7 +3512,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+ 0; if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) return make_parse_result(status_no_document_element, length - 1); @@ -3017,12 +3556,36 @@ PUGI__NS_BEGIN return encoding_utf8; } + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + return static_cast(end - dest) * sizeof(*dest); + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + if (opt_swap) + { + for (typename T::value_type i = dest; i != end; ++i) + *i = endian_swap(*i); + } + + return static_cast(end - dest) * sizeof(*dest); + } + #ifdef PUGIXML_WCHAR_MODE PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 1) return 0; - // discard last character if it's the lead of a surrogate pair + // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } @@ -3035,58 +3598,32 @@ PUGI__NS_BEGIN return length * sizeof(char_t); } - + // convert to utf8 if (encoding == encoding_utf8) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); // convert to utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); + return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); } // convert to utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); + return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); } // convert to latin1 if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); return 0; } #else @@ -3110,43 +3647,22 @@ PUGI__NS_BEGIN { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); + return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); } if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); + return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); } if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - return static_cast(end - dest); - } + return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); return 0; } #endif @@ -3162,11 +3678,6 @@ PUGI__NS_BEGIN PUGI__STATIC_ASSERT(bufcapacity >= 8); } - ~xml_buffered_writer() - { - flush(); - } - size_t flush() { flush(buffer, bufsize); @@ -3375,10 +3886,10 @@ PUGI__NS_BEGIN while (*s) { const char_t* prev = s; - + // While *s is a usual symbol PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); - + writer.write_buffer(prev, static_cast(s - prev)); switch (*s) @@ -3506,14 +4017,45 @@ PUGI__NS_BEGIN writer.write('-', '-', '>'); } - PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) + { + while (*s) + { + const char_t* prev = s; + + // look for ?> sequence - we can't output it since ?> terminates PI + while (*s && !(s[0] == '?' && s[1] == '>')) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(s[0] == '?' && s[1] == '>'); + + writer.write('?', ' ', '>'); + s += 2; + } + } + } + + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { - writer.write(' '); - writer.write_string(a->name ? a->name : default_name); + if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) + { + writer.write('\n'); + + text_output_indent(writer, indent, indent_length, depth + 1); + } + else + { + writer.write(' '); + } + + writer.write_string(a->name ? a->name + 0 : default_name); writer.write('=', '"'); if (a->value) @@ -3523,21 +4065,40 @@ PUGI__NS_BEGIN } } - PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; + const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<'); writer.write_string(name); if (node->first_attribute) - node_output_attributes(writer, node, flags); + node_output_attributes(writer, node, indent, indent_length, flags, depth); - if (flags & format_raw) + // element nodes can have value if parse_embed_pcdata was used + if (!node->value) { if (!node->first_child) - writer.write(' ', '/', '>'); + { + if (flags & format_no_empty_element_tags) + { + writer.write('>', '<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + if ((flags & format_raw) == 0) + writer.write(' '); + + writer.write('/', '>'); + + return false; + } + } else { writer.write('>'); @@ -3547,48 +4108,33 @@ PUGI__NS_BEGIN } else { - xml_node_struct* first = node->first_child; - - if (!first) - writer.write(' ', '/', '>', '\n'); - else if (!first->next_sibling && (PUGI__NODETYPE(first) == node_pcdata || PUGI__NODETYPE(first) == node_cdata)) - { - writer.write('>'); - - const char_t* value = first->value ? first->value : PUGIXML_TEXT(""); + writer.write('>'); - if (PUGI__NODETYPE(first) == node_pcdata) - text_output(writer, value, ctx_special_pcdata, flags); - else - text_output_cdata(writer, value); + text_output(writer, node->value, ctx_special_pcdata, flags); + if (!node->first_child) + { writer.write('<', '/'); writer.write_string(name); - writer.write('>', '\n'); + writer.write('>'); + + return false; } else { - writer.write('>', '\n'); - return true; } } - - return false; } - PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; + const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<', '/'); writer.write_string(name); - - if (flags & format_raw) - writer.write('>'); - else - writer.write('>', '\n'); + writer.write('>'); } PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) @@ -3598,40 +4144,35 @@ PUGI__NS_BEGIN switch (PUGI__NODETYPE(node)) { case node_pcdata: - text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags); - if ((flags & format_raw) == 0) writer.write('\n'); + 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("")); - if ((flags & format_raw) == 0) writer.write('\n'); + 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("")); - if ((flags & format_raw) == 0) writer.write('\n'); + node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); break; case node_pi: writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); + writer.write_string(node->name ? node->name + 0 : default_name); if (node->value) { writer.write(' '); - writer.write_string(node->value); + node_output_pi_value(writer, node->value); } writer.write('?', '>'); - if ((flags & format_raw) == 0) writer.write('\n'); break; case node_declaration: writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); - node_output_attributes(writer, node, flags); + writer.write_string(node->name ? node->name + 0 : default_name); + node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); writer.write('?', '>'); - if ((flags & format_raw) == 0) writer.write('\n'); break; case node_doctype: @@ -3645,17 +4186,23 @@ PUGI__NS_BEGIN } writer.write('>'); - if ((flags & format_raw) == 0) writer.write('\n'); break; default: - assert(!"Invalid node type"); + assert(false && "Invalid node type"); } } + enum indent_flags_t + { + indent_newline = 1, + indent_indent = 2 + }; + PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { - size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0; + size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; + unsigned int indent_flags = indent_indent; xml_node_struct* node = root; @@ -3664,30 +4211,52 @@ PUGI__NS_BEGIN assert(node); // begin writing current node - if (indent_length) - text_output_indent(writer, indent, indent_length, depth); + if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) + { + node_output_simple(writer, node, flags); - if (PUGI__NODETYPE(node) == node_element) + indent_flags = 0; + } + else { - if (node_output_start(writer, node, flags)) + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + if (PUGI__NODETYPE(node) == node_element) { - node = node->first_child; - depth++; - continue; + indent_flags = indent_newline | indent_indent; + + if (node_output_start(writer, node, indent, indent_length, flags, depth)) + { + // element nodes can have value if parse_embed_pcdata was used + if (node->value) + indent_flags = 0; + + node = node->first_child; + depth++; + continue; + } } - } - else if (PUGI__NODETYPE(node) == node_document) - { - if (node->first_child) + else if (PUGI__NODETYPE(node) == node_document) + { + indent_flags = indent_indent; + + if (node->first_child) + { + node = node->first_child; + continue; + } + } + else { - node = node->first_child; - continue; + node_output_simple(writer, node, flags); + + indent_flags = indent_newline | indent_indent; } } - else - { - node_output_simple(writer, node, flags); - } // continue to the next node while (node != root) @@ -3705,14 +4274,22 @@ PUGI__NS_BEGIN { depth--; - if (indent_length) + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); - node_output_end(writer, node, flags); + node_output_end(writer, node); + + indent_flags = indent_newline | indent_indent; } } } while (node != root); + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); } PUGI__FN bool has_declaration(xml_node_struct* node) @@ -3775,7 +4352,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 + 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); @@ -3790,7 +4368,7 @@ PUGI__NS_BEGIN source_header |= xml_memory_page_contents_shared_mask; } else - strcpy_insitu(dest, header, header_mask, source); + strcpy_insitu(dest, header, header_mask, source, strlength(source)); } } @@ -3856,6 +4434,15 @@ PUGI__NS_BEGIN } } + PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + { + xml_allocator& alloc = get_allocator(da); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; + + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + inline bool is_text_node(xml_node_struct* node) { xml_node_type type = PUGI__NODETYPE(node); @@ -3864,49 +4451,93 @@ PUGI__NS_BEGIN } // get value with conversion functions - PUGI__FN int get_integer_base(const char_t* value) + template U string_to_integer(const char_t* value, U minneg, U maxpos) { + U result = 0; const char_t* s = value; while (PUGI__IS_CHARTYPE(*s, ct_space)) s++; - if (*s == '-') - s++; + bool negative = (*s == '-'); - return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; - } + s += (*s == '+' || *s == '-'); - PUGI__FN int get_value_int(const char_t* value, int def) - { - if (!value) return def; + bool overflow = false; - int base = get_integer_base(value); + if (s[0] == '0' && (s[1] | ' ') == 'x') + { + s += 2; - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstol(value, 0, base)); - #else - return static_cast(strtol(value, 0, base)); - #endif - } + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; - PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def) - { - if (!value) return def; + const char_t* start = s; - int base = get_integer_base(value); + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 16 + (*s - '0'); + else if (static_cast((*s | ' ') - 'a') < 6) + result = result * 16 + ((*s | ' ') - 'a' + 10); + else + break; - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstoul(value, 0, base)); - #else - return static_cast(strtoul(value, 0, base)); - #endif + s++; + } + + size_t digits = static_cast(s - start); + + overflow = digits > sizeof(U) * 2; + } + else + { + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 10 + (*s - '0'); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); + + const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; + const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; + const size_t high_bit = sizeof(U) * 8 - 1; + + overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); + } + + if (negative) + return (overflow || result > minneg) ? 0 - minneg : 0 - result; + else + return (overflow || result > maxpos) ? maxpos : result; + } + + PUGI__FN int get_value_int(const char_t* value) + { + return string_to_integer(value, 0 - static_cast(INT_MIN), INT_MAX); } - PUGI__FN double get_value_double(const char_t* value, double def) + PUGI__FN unsigned int get_value_uint(const char_t* value) { - if (!value) return def; + return string_to_integer(value, 0, UINT_MAX); + } + PUGI__FN double get_value_double(const char_t* value) + { #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else @@ -3914,10 +4545,8 @@ PUGI__NS_BEGIN #endif } - PUGI__FN float get_value_float(const char_t* value, float def) + PUGI__FN float get_value_float(const char_t* value) { - if (!value) return def; - #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); #else @@ -3925,10 +4554,8 @@ PUGI__NS_BEGIN #endif } - PUGI__FN bool get_value_bool(const char_t* value, bool def) + PUGI__FN bool get_value_bool(const char_t* value) { - if (!value) return def; - // only look at first char char_t first = *value; @@ -3937,108 +4564,119 @@ PUGI__NS_BEGIN } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long get_value_llong(const char_t* value, long long def) + PUGI__FN long long get_value_llong(const char_t* value) { - if (!value) return def; - - int base = get_integer_base(value); + return string_to_integer(value, 0 - static_cast(LLONG_MIN), LLONG_MAX); + } - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoi64(value, 0, base); - #else - return wcstoll(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoi64(value, 0, base); - #else - return strtoll(value, 0, base); - #endif - #endif + PUGI__FN unsigned long long get_value_ullong(const char_t* value) + { + return string_to_integer(value, 0, ULLONG_MAX); } +#endif - PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def) + template PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) { - if (!value) return def; + char_t* result = end - 1; + U rest = negative ? 0 - value : value; - int base = get_integer_base(value); + do + { + *result-- = static_cast('0' + (rest % 10)); + rest /= 10; + } + while (rest); - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoui64(value, 0, base); - #else - return wcstoull(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoui64(value, 0, base); - #else - return strtoull(value, 0, base); - #endif - #endif + assert(result >= begin); + (void)begin; + + *result = '-'; + + return result + !negative; } -#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 + PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; - impl::widen_ascii(wbuf, buf); + assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); + + size_t offset = 0; + for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; - return strcpy_insitu(dest, header, header_mask, wbuf); + return strcpy_insitu(dest, header, header_mask, wbuf, offset); #else - return strcpy_insitu(dest, header, header_mask, buf); + return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); #endif } - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) + template + PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) { - char buf[128]; - snprintf(buf, sizeof(buf), "%d", value); - - return set_value_buffer(dest, header, header_mask, buf); + char_t buf[64]; + char_t* end = buf + sizeof(buf) / sizeof(buf[0]); + char_t* begin = integer_to_string(buf, end, value, negative); + + return strcpy_insitu(dest, header, header_mask, begin, end - begin); } - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value) + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) { char buf[128]; - snprintf(buf, sizeof(buf), "%u", value); + sprintf(buf, "%.9g", value); - return set_value_buffer(dest, header, header_mask, buf); + return set_value_ascii(dest, header, header_mask, buf); } - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value) + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) { char buf[128]; - snprintf(buf, sizeof(buf), "%g", value); + sprintf(buf, "%.17g", 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, bool value) - { - return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + return set_value_ascii(dest, header, header_mask, buf); } -#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 + PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) { - char buf[128]; - snprintf(buf, sizeof(buf), "%lld", value); - - return set_value_buffer(dest, header, header_mask, buf); + return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); } - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value) + PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) { - char buf[128]; - snprintf(buf, sizeof(buf), "%llu", value); - - return set_value_buffer(dest, header, header_mask, buf); + // check input buffer + if (!contents && size) return make_parse_result(status_io_error); + + // get actual encoding + xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); + + // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself + if (own || buffer != contents) *out_buffer = buffer; + + // store buffer for offset_debug + doc->buffer = buffer; + + // parse + xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); + + // remember encoding + res.encoding = buffer_encoding; + + return res; } -#endif // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) @@ -4068,7 +4706,7 @@ PUGI__NS_BEGIN // check for I/O errors if (length < 0) return status_io_error; - + // check for overflow size_t result = static_cast(length); @@ -4080,7 +4718,8 @@ PUGI__NS_BEGIN return status_ok; } - PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + // This function assumes that buffer has extra sizeof(char_t) writable bytes after size + PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us #ifdef PUGIXML_WCHAR_MODE @@ -4104,34 +4743,23 @@ PUGI__NS_BEGIN return size; } - PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) + PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) { if (!file) return make_parse_result(status_file_not_found); // get file size (can result in I/O errors) size_t size = 0; xml_parse_status size_status = get_file_size(file, size); + if (size_status != status_ok) return make_parse_result(size_status); - if (size_status != status_ok) - { - fclose(file); - return make_parse_result(size_status); - } - size_t max_suffix_size = sizeof(char_t); // allocate buffer for the whole file char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); - - if (!contents) - { - fclose(file); - return make_parse_result(status_out_of_memory); - } + if (!contents) return make_parse_result(status_out_of_memory); // read file in memory size_t read_size = fread(contents, 1, size, file); - fclose(file); if (read_size != size) { @@ -4140,8 +4768,13 @@ PUGI__NS_BEGIN } xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); - - return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding); + + return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); + } + + PUGI__FN void close_file(FILE* file) + { + fclose(file); } #ifndef PUGIXML_NO_STL @@ -4150,14 +4783,13 @@ PUGI__NS_BEGIN static xml_stream_chunk* create() { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); - + if (!memory) return 0; + return new (memory) xml_stream_chunk(); } - static void destroy(void* ptr) + static void destroy(xml_stream_chunk* chunk) { - xml_stream_chunk* chunk = static_cast(ptr); - // free chunk chain while (chunk) { @@ -4181,7 +4813,7 @@ PUGI__NS_BEGIN template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { - buffer_holder chunks(0, xml_stream_chunk::destroy); + auto_deleter > chunks(0, xml_stream_chunk::destroy); // read file to a chunk list size_t total = 0; @@ -4217,7 +4849,7 @@ PUGI__NS_BEGIN char* write = buffer; - for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next) + for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) { assert(write + chunk->size <= buffer + total); memcpy(write, chunk->data, chunk->size); @@ -4251,7 +4883,7 @@ PUGI__NS_BEGIN size_t max_suffix_size = sizeof(char_t); // read stream data into memory (guard against stream exceptions with buffer holder) - buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); + auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); if (!buffer.data) return status_out_of_memory; stream.read(static_cast(buffer.data), static_cast(read_length)); @@ -4262,14 +4894,14 @@ PUGI__NS_BEGIN // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); - + *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } - template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) + template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) { void* buffer = 0; size_t size = 0; @@ -4290,8 +4922,8 @@ PUGI__NS_BEGIN if (status != status_ok) return make_parse_result(status); xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); - - return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding); + + return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); } #endif @@ -4316,6 +4948,9 @@ PUGI__NS_BEGIN // second pass: convert to utf8 as_utf8_end(result, size, str, length); + // zero-terminate + result[size] = 0; + return result; } @@ -4346,44 +4981,24 @@ PUGI__NS_BEGIN xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); - int result = ferror(file); - - fclose(file); - - return result == 0; + return ferror(file) == 0; } - PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) + struct name_null_sentry { - // check input buffer - assert(contents || size == 0); - - // get actual encoding - xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); - - // get private buffer - char_t* buffer = 0; - size_t length = 0; - - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); - - // delete original buffer if we performed a conversion - if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); - - // store buffer for offset_debug - doc->buffer = buffer; + xml_node_struct* node; + char_t* name; - // parse - xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); - - // remember encoding - res.encoding = buffer_encoding; - - // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself - if (own || buffer != contents) *out_buffer = buffer; + name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) + { + node->name = 0; + } - return res; - } + ~name_null_sentry() + { + node->name = name; + } + }; PUGI__NS_END namespace pugi @@ -4427,7 +5042,7 @@ namespace pugi PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) { } - + PUGI__FN xml_tree_walker::~xml_tree_walker() { } @@ -4473,7 +5088,7 @@ namespace pugi { return (_attr == r._attr); } - + PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); @@ -4483,17 +5098,17 @@ namespace pugi { return (_attr < r._attr); } - + PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } - + PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } - + PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); @@ -4511,43 +5126,43 @@ namespace pugi PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { - return (_attr && _attr->value) ? _attr->value : def; + return (_attr && _attr->value) ? _attr->value + 0 : def; } PUGI__FN int xml_attribute::as_int(int def) const { - return impl::get_value_int(_attr ? _attr->value : 0, def); + return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { - return impl::get_value_uint(_attr ? _attr->value : 0, def); + return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; } PUGI__FN double xml_attribute::as_double(double def) const { - return impl::get_value_double(_attr ? _attr->value : 0, def); + return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; } PUGI__FN float xml_attribute::as_float(float def) const { - return impl::get_value_float(_attr ? _attr->value : 0, def); + return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; } PUGI__FN bool xml_attribute::as_bool(bool def) const { - return impl::get_value_bool(_attr ? _attr->value : 0, def); + return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : 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 (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : 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 (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; } #endif @@ -4558,12 +5173,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 @@ -4581,7 +5196,7 @@ namespace pugi set_value(rhs); return *this; } - + PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); @@ -4594,12 +5209,30 @@ namespace pugi return *this; } + PUGI__FN xml_attribute& xml_attribute::operator=(long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs) + { + set_value(rhs); + return *this; + } + PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } - + + PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) + { + set_value(rhs); + return *this; + } + PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); @@ -4623,29 +5256,43 @@ namespace pugi PUGI__FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; - - return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); + + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } - + PUGI__FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; - return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } PUGI__FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } PUGI__FN bool xml_attribute::set_value(double rhs) @@ -4654,27 +5301,34 @@ 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) + + PUGI__FN bool xml_attribute::set_value(float rhs) { if (!_attr) return false; 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; + + return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool xml_attribute::set_value(long long rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } #endif @@ -4697,7 +5351,7 @@ namespace pugi PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) { } - + PUGI__FN static void unspecified_bool_xml_node(xml_node***) { } @@ -4714,24 +5368,24 @@ 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 { return iterator(0, _root); } - + 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 { return attribute_iterator(0, _root); } - + PUGI__FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); @@ -4761,17 +5415,17 @@ namespace pugi { return (_root < r._root); } - + PUGI__FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } - + PUGI__FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } - + PUGI__FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); @@ -4781,22 +5435,22 @@ namespace pugi { return !_root; } - + 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 { return _root ? PUGI__NODETYPE(_root) : node_null; } - + 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 { if (!_root) return xml_node(); @@ -4814,14 +5468,14 @@ namespace pugi for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) if (i->name && impl::strequal(name_, i->name)) return xml_attribute(i); - + return xml_attribute(); } - + PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); @@ -4836,17 +5490,50 @@ namespace pugi PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const + { + xml_attribute_struct* hint = hint_._attr; + + // if hint is not an attribute of node, behavior is not defined + assert(!hint || (_root && impl::is_attribute_of(hint, _root))); + + if (!_root) return xml_attribute(); + + // optimistically search from hint up until the end + for (xml_attribute_struct* i = hint; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = i->next_attribute; + + return xml_attribute(i); + } + + // wrap around and search from the first attribute until the hint + // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails + for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) + if (j->name && impl::strequal(name_, j->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = j->next_attribute; + + return xml_attribute(j); + } + + return xml_attribute(); + } + PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); - + if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); else return xml_node(); } @@ -4869,9 +5556,13 @@ namespace pugi PUGI__FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); - + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root->value; + 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(""); @@ -4904,53 +5595,49 @@ 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); + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; - default: + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) return false; - } + + return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(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); + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; - default: + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) return false; - } + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(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); a.set_name(name_); - + return a; } PUGI__FN xml_attribute xml_node::prepend_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::prepend_attribute(a._attr, _root); @@ -4964,8 +5651,11 @@ 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); @@ -4979,8 +5669,11 @@ 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); @@ -4993,48 +5686,81 @@ namespace pugi PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); - xml_attribute result = append_attribute(proto.name()); - result.set_value(proto.value()); + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); - return result; + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); - xml_attribute result = prepend_attribute(proto.name()); - result.set_value(proto.value()); + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); - return result; + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - xml_attribute result = insert_attribute_after(proto.name(), attr); - result.set_value(proto.value()); + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); - return result; + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - xml_attribute result = insert_attribute_before(proto.name(), attr); - result.set_value(proto.value()); + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); - return result; + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; } PUGI__FN xml_node xml_node::append_child(xml_node_type 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); @@ -5047,12 +5773,15 @@ namespace pugi PUGI__FN xml_node xml_node::prepend_child(xml_node_type 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); - + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; @@ -5062,8 +5791,11 @@ 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); @@ -5077,8 +5809,11 @@ 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); @@ -5129,7 +5864,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); @@ -5143,7 +5881,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); @@ -5158,7 +5899,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); @@ -5173,7 +5917,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); @@ -5186,6 +5933,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; @@ -5199,6 +5949,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; @@ -5214,6 +5967,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; @@ -5229,6 +5985,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; @@ -5248,8 +6007,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; } @@ -5263,8 +6025,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; } @@ -5279,7 +6044,7 @@ namespace pugi // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense doc->header |= impl::xml_memory_page_contents_shared_mask; - + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) impl::xml_memory_page* page = 0; impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); @@ -5287,34 +6052,26 @@ namespace pugi if (!extra) return impl::make_parse_result(status_out_of_memory); - // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level - char_t* rootname = _root->name; - _root->name = 0; - - // parse - char_t* buffer = 0; - xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &buffer); - - // restore name - _root->name = rootname; - // add extra buffer to the list - extra->buffer = buffer; + extra->buffer = 0; extra->next = doc->extra_buffers; doc->extra_buffers = extra; - return res; + // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level + impl::name_null_sentry sentry(_root); + + return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) 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); } @@ -5324,10 +6081,10 @@ namespace pugi PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); - + 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(); @@ -5336,20 +6093,35 @@ namespace pugi #ifndef PUGIXML_NO_STL PUGI__FN string_t xml_node::path(char_t delimiter) const { - xml_node cursor = *this; // Make a copy. - - string_t result = cursor.name(); + if (!_root) return string_t(); + + size_t offset = 0; + + for (xml_node_struct* i = _root; i; i = i->parent) + { + offset += (i != _root); + offset += i->name ? impl::strlength(i->name) : 0; + } - while (cursor.parent()) + string_t result; + result.resize(offset); + + for (xml_node_struct* j = _root; j; j = j->parent) { - cursor = cursor.parent(); - - string_t temp = cursor.name(); - temp += delimiter; - temp += result; - result.swap(temp); + if (j != _root) + result[--offset] = delimiter; + + if (j->name && *j->name) + { + size_t length = impl::strlength(j->name); + + offset -= length; + memcpy(&result[offset], j->name, length * sizeof(char_t)); + } } + assert(offset == 0); + return result; } #endif @@ -5404,22 +6176,22 @@ namespace pugi PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; - + xml_node arg_begin = *this; if (!walker.begin(arg_begin)) return false; xml_node cur = first_child(); - + if (cur) { ++walker._depth; - do + do { xml_node arg_for_each = cur; if (!walker.for_each(arg_for_each)) return false; - + if (cur.first_child()) { ++walker._depth; @@ -5435,7 +6207,7 @@ namespace pugi --walker._depth; cur = cur.parent(); } - + if (cur != *this) cur = cur.next_sibling(); } @@ -5466,6 +6238,8 @@ namespace pugi impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, _root, indent, flags, depth); + + buffered_writer.flush(); } #ifndef PUGIXML_NO_STL @@ -5534,6 +6308,10 @@ namespace pugi { if (!_root || impl::is_text_node(_root)) return _root; + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root; + for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; @@ -5576,49 +6354,49 @@ 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 { xml_node_struct* d = _data(); - return (d && d->value) ? d->value : def; + return (d && d->value) ? d->value + 0 : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); - return impl::get_value_int(d ? d->value : 0, def); + return (d && d->value) ? impl::get_value_int(d->value) : 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 (d && d->value) ? impl::get_value_uint(d->value) : 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 (d && d->value) ? impl::get_value_double(d->value) : 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 (d && d->value) ? impl::get_value_float(d->value) : 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 (d && d->value) ? impl::get_value_bool(d->value) : def; } #ifdef PUGIXML_HAS_LONG_LONG @@ -5626,14 +6404,14 @@ namespace pugi { xml_node_struct* d = _data(); - return impl::get_value_llong(d ? d->value : 0, def); + return (d && d->value) ? impl::get_value_llong(d->value) : 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 (d && d->value) ? impl::get_value_ullong(d->value) : def; } #endif @@ -5641,20 +6419,41 @@ namespace pugi { xml_node_struct* dn = _data_new(); - return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; } PUGI__FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } PUGI__FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(float rhs) + { + xml_node_struct* dn = _data_new(); + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } @@ -5669,7 +6468,7 @@ namespace pugi { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #ifdef PUGIXML_HAS_LONG_LONG @@ -5677,14 +6476,14 @@ namespace pugi { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } PUGI__FN bool xml_text::set(unsigned long long rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } #endif @@ -5706,12 +6505,30 @@ namespace pugi return *this; } + PUGI__FN xml_text& xml_text::operator=(long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long rhs) + { + set(rhs); + return *this; + } + PUGI__FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } + PUGI__FN xml_text& xml_text::operator=(float rhs) + { + set(rhs); + return *this; + } + PUGI__FN xml_text& xml_text::operator=(bool rhs) { set(rhs); @@ -5765,7 +6582,7 @@ namespace pugi { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } - + PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; @@ -5780,7 +6597,7 @@ namespace pugi PUGI__FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() @@ -5826,7 +6643,7 @@ namespace pugi { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } - + PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; @@ -5841,7 +6658,7 @@ namespace pugi PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() @@ -5902,7 +6719,7 @@ namespace pugi PUGI__FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() @@ -5983,18 +6800,18 @@ namespace pugi PUGI__FN xml_document::xml_document(): _buffer(0) { - create(); + _create(); } PUGI__FN xml_document::~xml_document() { - destroy(); + _destroy(); } PUGI__FN void xml_document::reset() { - destroy(); - create(); + _destroy(); + _create(); } PUGI__FN void xml_document::reset(const xml_document& proto) @@ -6005,34 +6822,49 @@ namespace pugi append_copy(cur); } - PUGI__FN void xml_document::create() + PUGI__FN void xml_document::_create() { assert(!_root); - // 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)); + #ifdef PUGIXML_COMPACT + const size_t page_offset = sizeof(uint32_t); + #else + const size_t page_offset = 0; + #endif - // align upwards to page boundary - void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); + // initialize sentinel page + PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); // prepare page structure - impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); + impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); assert(page); page->busy_size = impl::xml_memory_page_size; + // setup first page marker + #ifdef PUGIXML_COMPACT + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); + *page->compact_page_marker = sizeof(impl::xml_memory_page); + #endif + // allocate new root - _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page)) impl::xml_document_struct(page); + _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); _root->prev_sibling_c = _root; // setup sentinel page page->allocator = static_cast(_root); + // setup hash table pointer in allocator + #ifdef PUGIXML_COMPACT + page->allocator->_hash = &static_cast(_root)->hash; + #endif + // verify the document allocation assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); } - PUGI__FN void xml_document::destroy() + PUGI__FN void xml_document::_destroy() { assert(_root); @@ -6050,7 +6882,7 @@ namespace pugi } // destroy dynamic storage, leave sentinel page (it's in static memory) - impl::xml_memory_page* root_page = reinterpret_cast(_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(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); @@ -6063,6 +6895,11 @@ namespace pugi page = next; } + #ifdef PUGIXML_COMPACT + // destroy hash table + static_cast(_root)->hash.clear(); + #endif + _root = 0; } @@ -6071,14 +6908,14 @@ namespace pugi { reset(); - return impl::load_stream_impl(*this, stream, options, encoding); + return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); - return impl::load_stream_impl(*this, stream, options, encoding_wchar); + return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); } #endif @@ -6103,18 +6940,20 @@ namespace pugi { reset(); - FILE* file = fopen(path_, "rb"); + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, "rb"), impl::close_file); - return impl::load_file_impl(*this, file, options, encoding); + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); - FILE* file = impl::open_file_wide(path_, L"rb"); + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); - return impl::load_file_impl(*this, file, options, encoding); + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) @@ -6162,6 +7001,8 @@ namespace pugi } impl::node_output(buffered_writer, _root, indent, flags, 0); + + buffered_writer.flush(); } #ifndef PUGIXML_NO_STL @@ -6182,14 +7023,18 @@ namespace pugi PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { - FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { - FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); } PUGI__FN xml_node xml_document::document_element() const @@ -6215,14 +7060,14 @@ namespace pugi { return impl::as_utf8_impl(str.c_str(), str.size()); } - + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } - + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); @@ -6354,7 +7199,7 @@ PUGI__NS_BEGIN if (begin == end) return begin; // last written element - I write = begin++; + I write = begin++; // merge unique elements while (begin != end) @@ -6518,20 +7363,28 @@ PUGI__NS_END // Allocator used for AST and evaluation stacks PUGI__NS_BEGIN + static const size_t xpath_memory_page_size = + #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE + PUGIXML_MEMORY_XPATH_PAGE_SIZE + #else + 4096 + #endif + ; + + static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); + struct xpath_memory_block - { + { xpath_memory_block* next; size_t capacity; - char data[ - #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE - PUGIXML_MEMORY_XPATH_PAGE_SIZE - #else - 4096 - #endif - ]; + union + { + char data[xpath_memory_page_size]; + double alignment; + }; }; - + class xpath_allocator { xpath_memory_block* _root; @@ -6548,15 +7401,15 @@ PUGI__NS_BEGIN error_handler = 0; #endif } - + void* allocate_nothrow(size_t size) { - // align size so that we're able to store pointers in subsequent blocks - size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + // round size up to block alignment boundary + size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); if (_root_size + size <= _root->capacity) { - void* buf = _root->data + _root_size; + void* buf = &_root->data[0] + _root_size; _root_size += size; return buf; } @@ -6571,13 +7424,13 @@ PUGI__NS_BEGIN xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); if (!block) return 0; - + block->next = _root; block->capacity = block_capacity; - + _root = block; _root_size = size; - + return block->data; } } @@ -6601,12 +7454,12 @@ PUGI__NS_BEGIN void* reallocate(void* ptr, size_t old_size, size_t new_size) { - // align size so that we're able to store pointers in subsequent blocks - old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + // round size up to block alignment boundary + old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); // we can only reallocate the last object - assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); + assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); // adjust root size so that we have not allocated the object at all bool only_object = (_root_size == old_size); @@ -6825,7 +7678,7 @@ PUGI__NS_BEGIN { return _uses_heap ? _length_heap : strlength(_buffer); } - + char_t* data(xpath_allocator* alloc) { // make private heap copy @@ -6915,14 +7768,18 @@ PUGI__NS_BEGIN case node_comment: case node_pi: return xpath_string::from_const(n.value()); - + case node_document: case node_element: { xpath_string result; + // element nodes can have value if parse_embed_pcdata was used + if (n.value()[0]) + result.append(xpath_string::from_const(n.value()), alloc); + xml_node cur = n.first_child(); - + while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) @@ -6940,16 +7797,16 @@ PUGI__NS_BEGIN if (cur != n) cur = cur.next_sibling(); } } - + return result; } - + default: return xpath_string(); } } } - + PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) { assert(ln->parent == rn->parent); @@ -6973,7 +7830,7 @@ PUGI__NS_BEGIN // if rn sibling chain ended ln must be before rn return !rs; } - + PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) { // find common ancestor at the same depth, if any @@ -7054,7 +7911,7 @@ PUGI__NS_BEGIN return 0; } - + struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const @@ -7078,10 +7935,10 @@ PUGI__NS_BEGIN for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; - + return false; } - + // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); @@ -7090,21 +7947,21 @@ PUGI__NS_BEGIN { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; - + ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; - + rn = rhs.parent(); } if (ln == rn) return false; if (!ln || !rn) return ln < rn; - + return node_is_before(ln.internal_object(), rn.internal_object()); } }; @@ -7117,20 +7974,22 @@ PUGI__NS_BEGIN else return rhs.attribute() ? false : lhs.node() < rhs.node(); } }; - + PUGI__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) - union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; - u[0].i = 0x7fc00000; - return u[0].f; + PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); + typedef uint32_t UI; // BCC5 workaround + union { float f; UI i; } u; + u.i = 0x7fc00000; + return u.f; #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } - + PUGI__FN bool is_nan(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) @@ -7143,7 +8002,7 @@ PUGI__NS_BEGIN return v != v; #endif } - + PUGI__FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) @@ -7175,12 +8034,12 @@ PUGI__NS_BEGIN return 0; #endif } - + PUGI__FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } - + PUGI__FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; @@ -7207,7 +8066,9 @@ PUGI__NS_BEGIN PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) { // get a scientific notation value with IEEE DBL_DIG decimals - snprintf(buffer, buffer_size, "%.*e", DBL_DIG, value); + sprintf(buffer, "%.*e", DBL_DIG, value); + assert(strlen(buffer) < buffer_size); + (void)!buffer_size; // get the exponent (possibly negative) char* exponent_string = strchr(buffer, 'e'); @@ -7299,7 +8160,7 @@ PUGI__NS_BEGIN return xpath_string::from_heap_preallocated(result, s); } - + PUGI__FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace @@ -7339,7 +8200,7 @@ PUGI__NS_BEGIN #ifdef PUGIXML_WCHAR_MODE return wcstod(string, 0); #else - return atof(string); + return strtod(string, 0); #endif } @@ -7366,7 +8227,7 @@ PUGI__NS_BEGIN return true; } - + PUGI__FN double round_nearest(double value) { return floor(value + 0.5); @@ -7378,17 +8239,17 @@ PUGI__NS_BEGIN // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } - + PUGI__FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } - + PUGI__FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); - + return p ? p + 1 : name; } @@ -7418,39 +8279,39 @@ PUGI__NS_BEGIN PUGI__FN const char_t* namespace_uri(xml_node node) { namespace_uri_predicate pred = node.name(); - + xml_node p = node; - + while (p) { xml_attribute a = p.find_attribute(pred); - + if (a) return a.value(); - + p = p.parent(); } - + return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) { namespace_uri_predicate pred = attr.name(); - + // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); - + xml_node p = parent; - + while (p) { xml_attribute a = p.find_attribute(pred); - + if (a) return a.value(); - + p = p.parent(); } - + return PUGIXML_TEXT(""); } @@ -7459,7 +8320,7 @@ PUGI__NS_BEGIN return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } - PUGI__FN void normalize_space(char_t* buffer) + PUGI__FN char_t* normalize_space(char_t* buffer) { char_t* write = buffer; @@ -7483,9 +8344,11 @@ PUGI__NS_BEGIN // zero-terminate *write = 0; + + return write; } - PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) + PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) { char_t* write = buffer; @@ -7503,6 +8366,8 @@ PUGI__NS_BEGIN // zero-terminate *write = 0; + + return write; } PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) @@ -7539,7 +8404,7 @@ PUGI__NS_BEGIN return static_cast(result); } - PUGI__FN void translate_table(char_t* buffer, const unsigned char* table) + PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) { char_t* write = buffer; @@ -7565,6 +8430,8 @@ PUGI__NS_BEGIN // zero-terminate *write = 0; + + return write; } inline bool is_xpath_attribute(const char_t* name) @@ -7574,7 +8441,7 @@ PUGI__NS_BEGIN struct xpath_variable_boolean: xpath_variable { - xpath_variable_boolean(): value(false) + xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) { } @@ -7584,7 +8451,7 @@ PUGI__NS_BEGIN struct xpath_variable_number: xpath_variable { - xpath_variable_number(): value(0) + xpath_variable_number(): xpath_variable(xpath_type_number), value(0) { } @@ -7594,7 +8461,7 @@ PUGI__NS_BEGIN struct xpath_variable_string: xpath_variable { - xpath_variable_string(): value(0) + xpath_variable_string(): xpath_variable(xpath_type_string), value(0) { } @@ -7609,6 +8476,10 @@ PUGI__NS_BEGIN struct xpath_variable_node_set: xpath_variable { + xpath_variable_node_set(): xpath_variable(xpath_type_node_set) + { + } + xpath_node_set value; char_t name[1]; }; @@ -7626,11 +8497,11 @@ PUGI__NS_BEGIN result += result << 10; result ^= result >> 6; } - + result += result << 3; result ^= result >> 11; result += result << 15; - + return result; } @@ -7698,11 +8569,33 @@ PUGI__NS_BEGIN break; default: - assert(!"Invalid variable type"); + assert(false && "Invalid variable type"); + } + } + + PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + { + switch (rhs->type()) + { + case xpath_type_node_set: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_number: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_string: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_boolean: + return lhs->set(static_cast(rhs)->value); + + default: + assert(false && "Invalid variable type"); + return false; } } - PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end) + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; @@ -7711,19 +8604,19 @@ PUGI__NS_BEGIN { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return 0; + if (!scratch) return false; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; - xpath_variable* result = set->get(scratch); + *out_result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); - return result; + return true; } PUGI__NS_END @@ -7762,9 +8655,9 @@ PUGI__NS_BEGIN else type = sorted; } - + if (type != order) reverse(begin, end); - + return order; } @@ -7784,7 +8677,7 @@ PUGI__NS_BEGIN return *min_element(begin, end, document_order_comparator()); default: - assert(!"Invalid node set type"); + assert(false && "Invalid node set type"); return xpath_node(); } } @@ -7877,7 +8770,7 @@ PUGI__NS_BEGIN { if (_type == xpath_node_set::type_unsorted) sort(_begin, _end, duplicate_comparator()); - + _end = unique(_begin, _end); } @@ -7985,12 +8878,12 @@ PUGI__NS_BEGIN { next(); } - + const char_t* state() const { return _cur; } - + void next() { const char_t* cur = _cur; @@ -8005,7 +8898,7 @@ PUGI__NS_BEGIN case 0: _cur_lexeme = lex_eof; break; - + case '>': if (*(cur+1) == '=') { @@ -8049,7 +8942,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_equal; break; - + case '+': cur += 1; _cur_lexeme = lex_plus; @@ -8073,7 +8966,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_union; break; - + case '$': cur += 1; @@ -8091,7 +8984,7 @@ PUGI__NS_BEGIN } _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_var_ref; } else @@ -8112,7 +9005,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_close_brace; break; - + case '[': cur += 1; _cur_lexeme = lex_open_square_brace; @@ -8143,7 +9036,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_slash; } break; - + case '.': if (*(cur+1) == '.') { @@ -8159,7 +9052,7 @@ PUGI__NS_BEGIN while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_number; } else @@ -8185,7 +9078,7 @@ PUGI__NS_BEGIN _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; - + if (!*cur) _cur_lexeme = lex_none; else @@ -8215,7 +9108,7 @@ PUGI__NS_BEGIN _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - + if (*cur == '.') { cur++; @@ -8248,7 +9141,7 @@ PUGI__NS_BEGIN } _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_string; } else @@ -8359,7 +9252,7 @@ PUGI__NS_BEGIN axis_preceding_sibling, axis_self }; - + enum nodetest_t { nodetest_none, @@ -8394,7 +9287,7 @@ PUGI__NS_BEGIN }; template const axis_t axis_to_type::axis = N; - + class xpath_ast_node { private: @@ -8514,7 +9407,7 @@ PUGI__NS_BEGIN } } - assert(!"Wrong types"); + assert(false && "Wrong types"); return false; } @@ -8589,7 +9482,7 @@ PUGI__NS_BEGIN } else { - assert(!"Wrong types"); + assert(false && "Wrong types"); return false; } } @@ -8700,9 +9593,9 @@ PUGI__NS_BEGIN bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) { - assert(a); + 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) { @@ -8713,7 +9606,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_node: case nodetest_all: if (is_xpath_attribute(name)) @@ -8722,7 +9615,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) { @@ -8730,17 +9623,17 @@ PUGI__NS_BEGIN return true; } break; - + default: ; } return false; } - + bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) { - assert(n); + assert(n); xml_node_type type = PUGI__NODETYPE(n); @@ -8753,11 +9646,11 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_node: ns.push_back(xml_node(n), alloc); return true; - + case nodetest_type_comment: if (type == node_comment) { @@ -8765,7 +9658,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_text: if (type == node_pcdata || type == node_cdata) { @@ -8773,7 +9666,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_pi: if (type == node_pi) { @@ -8781,7 +9674,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_pi: if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) { @@ -8789,7 +9682,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all: if (type == node_element) { @@ -8797,7 +9690,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all_in_namespace: if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) { @@ -8807,7 +9700,7 @@ PUGI__NS_BEGIN break; default: - assert(!"Unknown axis"); + assert(false && "Unknown axis"); } return false; @@ -8824,33 +9717,33 @@ PUGI__NS_BEGIN for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) if (step_push(ns, a, n, alloc) & once) return; - + break; } - + case axis_child: { for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) if (step_push(ns, n, alloc) & once) return; - + xml_node_struct* cur = n->first_child; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + if (cur->first_child) cur = cur->first_child; else @@ -8861,32 +9754,32 @@ PUGI__NS_BEGIN if (cur == n) return; } - + cur = cur->next_sibling; } } - + break; } - + case axis_following_sibling: { for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_preceding_sibling: { for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_following: { xml_node_struct* cur = n; @@ -8965,7 +9858,7 @@ PUGI__NS_BEGIN break; } - + case axis_ancestor: case axis_ancestor_or_self: { @@ -8974,15 +9867,15 @@ PUGI__NS_BEGIN return; xml_node_struct* cur = n->parent; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + cur = cur->parent; } - + break; } @@ -9000,12 +9893,12 @@ PUGI__NS_BEGIN break; } - + default: - assert(!"Unimplemented axis"); + assert(false && "Unimplemented axis"); } } - + template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; @@ -9020,15 +9913,15 @@ PUGI__NS_BEGIN return; xml_node_struct* cur = p; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + cur = cur->parent; } - + break; } @@ -9044,7 +9937,7 @@ PUGI__NS_BEGIN case axis_following: { xml_node_struct* cur = p; - + while (cur) { if (cur->first_child) @@ -9081,9 +9974,9 @@ PUGI__NS_BEGIN step_fill(ns, p, alloc, once, v); break; } - + default: - assert(!"Unimplemented axis"); + assert(false && "Unimplemented axis"); } } @@ -9125,7 +10018,7 @@ PUGI__NS_BEGIN // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); - + step_fill(ns, *it, stack.result, once, v); if (_right) apply_predicates(ns, size, stack, eval); } @@ -9143,7 +10036,7 @@ PUGI__NS_BEGIN return ns; } - + public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) @@ -9158,14 +10051,14 @@ PUGI__NS_BEGIN assert(type == ast_number_constant); _data.number = value; } - + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } - + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { @@ -9200,25 +10093,25 @@ PUGI__NS_BEGIN { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); - + case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); - + case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); - + case ast_op_less: return compare_rel(_left, _right, c, stack, less()); - + case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); - + case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); @@ -9244,43 +10137,43 @@ PUGI__NS_BEGIN case ast_func_boolean: return _left->eval_boolean(c, stack); - + case ast_func_not: return !_left->eval_boolean(c, stack); - + case ast_func_true: return true; - + case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; - + xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); - + for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); - + if (a) { const char_t* value = a.value(); - + // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } - + return *value == 0 || *value == '-'; } } - + return false; } @@ -9309,15 +10202,15 @@ PUGI__NS_BEGIN { case xpath_type_number: return convert_number_to_boolean(eval_number(c, stack)); - + case xpath_type_string: { xpath_allocator_capture cr(stack.result); return !eval_string(c, stack).empty(); } - - case xpath_type_node_set: + + case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); @@ -9325,7 +10218,7 @@ PUGI__NS_BEGIN } default: - assert(!"Wrong expression for return type boolean"); + assert(false && "Wrong expression for return type boolean"); return false; } } @@ -9338,7 +10231,7 @@ PUGI__NS_BEGIN { case ast_op_add: return _left->eval_number(c, stack) + _right->eval_number(c, stack); - + case ast_op_subtract: return _left->eval_number(c, stack) - _right->eval_number(c, stack); @@ -9359,7 +10252,7 @@ PUGI__NS_BEGIN case ast_func_last: return static_cast(c.size); - + case ast_func_position: return static_cast(c.position); @@ -9369,28 +10262,28 @@ PUGI__NS_BEGIN return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); } - + case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } - + case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } - + case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } - + case ast_func_number_1: return _left->eval_number(c, stack); @@ -9399,36 +10292,36 @@ PUGI__NS_BEGIN xpath_allocator_capture cr(stack.result); double r = 0; - + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); - + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } - + return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); - + return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); - + return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); - + case ast_variable: { assert(_rettype == _data.variable->type()); @@ -9445,30 +10338,30 @@ PUGI__NS_BEGIN { case xpath_type_boolean: return eval_boolean(c, stack) ? 1 : 0; - + case xpath_type_string: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } - + case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } - + default: - assert(!"Wrong expression for return type number"); + assert(false && "Wrong expression for return type number"); return 0; } - + } } } - + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) { assert(_type == ast_func_concat); @@ -9524,11 +10417,11 @@ PUGI__NS_BEGIN { case ast_string_constant: return xpath_string::from_const(_data.string); - + case ast_func_local_name_0: { xpath_node na = c.n; - + return xpath_string::from_const(local_name(na)); } @@ -9538,14 +10431,14 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; - + return xpath_string::from_const(qualified_name(na)); } @@ -9555,14 +10448,14 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; - + return xpath_string::from_const(namespace_uri(na)); } @@ -9572,7 +10465,7 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(namespace_uri(na)); } @@ -9595,10 +10488,10 @@ PUGI__NS_BEGIN xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); - + return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); } - + case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); @@ -9607,7 +10500,7 @@ PUGI__NS_BEGIN xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); - + const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); @@ -9627,19 +10520,19 @@ PUGI__NS_BEGIN size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); - + if (is_nan(first)) return xpath_string(); // NaN else if (first >= s_length + 1) return xpath_string(); - + size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + s.length(); - + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } - + case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); @@ -9651,12 +10544,12 @@ PUGI__NS_BEGIN double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); - + if (is_nan(first) || is_nan(last)) return xpath_string(); else if (first >= s_length + 1) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); - + size_t pos = first < 1 ? 1 : static_cast(first); size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); @@ -9671,18 +10564,20 @@ PUGI__NS_BEGIN { xpath_string s = string_value(c.n, stack.result); - normalize_space(s.data(stack.result)); + char_t* begin = s.data(stack.result); + char_t* end = normalize_space(begin); - return s; + return xpath_string::from_heap_preallocated(begin, end); } case ast_func_normalize_space_1: { xpath_string s = _left->eval_string(c, stack); - normalize_space(s.data(stack.result)); - - return s; + char_t* begin = s.data(stack.result); + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); } case ast_func_translate: @@ -9695,18 +10590,20 @@ PUGI__NS_BEGIN xpath_string from = _right->eval_string(c, swapped_stack); xpath_string to = _right->_next->eval_string(c, swapped_stack); - translate(s.data(stack.result), from.c_str(), to.c_str(), to.length()); + char_t* begin = s.data(stack.result); + char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); - return s; + return xpath_string::from_heap_preallocated(begin, end); } case ast_opt_translate_table: { xpath_string s = _left->eval_string(c, stack); - translate_table(s.data(stack.result), _data.table); + char_t* begin = s.data(stack.result); + char_t* end = translate_table(begin, _data.table); - return s; + return xpath_string::from_heap_preallocated(begin, end); } case ast_variable: @@ -9725,10 +10622,10 @@ PUGI__NS_BEGIN { case xpath_type_boolean: return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - + case xpath_type_number: return convert_number_to_string(eval_number(c, stack), stack.result); - + case xpath_type_node_set: { xpath_allocator_capture cr(stack.temp); @@ -9738,9 +10635,9 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); } - + default: - assert(!"Wrong expression for return type string"); + assert(false && "Wrong expression for return type string"); return xpath_string(); } } @@ -9779,20 +10676,20 @@ PUGI__NS_BEGIN bool once = eval_once(set.type(), eval); apply_predicate(set, 0, stack, once); - + return set; } - + case ast_func_id: return xpath_node_set_raw(); - + case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, eval, axis_to_type()); - + case axis_ancestor_or_self: return step_do(c, stack, eval, axis_to_type()); @@ -9801,7 +10698,7 @@ PUGI__NS_BEGIN case axis_child: return step_do(c, stack, eval, axis_to_type()); - + case axis_descendant: return step_do(c, stack, eval, axis_to_type()); @@ -9810,28 +10707,28 @@ PUGI__NS_BEGIN case axis_following: return step_do(c, stack, eval, axis_to_type()); - + case axis_following_sibling: return step_do(c, stack, eval, axis_to_type()); - + case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); - + case axis_parent: return step_do(c, stack, eval, axis_to_type()); - + case axis_preceding: return step_do(c, stack, eval, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, eval, axis_to_type()); - + case axis_self: return step_do(c, stack, eval, axis_to_type()); default: - assert(!"Unknown axis"); + assert(false && "Unknown axis"); return xpath_node_set_raw(); } } @@ -9870,17 +10767,27 @@ PUGI__NS_BEGIN } default: - assert(!"Wrong expression for return type node set"); + assert(false && "Wrong expression for return type node set"); return xpath_node_set_raw(); } } void optimize(xpath_allocator* alloc) { - if (_left) _left->optimize(alloc); - if (_right) _right->optimize(alloc); - if (_next) _next->optimize(alloc); + if (_left) + _left->optimize(alloc); + + if (_right) + _right->optimize(alloc); + if (_next) + _next->optimize(alloc); + + optimize_self(alloc); + } + + void optimize_self(xpath_allocator* alloc) + { // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && @@ -9938,7 +10845,7 @@ PUGI__NS_BEGIN _type = ast_opt_compare_attribute; } } - + bool is_posinv_expr() const { switch (_type) @@ -9962,10 +10869,10 @@ PUGI__NS_BEGIN default: if (_left && !_left->is_posinv_expr()) return false; - + for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv_expr()) return false; - + return true; } } @@ -10059,7 +10966,8 @@ PUGI__NS_BEGIN { assert(argc <= 1); - if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) + throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); } @@ -10071,13 +10979,15 @@ PUGI__NS_BEGIN case 'b': if (name == PUGIXML_TEXT("boolean") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); - + break; - + case 'c': if (name == PUGIXML_TEXT("count") && argc == 1) { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); + if (args[0]->rettype() != xpath_type_node_set) + throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); } else if (name == PUGIXML_TEXT("contains") && argc == 2) @@ -10086,23 +10996,23 @@ PUGI__NS_BEGIN return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("ceiling") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); - + break; - + case 'f': if (name == PUGIXML_TEXT("false") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); else if (name == PUGIXML_TEXT("floor") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); - + break; - + case 'i': if (name == PUGIXML_TEXT("id") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); - + break; - + case 'l': if (name == PUGIXML_TEXT("last") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); @@ -10110,9 +11020,9 @@ PUGI__NS_BEGIN return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("local-name") && argc <= 1) return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); - + break; - + case 'n': if (name == PUGIXML_TEXT("name") && argc <= 1) return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); @@ -10124,21 +11034,21 @@ PUGI__NS_BEGIN return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("number") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); - + break; - + case 'p': if (name == PUGIXML_TEXT("position") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); - + break; - + case 'r': if (name == PUGIXML_TEXT("round") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); break; - + case 's': if (name == PUGIXML_TEXT("string") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); @@ -10159,13 +11069,13 @@ PUGI__NS_BEGIN } break; - + case 't': if (name == PUGIXML_TEXT("translate") && argc == 3) return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("true") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); - + break; default: @@ -10190,37 +11100,37 @@ PUGI__NS_BEGIN return axis_ancestor_or_self; else if (name == PUGIXML_TEXT("attribute")) return axis_attribute; - + break; - + case 'c': if (name == PUGIXML_TEXT("child")) return axis_child; - + break; - + case 'd': if (name == PUGIXML_TEXT("descendant")) return axis_descendant; else if (name == PUGIXML_TEXT("descendant-or-self")) return axis_descendant_or_self; - + break; - + case 'f': if (name == PUGIXML_TEXT("following")) return axis_following; else if (name == PUGIXML_TEXT("following-sibling")) return axis_following_sibling; - + break; - + case 'n': if (name == PUGIXML_TEXT("namespace")) return axis_namespace; - + break; - + case 'p': if (name == PUGIXML_TEXT("parent")) return axis_parent; @@ -10228,13 +11138,13 @@ PUGI__NS_BEGIN return axis_preceding; else if (name == PUGIXML_TEXT("preceding-sibling")) return axis_preceding_sibling; - + break; - + case 's': if (name == PUGIXML_TEXT("self")) return axis_self; - + break; default: @@ -10272,7 +11182,7 @@ PUGI__NS_BEGIN return nodetest_type_text; break; - + default: break; } @@ -10292,7 +11202,9 @@ PUGI__NS_BEGIN if (!_variables) throw_error("Unknown variable: variable set is not provided"); - xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end); + xpath_variable* var = 0; + if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) + throw_error_oom(); if (!var) throw_error("Unknown variable: variable set does not contain the given name"); @@ -10343,12 +11255,12 @@ PUGI__NS_BEGIN { xpath_ast_node* args[2] = {0}; size_t argc = 0; - + xpath_lexer_string function = _lexer.contents(); _lexer.next(); - + xpath_ast_node* last_arg = 0; - + if (_lexer.current() != lex_open_brace) throw_error("Unrecognized function call"); _lexer.next(); @@ -10361,16 +11273,16 @@ PUGI__NS_BEGIN if (_lexer.current() != lex_comma) throw_error("No comma between function arguments"); _lexer.next(); - + xpath_ast_node* n = parse_expression(); - + if (argc < 2) args[argc] = n; else last_arg->set_next(n); argc++; last_arg = n; } - + _lexer.next(); return parse_function(function, argc, args); @@ -10382,7 +11294,7 @@ PUGI__NS_BEGIN return 0; } } - + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate // Predicate ::= '[' PredicateExpr ']' // PredicateExpr ::= Expr @@ -10396,19 +11308,20 @@ PUGI__NS_BEGIN xpath_ast_node* expr = parse_expression(); - if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); + if (n->rettype() != xpath_type_node_set) + throw_error("Predicate has to be applied to node set"); n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); - + _lexer.next(); } - + return n; } - + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep // AxisSpecifier ::= AxisName '::' | '@'? // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' @@ -10426,25 +11339,25 @@ PUGI__NS_BEGIN { axis = axis_attribute; axis_specified = true; - + _lexer.next(); } else if (_lexer.current() == lex_dot) { _lexer.next(); - + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); } else if (_lexer.current() == lex_double_dot) { _lexer.next(); - + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); } - + nodetest_t nt_type = nodetest_none; xpath_lexer_string nt_name; - + if (_lexer.current() == lex_string) { // node name test @@ -10455,11 +11368,13 @@ PUGI__NS_BEGIN if (_lexer.current() == lex_double_colon) { // parse axis name - if (axis_specified) throw_error("Two axis specifiers in one step"); + if (axis_specified) + throw_error("Two axis specifiers in one step"); axis = parse_axis_name(nt_name, axis_specified); - if (!axis_specified) throw_error("Unknown axis"); + if (!axis_specified) + throw_error("Unknown axis"); // read actual node test _lexer.next(); @@ -10477,40 +11392,42 @@ PUGI__NS_BEGIN } else throw_error("Unrecognized node test"); } - + if (nt_type == nodetest_none) { // node type test or processing-instruction if (_lexer.current() == lex_open_brace) { _lexer.next(); - + if (_lexer.current() == lex_close_brace) { _lexer.next(); nt_type = parse_node_test_type(nt_name); - if (nt_type == nodetest_none) throw_error("Unrecognized node type"); - + if (nt_type == nodetest_none) + throw_error("Unrecognized node type"); + nt_name = xpath_lexer_string(); } else if (nt_name == PUGIXML_TEXT("processing-instruction")) { if (_lexer.current() != lex_quoted_string) throw_error("Only literals are allowed as arguments to processing-instruction()"); - + nt_type = nodetest_pi; nt_name = _lexer.contents(); _lexer.next(); - + if (_lexer.current() != lex_close_brace) throw_error("Unmatched brace near processing-instruction()"); _lexer.next(); } else + { throw_error("Unmatched brace near node type test"); - + } } // QName or NCName:* else @@ -10518,10 +11435,13 @@ PUGI__NS_BEGIN if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* { nt_name.end--; // erase * - + nt_type = nodetest_all_in_namespace; } - else nt_type = nodetest_name; + else + { + nt_type = nodetest_name; + } } } } @@ -10530,38 +11450,42 @@ PUGI__NS_BEGIN nt_type = nodetest_all; _lexer.next(); } - else throw_error("Unrecognized node test"); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); - + else + { + throw_error("Unrecognized node test"); + } + + const char_t* nt_name_copy = alloc_string(nt_name); + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, nt_name_copy); + xpath_ast_node* last = 0; - + while (_lexer.current() == lex_open_square_brace) { _lexer.next(); - + xpath_ast_node* expr = parse_expression(); xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); - + if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); _lexer.next(); - + if (last) last->set_next(pred); else n->set_right(pred); - + last = pred; } return n; } - + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) { xpath_ast_node* n = parse_step(set); - + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); @@ -10569,13 +11493,13 @@ PUGI__NS_BEGIN if (l == lex_double_slash) n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - + n = parse_step(n); } - + return n; } - + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath xpath_ast_node* parse_location_path() @@ -10583,7 +11507,7 @@ PUGI__NS_BEGIN if (_lexer.current() == lex_slash) { _lexer.next(); - + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path @@ -10597,17 +11521,17 @@ PUGI__NS_BEGIN else if (_lexer.current() == lex_double_slash) { _lexer.next(); - + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - + return parse_relative_location_path(n); } // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 return parse_relative_location_path(0); } - + // PathExpr ::= LocationPath // | FilterExpr // | FilterExpr '/' RelativeLocationPath @@ -10623,7 +11547,7 @@ PUGI__NS_BEGIN // '(' in case of it being an expression, string literal, number constant or // function call. - if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { @@ -10631,29 +11555,31 @@ PUGI__NS_BEGIN { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); - + while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; - + if (*state != '(') return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. - if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); + if (parse_node_test_type(_lexer.contents()) != nodetest_none) + return parse_location_path(); } - + xpath_ast_node* n = parse_filter_expression(); if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); - + if (l == lex_double_slash) { - if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); + if (n->rettype() != xpath_type_node_set) + throw_error("Step has to be applied to node set"); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); } - + // select from location path return parse_relative_location_path(n); } @@ -10670,7 +11596,9 @@ PUGI__NS_BEGIN return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); } else + { return parse_location_path(); + } } struct binary_op_t @@ -10799,13 +11727,11 @@ PUGI__NS_BEGIN xpath_ast_node* parse() { xpath_ast_node* result = parse_expression(); - + + // check if there are unparsed tokens left if (_lexer.current() != lex_eof) - { - // there are still unparsed tokens left, error throw_error("Incorrect query"); - } - + return result; } @@ -10828,19 +11754,18 @@ PUGI__NS_BEGIN static xpath_query_impl* create() { void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); + if (!memory) return 0; return new (memory) xpath_query_impl(); } - static void destroy(void* ptr) + static void destroy(xpath_query_impl* impl) { - if (!ptr) return; - // free all allocated pages - static_cast(ptr)->alloc.release(); + impl->alloc.release(); // free allocator memory (with the first page) - xml_memory::deallocate(ptr); + xml_memory::deallocate(impl); } xpath_query_impl(): root(0), alloc(&block) @@ -10894,7 +11819,7 @@ namespace pugi { assert(_result.error); } - + PUGI__FN const char* xpath_exception::what() const throw() { return _result.error; @@ -10905,15 +11830,15 @@ namespace pugi return _result; } #endif - + PUGI__FN xpath_node::xpath_node() { } - + PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } - + PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } @@ -10922,12 +11847,12 @@ namespace pugi { return _attribute ? xml_node() : _node; } - + PUGI__FN xml_attribute xpath_node::attribute() const { return _attribute; } - + PUGI__FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); @@ -10941,7 +11866,7 @@ namespace pugi { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } - + PUGI__FN bool xpath_node::operator!() const { return !(_node || _attribute); @@ -10951,7 +11876,7 @@ namespace pugi { return _node == n._node && _attribute == n._attribute; } - + PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; @@ -10969,7 +11894,7 @@ namespace pugi } #endif - PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) + PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) { assert(begin_ <= end_); @@ -10985,6 +11910,7 @@ namespace pugi _begin = &_storage; _end = &_storage + size_; + _type = type_; } else { @@ -11001,60 +11927,94 @@ namespace pugi } memcpy(storage, begin_, size_ * sizeof(xpath_node)); - + // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // finalize _begin = storage; _end = storage + size_; + _type = type_; } } +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) + { + _type = rhs._type; + _storage = rhs._storage; + _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _end = _begin + (rhs._end - rhs._begin); + + rhs._type = type_unsorted; + rhs._begin = &rhs._storage; + rhs._end = rhs._begin; + } +#endif + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) { } - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) { - _assign(begin_, end_); + _assign(begin_, end_, type_); } PUGI__FN xpath_node_set::~xpath_node_set() { - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); } - - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) + + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) { - _assign(ns._begin, ns._end); + _assign(ns._begin, ns._end, ns._type); } - + PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; - - _type = ns._type; - _assign(ns._begin, ns._end); + + _assign(ns._begin, ns._end, ns._type); return *this; } +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _move(rhs); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) + { + if (this == &rhs) return *this; + + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + + _move(rhs); + + return *this; + } +#endif + PUGI__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } - + PUGI__FN size_t xpath_node_set::size() const { return _end - _begin; } - + PUGI__FN bool xpath_node_set::empty() const { return _begin == _end; } - + PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); @@ -11065,12 +12025,12 @@ namespace pugi { return _begin; } - + PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } - + PUGI__FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); @@ -11095,7 +12055,7 @@ namespace pugi return error ? error : "No error"; } - PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0) + PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } @@ -11116,7 +12076,7 @@ namespace pugi return static_cast(this)->name; default: - assert(!"Invalid variable type"); + assert(false && "Invalid variable type"); return 0; } } @@ -11194,27 +12154,80 @@ namespace pugi PUGI__FN xpath_variable_set::xpath_variable_set() { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _destroy(_data[i]); + } + + PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + + _assign(rhs); + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + { + if (this == &rhs) return *this; + + _assign(rhs); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { - xpath_variable* var = _data[i]; + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + } - while (var) - { - xpath_variable* next = var->_next; + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _destroy(_data[i]); - impl::delete_xpath_variable(var->_type, var); + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } - var = next; - } + return *this; + } +#endif + + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + { + xpath_variable_set temp; + + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) + return; + + _swap(temp); + } + + PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* chain = _data[i]; + + _data[i] = rhs._data[i]; + rhs._data[i] = chain; } } - PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const + PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; @@ -11227,6 +12240,45 @@ namespace pugi return 0; } + PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + { + xpath_variable* last = 0; + + while (var) + { + // allocate storage for new variable + xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); + if (!nvar) return false; + + // link the variable to the result immediately to handle failures gracefully + if (last) + last->_next = nvar; + else + *out_result = nvar; + + last = nvar; + + // copy the value; this can fail due to out-of-memory conditions + if (!impl::copy_xpath_variable(nvar, var)) return false; + + var = var->_next; + } + + return true; + } + + PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + { + while (var) + { + xpath_variable* next = var->_next; + + impl::delete_xpath_variable(var->_type, var); + + var = next; + } + } + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); @@ -11242,7 +12294,6 @@ namespace pugi if (result) { - result->_type = type; result->_next = _data[hash]; _data[hash] = result; @@ -11277,12 +12328,12 @@ namespace pugi PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) { - return find(name); + return _find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { - return find(name); + return _find(name); } PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) @@ -11299,7 +12350,8 @@ namespace pugi } else { - impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); + using impl::auto_deleter; // MSVC7 workaround + auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); @@ -11307,16 +12359,46 @@ namespace pugi { qimpl->root->optimize(&qimpl->alloc); - _impl = static_cast(impl_holder.release()); + _impl = impl.release(); _result.error = 0; } } } + PUGI__FN xpath_query::xpath_query(): _impl(0) + { + } + PUGI__FN xpath_query::~xpath_query() { - impl::xpath_query_impl::destroy(_impl); + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) + { + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + } + + PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) + { + if (this == &rhs) return *this; + + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + + return *this; } +#endif PUGI__FN xpath_value_type xpath_query::return_type() const { @@ -11328,21 +12410,21 @@ namespace pugi PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; - + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return false; #endif - + return static_cast(_impl)->root->eval_boolean(c, sd.stack); } - + PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); - + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; @@ -11371,7 +12453,7 @@ namespace pugi impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); size_t full_size = r.length() + 1; - + if (capacity > 0) { size_t size = (full_size < capacity) ? full_size : capacity; @@ -11380,7 +12462,7 @@ namespace pugi memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); buffer[size - 1] = 0; } - + return full_size; } @@ -11493,6 +12575,9 @@ namespace pugi #undef PUGI__NS_END #undef PUGI__FN #undef PUGI__FN_NO_INLINE +#undef PUGI__GETHEADER_IMPL +#undef PUGI__GETPAGE_IMPL +#undef PUGI__GETPAGE #undef PUGI__NODETYPE #undef PUGI__IS_CHARTYPE_IMPL #undef PUGI__IS_CHARTYPE @@ -11512,7 +12597,7 @@ namespace pugi #endif /** - * Copyright (c) 2006-2014 Arseny Kapoulkine + * Copyright (c) 2006-2016 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -11525,7 +12610,7 @@ namespace pugi * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND diff --git a/third_party/pugixml-1.5/pugixml.hpp b/third_party/pugixml-1.8/pugixml.hpp similarity index 92% rename from third_party/pugixml-1.5/pugixml.hpp rename to third_party/pugixml-1.8/pugixml.hpp index 917ef4aa77f..6288d4bd929 100644 --- a/third_party/pugixml-1.5/pugixml.hpp +++ b/third_party/pugixml-1.8/pugixml.hpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.5 + * pugixml parser - version 1.8 * -------------------------------------------------------- - * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2016, 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 @@ -13,7 +13,7 @@ #ifndef PUGIXML_VERSION // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 150 +# define PUGIXML_VERSION 180 #endif // Include user configuration file (this can define various configuration macros) @@ -65,13 +65,31 @@ // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG -# if defined(__cplusplus) && __cplusplus >= 201103 +# if __cplusplus >= 201103 # define PUGIXML_HAS_LONG_LONG # elif defined(_MSC_VER) && _MSC_VER >= 1400 # define PUGIXML_HAS_LONG_LONG # endif #endif +// If the platform is known to have move semantics support, compile move ctor/operator implementation +#ifndef PUGIXML_HAS_MOVE +# if __cplusplus >= 201103 +# define PUGIXML_HAS_MOVE +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_HAS_MOVE +# endif +#endif + +// If C++ is 2011 or higher, add 'override' qualifiers +#ifndef PUGIXML_OVERRIDE +# if __cplusplus >= 201103 +# define PUGIXML_OVERRIDE override +# else +# define PUGIXML_OVERRIDE +# endif +#endif + // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t @@ -133,13 +151,13 @@ namespace pugi // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. const unsigned int parse_eol = 0x0020; - + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. const unsigned int parse_wconv_attribute = 0x0040; // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. const unsigned int parse_wnorm_attribute = 0x0080; - + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. const unsigned int parse_declaration = 0x0100; @@ -158,6 +176,11 @@ namespace pugi // is a valid document. This flag is off by default. const unsigned int parse_fragment = 0x1000; + // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of + // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. + // This flag is off by default. + const unsigned int parse_embed_pcdata = 0x2000; + // The default parsing mode. // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. @@ -184,16 +207,16 @@ namespace pugi }; // Formatting flags - + // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default. const unsigned int format_indent = 0x01; - + // Write encoding-specific BOM to the output stream. This flag is off by default. const unsigned int format_write_bom = 0x02; // Use raw output mode (no indentation and no line breaks are written). This flag is off by default. const unsigned int format_raw = 0x04; - + // Omit default XML declaration even if there is no declaration in the document. This flag is off by default. const unsigned int format_no_declaration = 0x08; @@ -203,10 +226,16 @@ namespace pugi // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default. const unsigned int format_save_file_text = 0x20; + // Write every attribute on a new line with appropriate indentation. This flag is off by default. + const unsigned int format_indent_attributes = 0x40; + + // Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default. + const unsigned int format_no_empty_element_tags = 0x80; + // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; - + // Forward declarations struct xml_attribute_struct; struct xml_node_struct; @@ -222,7 +251,7 @@ namespace pugi class xml_node; class xml_text; - + #ifndef PUGIXML_NO_XPATH class xpath_node; class xpath_node_set; @@ -265,7 +294,7 @@ namespace pugi // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio xml_writer_file(void* file); - virtual void write(const void* data, size_t size); + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; private: void* file; @@ -280,7 +309,7 @@ namespace pugi xml_writer_stream(std::basic_ostream >& stream); xml_writer_stream(std::basic_ostream >& stream); - virtual void write(const void* data, size_t size); + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; private: std::basic_ostream >* narrow_stream; @@ -296,13 +325,13 @@ namespace pugi private: xml_attribute_struct* _attr; - + typedef void (*unspecified_bool_type)(xml_attribute***); public: // Default constructor. Constructs an empty attribute. xml_attribute(); - + // Constructs attribute from internal pointer explicit xml_attribute(xml_attribute_struct* attr); @@ -351,7 +380,10 @@ namespace pugi // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set_value(int rhs); bool set_value(unsigned int rhs); + bool set_value(long rhs); + bool set_value(unsigned long rhs); bool set_value(double rhs); + bool set_value(float rhs); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -363,7 +395,10 @@ namespace pugi xml_attribute& operator=(const char_t* rhs); xml_attribute& operator=(int rhs); xml_attribute& operator=(unsigned int rhs); + xml_attribute& operator=(long rhs); + xml_attribute& operator=(unsigned long rhs); xml_attribute& operator=(double rhs); + xml_attribute& operator=(float rhs); xml_attribute& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -412,7 +447,7 @@ namespace pugi // Borland C++ workaround bool operator!() const; - + // Comparison operators (compares wrapped node pointers) bool operator==(const xml_node& r) const; bool operator!=(const xml_node& r) const; @@ -431,9 +466,9 @@ namespace pugi const char_t* name() const; // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. const char_t* value() const; - + // Get attribute list xml_attribute first_attribute() const; xml_attribute last_attribute() const; @@ -445,7 +480,7 @@ namespace pugi // Get next/previous sibling in the children list of the parent node xml_node next_sibling() const; xml_node previous_sibling() const; - + // Get parent node xml_node parent() const; @@ -461,6 +496,9 @@ namespace pugi xml_node next_sibling(const char_t* name) const; xml_node previous_sibling(const char_t* name) const; + // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) + xml_attribute attribute(const char_t* name, xml_attribute& hint) const; + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA const char_t* child_value() const; @@ -470,7 +508,7 @@ namespace pugi // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); - + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. xml_attribute append_attribute(const char_t* name); xml_attribute prepend_attribute(const char_t* name); @@ -524,11 +562,11 @@ namespace pugi template xml_attribute find_attribute(Predicate pred) const { if (!_root) return xml_attribute(); - + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) if (pred(attrib)) return attrib; - + return xml_attribute(); } @@ -536,11 +574,11 @@ namespace pugi template xml_node find_child(Predicate pred) const { if (!_root) return xml_node(); - + for (xml_node node = first_child(); node; node = node.next_sibling()) if (pred(node)) return node; - + return xml_node(); } @@ -550,7 +588,7 @@ namespace pugi if (!_root) return xml_node(); xml_node cur = first_child(); - + while (cur._root && cur._root != _root) { if (pred(cur)) return cur; @@ -582,7 +620,7 @@ namespace pugi // Recursively traverse subtree with xml_tree_walker bool traverse(xml_tree_walker& walker); - + #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; @@ -597,7 +635,7 @@ namespace pugi xpath_node select_single_node(const xpath_query& query) const; #endif - + // Print subtree using a writer object void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; @@ -693,7 +731,10 @@ namespace pugi // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set(int rhs); bool set(unsigned int rhs); + bool set(long rhs); + bool set(unsigned long rhs); bool set(double rhs); + bool set(float rhs); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -705,7 +746,10 @@ namespace pugi xml_text& operator=(const char_t* rhs); xml_text& operator=(int rhs); xml_text& operator=(unsigned int rhs); + xml_text& operator=(long rhs); + xml_text& operator=(unsigned long rhs); xml_text& operator=(double rhs); + xml_text& operator=(float rhs); xml_text& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -857,11 +901,11 @@ namespace pugi private: int _depth; - + protected: // Get current traversal depth int depth() const; - + public: xml_tree_walker(); virtual ~xml_tree_walker(); @@ -932,13 +976,13 @@ namespace pugi char_t* _buffer; char _memory[192]; - + // Non-copyable semantics xml_document(const xml_document&); - const xml_document& operator=(const xml_document&); + xml_document& operator=(const xml_document&); - void create(); - void destroy(); + void _create(); + void _destroy(); public: // Default constructor, makes empty document @@ -1036,12 +1080,12 @@ namespace pugi xpath_value_type _type; xpath_variable* _next; - xpath_variable(); + xpath_variable(xpath_value_type type); // Non-copyable semantics xpath_variable(const xpath_variable&); xpath_variable& operator=(const xpath_variable&); - + public: // Get variable name const char_t* name() const; @@ -1068,17 +1112,29 @@ namespace pugi private: xpath_variable* _data[64]; - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set&); - xpath_variable_set& operator=(const xpath_variable_set&); + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); - xpath_variable* find(const char_t* name) const; + xpath_variable* _find(const char_t* name) const; + + static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs); + xpath_variable_set& operator=(xpath_variable_set&& rhs); + #endif + // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); @@ -1111,26 +1167,35 @@ namespace pugi // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + // Constructor + xpath_query(); + // Destructor ~xpath_query(); + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_query(xpath_query&& rhs); + xpath_query& operator=(xpath_query&& rhs); + #endif + // Get query expression return type xpath_value_type return_type() const; - + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. bool evaluate_boolean(const xpath_node& n) const; - + // Evaluate expression as double value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. double evaluate_number(const xpath_node& n) const; - + #ifndef PUGIXML_NO_STL // Evaluate expression as string value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. string_t evaluate_string(const xpath_node& n) const; #endif - + // Evaluate expression as string value in the specified context; performs type conversion if necessary. // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. @@ -1157,7 +1222,7 @@ namespace pugi // Borland C++ workaround bool operator!() const; }; - + #ifndef PUGIXML_NO_EXCEPTIONS // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception @@ -1170,26 +1235,26 @@ namespace pugi explicit xpath_exception(const xpath_parse_result& result); // Get error message - virtual const char* what() const throw(); + virtual const char* what() const throw() PUGIXML_OVERRIDE; // Get parse result const xpath_parse_result& result() const; }; #endif - + // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { private: xml_node _node; xml_attribute _attribute; - + typedef void (*unspecified_bool_type)(xpath_node***); public: // Default constructor; constructs empty XPath node xpath_node(); - + // Construct XPath node from XML node/attribute xpath_node(const xml_node& node); xpath_node(const xml_attribute& attribute, const xml_node& parent); @@ -1197,13 +1262,13 @@ namespace pugi // Get node/attribute, if any xml_node node() const; xml_attribute attribute() const; - + // Get parent of contained node/attribute xml_node parent() const; // Safe bool conversion operator operator unspecified_bool_type() const; - + // Borland C++ workaround bool operator!() const; @@ -1229,13 +1294,13 @@ namespace pugi type_sorted, // Sorted by document order (ascending) type_sorted_reverse // Sorted by document order (descending) }; - + // Constant iterator type typedef const xpath_node* const_iterator; // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work typedef const xpath_node* iterator; - + // Default constructor. Constructs empty set. xpath_node_set(); @@ -1244,42 +1309,49 @@ namespace pugi // Destructor ~xpath_node_set(); - + // Copy constructor/assignment operator xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_node_set(xpath_node_set&& rhs); + xpath_node_set& operator=(xpath_node_set&& rhs); + #endif + // Get collection type type_t type() const; - + // Get collection size size_t size() const; // Indexing operator const xpath_node& operator[](size_t index) const; - + // Collection iterators const_iterator begin() const; const_iterator end() const; // Sort the collection in ascending/descending order by document order void sort(bool reverse = false); - + // Get first node in the collection by document order xpath_node first() const; - + // Check if collection is empty bool empty() const; - + private: type_t _type; - + xpath_node _storage; - + xpath_node* _begin; xpath_node* _end; - void _assign(const_iterator begin, const_iterator end); + void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs); }; #endif @@ -1287,7 +1359,7 @@ namespace pugi // Convert wide string to UTF8 std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); - + // Convert UTF8 to wide string std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); @@ -1295,13 +1367,13 @@ namespace pugi // Memory allocation function interface; returns pointer to allocated memory or NULL on failure typedef void* (*allocation_function)(size_t size); - + // Memory deallocation function interface typedef void (*deallocation_function)(void* ptr); // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - + // Get current memory management functions allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); @@ -1329,8 +1401,15 @@ namespace std #endif +// Make sure implementation is included in header-only mode +// Use macro expansion in #include to work around QMake (QTBUG-11923) +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) +# define PUGIXML_SOURCE "pugixml.cpp" +# include PUGIXML_SOURCE +#endif + /** - * Copyright (c) 2006-2014 Arseny Kapoulkine + * Copyright (c) 2006-2016 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -1343,7 +1422,7 @@ namespace std * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND