Changeset 1055 for XmlTools2/trunk


Ignore:
Timestamp:
Oct 28, 2016, 11:05:06 PM (8 years ago)
Author:
s10k
Message:

XmlTools 2.0c

Location:
XmlTools2/trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • XmlTools2/trunk/XmlTools.pro

    r964 r1055  
    11TEMPLATE = app
    22CONFIG += console
     3CONFIG   += c++14
    34QT       += core
    45QT       -= gui # not needed
  • XmlTools2/trunk/libs/pugiconfig.hpp

    r906 r1055  
    11/**
    2  * pugixml parser - version 1.2
     2 * pugixml parser - version 1.7
    33 * --------------------------------------------------------
    4  * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
     4 * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
    55 * Report bugs and download new versions at http://pugixml.org/
    66 *
     
    1717// Uncomment this to enable wchar_t mode
    1818// #define PUGIXML_WCHAR_MODE
     19
     20// Uncomment this to enable compact mode
     21// #define PUGIXML_COMPACT
    1922
    2023// Uncomment this to disable XPath
     
    3336// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
    3437
    35 // Uncomment this to switch to header-only version
    36 // #define PUGIXML_HEADER_ONLY
    37 // #include "pugixml.cpp"
    38 
    3938// Tune these constants to adjust memory-related behavior
    4039// #define PUGIXML_MEMORY_PAGE_SIZE 32768
     
    4241// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
    4342
     43// Uncomment this to switch to header-only version
     44// #define PUGIXML_HEADER_ONLY
     45
     46// Uncomment this to enable long long support
     47// #define PUGIXML_HAS_LONG_LONG
     48
    4449#endif
    4550
    4651/**
    47  * Copyright (c) 2006-2012 Arseny Kapoulkine
     52 * Copyright (c) 2006-2015 Arseny Kapoulkine
    4853 *
    4954 * Permission is hereby granted, free of charge, to any person
  • XmlTools2/trunk/libs/pugixml.cpp

    r906 r1055  
    11/**
    2  * pugixml parser - version 1.2
     2 * pugixml parser - version 1.7
    33 * --------------------------------------------------------
    4  * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
     4 * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
    55 * Report bugs and download new versions at http://pugixml.org/
    66 *
     
    2121#include <string.h>
    2222#include <assert.h>
    23 #include <wchar.h>
     23#include <limits.h>
     24
     25#ifdef PUGIXML_WCHAR_MODE
     26#       include <wchar.h>
     27#endif
    2428
    2529#ifndef PUGIXML_NO_XPATH
     
    8286#endif
    8387
     88// Branch weight controls
     89#if defined(__GNUC__)
     90#       define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
     91#else
     92#       define PUGI__UNLIKELY(cond) (cond)
     93#endif
     94
    8495// Simple static assertion
    8596#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
     
    96107using std::memcpy;
    97108using std::memmove;
     109using std::memset;
    98110#endif
    99111
     
    124136#       include <stdint.h>
    125137#else
     138namespace pugi
     139{
    126140#       ifndef _UINTPTR_T_DEFINED
    127 // No native uintptr_t in MSVC6 and in some WinCE versions
    128 typedef size_t uintptr_t;
    129 #define _UINTPTR_T_DEFINED
     141        typedef size_t uintptr_t;
    130142#       endif
    131 PUGI__NS_BEGIN
     143
    132144        typedef unsigned __int8 uint8_t;
    133145        typedef unsigned __int16 uint16_t;
    134146        typedef unsigned __int32 uint32_t;
    135 PUGI__NS_END
     147}
    136148#endif
    137149
     
    155167        };
    156168
     169        // Global allocation functions are stored in class statics so that in header mode linker deduplicates them
     170        // Without a template<> we'll get multiple definitions of the same static
    157171        template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
    158172        template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
     
    196210                return lhs[count] == 0;
    197211        }
    198        
    199 #ifdef PUGIXML_WCHAR_MODE
    200         // Convert string to wide string, assuming all symbols are ASCII
    201         PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
    202         {
    203                 for (const char* i = source; *i; ++i) *dest++ = *i;
    204                 *dest = 0;
    205         }
    206 #endif
     212
     213        // Get length of wide string, even if CRT lacks wide character support
     214        PUGI__FN size_t strlength_wide(const wchar_t* s)
     215        {
     216                assert(s);
     217
     218        #ifdef PUGIXML_WCHAR_MODE
     219                return wcslen(s);
     220        #else
     221                const wchar_t* end = s;
     222                while (*end) end++;
     223                return static_cast<size_t>(end - s);
     224        #endif
     225        }
    207226PUGI__NS_END
    208227
    209 #if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
    210 // auto_ptr-like buffer holder for exception recovery
     228// auto_ptr-like object for exception recovery
    211229PUGI__NS_BEGIN
    212         struct buffer_holder
    213         {
    214                 void* data;
    215                 void (*deleter)(void*);
    216 
    217                 buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
    218                 {
    219                 }
    220 
    221                 ~buffer_holder()
     230        template <typename T, typename D = void(*)(T*)> struct auto_deleter
     231        {
     232                T* data;
     233                D deleter;
     234
     235                auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)
     236                {
     237                }
     238
     239                ~auto_deleter()
    222240                {
    223241                        if (data) deleter(data);
    224242                }
    225243
    226                 void* release()
    227                 {
    228                         void* result = data;
     244                T* release()
     245                {
     246                        T* result = data;
    229247                        data = 0;
    230248                        return result;
    231249                }
    232250        };
     251PUGI__NS_END
     252
     253#ifdef PUGIXML_COMPACT
     254PUGI__NS_BEGIN
     255        class compact_hash_table
     256        {
     257        public:
     258                compact_hash_table(): _items(0), _capacity(0), _count(0)
     259                {
     260                }
     261
     262                void clear()
     263                {
     264                        if (_items)
     265                        {
     266                                xml_memory::deallocate(_items);
     267                                _items = 0;
     268                                _capacity = 0;
     269                                _count = 0;
     270                        }
     271                }
     272
     273                void** find(const void* key)
     274                {
     275                        assert(key);
     276
     277                        if (_capacity == 0) return 0;
     278
     279                        size_t hashmod = _capacity - 1;
     280                        size_t bucket = hash(key) & hashmod;
     281
     282                        for (size_t probe = 0; probe <= hashmod; ++probe)
     283                        {
     284                                item_t& probe_item = _items[bucket];
     285
     286                                if (probe_item.key == key)
     287                                        return &probe_item.value;
     288
     289                                if (probe_item.key == 0)
     290                                        return 0;
     291
     292                                // hash collision, quadratic probing
     293                                bucket = (bucket + probe + 1) & hashmod;
     294                        }
     295
     296                        assert(!"Hash table is full");
     297                        return 0;
     298                }
     299
     300                void** insert(const void* key)
     301                {
     302                        assert(key);
     303                        assert(_capacity != 0 && _count < _capacity - _capacity / 4);
     304
     305                        size_t hashmod = _capacity - 1;
     306                        size_t bucket = hash(key) & hashmod;
     307
     308                        for (size_t probe = 0; probe <= hashmod; ++probe)
     309                        {
     310                                item_t& probe_item = _items[bucket];
     311
     312                                if (probe_item.key == 0)
     313                                {
     314                                        probe_item.key = key;
     315                                        _count++;
     316                                        return &probe_item.value;
     317                                }
     318
     319                                if (probe_item.key == key)
     320                                        return &probe_item.value;
     321
     322                                // hash collision, quadratic probing
     323                                bucket = (bucket + probe + 1) & hashmod;
     324                        }
     325
     326                        assert(!"Hash table is full");
     327                        return 0;
     328                }
     329
     330                bool reserve()
     331                {
     332                        if (_count + 16 >= _capacity - _capacity / 4)
     333                                return rehash();
     334
     335                        return true;
     336                }
     337
     338        private:
     339                struct item_t
     340                {
     341                        const void* key;
     342                        void* value;
     343                };
     344
     345                item_t* _items;
     346                size_t _capacity;
     347
     348                size_t _count;
     349
     350                bool rehash();
     351
     352                static unsigned int hash(const void* key)
     353                {
     354                        unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));
     355
     356                        // MurmurHash3 32-bit finalizer
     357                        h ^= h >> 16;
     358                        h *= 0x85ebca6bu;
     359                        h ^= h >> 13;
     360                        h *= 0xc2b2ae35u;
     361                        h ^= h >> 16;
     362
     363                        return h;
     364                }
     365        };
     366
     367        PUGI__FN_NO_INLINE bool compact_hash_table::rehash()
     368        {
     369                compact_hash_table rt;
     370                rt._capacity = (_capacity == 0) ? 32 : _capacity * 2;
     371                rt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * rt._capacity));
     372
     373                if (!rt._items)
     374                        return false;
     375
     376                memset(rt._items, 0, sizeof(item_t) * rt._capacity);
     377
     378                for (size_t i = 0; i < _capacity; ++i)
     379                        if (_items[i].key)
     380                                *rt.insert(_items[i].key) = _items[i].value;
     381
     382                if (_items)
     383                        xml_memory::deallocate(_items);
     384
     385                _capacity = rt._capacity;
     386                _items = rt._items;
     387
     388                assert(_count == rt._count);
     389
     390                return true;
     391        }
     392
    233393PUGI__NS_END
    234394#endif
     
    243403                ;
    244404
    245         static const uintptr_t xml_memory_page_alignment = 32;
     405#ifdef PUGIXML_COMPACT
     406        static const uintptr_t xml_memory_block_alignment = 4;
     407
     408        static const uintptr_t xml_memory_page_alignment = sizeof(void*);
     409#else
     410        static const uintptr_t xml_memory_block_alignment = sizeof(void*);
     411
     412        static const uintptr_t xml_memory_page_alignment = 64;
    246413        static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
     414#endif
     415
     416        // extra metadata bits
     417        static const uintptr_t xml_memory_page_contents_shared_mask = 32;
    247418        static const uintptr_t xml_memory_page_name_allocated_mask = 16;
    248419        static const uintptr_t xml_memory_page_value_allocated_mask = 8;
    249420        static const uintptr_t xml_memory_page_type_mask = 7;
    250421
     422        // combined masks for string uniqueness
     423        static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask;
     424        static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask;
     425
     426#ifdef PUGIXML_COMPACT
     427        #define PUGI__GETPAGE_IMPL(header) (header).get_page()
     428#else
     429        #define PUGI__GETPAGE_IMPL(header) reinterpret_cast<impl::xml_memory_page*>((header) & impl::xml_memory_page_pointer_mask)
     430#endif
     431
     432        #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
     433        #define PUGI__NODETYPE(n) static_cast<xml_node_type>(((n)->header & impl::xml_memory_page_type_mask) + 1)
     434
    251435        struct xml_allocator;
    252436
     
    255439                static xml_memory_page* construct(void* memory)
    256440                {
    257                         if (!memory) return 0; //$ redundant, left for performance
    258 
    259441                        xml_memory_page* result = static_cast<xml_memory_page*>(memory);
    260442
    261443                        result->allocator = 0;
    262                         result->memory = 0;
    263444                        result->prev = 0;
    264445                        result->next = 0;
     
    266447                        result->freed_size = 0;
    267448
     449                #ifdef PUGIXML_COMPACT
     450                        result->compact_string_base = 0;
     451                        result->compact_shared_parent = 0;
     452                        result->compact_page_marker = 0;
     453                #endif
     454
    268455                        return result;
    269456                }
    270457
    271458                xml_allocator* allocator;
    272 
    273                 void* memory;
    274459
    275460                xml_memory_page* prev;
     
    279464                size_t freed_size;
    280465
    281                 char data[1];
     466        #ifdef PUGIXML_COMPACT
     467                char_t* compact_string_base;
     468                void* compact_shared_parent;
     469                uint32_t* compact_page_marker;
     470        #endif
    282471        };
    283472
     
    292481                xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
    293482                {
     483                #ifdef PUGIXML_COMPACT
     484                        _hash = 0;
     485                #endif
    294486                }
    295487
    296488                xml_memory_page* allocate_page(size_t data_size)
    297489                {
    298                         size_t size = offsetof(xml_memory_page, data) + data_size;
     490                        size_t size = sizeof(xml_memory_page) + data_size;
    299491
    300492                        // allocate block with some alignment, leaving memory for worst-case padding
     
    302494                        if (!memory) return 0;
    303495
    304                         // align upwards to page boundary
    305                         void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
     496                        // align to next page boundary (note: this guarantees at least 1 usable byte before the page)
     497                        char* page_memory = reinterpret_cast<char*>((reinterpret_cast<uintptr_t>(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1));
    306498
    307499                        // prepare page structure
    308500                        xml_memory_page* page = xml_memory_page::construct(page_memory);
    309 
    310                         page->memory = memory;
     501                        assert(page);
     502
    311503                        page->allocator = _root->allocator;
    312504
     505                        // record the offset for freeing the memory block
     506                        assert(page_memory > memory && page_memory - static_cast<char*>(memory) <= 127);
     507                        page_memory[-1] = static_cast<char>(page_memory - static_cast<char*>(memory));
     508
    313509                        return page;
    314510                }
     
    316512                static void deallocate_page(xml_memory_page* page)
    317513                {
    318                         xml_memory::deallocate(page->memory);
     514                        char* page_memory = reinterpret_cast<char*>(page);
     515
     516                        xml_memory::deallocate(page_memory - page_memory[-1]);
    319517                }
    320518
     
    323521                void* allocate_memory(size_t size, xml_memory_page*& out_page)
    324522                {
    325                         if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
    326 
    327                         void* buf = _root->data + _busy_size;
     523                        if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size))
     524                                return allocate_memory_oob(size, out_page);
     525
     526                        void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;
    328527
    329528                        _busy_size += size;
     
    334533                }
    335534
     535        #ifdef PUGIXML_COMPACT
     536                void* allocate_object(size_t size, xml_memory_page*& out_page)
     537                {
     538                        void* result = allocate_memory(size + sizeof(uint32_t), out_page);
     539                        if (!result) return 0;
     540
     541                        // adjust for marker
     542                        ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);
     543
     544                        if (PUGI__UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
     545                        {
     546                                // insert new marker
     547                                uint32_t* marker = static_cast<uint32_t*>(result);
     548
     549                                *marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));
     550                                out_page->compact_page_marker = marker;
     551
     552                                // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block
     553                                // this will make sure deallocate_memory correctly tracks the size
     554                                out_page->freed_size += sizeof(uint32_t);
     555
     556                                return marker + 1;
     557                        }
     558                        else
     559                        {
     560                                // roll back uint32_t part
     561                                _busy_size -= sizeof(uint32_t);
     562
     563                                return result;
     564                        }
     565                }
     566        #else
     567                void* allocate_object(size_t size, xml_memory_page*& out_page)
     568                {
     569                        return allocate_memory(size, out_page);
     570                }
     571        #endif
     572
    336573                void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
    337574                {
    338575                        if (page == _root) page->busy_size = _busy_size;
    339576
    340                         assert(ptr >= page->data && ptr < page->data + page->busy_size);
     577                        assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);
    341578                        (void)!ptr;
    342579
     
    351588
    352589                                        // top page freed, just reset sizes
    353                                         page->busy_size = page->freed_size = 0;
     590                                        page->busy_size = 0;
     591                                        page->freed_size = 0;
     592
     593                                #ifdef PUGIXML_COMPACT
     594                                        // reset compact state to maximize efficiency
     595                                        page->compact_string_base = 0;
     596                                        page->compact_shared_parent = 0;
     597                                        page->compact_page_marker = 0;
     598                                #endif
     599
    354600                                        _busy_size = 0;
    355601                                }
     
    371617                char_t* allocate_string(size_t length)
    372618                {
     619                        static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
     620
     621                        PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
     622
    373623                        // allocate memory for string and header block
    374624                        size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
    375625                       
    376                         // round size up to pointer alignment boundary
    377                         size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
     626                        // round size up to block alignment boundary
     627                        size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);
    378628
    379629                        xml_memory_page* page;
     
    383633
    384634                        // setup header
    385                         ptrdiff_t page_offset = reinterpret_cast<char*>(header) - page->data;
    386 
    387                         assert(page_offset >= 0 && page_offset < (1 << 16));
    388                         header->page_offset = static_cast<uint16_t>(page_offset);
     635                        ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
     636
     637                        assert(page_offset % xml_memory_block_alignment == 0);
     638                        assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);
     639                        header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);
    389640
    390641                        // full_size == 0 for large strings that occupy the whole page
    391                         assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
    392                         header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);
     642                        assert(full_size % xml_memory_block_alignment == 0);
     643                        assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));
     644                        header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0);
    393645
    394646                        // round-trip through void* to avoid 'cast increases required alignment of target type' warning
     
    404656                        // get header
    405657                        xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
     658                        assert(header);
    406659
    407660                        // deallocate
    408                         size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;
     661                        size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
    409662                        xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
    410663
    411664                        // if full_size == 0 then this string occupies the whole page
    412                         size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
     665                        size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;
    413666
    414667                        deallocate_memory(header, full_size, page);
     668                }
     669
     670                bool reserve()
     671                {
     672                #ifdef PUGIXML_COMPACT
     673                        return _hash->reserve();
     674                #else
     675                        return true;
     676                #endif
    415677                }
    416678
    417679                xml_memory_page* _root;
    418680                size_t _busy_size;
     681
     682        #ifdef PUGIXML_COMPACT
     683                compact_hash_table* _hash;
     684        #endif
    419685        };
    420686
     
    450716                        _root->prev->next = page;
    451717                        _root->prev = page;
    452                 }
    453 
    454                 // allocate inside page
    455                 page->busy_size = size;
    456 
    457                 return page->data;
     718
     719                        page->busy_size = size;
     720                }
     721
     722                return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
    458723        }
    459724PUGI__NS_END
    460725
     726#ifdef PUGIXML_COMPACT
     727PUGI__NS_BEGIN
     728        static const uintptr_t compact_alignment_log2 = 2;
     729        static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
     730
     731        class compact_header
     732        {
     733        public:
     734                compact_header(xml_memory_page* page, unsigned int flags)
     735                {
     736                        PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);
     737
     738                        ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));
     739                        assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);
     740
     741                        _page = static_cast<unsigned char>(offset >> compact_alignment_log2);
     742                        _flags = static_cast<unsigned char>(flags);
     743                }
     744
     745                void operator&=(uintptr_t mod)
     746                {
     747                        _flags &= static_cast<unsigned char>(mod);
     748                }
     749
     750                void operator|=(uintptr_t mod)
     751                {
     752                        _flags |= static_cast<unsigned char>(mod);
     753                }
     754
     755                uintptr_t operator&(uintptr_t mod) const
     756                {
     757                        return _flags & mod;
     758                }
     759
     760                xml_memory_page* get_page() const
     761                {
     762                        // round-trip through void* to silence 'cast increases required alignment of target type' warnings
     763                        const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);
     764                        const char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker));
     765
     766                        return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page)));
     767                }
     768
     769        private:
     770                unsigned char _page;
     771                unsigned char _flags;
     772        };
     773
     774        PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
     775        {
     776                const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);
     777
     778                return header->get_page();
     779        }
     780
     781        template <int header_offset, typename T> PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
     782        {
     783                return static_cast<T*>(*compact_get_page(object, header_offset)->allocator->_hash->find(object));
     784        }
     785
     786        template <int header_offset, typename T> PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
     787        {
     788                *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value;
     789        }
     790
     791        template <typename T, int header_offset, int start = -126> class compact_pointer
     792        {
     793        public:
     794                compact_pointer(): _data(0)
     795                {
     796                }
     797
     798                void operator=(const compact_pointer& rhs)
     799                {
     800                        *this = rhs + 0;
     801                }
     802
     803                void operator=(T* value)
     804                {
     805                        if (value)
     806                        {
     807                                // value is guaranteed to be compact-aligned; 'this' is not
     808                                // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
     809                                // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
     810                                // compensate for arithmetic shift rounding for negative values
     811                                ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
     812                                ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;
     813
     814                                if (static_cast<uintptr_t>(offset) <= 253)
     815                                        _data = static_cast<unsigned char>(offset + 1);
     816                                else
     817                                {
     818                                        compact_set_value<header_offset>(this, value);
     819
     820                                        _data = 255;
     821                                }
     822                        }
     823                        else
     824                                _data = 0;
     825                }
     826
     827                operator T*() const
     828                {
     829                        if (_data)
     830                        {
     831                                if (_data < 255)
     832                                {
     833                                        uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
     834
     835                                        return reinterpret_cast<T*>(base + ((_data - 1 + start) << compact_alignment_log2));
     836                                }
     837                                else
     838                                        return compact_get_value<header_offset, T>(this);
     839                        }
     840                        else
     841                                return 0;
     842                }
     843
     844                T* operator->() const
     845                {
     846                        return *this;
     847                }
     848
     849        private:
     850                unsigned char _data;
     851        };
     852
     853        template <typename T, int header_offset> class compact_pointer_parent
     854        {
     855        public:
     856                compact_pointer_parent(): _data(0)
     857                {
     858                }
     859
     860                void operator=(const compact_pointer_parent& rhs)
     861                {
     862                        *this = rhs + 0;
     863                }
     864
     865                void operator=(T* value)
     866                {
     867                        if (value)
     868                        {
     869                                // value is guaranteed to be compact-aligned; 'this' is not
     870                                // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
     871                                // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
     872                                // compensate for arithmetic shift behavior for negative values
     873                                ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
     874                                ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;
     875
     876                                if (static_cast<uintptr_t>(offset) <= 65533)
     877                                {
     878                                        _data = static_cast<unsigned short>(offset + 1);
     879                                }
     880                                else
     881                                {
     882                                        xml_memory_page* page = compact_get_page(this, header_offset);
     883
     884                                        if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
     885                                                page->compact_shared_parent = value;
     886
     887                                        if (page->compact_shared_parent == value)
     888                                        {
     889                                                _data = 65534;
     890                                        }
     891                                        else
     892                                        {
     893                                                compact_set_value<header_offset>(this, value);
     894
     895                                                _data = 65535;
     896                                        }
     897                                }
     898                        }
     899                        else
     900                        {
     901                                _data = 0;
     902                        }
     903                }
     904
     905                operator T*() const
     906                {
     907                        if (_data)
     908                        {
     909                                if (_data < 65534)
     910                                {
     911                                        uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
     912
     913                                        return reinterpret_cast<T*>(base + ((_data - 1 - 65533) << compact_alignment_log2));
     914                                }
     915                                else if (_data == 65534)
     916                                        return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
     917                                else
     918                                        return compact_get_value<header_offset, T>(this);
     919                        }
     920                        else
     921                                return 0;
     922                }
     923
     924                T* operator->() const
     925                {
     926                        return *this;
     927                }
     928
     929        private:
     930                uint16_t _data;
     931        };
     932
     933        template <int header_offset, int base_offset> class compact_string
     934        {
     935        public:
     936                compact_string(): _data(0)
     937                {
     938                }
     939
     940                void operator=(const compact_string& rhs)
     941                {
     942                        *this = rhs + 0;
     943                }
     944
     945                void operator=(char_t* value)
     946                {
     947                        if (value)
     948                        {
     949                                xml_memory_page* page = compact_get_page(this, header_offset);
     950
     951                                if (PUGI__UNLIKELY(page->compact_string_base == 0))
     952                                        page->compact_string_base = value;
     953
     954                                ptrdiff_t offset = value - page->compact_string_base;
     955
     956                                if (static_cast<uintptr_t>(offset) < (65535 << 7))
     957                                {
     958                                        // round-trip through void* to silence 'cast increases required alignment of target type' warnings
     959                                        uint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset));
     960
     961                                        if (*base == 0)
     962                                        {
     963                                                *base = static_cast<uint16_t>((offset >> 7) + 1);
     964                                                _data = static_cast<unsigned char>((offset & 127) + 1);
     965                                        }
     966                                        else
     967                                        {
     968                                                ptrdiff_t remainder = offset - ((*base - 1) << 7);
     969
     970                                                if (static_cast<uintptr_t>(remainder) <= 253)
     971                                                {
     972                                                        _data = static_cast<unsigned char>(remainder + 1);
     973                                                }
     974                                                else
     975                                                {
     976                                                        compact_set_value<header_offset>(this, value);
     977
     978                                                        _data = 255;
     979                                                }
     980                                        }
     981                                }
     982                                else
     983                                {
     984                                        compact_set_value<header_offset>(this, value);
     985
     986                                        _data = 255;
     987                                }
     988                        }
     989                        else
     990                        {
     991                                _data = 0;
     992                        }
     993                }
     994
     995                operator char_t*() const
     996                {
     997                        if (_data)
     998                        {
     999                                if (_data < 255)
     1000                                {
     1001                                        xml_memory_page* page = compact_get_page(this, header_offset);
     1002
     1003                                        // round-trip through void* to silence 'cast increases required alignment of target type' warnings
     1004                                        const uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset));
     1005                                        assert(*base);
     1006
     1007                                        ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);
     1008
     1009                                        return page->compact_string_base + offset;
     1010                                }
     1011                                else
     1012                                {
     1013                                        return compact_get_value<header_offset, char_t>(this);
     1014                                }
     1015                        }
     1016                        else
     1017                                return 0;
     1018                }
     1019
     1020        private:
     1021                unsigned char _data;
     1022        };
     1023PUGI__NS_END
     1024#endif
     1025
     1026#ifdef PUGIXML_COMPACT
    4611027namespace pugi
    4621028{
    463         /// A 'name=value' XML attribute structure.
    4641029        struct xml_attribute_struct
    4651030        {
    466                 /// Default ctor
    467                 xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
    468                 {
    469                 }
    470 
    471                 uintptr_t header;
    472 
    473                 char_t* name;   ///< Pointer to attribute name.
    474                 char_t* value;  ///< Pointer to attribute value.
    475 
    476                 xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list)
    477                 xml_attribute_struct* next_attribute;   ///< Next attribute
     1031                xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
     1032                {
     1033                        PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
     1034                }
     1035
     1036                impl::compact_header header;
     1037
     1038                uint16_t namevalue_base;
     1039
     1040                impl::compact_string<4, 2> name;
     1041                impl::compact_string<5, 3> value;
     1042
     1043                impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c;
     1044                impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute;
    4781045        };
    4791046
    480         /// An XML document tree node.
    4811047        struct xml_node_struct
    4821048        {
    483                 /// Default ctor
    484                 /// \param type - node type
    485                 xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
    486                 {
    487                 }
    488 
    489                 uintptr_t header;
    490 
    491                 xml_node_struct*                parent;                                 ///< Pointer to parent
    492 
    493                 char_t*                                 name;                                   ///< Pointer to element name.
    494                 char_t*                                 value;                                  ///< Pointer to any associated string data.
    495 
    496                 xml_node_struct*                first_child;                    ///< First child
    497                
    498                 xml_node_struct*                prev_sibling_c;                 ///< Left brother (cyclic list)
    499                 xml_node_struct*                next_sibling;                   ///< Right brother
    500                
    501                 xml_attribute_struct*   first_attribute;                ///< First attribute
     1049                xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type - 1), namevalue_base(0)
     1050                {
     1051                        PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12);
     1052                }
     1053
     1054                impl::compact_header header;
     1055
     1056                uint16_t namevalue_base;
     1057
     1058                impl::compact_string<4, 2> name;
     1059                impl::compact_string<5, 3> value;
     1060
     1061                impl::compact_pointer_parent<xml_node_struct, 6> parent;
     1062
     1063                impl::compact_pointer<xml_node_struct, 8, 0> first_child;
     1064
     1065                impl::compact_pointer<xml_node_struct,  9>    prev_sibling_c;
     1066                impl::compact_pointer<xml_node_struct, 10, 0> next_sibling;
     1067
     1068                impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute;
    5021069        };
    5031070}
     1071#else
     1072namespace pugi
     1073{
     1074        struct xml_attribute_struct
     1075        {
     1076                xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
     1077                {
     1078                }
     1079
     1080                uintptr_t header;
     1081
     1082                char_t* name;
     1083                char_t* value;
     1084
     1085                xml_attribute_struct* prev_attribute_c;
     1086                xml_attribute_struct* next_attribute;
     1087        };
     1088
     1089        struct xml_node_struct
     1090        {
     1091                xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
     1092                {
     1093                }
     1094
     1095                uintptr_t header;
     1096
     1097                char_t* name;
     1098                char_t* value;
     1099
     1100                xml_node_struct* parent;
     1101
     1102                xml_node_struct* first_child;
     1103
     1104                xml_node_struct* prev_sibling_c;
     1105                xml_node_struct* next_sibling;
     1106
     1107                xml_attribute_struct* first_attribute;
     1108        };
     1109}
     1110#endif
    5041111
    5051112PUGI__NS_BEGIN
     1113        struct xml_extra_buffer
     1114        {
     1115                char_t* buffer;
     1116                xml_extra_buffer* next;
     1117        };
     1118
    5061119        struct xml_document_struct: public xml_node_struct, public xml_allocator
    5071120        {
    508                 xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0)
    509                 {
     1121                xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)
     1122                {
     1123                #ifdef PUGIXML_COMPACT
     1124                        _hash = &hash;
     1125                #endif
    5101126                }
    5111127
    5121128                const char_t* buffer;
     1129
     1130                xml_extra_buffer* extra_buffers;
     1131
     1132        #ifdef PUGIXML_COMPACT
     1133                compact_hash_table hash;
     1134        #endif
    5131135        };
    5141136
    515         inline xml_allocator& get_allocator(const xml_node_struct* node)
    516         {
    517                 assert(node);
    518 
    519                 return *reinterpret_cast<xml_memory_page*>(node->header & xml_memory_page_pointer_mask)->allocator;
     1137        template <typename Object> inline xml_allocator& get_allocator(const Object* object)
     1138        {
     1139                assert(object);
     1140
     1141                return *PUGI__GETPAGE(object)->allocator;
     1142        }
     1143
     1144        template <typename Object> inline xml_document_struct& get_document(const Object* object)
     1145        {
     1146                assert(object);
     1147
     1148                return *static_cast<xml_document_struct*>(PUGI__GETPAGE(object)->allocator);
    5201149        }
    5211150PUGI__NS_END
     
    5261155        {
    5271156                xml_memory_page* page;
    528                 void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
     1157                void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);
     1158                if (!memory) return 0;
    5291159
    5301160                return new (memory) xml_attribute_struct(page);
     
    5341164        {
    5351165                xml_memory_page* page;
    536                 void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
     1166                void* memory = alloc.allocate_object(sizeof(xml_node_struct), page);
     1167                if (!memory) return 0;
    5371168
    5381169                return new (memory) xml_node_struct(page, type);
     
    5411172        inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
    5421173        {
    543                 uintptr_t header = a->header;
    544 
    545                 if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
    546                 if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
    547 
    548                 alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
     1174                if (a->header & impl::xml_memory_page_name_allocated_mask)
     1175                        alloc.deallocate_string(a->name);
     1176
     1177                if (a->header & impl::xml_memory_page_value_allocated_mask)
     1178                        alloc.deallocate_string(a->value);
     1179
     1180                alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a));
    5491181        }
    5501182
    5511183        inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
    5521184        {
    553                 uintptr_t header = n->header;
    554 
    555                 if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
    556                 if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
     1185                if (n->header & impl::xml_memory_page_name_allocated_mask)
     1186                        alloc.deallocate_string(n->name);
     1187
     1188                if (n->header & impl::xml_memory_page_value_allocated_mask)
     1189                        alloc.deallocate_string(n->value);
    5571190
    5581191                for (xml_attribute_struct* attr = n->first_attribute; attr; )
     
    5741207                }
    5751208
    576                 alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
    577         }
    578 
    579         PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
    580         {
     1209                alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));
     1210        }
     1211
     1212        inline void append_node(xml_node_struct* child, xml_node_struct* node)
     1213        {
     1214                child->parent = node;
     1215
     1216                xml_node_struct* head = node->first_child;
     1217
     1218                if (head)
     1219                {
     1220                        xml_node_struct* tail = head->prev_sibling_c;
     1221
     1222                        tail->next_sibling = child;
     1223                        child->prev_sibling_c = tail;
     1224                        head->prev_sibling_c = child;
     1225                }
     1226                else
     1227                {
     1228                        node->first_child = child;
     1229                        child->prev_sibling_c = child;
     1230                }
     1231        }
     1232
     1233        inline void prepend_node(xml_node_struct* child, xml_node_struct* node)
     1234        {
     1235                child->parent = node;
     1236
     1237                xml_node_struct* head = node->first_child;
     1238
     1239                if (head)
     1240                {
     1241                        child->prev_sibling_c = head->prev_sibling_c;
     1242                        head->prev_sibling_c = child;
     1243                }
     1244                else
     1245                        child->prev_sibling_c = child;
     1246
     1247                child->next_sibling = head;
     1248                node->first_child = child;
     1249        }
     1250
     1251        inline void insert_node_after(xml_node_struct* child, xml_node_struct* node)
     1252        {
     1253                xml_node_struct* parent = node->parent;
     1254
     1255                child->parent = parent;
     1256
     1257                if (node->next_sibling)
     1258                        node->next_sibling->prev_sibling_c = child;
     1259                else
     1260                        parent->first_child->prev_sibling_c = child;
     1261
     1262                child->next_sibling = node->next_sibling;
     1263                child->prev_sibling_c = node;
     1264
     1265                node->next_sibling = child;
     1266        }
     1267
     1268        inline void insert_node_before(xml_node_struct* child, xml_node_struct* node)
     1269        {
     1270                xml_node_struct* parent = node->parent;
     1271
     1272                child->parent = parent;
     1273
     1274                if (node->prev_sibling_c->next_sibling)
     1275                        node->prev_sibling_c->next_sibling = child;
     1276                else
     1277                        parent->first_child = child;
     1278
     1279                child->prev_sibling_c = node->prev_sibling_c;
     1280                child->next_sibling = node;
     1281
     1282                node->prev_sibling_c = child;
     1283        }
     1284
     1285        inline void remove_node(xml_node_struct* node)
     1286        {
     1287                xml_node_struct* parent = node->parent;
     1288
     1289                if (node->next_sibling)
     1290                        node->next_sibling->prev_sibling_c = node->prev_sibling_c;
     1291                else
     1292                        parent->first_child->prev_sibling_c = node->prev_sibling_c;
     1293
     1294                if (node->prev_sibling_c->next_sibling)
     1295                        node->prev_sibling_c->next_sibling = node->next_sibling;
     1296                else
     1297                        parent->first_child = node->next_sibling;
     1298
     1299                node->parent = 0;
     1300                node->prev_sibling_c = 0;
     1301                node->next_sibling = 0;
     1302        }
     1303
     1304        inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node)
     1305        {
     1306                xml_attribute_struct* head = node->first_attribute;
     1307
     1308                if (head)
     1309                {
     1310                        xml_attribute_struct* tail = head->prev_attribute_c;
     1311
     1312                        tail->next_attribute = attr;
     1313                        attr->prev_attribute_c = tail;
     1314                        head->prev_attribute_c = attr;
     1315                }
     1316                else
     1317                {
     1318                        node->first_attribute = attr;
     1319                        attr->prev_attribute_c = attr;
     1320                }
     1321        }
     1322
     1323        inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node)
     1324        {
     1325                xml_attribute_struct* head = node->first_attribute;
     1326
     1327                if (head)
     1328                {
     1329                        attr->prev_attribute_c = head->prev_attribute_c;
     1330                        head->prev_attribute_c = attr;
     1331                }
     1332                else
     1333                        attr->prev_attribute_c = attr;
     1334
     1335                attr->next_attribute = head;
     1336                node->first_attribute = attr;
     1337        }
     1338
     1339        inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
     1340        {
     1341                if (place->next_attribute)
     1342                        place->next_attribute->prev_attribute_c = attr;
     1343                else
     1344                        node->first_attribute->prev_attribute_c = attr;
     1345
     1346                attr->next_attribute = place->next_attribute;
     1347                attr->prev_attribute_c = place;
     1348                place->next_attribute = attr;
     1349        }
     1350
     1351        inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
     1352        {
     1353                if (place->prev_attribute_c->next_attribute)
     1354                        place->prev_attribute_c->next_attribute = attr;
     1355                else
     1356                        node->first_attribute = attr;
     1357
     1358                attr->prev_attribute_c = place->prev_attribute_c;
     1359                attr->next_attribute = place;
     1360                place->prev_attribute_c = attr;
     1361        }
     1362
     1363        inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)
     1364        {
     1365                if (attr->next_attribute)
     1366                        attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
     1367                else
     1368                        node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
     1369
     1370                if (attr->prev_attribute_c->next_attribute)
     1371                        attr->prev_attribute_c->next_attribute = attr->next_attribute;
     1372                else
     1373                        node->first_attribute = attr->next_attribute;
     1374
     1375                attr->prev_attribute_c = 0;
     1376                attr->next_attribute = 0;
     1377        }
     1378
     1379        PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
     1380        {
     1381                if (!alloc.reserve()) return 0;
     1382
    5811383                xml_node_struct* child = allocate_node(alloc, type);
    5821384                if (!child) return 0;
    5831385
    584                 child->parent = node;
    585 
    586                 xml_node_struct* first_child = node->first_child;
    587                        
    588                 if (first_child)
    589                 {
    590                         xml_node_struct* last_child = first_child->prev_sibling_c;
    591 
    592                         last_child->next_sibling = child;
    593                         child->prev_sibling_c = last_child;
    594                         first_child->prev_sibling_c = child;
    595                 }
    596                 else
    597                 {
    598                         node->first_child = child;
    599                         child->prev_sibling_c = child;
    600                 }
    601                        
     1386                append_node(child, node);
     1387
    6021388                return child;
    6031389        }
    6041390
    605         PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)
    606         {
    607                 xml_attribute_struct* a = allocate_attribute(alloc);
    608                 if (!a) return 0;
    609 
    610                 xml_attribute_struct* first_attribute = node->first_attribute;
    611 
    612                 if (first_attribute)
    613                 {
    614                         xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;
    615 
    616                         last_attribute->next_attribute = a;
    617                         a->prev_attribute_c = last_attribute;
    618                         first_attribute->prev_attribute_c = a;
    619                 }
    620                 else
    621                 {
    622                         node->first_attribute = a;
    623                         a->prev_attribute_c = a;
    624                 }
    625                        
    626                 return a;
     1391        PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
     1392        {
     1393                if (!alloc.reserve()) return 0;
     1394
     1395                xml_attribute_struct* attr = allocate_attribute(alloc);
     1396                if (!attr) return 0;
     1397
     1398                append_attribute(attr, node);
     1399
     1400                return attr;
    6271401        }
    6281402PUGI__NS_END
     
    8241598        };
    8251599
    826         template <size_t size> struct wchar_selector;
    827 
    828         template <> struct wchar_selector<2>
    829         {
    830                 typedef uint16_t type;
    831                 typedef utf16_counter counter;
    832                 typedef utf16_writer writer;
    833         };
    834 
    835         template <> struct wchar_selector<4>
    836         {
    837                 typedef uint32_t type;
    838                 typedef utf32_counter counter;
    839                 typedef utf32_writer writer;
    840         };
    841 
    842         typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
    843         typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
    844 
    845         template <typename Traits, typename opt_swap = opt_false> struct utf_decoder
    846         {
    847                 static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)
     1600        struct utf8_decoder
     1601        {
     1602                typedef uint8_t type;
     1603
     1604                template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
    8481605                {
    8491606                        const uint8_t utf8_byte_mask = 0x3f;
     
    9061663                        return result;
    9071664                }
    908 
    909                 static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)
    910                 {
    911                         const uint16_t* end = data + size;
    912 
    913                         while (data < end)
     1665        };
     1666
     1667        template <typename opt_swap> struct utf16_decoder
     1668        {
     1669                typedef uint16_t type;
     1670
     1671                template <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits)
     1672                {
     1673                        while (size)
    9141674                        {
    9151675                                uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;
     
    9201680                                        result = Traits::low(result, lead);
    9211681                                        data += 1;
     1682                                        size -= 1;
    9221683                                }
    9231684                                // U+E000..U+FFFF
     
    9261687                                        result = Traits::low(result, lead);
    9271688                                        data += 1;
     1689                                        size -= 1;
    9281690                                }
    9291691                                // surrogate pair lead
    930                                 else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && data + 1 < end)
     1692                                else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2)
    9311693                                {
    9321694                                        uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
     
    9361698                                                result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
    9371699                                                data += 2;
     1700                                                size -= 2;
    9381701                                        }
    9391702                                        else
    9401703                                        {
    9411704                                                data += 1;
     1705                                                size -= 1;
    9421706                                        }
    9431707                                }
     
    9451709                                {
    9461710                                        data += 1;
     1711                                        size -= 1;
    9471712                                }
    9481713                        }
     
    9501715                        return result;
    9511716                }
    952 
    953                 static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)
    954                 {
    955                         const uint32_t* end = data + size;
    956 
    957                         while (data < end)
     1717        };
     1718
     1719        template <typename opt_swap> struct utf32_decoder
     1720        {
     1721                typedef uint32_t type;
     1722
     1723                template <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits)
     1724                {
     1725                        while (size)
    9581726                        {
    9591727                                uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
     
    9641732                                        result = Traits::low(result, lead);
    9651733                                        data += 1;
     1734                                        size -= 1;
    9661735                                }
    9671736                                // U+10000..U+10FFFF
     
    9701739                                        result = Traits::high(result, lead);
    9711740                                        data += 1;
     1741                                        size -= 1;
    9721742                                }
    9731743                        }
     
    9751745                        return result;
    9761746                }
    977 
    978                 static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)
    979                 {
    980                         for (size_t i = 0; i < size; ++i)
    981                         {
    982                                 result = Traits::low(result, data[i]);
     1747        };
     1748
     1749        struct latin1_decoder
     1750        {
     1751                typedef uint8_t type;
     1752
     1753                template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
     1754                {
     1755                        while (size)
     1756                        {
     1757                                result = Traits::low(result, *data);
     1758                                data += 1;
     1759                                size -= 1;
    9831760                        }
    9841761
    9851762                        return result;
    9861763                }
    987 
    988                 static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)
    989                 {
    990                         return decode_utf16_block(data, size, result);
    991                 }
    992 
    993                 static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)
    994                 {
    995                         return decode_utf32_block(data, size, result);
    996                 }
    997 
    998                 static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)
    999                 {
    1000                         return decode_wchar_block_impl(reinterpret_cast<const wchar_selector<sizeof(wchar_t)>::type*>(data), size, result);
    1001                 }
    10021764        };
    10031765
    1004         template <typename T> PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)
    1005         {
    1006                 for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);
    1007         }
     1766        template <size_t size> struct wchar_selector;
     1767
     1768        template <> struct wchar_selector<2>
     1769        {
     1770                typedef uint16_t type;
     1771                typedef utf16_counter counter;
     1772                typedef utf16_writer writer;
     1773                typedef utf16_decoder<opt_false> decoder;
     1774        };
     1775
     1776        template <> struct wchar_selector<4>
     1777        {
     1778                typedef uint32_t type;
     1779                typedef utf32_counter counter;
     1780                typedef utf32_writer writer;
     1781                typedef utf32_decoder<opt_false> decoder;
     1782        };
     1783
     1784        typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
     1785        typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
     1786
     1787        struct wchar_decoder
     1788        {
     1789                typedef wchar_t type;
     1790
     1791                template <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits)
     1792                {
     1793                        typedef wchar_selector<sizeof(wchar_t)>::decoder decoder;
     1794
     1795                        return decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits);
     1796                }
     1797        };
    10081798
    10091799#ifdef PUGIXML_WCHAR_MODE
    10101800        PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
    10111801        {
    1012                 for (size_t i = 0; i < length; ++i) result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
     1802                for (size_t i = 0; i < length; ++i)
     1803                        result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
    10131804        }
    10141805#endif
     
    11571948        PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
    11581949        {
     1950                size_t length = size / sizeof(char_t);
     1951
    11591952                if (is_mutable)
    11601953                {
    11611954                        out_buffer = static_cast<char_t*>(const_cast<void*>(contents));
     1955                        out_length = length;
    11621956                }
    11631957                else
    11641958                {
    1165                         void* buffer = xml_memory::allocate(size > 0 ? size : 1);
     1959                        char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
    11661960                        if (!buffer) return false;
    11671961
    1168                         memcpy(buffer, contents, size);
    1169 
    1170                         out_buffer = static_cast<char_t*>(buffer);
    1171                 }
    1172 
    1173                 out_length = size / sizeof(char_t);
     1962                        if (contents)
     1963                                memcpy(buffer, contents, length * sizeof(char_t));
     1964                        else
     1965                                assert(length == 0);
     1966
     1967                        buffer[length] = 0;
     1968
     1969                        out_buffer = buffer;
     1970                        out_length = length + 1;
     1971                }
    11741972
    11751973                return true;
     
    11861984        {
    11871985                const char_t* data = static_cast<const char_t*>(contents);
    1188        
     1986                size_t length = size / sizeof(char_t);
     1987
    11891988                if (is_mutable)
    11901989                {
    1191                         out_buffer = const_cast<char_t*>(data);
     1990                        char_t* buffer = const_cast<char_t*>(data);
     1991
     1992                        convert_wchar_endian_swap(buffer, data, length);
     1993
     1994                        out_buffer = buffer;
     1995                        out_length = length;
    11921996                }
    11931997                else
    11941998                {
    1195                         out_buffer = static_cast<char_t*>(xml_memory::allocate(size > 0 ? size : 1));
    1196                         if (!out_buffer) return false;
    1197                 }
    1198 
    1199                 out_length = size / sizeof(char_t);
    1200 
    1201                 convert_wchar_endian_swap(out_buffer, data, out_length);
     1999                        char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
     2000                        if (!buffer) return false;
     2001
     2002                        convert_wchar_endian_swap(buffer, data, length);
     2003                        buffer[length] = 0;
     2004
     2005                        out_buffer = buffer;
     2006                        out_length = length + 1;
     2007                }
    12022008
    12032009                return true;
    12042010        }
    12052011
    1206         PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
    1207         {
    1208                 const uint8_t* data = static_cast<const uint8_t*>(contents);
     2012        template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
     2013        {
     2014                const typename D::type* data = static_cast<const typename D::type*>(contents);
     2015                size_t data_length = size / sizeof(typename D::type);
    12092016
    12102017                // first pass: get length in wchar_t units
    1211                 out_length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
     2018                size_t length = D::process(data, data_length, 0, wchar_counter());
    12122019
    12132020                // allocate buffer of suitable length
    1214                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1215                 if (!out_buffer) return false;
    1216 
    1217                 // second pass: convert utf8 input to wchar_t
    1218                 wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
    1219                 wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, out_begin);
    1220 
    1221                 assert(out_end == out_begin + out_length);
    1222                 (void)!out_end;
    1223 
    1224                 return true;
    1225         }
    1226 
    1227         template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
    1228         {
    1229                 const uint16_t* data = static_cast<const uint16_t*>(contents);
    1230                 size_t length = size / sizeof(uint16_t);
    1231 
    1232                 // first pass: get length in wchar_t units
    1233                 out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf16_block(data, length, 0);
    1234 
    1235                 // allocate buffer of suitable length
    1236                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1237                 if (!out_buffer) return false;
     2021                char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
     2022                if (!buffer) return false;
    12382023
    12392024                // second pass: convert utf16 input to wchar_t
    1240                 wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
    1241                 wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
    1242 
    1243                 assert(out_end == out_begin + out_length);
    1244                 (void)!out_end;
    1245 
    1246                 return true;
    1247         }
    1248 
    1249         template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
    1250         {
    1251                 const uint32_t* data = static_cast<const uint32_t*>(contents);
    1252                 size_t length = size / sizeof(uint32_t);
    1253 
    1254                 // first pass: get length in wchar_t units
    1255                 out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf32_block(data, length, 0);
    1256 
    1257                 // allocate buffer of suitable length
    1258                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1259                 if (!out_buffer) return false;
    1260 
    1261                 // second pass: convert utf32 input to wchar_t
    1262                 wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
    1263                 wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
    1264 
    1265                 assert(out_end == out_begin + out_length);
    1266                 (void)!out_end;
    1267 
    1268                 return true;
    1269         }
    1270 
    1271         PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
    1272         {
    1273                 const uint8_t* data = static_cast<const uint8_t*>(contents);
    1274 
    1275                 // get length in wchar_t units
    1276                 out_length = size;
    1277 
    1278                 // allocate buffer of suitable length
    1279                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1280                 if (!out_buffer) return false;
    1281 
    1282                 // convert latin1 input to wchar_t
    1283                 wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
    1284                 wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_latin1_block(data, size, out_begin);
    1285 
    1286                 assert(out_end == out_begin + out_length);
    1287                 (void)!out_end;
     2025                wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
     2026                wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer());
     2027
     2028                assert(oend == obegin + length);
     2029                *oend = 0;
     2030
     2031                out_buffer = buffer;
     2032                out_length = length + 1;
    12882033
    12892034                return true;
     
    12962041
    12972042                // fast path: no conversion required
    1298                 if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
     2043                if (encoding == wchar_encoding)
     2044                        return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
    12992045
    13002046                // only endian-swapping is required
    1301                 if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
     2047                if (need_endian_swap_utf(encoding, wchar_encoding))
     2048                        return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
    13022049
    13032050                // source encoding is utf8
    1304                 if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size);
     2051                if (encoding == encoding_utf8)
     2052                        return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder());
    13052053
    13062054                // source encoding is utf16
     
    13102058
    13112059                        return (native_encoding == encoding) ?
    1312                                 convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
    1313                                 convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
     2060                                convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
     2061                                convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
    13142062                }
    13152063
     
    13202068
    13212069                        return (native_encoding == encoding) ?
    1322                                 convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
    1323                                 convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
     2070                                convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
     2071                                convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
    13242072                }
    13252073
    13262074                // source encoding is latin1
    1327                 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);
     2075                if (encoding == encoding_latin1)
     2076                        return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
    13282077
    13292078                assert(!"Invalid encoding");
     
    13312080        }
    13322081#else
    1333         template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
    1334         {
    1335                 const uint16_t* data = static_cast<const uint16_t*>(contents);
    1336                 size_t length = size / sizeof(uint16_t);
     2082        template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
     2083        {
     2084                const typename D::type* data = static_cast<const typename D::type*>(contents);
     2085                size_t data_length = size / sizeof(typename D::type);
    13372086
    13382087                // first pass: get length in utf8 units
    1339                 out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf16_block(data, length, 0);
     2088                size_t length = D::process(data, data_length, 0, utf8_counter());
    13402089
    13412090                // allocate buffer of suitable length
    1342                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1343                 if (!out_buffer) return false;
     2091                char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
     2092                if (!buffer) return false;
    13442093
    13452094                // second pass: convert utf16 input to utf8
    1346                 uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
    1347                 uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
    1348 
    1349                 assert(out_end == out_begin + out_length);
    1350                 (void)!out_end;
    1351 
    1352                 return true;
    1353         }
    1354 
    1355         template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
    1356         {
    1357                 const uint32_t* data = static_cast<const uint32_t*>(contents);
    1358                 size_t length = size / sizeof(uint32_t);
    1359 
    1360                 // first pass: get length in utf8 units
    1361                 out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf32_block(data, length, 0);
    1362 
    1363                 // allocate buffer of suitable length
    1364                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1365                 if (!out_buffer) return false;
    1366 
    1367                 // second pass: convert utf32 input to utf8
    1368                 uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
    1369                 uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
    1370 
    1371                 assert(out_end == out_begin + out_length);
    1372                 (void)!out_end;
     2095                uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
     2096                uint8_t* oend = D::process(data, data_length, obegin, utf8_writer());
     2097
     2098                assert(oend == obegin + length);
     2099                *oend = 0;
     2100
     2101                out_buffer = buffer;
     2102                out_length = length + 1;
    13732103
    13742104                return true;
     
    13872117        {
    13882118                const uint8_t* data = static_cast<const uint8_t*>(contents);
     2119                size_t data_length = size;
    13892120
    13902121                // get size of prefix that does not need utf8 conversion
    1391                 size_t prefix_length = get_latin1_7bit_prefix_length(data, size);
    1392                 assert(prefix_length <= size);
     2122                size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);
     2123                assert(prefix_length <= data_length);
    13932124
    13942125                const uint8_t* postfix = data + prefix_length;
    1395                 size_t postfix_length = size - prefix_length;
     2126                size_t postfix_length = data_length - prefix_length;
    13962127
    13972128                // if no conversion is needed, just return the original buffer
     
    13992130
    14002131                // first pass: get length in utf8 units
    1401                 out_length = prefix_length + utf_decoder<utf8_counter>::decode_latin1_block(postfix, postfix_length, 0);
     2132                size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter());
    14022133
    14032134                // allocate buffer of suitable length
    1404                 out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
    1405                 if (!out_buffer) return false;
     2135                char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
     2136                if (!buffer) return false;
    14062137
    14072138                // second pass: convert latin1 input to utf8
    1408                 memcpy(out_buffer, data, prefix_length);
    1409 
    1410                 uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
    1411                 uint8_t* out_end = utf_decoder<utf8_writer>::decode_latin1_block(postfix, postfix_length, out_begin + prefix_length);
    1412 
    1413                 assert(out_end == out_begin + out_length);
    1414                 (void)!out_end;
     2139                memcpy(buffer, data, prefix_length);
     2140
     2141                uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
     2142                uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer());
     2143
     2144                assert(oend == obegin + length);
     2145                *oend = 0;
     2146
     2147                out_buffer = buffer;
     2148                out_length = length + 1;
    14152149
    14162150                return true;
     
    14202154        {
    14212155                // fast path: no conversion required
    1422                 if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
     2156                if (encoding == encoding_utf8)
     2157                        return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
    14232158
    14242159                // source encoding is utf16
     
    14282163
    14292164                        return (native_encoding == encoding) ?
    1430                                 convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
    1431                                 convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
     2165                                convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
     2166                                convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
    14322167                }
    14332168
     
    14382173
    14392174                        return (native_encoding == encoding) ?
    1440                                 convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
    1441                                 convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
     2175                                convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
     2176                                convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
    14422177                }
    14432178
    14442179                // source encoding is latin1
    1445                 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
     2180                if (encoding == encoding_latin1)
     2181                        return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
    14462182
    14472183                assert(!"Invalid encoding");
     
    14532189        {
    14542190                // get length in utf8 characters
    1455                 return utf_decoder<utf8_counter>::decode_wchar_block(str, length, 0);
     2191                return wchar_decoder::process(str, length, 0, utf8_counter());
    14562192        }
    14572193
     
    14602196                // convert to utf8
    14612197                uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
    1462                 uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(str, length, begin);
     2198                uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer());
    14632199       
    14642200                assert(begin + size == end);
    14652201                (void)!end;
    1466 
    1467                 // zero-terminate
    1468                 buffer[size] = 0;
     2202                (void)!size;
    14692203        }
    14702204       
     
    14902224
    14912225                // first pass: get length in wchar_t units
    1492                 size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
     2226                size_t length = utf8_decoder::process(data, size, 0, wchar_counter());
    14932227
    14942228                // allocate resulting string
     
    15002234                {
    15012235                        wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
    1502                         wchar_writer::value_type end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, begin);
     2236                        wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer());
    15032237
    15042238                        assert(begin + length == end);
     
    15102244#endif
    15112245
    1512         inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)
    1513         {
    1514                 assert(target);
     2246        template <typename Header>
     2247        inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)
     2248        {
     2249                // never reuse shared memory
     2250                if (header & xml_memory_page_contents_shared_mask) return false;
     2251
    15152252                size_t target_length = strlength(target);
    15162253
    15172254                // always reuse document buffer memory if possible
    1518                 if (!allocated) return target_length >= length;
     2255                if ((header & header_mask) == 0) return target_length >= length;
    15192256
    15202257                // reuse heap memory if waste is not too great
     
    15242261        }
    15252262
    1526         PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
    1527         {
    1528                 size_t source_length = strlength(source);
    1529 
     2263        template <typename String, typename Header>
     2264        PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)
     2265        {
    15302266                if (source_length == 0)
    15312267                {
    15322268                        // empty string and null pointer are equivalent, so just deallocate old memory
    1533                         xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
     2269                        xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
    15342270
    15352271                        if (header & header_mask) alloc->deallocate_string(dest);
     
    15412277                        return true;
    15422278                }
    1543                 else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))
     2279                else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest))
    15442280                {
    15452281                        // we can reuse old buffer, so just copy the new data (including zero terminator)
    1546                         memcpy(dest, source, (source_length + 1) * sizeof(char_t));
     2282                        memcpy(dest, source, source_length * sizeof(char_t));
     2283                        dest[source_length] = 0;
    15472284                       
    15482285                        return true;
     
    15502287                else
    15512288                {
    1552                         xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
     2289                        xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
     2290
     2291                        if (!alloc->reserve()) return false;
    15532292
    15542293                        // allocate new buffer
     
    15572296
    15582297                        // copy the string (including zero terminator)
    1559                         memcpy(buf, source, (source_length + 1) * sizeof(char_t));
     2298                        memcpy(buf, source, source_length * sizeof(char_t));
     2299                        buf[source_length] = 0;
    15602300
    15612301                        // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
     
    16542394                                        for (;;)
    16552395                                        {
    1656                                                 if (static_cast<unsigned int>(ch - '0') <= 9)
     2396                                                if (static_cast<unsigned int>(static_cast<unsigned int>(ch) - '0') <= 9)
    16572397                                                        ucsc = 10 * ucsc + (ch - '0');
    16582398                                                else if (ch == ';')
     
    17522492        }
    17532493
    1754         // Utility macro for last character handling
    1755         #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
     2494        // Parser utilities
     2495        #define PUGI__ENDSWITH(c, e)        ((c) == (e) || ((c) == 0 && endch == (e)))
     2496        #define PUGI__SKIPWS()              { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
     2497        #define PUGI__OPTSET(OPT)           ( optmsk & (OPT) )
     2498        #define PUGI__PUSHNODE(TYPE)        { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
     2499        #define PUGI__POPNODE()             { cursor = cursor->parent; }
     2500        #define PUGI__SCANFOR(X)            { while (*s != 0 && !(X)) ++s; }
     2501        #define PUGI__SCANWHILE(X)          { while (X) ++s; }
     2502        #define PUGI__SCANWHILE_UNROLL(X)   { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } }
     2503        #define PUGI__ENDSEG()              { ch = *s; *s = 0; ++s; }
     2504        #define PUGI__THROW_ERROR(err, m)   return error_offset = m, error_status = err, static_cast<char_t*>(0)
     2505        #define PUGI__CHECK_ERROR(err, m)   { if (*s == 0) PUGI__THROW_ERROR(err, m); }
    17562506
    17572507        PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
     
    17612511                while (true)
    17622512                {
    1763                         while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;
     2513                        PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment));
    17642514               
    17652515                        if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
     
    17692519                                if (*s == '\n') g.push(s, 1);
    17702520                        }
    1771                         else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here
     2521                        else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
    17722522                        {
    17732523                                *g.flush(s) = 0;
     
    17892539                while (true)
    17902540                {
    1791                         while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;
     2541                        PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata));
    17922542                       
    17932543                        if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
     
    17972547                                if (*s == '\n') g.push(s, 1);
    17982548                        }
    1799                         else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here
     2549                        else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
    18002550                        {
    18012551                                *g.flush(s) = 0;
     
    18132563        typedef char_t* (*strconv_pcdata_t)(char_t*);
    18142564               
    1815         template <typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
     2565        template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
    18162566        {
    18172567                static char_t* parse(char_t* s)
    18182568                {
    18192569                        gap g;
    1820                        
     2570
     2571                        char_t* begin = s;
     2572
    18212573                        while (true)
    18222574                        {
    1823                                 while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;
    1824                                        
     2575                                PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata));
     2576
    18252577                                if (*s == '<') // PCDATA ends here
    18262578                                {
    1827                                         *g.flush(s) = 0;
     2579                                        char_t* end = g.flush(s);
     2580
     2581                                        if (opt_trim::value)
     2582                                                while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
     2583                                                        --end;
     2584
     2585                                        *end = 0;
    18282586                                       
    18292587                                        return s + 1;
     
    18412599                                else if (*s == 0)
    18422600                                {
     2601                                        char_t* end = g.flush(s);
     2602
     2603                                        if (opt_trim::value)
     2604                                                while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
     2605                                                        --end;
     2606
     2607                                        *end = 0;
     2608
    18432609                                        return s;
    18442610                                }
     
    18502616        PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
    18512617        {
    1852                 PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20);
    1853 
    1854                 switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes)
    1855                 {
    1856                 case 0: return strconv_pcdata_impl<opt_false, opt_false>::parse;
    1857                 case 1: return strconv_pcdata_impl<opt_false, opt_true>::parse;
    1858                 case 2: return strconv_pcdata_impl<opt_true, opt_false>::parse;
    1859                 case 3: return strconv_pcdata_impl<opt_true, opt_true>::parse;
    1860                 default: return 0; // should not get here
     2618                PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
     2619
     2620                switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim)
     2621                {
     2622                case 0: return strconv_pcdata_impl<opt_false, opt_false, opt_false>::parse;
     2623                case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;
     2624                case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;
     2625                case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;
     2626                case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;
     2627                case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;
     2628                case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;
     2629                case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;
     2630                default: assert(false); return 0; // should not get here
    18612631                }
    18622632        }
     
    18832653                        while (true)
    18842654                        {
    1885                                 while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;
     2655                                PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
    18862656                               
    18872657                                if (*s == end_quote)
     
    19242694                        while (true)
    19252695                        {
    1926                                 while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;
     2696                                PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws));
    19272697                               
    19282698                                if (*s == end_quote)
     
    19602730                        while (true)
    19612731                        {
    1962                                 while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
     2732                                PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
    19632733                               
    19642734                                if (*s == end_quote)
     
    19922762                        while (true)
    19932763                        {
    1994                                 while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
     2764                                PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
    19952765                               
    19962766                                if (*s == end_quote)
     
    20352805                case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
    20362806                case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
    2037                 default: return 0; // should not get here
     2807                default: assert(false); return 0; // should not get here
    20382808                }
    20392809        }
     
    20512821        {
    20522822                xml_allocator alloc;
     2823                xml_allocator* alloc_state;
    20532824                char_t* error_offset;
    20542825                xml_parse_status error_status;
    20552826               
    2056                 // Parser utilities.
    2057                 #define PUGI__SKIPWS()                  { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
    2058                 #define PUGI__OPTSET(OPT)                       ( optmsk & (OPT) )
    2059                 #define PUGI__PUSHNODE(TYPE)            { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
    2060                 #define PUGI__POPNODE()                 { cursor = cursor->parent; }
    2061                 #define PUGI__SCANFOR(X)                        { while (*s != 0 && !(X)) ++s; }
    2062                 #define PUGI__SCANWHILE(X)              { while ((X)) ++s; }
    2063                 #define PUGI__ENDSEG()                  { ch = *s; *s = 0; ++s; }
    2064                 #define PUGI__THROW_ERROR(err, m)       return error_offset = m, error_status = err, static_cast<char_t*>(0)
    2065                 #define PUGI__CHECK_ERROR(err, m)       { if (*s == 0) PUGI__THROW_ERROR(err, m); }
    2066                
    2067                 xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
    2068                 {
     2827                xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok)
     2828                {
     2829                }
     2830
     2831                ~xml_parser()
     2832                {
     2833                        *alloc_state = alloc;
    20692834                }
    20702835
     
    21022867                                if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
    21032868
    2104                                 s += 4;
     2869                                s += 3;
    21052870                        }
    21062871                        else PUGI__THROW_ERROR(status_bad_doctype, s);
     
    21112876                char_t* parse_doctype_ignore(char_t* s)
    21122877                {
     2878                        size_t depth = 0;
     2879
    21132880                        assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
    2114                         s++;
     2881                        s += 3;
    21152882
    21162883                        while (*s)
     
    21192886                                {
    21202887                                        // nested ignore section
    2121                                         s = parse_doctype_ignore(s);
    2122                                         if (!s) return s;
     2888                                        s += 3;
     2889                                        depth++;
    21232890                                }
    21242891                                else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
     
    21272894                                        s += 3;
    21282895
    2129                                         return s;
     2896                                        if (depth == 0)
     2897                                                return s;
     2898
     2899                                        depth--;
    21302900                                }
    21312901                                else s++;
     
    21352905                }
    21362906
    2137                 char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
    2138                 {
    2139                         assert(s[0] == '<' && s[1] == '!');
    2140                         s++;
     2907                char_t* parse_doctype_group(char_t* s, char_t endch)
     2908                {
     2909                        size_t depth = 0;
     2910
     2911                        assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
     2912                        s += 2;
    21412913
    21422914                        while (*s)
     
    21532925                                        {
    21542926                                                // some control group
    2155                                                 s = parse_doctype_group(s, endch, false);
    2156                                                 if (!s) return s;
     2927                                                s += 2;
     2928                                                depth++;
    21572929                                        }
    21582930                                }
     
    21652937                                else if (*s == '>')
    21662938                                {
     2939                                        if (depth == 0)
     2940                                                return s;
     2941
     2942                                        depth--;
    21672943                                        s++;
    2168 
    2169                                         return s;
    21702944                                }
    21712945                                else s++;
    21722946                        }
    21732947
    2174                         if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
     2948                        if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
    21752949
    21762950                        return s;
     
    22052979                                        {
    22062980                                                // Scan for terminating '-->'.
    2207                                                 PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));
     2981                                                PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>'));
    22082982                                                PUGI__CHECK_ERROR(status_bad_comment, s);
    22092983
     
    22373011                                                {
    22383012                                                        // Scan for terminating ']]>'.
    2239                                                         PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
     3013                                                        PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
    22403014                                                        PUGI__CHECK_ERROR(status_bad_cdata, s);
    22413015
     
    22463020                                        {
    22473021                                                // Scan for terminating ']]>'.
    2248                                                 PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
     3022                                                PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
    22493023                                                PUGI__CHECK_ERROR(status_bad_cdata, s);
    22503024
     
    22563030                                else PUGI__THROW_ERROR(status_bad_cdata, s);
    22573031                        }
    2258                         else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))
     3032                        else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E'))
    22593033                        {
    22603034                                s -= 2;
     
    22643038                                char_t* mark = s + 9;
    22653039
    2266                                 s = parse_doctype_group(s, endch, true);
     3040                                s = parse_doctype_group(s, endch);
    22673041                                if (!s) return s;
    22683042
     3043                                assert((*s == 0 && endch == '>') || *s == '>');
     3044                                if (*s) *s++ = 0;
     3045
    22693046                                if (PUGI__OPTSET(parse_doctype))
    22703047                                {
     
    22743051
    22753052                                        cursor->value = mark;
    2276 
    2277                                         assert((s[0] == 0 && endch == '>') || s[-1] == '>');
    2278                                         s[*s == 0 ? 0 : -1] = 0;
    2279 
    2280                                         PUGI__POPNODE();
    22813053                                }
    22823054                        }
     
    23303102                                {
    23313103                                        // empty node
    2332                                         if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
     3104                                        if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
    23333105                                        s += (*s == '>');
    23343106
     
    23423114                                        char_t* value = s;
    23433115
    2344                                         PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
     3116                                        PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
    23453117                                        PUGI__CHECK_ERROR(status_bad_pi, s);
    23463118
     
    23573129                                                // store value and step over >
    23583130                                                cursor->value = value;
     3131
    23593132                                                PUGI__POPNODE();
    23603133
     
    23693142                        {
    23703143                                // scan for tag end
    2371                                 PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
     3144                                PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
    23723145                                PUGI__CHECK_ERROR(status_bad_pi, s);
    23733146
     
    23813154                }
    23823155
    2383                 char_t* parse(char_t* s, xml_node_struct* xmldoc, unsigned int optmsk, char_t endch)
     3156                char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)
    23843157                {
    23853158                        strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
     
    23873160                       
    23883161                        char_t ch = 0;
    2389                         xml_node_struct* cursor = xmldoc;
     3162                        xml_node_struct* cursor = root;
    23903163                        char_t* mark = s;
    23913164
     
    24033176                                                cursor->name = s;
    24043177
    2405                                                 PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
     3178                                                PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
    24063179                                                PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
    24073180
     
    24193192                                                                if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
    24203193                                                                {
    2421                                                                         xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute.
     3194                                                                        xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute.
    24223195                                                                        if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
    24233196
    24243197                                                                        a->name = s; // Save the offset.
    24253198
    2426                                                                         PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
    2427                                                                         PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
    2428 
     3199                                                                        PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
    24293200                                                                        PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
    2430                                                                         PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
    24313201
    24323202                                                                        if (PUGI__IS_CHARTYPE(ch, ct_space))
    24333203                                                                        {
    24343204                                                                                PUGI__SKIPWS(); // Eat any whitespace.
    2435                                                                                 PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
    24363205
    24373206                                                                                ch = *s;
     
    24963265                                                else if (ch == '/') // '<#.../'
    24973266                                                {
    2498                                                         if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
     3267                                                        if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
    24993268
    25003269                                                        PUGI__POPNODE(); // Pop.
     
    25493318
    25503319                                                assert(cursor);
    2551                                                 if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;
     3320                                                if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
    25523321                                        }
    25533322                                        else if (*s == '!') // '<!...'
     
    25653334                                        PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
    25663335
    2567                                         if (*s == '<')
     3336                                        if (*s == '<' || !*s)
    25683337                                        {
    25693338                                                // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
    25703339                                                assert(mark != s);
    25713340
    2572                                                 if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single))
     3341                                                if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))
    25733342                                                {
    25743343                                                        continue;
     
    25763345                                                else if (PUGI__OPTSET(parse_ws_pcdata_single))
    25773346                                                {
    2578                                                         if (s[1] != '/' || cursor->first_child) continue;
     3347                                                        if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
    25793348                                                }
    25803349                                        }
    25813350
    2582                                         s = mark;
     3351                                        if (!PUGI__OPTSET(parse_trim_pcdata))
     3352                                                s = mark;
    25833353                                                       
    2584                                         if (cursor->parent)
     3354                                        if (cursor->parent || PUGI__OPTSET(parse_fragment))
    25853355                                        {
    25863356                                                PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
     
    26073377
    26083378                        // check that last tag is closed
    2609                         if (cursor != xmldoc) PUGI__THROW_ERROR(status_end_element_mismatch, s);
     3379                        if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);
    26103380
    26113381                        return s;
    26123382                }
    26133383
    2614                 static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk)
    2615                 {
    2616                         xml_document_struct* xmldoc = static_cast<xml_document_struct*>(root);
    2617 
    2618                         // store buffer for offset_debug
    2619                         xmldoc->buffer = buffer;
    2620 
     3384        #ifdef PUGIXML_WCHAR_MODE
     3385                static char_t* parse_skip_bom(char_t* s)
     3386                {
     3387                        unsigned int bom = 0xfeff;
     3388                        return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;
     3389                }
     3390        #else
     3391                static char_t* parse_skip_bom(char_t* s)
     3392                {
     3393                        return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s;
     3394                }
     3395        #endif
     3396
     3397                static bool has_element_node_siblings(xml_node_struct* node)
     3398                {
     3399                        while (node)
     3400                        {
     3401                                if (PUGI__NODETYPE(node) == node_element) return true;
     3402
     3403                                node = node->next_sibling;
     3404                        }
     3405
     3406                        return false;
     3407                }
     3408
     3409                static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
     3410                {
    26213411                        // early-out for empty documents
    2622                         if (length == 0) return make_parse_result(status_ok);
    2623 
     3412                        if (length == 0)
     3413                                return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
     3414
     3415                        // get last child of the root before parsing
     3416                        xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
     3417       
    26243418                        // create parser on stack
    2625                         xml_parser parser(*xmldoc);
     3419                        xml_parser parser(static_cast<xml_allocator*>(xmldoc));
    26263420
    26273421                        // save last character and make buffer zero-terminated (speeds up parsing)
     
    26293423                        buffer[length - 1] = 0;
    26303424                       
     3425                        // skip BOM to make sure it does not end up as part of parse output
     3426                        char_t* buffer_data = parse_skip_bom(buffer);
     3427
    26313428                        // perform actual parsing
    2632                         parser.parse(buffer, xmldoc, optmsk, endch);
     3429                        parser.parse_tree(buffer_data, root, optmsk, endch);
    26333430
    26343431                        xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
    26353432                        assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
    26363433
    2637                         // update allocator state
    2638                         *static_cast<xml_allocator*>(xmldoc) = parser.alloc;
    2639 
    2640                         // since we removed last character, we have to handle the only possible false positive
    2641                         if (result && endch == '<')
    2642                         {
    2643                                 // there's no possible well-formed document with < at the end
    2644                                 return make_parse_result(status_unrecognized_tag, length);
     3434                        if (result)
     3435                        {
     3436                                // since we removed last character, we have to handle the only possible false positive (stray <)
     3437                                if (endch == '<')
     3438                                        return make_parse_result(status_unrecognized_tag, length - 1);
     3439
     3440                                // check if there are any element nodes parsed
     3441                                xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0;
     3442
     3443                                if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
     3444                                        return make_parse_result(status_no_document_element, length - 1);
     3445                        }
     3446                        else
     3447                        {
     3448                                // roll back offset if it occurs on a null terminator in the source buffer
     3449                                if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)
     3450                                        result.offset--;
    26453451                        }
    26463452
     
    26773483        }
    26783484
     3485        template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T)
     3486        {
     3487                PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
     3488
     3489                typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
     3490
     3491                return static_cast<size_t>(end - dest) * sizeof(*dest);
     3492        }
     3493
     3494        template <typename D, typename T> 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)
     3495        {
     3496                PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
     3497
     3498                typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
     3499
     3500                if (opt_swap)
     3501                {
     3502                        for (typename T::value_type i = dest; i != end; ++i)
     3503                                *i = endian_swap(*i);
     3504                }
     3505
     3506                return static_cast<size_t>(end - dest) * sizeof(*dest);
     3507        }
     3508
    26793509#ifdef PUGIXML_WCHAR_MODE
    26803510        PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
    26813511        {
    2682                 assert(length > 0);
     3512                if (length < 1) return 0;
    26833513
    26843514                // discard last character if it's the lead of a surrogate pair
     
    26863516        }
    26873517
    2688         PUGI__FN size_t convert_buffer(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
     3518        PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
    26893519        {
    26903520                // only endian-swapping is required
     
    26983528                // convert to utf8
    26993529                if (encoding == encoding_utf8)
    2700                 {
    2701                         uint8_t* dest = r_u8;
    2702                         uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(data, length, dest);
    2703 
    2704                         return static_cast<size_t>(end - dest);
    2705                 }
     3530                        return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer());
    27063531
    27073532                // convert to utf16
    27083533                if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
    27093534                {
    2710                         uint16_t* dest = r_u16;
    2711 
    2712                         // convert to native utf16
    2713                         uint16_t* end = utf_decoder<utf16_writer>::decode_wchar_block(data, length, dest);
    2714 
    2715                         // swap if necessary
    27163535                        xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
    27173536
    2718                         if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
    2719 
    2720                         return static_cast<size_t>(end - dest) * sizeof(uint16_t);
     3537                        return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding);
    27213538                }
    27223539
     
    27243541                if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
    27253542                {
    2726                         uint32_t* dest = r_u32;
    2727 
    2728                         // convert to native utf32
    2729                         uint32_t* end = utf_decoder<utf32_writer>::decode_wchar_block(data, length, dest);
    2730 
    2731                         // swap if necessary
    27323543                        xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
    27333544
    2734                         if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
    2735 
    2736                         return static_cast<size_t>(end - dest) * sizeof(uint32_t);
     3545                        return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding);
    27373546                }
    27383547
    27393548                // convert to latin1
    27403549                if (encoding == encoding_latin1)
    2741                 {
    2742                         uint8_t* dest = r_u8;
    2743                         uint8_t* end = utf_decoder<latin1_writer>::decode_wchar_block(data, length, dest);
    2744 
    2745                         return static_cast<size_t>(end - dest);
    2746                 }
     3550                        return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
    27473551
    27483552                assert(!"Invalid encoding");
     
    27523556        PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
    27533557        {
    2754                 assert(length > 4);
     3558                if (length < 5) return 0;
    27553559
    27563560                for (size_t i = 1; i <= 4; ++i)
     
    27663570        }
    27673571
    2768         PUGI__FN size_t convert_buffer(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
     3572        PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
    27693573        {
    27703574                if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
    27713575                {
    2772                         uint16_t* dest = r_u16;
    2773 
    2774                         // convert to native utf16
    2775                         uint16_t* end = utf_decoder<utf16_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
    2776 
    2777                         // swap if necessary
    27783576                        xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
    27793577
    2780                         if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
    2781 
    2782                         return static_cast<size_t>(end - dest) * sizeof(uint16_t);
     3578                        return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding);
    27833579                }
    27843580
    27853581                if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
    27863582                {
    2787                         uint32_t* dest = r_u32;
    2788 
    2789                         // convert to native utf32
    2790                         uint32_t* end = utf_decoder<utf32_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
    2791 
    2792                         // swap if necessary
    27933583                        xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
    27943584
    2795                         if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
    2796 
    2797                         return static_cast<size_t>(end - dest) * sizeof(uint32_t);
     3585                        return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding);
    27983586                }
    27993587
    28003588                if (encoding == encoding_latin1)
    2801                 {
    2802                         uint8_t* dest = r_u8;
    2803                         uint8_t* end = utf_decoder<latin1_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
    2804 
    2805                         return static_cast<size_t>(end - dest);
    2806                 }
     3589                        return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
    28073590
    28083591                assert(!"Invalid encoding");
     
    28223605                }
    28233606
    2824                 ~xml_buffered_writer()
    2825                 {
    2826                         flush();
    2827                 }
    2828 
    2829                 void flush()
     3607                size_t flush()
    28303608                {
    28313609                        flush(buffer, bufsize);
    28323610                        bufsize = 0;
     3611                        return 0;
    28333612                }
    28343613
     
    28433622                        {
    28443623                                // convert chunk
    2845                                 size_t result = convert_buffer(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
     3624                                size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
    28463625                                assert(result <= sizeof(scratch));
    28473626
     
    28513630                }
    28523631
    2853                 void write(const char_t* data, size_t length)
    2854                 {
    2855                         if (bufsize + length > bufcapacity)
    2856                         {
    2857                                 // flush the remaining buffer contents
    2858                                 flush();
    2859 
    2860                                 // handle large chunks
    2861                                 if (length > bufcapacity)
    2862                                 {
    2863                                         if (encoding == get_write_native_encoding())
    2864                                         {
    2865                                                 // fast path, can just write data chunk
    2866                                                 writer.write(data, length * sizeof(char_t));
    2867                                                 return;
    2868                                         }
    2869 
    2870                                         // need to convert in suitable chunks
    2871                                         while (length > bufcapacity)
    2872                                         {
    2873                                                 // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
    2874                                                 // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
    2875                                                 size_t chunk_size = get_valid_length(data, bufcapacity);
    2876 
    2877                                                 // convert chunk and write
    2878                                                 flush(data, chunk_size);
    2879 
    2880                                                 // iterate
    2881                                                 data += chunk_size;
    2882                                                 length -= chunk_size;
    2883                                         }
    2884 
    2885                                         // small tail is copied below
    2886                                         bufsize = 0;
    2887                                 }
     3632                void write_direct(const char_t* data, size_t length)
     3633                {
     3634                        // flush the remaining buffer contents
     3635                        flush();
     3636
     3637                        // handle large chunks
     3638                        if (length > bufcapacity)
     3639                        {
     3640                                if (encoding == get_write_native_encoding())
     3641                                {
     3642                                        // fast path, can just write data chunk
     3643                                        writer.write(data, length * sizeof(char_t));
     3644                                        return;
     3645                                }
     3646
     3647                                // need to convert in suitable chunks
     3648                                while (length > bufcapacity)
     3649                                {
     3650                                        // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
     3651                                        // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
     3652                                        size_t chunk_size = get_valid_length(data, bufcapacity);
     3653                                        assert(chunk_size);
     3654
     3655                                        // convert chunk and write
     3656                                        flush(data, chunk_size);
     3657
     3658                                        // iterate
     3659                                        data += chunk_size;
     3660                                        length -= chunk_size;
     3661                                }
     3662
     3663                                // small tail is copied below
     3664                                bufsize = 0;
    28883665                        }
    28893666
     
    28923669                }
    28933670
    2894                 void write(const char_t* data)
    2895                 {
    2896                         write(data, strlength(data));
     3671                void write_buffer(const char_t* data, size_t length)
     3672                {
     3673                        size_t offset = bufsize;
     3674
     3675                        if (offset + length <= bufcapacity)
     3676                        {
     3677                                memcpy(buffer + offset, data, length * sizeof(char_t));
     3678                                bufsize = offset + length;
     3679                        }
     3680                        else
     3681                        {
     3682                                write_direct(data, length);
     3683                        }
     3684                }
     3685
     3686                void write_string(const char_t* data)
     3687                {
     3688                        // write the part of the string that fits in the buffer
     3689                        size_t offset = bufsize;
     3690
     3691                        while (*data && offset < bufcapacity)
     3692                                buffer[offset++] = *data++;
     3693
     3694                        // write the rest
     3695                        if (offset < bufcapacity)
     3696                        {
     3697                                bufsize = offset;
     3698                        }
     3699                        else
     3700                        {
     3701                                // backtrack a bit if we have split the codepoint
     3702                                size_t length = offset - bufsize;
     3703                                size_t extra = length - get_valid_length(data - length, length);
     3704
     3705                                bufsize = offset - extra;
     3706
     3707                                write_direct(data - extra, strlength(data) + extra);
     3708                        }
    28973709                }
    28983710
    28993711                void write(char_t d0)
    29003712                {
    2901                         if (bufsize + 1 > bufcapacity) flush();
    2902 
    2903                         buffer[bufsize + 0] = d0;
    2904                         bufsize += 1;
     3713                        size_t offset = bufsize;
     3714                        if (offset > bufcapacity - 1) offset = flush();
     3715
     3716                        buffer[offset + 0] = d0;
     3717                        bufsize = offset + 1;
    29053718                }
    29063719
    29073720                void write(char_t d0, char_t d1)
    29083721                {
    2909                         if (bufsize + 2 > bufcapacity) flush();
    2910 
    2911                         buffer[bufsize + 0] = d0;
    2912                         buffer[bufsize + 1] = d1;
    2913                         bufsize += 2;
     3722                        size_t offset = bufsize;
     3723                        if (offset > bufcapacity - 2) offset = flush();
     3724
     3725                        buffer[offset + 0] = d0;
     3726                        buffer[offset + 1] = d1;
     3727                        bufsize = offset + 2;
    29143728                }
    29153729
    29163730                void write(char_t d0, char_t d1, char_t d2)
    29173731                {
    2918                         if (bufsize + 3 > bufcapacity) flush();
    2919 
    2920                         buffer[bufsize + 0] = d0;
    2921                         buffer[bufsize + 1] = d1;
    2922                         buffer[bufsize + 2] = d2;
    2923                         bufsize += 3;
     3732                        size_t offset = bufsize;
     3733                        if (offset > bufcapacity - 3) offset = flush();
     3734
     3735                        buffer[offset + 0] = d0;
     3736                        buffer[offset + 1] = d1;
     3737                        buffer[offset + 2] = d2;
     3738                        bufsize = offset + 3;
    29243739                }
    29253740
    29263741                void write(char_t d0, char_t d1, char_t d2, char_t d3)
    29273742                {
    2928                         if (bufsize + 4 > bufcapacity) flush();
    2929 
    2930                         buffer[bufsize + 0] = d0;
    2931                         buffer[bufsize + 1] = d1;
    2932                         buffer[bufsize + 2] = d2;
    2933                         buffer[bufsize + 3] = d3;
    2934                         bufsize += 4;
     3743                        size_t offset = bufsize;
     3744                        if (offset > bufcapacity - 4) offset = flush();
     3745
     3746                        buffer[offset + 0] = d0;
     3747                        buffer[offset + 1] = d1;
     3748                        buffer[offset + 2] = d2;
     3749                        buffer[offset + 3] = d3;
     3750                        bufsize = offset + 4;
    29353751                }
    29363752
    29373753                void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
    29383754                {
    2939                         if (bufsize + 5 > bufcapacity) flush();
    2940 
    2941                         buffer[bufsize + 0] = d0;
    2942                         buffer[bufsize + 1] = d1;
    2943                         buffer[bufsize + 2] = d2;
    2944                         buffer[bufsize + 3] = d3;
    2945                         buffer[bufsize + 4] = d4;
    2946                         bufsize += 5;
     3755                        size_t offset = bufsize;
     3756                        if (offset > bufcapacity - 5) offset = flush();
     3757
     3758                        buffer[offset + 0] = d0;
     3759                        buffer[offset + 1] = d1;
     3760                        buffer[offset + 2] = d2;
     3761                        buffer[offset + 3] = d3;
     3762                        buffer[offset + 4] = d4;
     3763                        bufsize = offset + 5;
    29473764                }
    29483765
    29493766                void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
    29503767                {
    2951                         if (bufsize + 6 > bufcapacity) flush();
    2952 
    2953                         buffer[bufsize + 0] = d0;
    2954                         buffer[bufsize + 1] = d1;
    2955                         buffer[bufsize + 2] = d2;
    2956                         buffer[bufsize + 3] = d3;
    2957                         buffer[bufsize + 4] = d4;
    2958                         buffer[bufsize + 5] = d5;
    2959                         bufsize += 6;
     3768                        size_t offset = bufsize;
     3769                        if (offset > bufcapacity - 6) offset = flush();
     3770
     3771                        buffer[offset + 0] = d0;
     3772                        buffer[offset + 1] = d1;
     3773                        buffer[offset + 2] = d2;
     3774                        buffer[offset + 3] = d3;
     3775                        buffer[offset + 4] = d4;
     3776                        buffer[offset + 5] = d5;
     3777                        bufsize = offset + 6;
    29603778                }
    29613779
     
    29973815                       
    29983816                        // While *s is a usual symbol
    2999                         while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;
     3817                        PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type));
    30003818               
    3001                         writer.write(prev, static_cast<size_t>(s - prev));
     3819                        writer.write_buffer(prev, static_cast<size_t>(s - prev));
    30023820
    30033821                        switch (*s)
     
    30343852        {
    30353853                if (flags & format_no_escapes)
    3036                         writer.write(s);
     3854                        writer.write_string(s);
    30373855                else
    30383856                        text_output_escaped(writer, s, type);
     
    30543872                        if (*s) s += 2;
    30553873
    3056                         writer.write(prev, static_cast<size_t>(s - prev));
     3874                        writer.write_buffer(prev, static_cast<size_t>(s - prev));
    30573875
    30583876                        writer.write(']', ']', '>');
     
    30613879        }
    30623880
    3063         PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
     3881        PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
     3882        {
     3883                switch (indent_length)
     3884                {
     3885                case 1:
     3886                {
     3887                        for (unsigned int i = 0; i < depth; ++i)
     3888                                writer.write(indent[0]);
     3889                        break;
     3890                }
     3891
     3892                case 2:
     3893                {
     3894                        for (unsigned int i = 0; i < depth; ++i)
     3895                                writer.write(indent[0], indent[1]);
     3896                        break;
     3897                }
     3898
     3899                case 3:
     3900                {
     3901                        for (unsigned int i = 0; i < depth; ++i)
     3902                                writer.write(indent[0], indent[1], indent[2]);
     3903                        break;
     3904                }
     3905
     3906                case 4:
     3907                {
     3908                        for (unsigned int i = 0; i < depth; ++i)
     3909                                writer.write(indent[0], indent[1], indent[2], indent[3]);
     3910                        break;
     3911                }
     3912
     3913                default:
     3914                {
     3915                        for (unsigned int i = 0; i < depth; ++i)
     3916                                writer.write_buffer(indent, indent_length);
     3917                }
     3918                }
     3919        }
     3920
     3921        PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
     3922        {
     3923                writer.write('<', '!', '-', '-');
     3924
     3925                while (*s)
     3926                {
     3927                        const char_t* prev = s;
     3928
     3929                        // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body
     3930                        while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;
     3931
     3932                        writer.write_buffer(prev, static_cast<size_t>(s - prev));
     3933
     3934                        if (*s)
     3935                        {
     3936                                assert(*s == '-');
     3937
     3938                                writer.write('-', ' ');
     3939                                ++s;
     3940                        }
     3941                }
     3942
     3943                writer.write('-', '-', '>');
     3944        }
     3945
     3946        PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
     3947        {
     3948                while (*s)
     3949                {
     3950                        const char_t* prev = s;
     3951
     3952                        // look for ?> sequence - we can't output it since ?> terminates PI
     3953                        while (*s && !(s[0] == '?' && s[1] == '>')) ++s;
     3954
     3955                        writer.write_buffer(prev, static_cast<size_t>(s - prev));
     3956
     3957                        if (*s)
     3958                        {
     3959                                assert(s[0] == '?' && s[1] == '>');
     3960
     3961                                writer.write('?', ' ', '>');
     3962                                s += 2;
     3963                        }
     3964                }
     3965        }
     3966
     3967        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)
    30643968        {
    30653969                const char_t* default_name = PUGIXML_TEXT(":anonymous");
    30663970
    3067                 for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
    3068                 {
    3069                         writer.write(' ');
    3070                         writer.write(a.name()[0] ? a.name() : default_name);
     3971                for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
     3972                {
     3973                        if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes)
     3974                        {
     3975                                writer.write('\n');
     3976
     3977                                text_output_indent(writer, indent, indent_length, depth + 1);
     3978                        }
     3979                        else
     3980                        {
     3981                                writer.write(' ');
     3982                        }
     3983
     3984                        writer.write_string(a->name ? a->name + 0 : default_name);
    30713985                        writer.write('=', '"');
    30723986
    3073                         text_output(writer, a.value(), ctx_special_attr, flags);
     3987                        if (a->value)
     3988                                text_output(writer, a->value, ctx_special_attr, flags);
    30743989
    30753990                        writer.write('"');
     
    30773992        }
    30783993
    3079         PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)
     3994        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)
    30803995        {
    30813996                const char_t* default_name = PUGIXML_TEXT(":anonymous");
    3082 
    3083                 if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
    3084                         for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
    3085 
    3086                 switch (node.type())
    3087                 {
    3088                 case node_document:
    3089                 {
    3090                         for (xml_node n = node.first_child(); n; n = n.next_sibling())
    3091                                 node_output(writer, n, indent, flags, depth);
    3092                         break;
    3093                 }
    3094                        
    3095                 case node_element:
    3096                 {
    3097                         const char_t* name = node.name()[0] ? node.name() : default_name;
    3098 
    3099                         writer.write('<');
    3100                         writer.write(name);
    3101 
    3102                         node_output_attributes(writer, node, flags);
    3103 
    3104                         if (flags & format_raw)
    3105                         {
    3106                                 if (!node.first_child())
    3107                                         writer.write(' ', '/', '>');
     3997                const char_t* name = node->name ? node->name + 0 : default_name;
     3998
     3999                writer.write('<');
     4000                writer.write_string(name);
     4001
     4002                if (node->first_attribute)
     4003                        node_output_attributes(writer, node, indent, indent_length, flags, depth);
     4004
     4005                if (!node->first_child)
     4006                {
     4007                        writer.write(' ', '/', '>');
     4008
     4009                        return false;
     4010                }
     4011                else
     4012                {
     4013                        writer.write('>');
     4014
     4015                        return true;
     4016                }
     4017        }
     4018
     4019        PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node)
     4020        {
     4021                const char_t* default_name = PUGIXML_TEXT(":anonymous");
     4022                const char_t* name = node->name ? node->name + 0 : default_name;
     4023
     4024                writer.write('<', '/');
     4025                writer.write_string(name);
     4026                writer.write('>');
     4027        }
     4028
     4029        PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
     4030        {
     4031                const char_t* default_name = PUGIXML_TEXT(":anonymous");
     4032
     4033                switch (PUGI__NODETYPE(node))
     4034                {
     4035                        case node_pcdata:
     4036                                text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
     4037                                break;
     4038
     4039                        case node_cdata:
     4040                                text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
     4041                                break;
     4042
     4043                        case node_comment:
     4044                                node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
     4045                                break;
     4046
     4047                        case node_pi:
     4048                                writer.write('<', '?');
     4049                                writer.write_string(node->name ? node->name + 0 : default_name);
     4050
     4051                                if (node->value)
     4052                                {
     4053                                        writer.write(' ');
     4054                                        node_output_pi_value(writer, node->value);
     4055                                }
     4056
     4057                                writer.write('?', '>');
     4058                                break;
     4059
     4060                        case node_declaration:
     4061                                writer.write('<', '?');
     4062                                writer.write_string(node->name ? node->name + 0 : default_name);
     4063                                node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0);
     4064                                writer.write('?', '>');
     4065                                break;
     4066
     4067                        case node_doctype:
     4068                                writer.write('<', '!', 'D', 'O', 'C');
     4069                                writer.write('T', 'Y', 'P', 'E');
     4070
     4071                                if (node->value)
     4072                                {
     4073                                        writer.write(' ');
     4074                                        writer.write_string(node->value);
     4075                                }
     4076
     4077                                writer.write('>');
     4078                                break;
     4079
     4080                        default:
     4081                                assert(!"Invalid node type");
     4082                }
     4083        }
     4084
     4085        enum indent_flags_t
     4086        {
     4087                indent_newline = 1,
     4088                indent_indent = 2
     4089        };
     4090
     4091        PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
     4092        {
     4093                size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0;
     4094                unsigned int indent_flags = indent_indent;
     4095
     4096                xml_node_struct* node = root;
     4097
     4098                do
     4099                {
     4100                        assert(node);
     4101
     4102                        // begin writing current node
     4103                        if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
     4104                        {
     4105                                node_output_simple(writer, node, flags);
     4106
     4107                                indent_flags = 0;
     4108                        }
     4109                        else
     4110                        {
     4111                                if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
     4112                                        writer.write('\n');
     4113
     4114                                if ((indent_flags & indent_indent) && indent_length)
     4115                                        text_output_indent(writer, indent, indent_length, depth);
     4116
     4117                                if (PUGI__NODETYPE(node) == node_element)
     4118                                {
     4119                                        indent_flags = indent_newline | indent_indent;
     4120
     4121                                        if (node_output_start(writer, node, indent, indent_length, flags, depth))
     4122                                        {
     4123                                                node = node->first_child;
     4124                                                depth++;
     4125                                                continue;
     4126                                        }
     4127                                }
     4128                                else if (PUGI__NODETYPE(node) == node_document)
     4129                                {
     4130                                        indent_flags = indent_indent;
     4131
     4132                                        if (node->first_child)
     4133                                        {
     4134                                                node = node->first_child;
     4135                                                continue;
     4136                                        }
     4137                                }
    31084138                                else
    31094139                                {
    3110                                         writer.write('>');
    3111 
    3112                                         for (xml_node n = node.first_child(); n; n = n.next_sibling())
    3113                                                 node_output(writer, n, indent, flags, depth + 1);
    3114 
    3115                                         writer.write('<', '/');
    3116                                         writer.write(name);
    3117                                         writer.write('>');
    3118                                 }
    3119                         }
    3120                         else if (!node.first_child())
    3121                                 writer.write(' ', '/', '>', '\n');
    3122                         else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
    3123                         {
    3124                                 writer.write('>');
    3125 
    3126                                 if (node.first_child().type() == node_pcdata)
    3127                                         text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
    3128                                 else
    3129                                         text_output_cdata(writer, node.first_child().value());
    3130 
    3131                                 writer.write('<', '/');
    3132                                 writer.write(name);
    3133                                 writer.write('>', '\n');
    3134                         }
    3135                         else
    3136                         {
    3137                                 writer.write('>', '\n');
    3138                                
    3139                                 for (xml_node n = node.first_child(); n; n = n.next_sibling())
    3140                                         node_output(writer, n, indent, flags, depth + 1);
    3141 
    3142                                 if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
    3143                                         for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
    3144                                
    3145                                 writer.write('<', '/');
    3146                                 writer.write(name);
    3147                                 writer.write('>', '\n');
    3148                         }
    3149 
    3150                         break;
    3151                 }
    3152                
    3153                 case node_pcdata:
    3154                         text_output(writer, node.value(), ctx_special_pcdata, flags);
    3155                         if ((flags & format_raw) == 0) writer.write('\n');
    3156                         break;
    3157 
    3158                 case node_cdata:
    3159                         text_output_cdata(writer, node.value());
    3160                         if ((flags & format_raw) == 0) writer.write('\n');
    3161                         break;
    3162 
    3163                 case node_comment:
    3164                         writer.write('<', '!', '-', '-');
    3165                         writer.write(node.value());
    3166                         writer.write('-', '-', '>');
    3167                         if ((flags & format_raw) == 0) writer.write('\n');
    3168                         break;
    3169 
    3170                 case node_pi:
    3171                 case node_declaration:
    3172                         writer.write('<', '?');
    3173                         writer.write(node.name()[0] ? node.name() : default_name);
    3174 
    3175                         if (node.type() == node_declaration)
    3176                         {
    3177                                 node_output_attributes(writer, node, flags);
    3178                         }
    3179                         else if (node.value()[0])
    3180                         {
    3181                                 writer.write(' ');
    3182                                 writer.write(node.value());
    3183                         }
    3184 
    3185                         writer.write('?', '>');
    3186                         if ((flags & format_raw) == 0) writer.write('\n');
    3187                         break;
    3188 
    3189                 case node_doctype:
    3190                         writer.write('<', '!', 'D', 'O', 'C');
    3191                         writer.write('T', 'Y', 'P', 'E');
    3192 
    3193                         if (node.value()[0])
    3194                         {
    3195                                 writer.write(' ');
    3196                                 writer.write(node.value());
    3197                         }
    3198 
    3199                         writer.write('>');
    3200                         if ((flags & format_raw) == 0) writer.write('\n');
    3201                         break;
    3202 
    3203                 default:
    3204                         assert(!"Invalid node type");
    3205                 }
    3206         }
    3207 
    3208         inline bool has_declaration(const xml_node& node)
    3209         {
    3210                 for (xml_node child = node.first_child(); child; child = child.next_sibling())
    3211                 {
    3212                         xml_node_type type = child.type();
     4140                                        node_output_simple(writer, node, flags);
     4141
     4142                                        indent_flags = indent_newline | indent_indent;
     4143                                }
     4144                        }
     4145
     4146                        // continue to the next node
     4147                        while (node != root)
     4148                        {
     4149                                if (node->next_sibling)
     4150                                {
     4151                                        node = node->next_sibling;
     4152                                        break;
     4153                                }
     4154
     4155                                node = node->parent;
     4156
     4157                                // write closing node
     4158                                if (PUGI__NODETYPE(node) == node_element)
     4159                                {
     4160                                        depth--;
     4161
     4162                                        if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
     4163                                                writer.write('\n');
     4164
     4165                                        if ((indent_flags & indent_indent) && indent_length)
     4166                                                text_output_indent(writer, indent, indent_length, depth);
     4167
     4168                                        node_output_end(writer, node);
     4169
     4170                                        indent_flags = indent_newline | indent_indent;
     4171                                }
     4172                        }
     4173                }
     4174                while (node != root);
     4175
     4176                if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
     4177                        writer.write('\n');
     4178        }
     4179
     4180        PUGI__FN bool has_declaration(xml_node_struct* node)
     4181        {
     4182                for (xml_node_struct* child = node->first_child; child; child = child->next_sibling)
     4183                {
     4184                        xml_node_type type = PUGI__NODETYPE(child);
    32134185
    32144186                        if (type == node_declaration) return true;
     
    32194191        }
    32204192
    3221         inline bool allow_insert_child(xml_node_type parent, xml_node_type child)
     4193        PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)
     4194        {
     4195                for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
     4196                        if (a == attr)
     4197                                return true;
     4198
     4199                return false;
     4200        }
     4201
     4202        PUGI__FN bool allow_insert_attribute(xml_node_type parent)
     4203        {
     4204                return parent == node_element || parent == node_declaration;
     4205        }
     4206
     4207        PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
    32224208        {
    32234209                if (parent != node_document && parent != node_element) return false;
     
    32284214        }
    32294215
    3230         PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)
    3231         {
    3232                 assert(dest.type() == source.type());
    3233 
    3234                 switch (source.type())
    3235                 {
    3236                 case node_element:
    3237                 {
    3238                         dest.set_name(source.name());
    3239 
    3240                         for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
    3241                                 dest.append_attribute(a.name()).set_value(a.value());
    3242 
    3243                         for (xml_node c = source.first_child(); c; c = c.next_sibling())
    3244                         {
    3245                                 if (c == skip) continue;
    3246 
    3247                                 xml_node cc = dest.append_child(c.type());
    3248                                 assert(cc);
    3249 
    3250                                 recursive_copy_skip(cc, c, skip);
    3251                         }
    3252 
    3253                         break;
    3254                 }
    3255 
    3256                 case node_pcdata:
    3257                 case node_cdata:
    3258                 case node_comment:
    3259                 case node_doctype:
    3260                         dest.set_value(source.value());
    3261                         break;
    3262 
    3263                 case node_pi:
    3264                         dest.set_name(source.name());
    3265                         dest.set_value(source.value());
    3266                         break;
    3267 
    3268                 case node_declaration:
    3269                 {
    3270                         dest.set_name(source.name());
    3271 
    3272                         for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
    3273                                 dest.append_attribute(a.name()).set_value(a.value());
    3274 
    3275                         break;
    3276                 }
    3277 
    3278                 default:
    3279                         assert(!"Invalid node type");
    3280                 }
     4216        PUGI__FN bool allow_move(xml_node parent, xml_node child)
     4217        {
     4218                // check that child can be a child of parent
     4219                if (!allow_insert_child(parent.type(), child.type()))
     4220                        return false;
     4221
     4222                // check that node is not moved between documents
     4223                if (parent.root() != child.root())
     4224                        return false;
     4225
     4226                // check that new parent is not in the child subtree
     4227                xml_node cur = parent;
     4228
     4229                while (cur)
     4230                {
     4231                        if (cur == child)
     4232                                return false;
     4233
     4234                        cur = cur.parent();
     4235                }
     4236
     4237                return true;
     4238        }
     4239
     4240        template <typename String, typename Header>
     4241        PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)
     4242        {
     4243                assert(!dest && (header & header_mask) == 0);
     4244
     4245                if (source)
     4246                {
     4247                        if (alloc && (source_header & header_mask) == 0)
     4248                        {
     4249                                dest = source;
     4250
     4251                                // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared
     4252                                header |= xml_memory_page_contents_shared_mask;
     4253                                source_header |= xml_memory_page_contents_shared_mask;
     4254                        }
     4255                        else
     4256                                strcpy_insitu(dest, header, header_mask, source, strlength(source));
     4257                }
     4258        }
     4259
     4260        PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc)
     4261        {
     4262                node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc);
     4263                node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc);
     4264
     4265                for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute)
     4266                {
     4267                        xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
     4268
     4269                        if (da)
     4270                        {
     4271                                node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
     4272                                node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
     4273                        }
     4274                }
     4275        }
     4276
     4277        PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)
     4278        {
     4279                xml_allocator& alloc = get_allocator(dn);
     4280                xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0;
     4281
     4282                node_copy_contents(dn, sn, shared_alloc);
     4283
     4284                xml_node_struct* dit = dn;
     4285                xml_node_struct* sit = sn->first_child;
     4286
     4287                while (sit && sit != sn)
     4288                {
     4289                        if (sit != dn)
     4290                        {
     4291                                xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
     4292
     4293                                if (copy)
     4294                                {
     4295                                        node_copy_contents(copy, sit, shared_alloc);
     4296
     4297                                        if (sit->first_child)
     4298                                        {
     4299                                                dit = copy;
     4300                                                sit = sit->first_child;
     4301                                                continue;
     4302                                        }
     4303                                }
     4304                        }
     4305
     4306                        // continue to the next node
     4307                        do
     4308                        {
     4309                                if (sit->next_sibling)
     4310                                {
     4311                                        sit = sit->next_sibling;
     4312                                        break;
     4313                                }
     4314
     4315                                sit = sit->parent;
     4316                                dit = dit->parent;
     4317                        }
     4318                        while (sit != sn);
     4319                }
     4320        }
     4321
     4322        PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)
     4323        {
     4324                xml_allocator& alloc = get_allocator(da);
     4325                xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0;
     4326
     4327                node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
     4328                node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
    32814329        }
    32824330
    32834331        inline bool is_text_node(xml_node_struct* node)
    32844332        {
    3285                 xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
     4333                xml_node_type type = PUGI__NODETYPE(node);
    32864334
    32874335                return type == node_pcdata || type == node_cdata;
     
    32894337
    32904338        // get value with conversion functions
    3291         PUGI__FN int get_value_int(const char_t* value, int def)
    3292         {
    3293                 if (!value) return def;
    3294 
    3295         #ifdef PUGIXML_WCHAR_MODE
    3296                 return static_cast<int>(wcstol(value, 0, 10));
    3297         #else
    3298                 return static_cast<int>(strtol(value, 0, 10));
    3299         #endif
    3300         }
    3301 
    3302         PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)
    3303         {
    3304                 if (!value) return def;
    3305 
    3306         #ifdef PUGIXML_WCHAR_MODE
    3307                 return static_cast<unsigned int>(wcstoul(value, 0, 10));
    3308         #else
    3309                 return static_cast<unsigned int>(strtoul(value, 0, 10));
    3310         #endif
    3311         }
    3312 
    3313         PUGI__FN double get_value_double(const char_t* value, double def)
    3314         {
    3315                 if (!value) return def;
    3316 
     4339        template <typename U> U string_to_integer(const char_t* value, U minneg, U maxpos)
     4340        {
     4341                U result = 0;
     4342                const char_t* s = value;
     4343
     4344                while (PUGI__IS_CHARTYPE(*s, ct_space))
     4345                        s++;
     4346
     4347                bool negative = (*s == '-');
     4348
     4349                s += (*s == '+' || *s == '-');
     4350
     4351                bool overflow = false;
     4352
     4353                if (s[0] == '0' && (s[1] | ' ') == 'x')
     4354                {
     4355                        s += 2;
     4356
     4357                        // since overflow detection relies on length of the sequence skip leading zeros
     4358                        while (*s == '0')
     4359                                s++;
     4360
     4361                        const char_t* start = s;
     4362
     4363                        for (;;)
     4364                        {
     4365                                if (static_cast<unsigned>(*s - '0') < 10)
     4366                                        result = result * 16 + (*s - '0');
     4367                                else if (static_cast<unsigned>((*s | ' ') - 'a') < 6)
     4368                                        result = result * 16 + ((*s | ' ') - 'a' + 10);
     4369                                else
     4370                                        break;
     4371
     4372                                s++;
     4373                        }
     4374
     4375                        size_t digits = static_cast<size_t>(s - start);
     4376
     4377                        overflow = digits > sizeof(U) * 2;
     4378                }
     4379                else
     4380                {
     4381                        // since overflow detection relies on length of the sequence skip leading zeros
     4382                        while (*s == '0')
     4383                                s++;
     4384
     4385                        const char_t* start = s;
     4386
     4387                        for (;;)
     4388                        {
     4389                                if (static_cast<unsigned>(*s - '0') < 10)
     4390                                        result = result * 10 + (*s - '0');
     4391                                else
     4392                                        break;
     4393
     4394                                s++;
     4395                        }
     4396
     4397                        size_t digits = static_cast<size_t>(s - start);
     4398
     4399                        PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);
     4400
     4401                        const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5;
     4402                        const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6';
     4403                        const size_t high_bit = sizeof(U) * 8 - 1;
     4404
     4405                        overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit)));
     4406                }
     4407
     4408                if (negative)
     4409                        return (overflow || result > minneg) ? 0 - minneg : 0 - result;
     4410                else
     4411                        return (overflow || result > maxpos) ? maxpos : result;
     4412        }
     4413
     4414        PUGI__FN int get_value_int(const char_t* value)
     4415        {
     4416                return string_to_integer<unsigned int>(value, 0 - static_cast<unsigned int>(INT_MIN), INT_MAX);
     4417        }
     4418
     4419        PUGI__FN unsigned int get_value_uint(const char_t* value)
     4420        {
     4421                return string_to_integer<unsigned int>(value, 0, UINT_MAX);
     4422        }
     4423
     4424        PUGI__FN double get_value_double(const char_t* value)
     4425        {
    33174426        #ifdef PUGIXML_WCHAR_MODE
    33184427                return wcstod(value, 0);
     
    33224431        }
    33234432
    3324         PUGI__FN float get_value_float(const char_t* value, float def)
    3325         {
    3326                 if (!value) return def;
    3327 
     4433        PUGI__FN float get_value_float(const char_t* value)
     4434        {
    33284435        #ifdef PUGIXML_WCHAR_MODE
    33294436                return static_cast<float>(wcstod(value, 0));
     
    33334440        }
    33344441
    3335         PUGI__FN bool get_value_bool(const char_t* value, bool def)
    3336         {
    3337                 if (!value) return def;
    3338 
     4442        PUGI__FN bool get_value_bool(const char_t* value)
     4443        {
    33394444                // only look at first char
    33404445                char_t first = *value;
     
    33444449        }
    33454450
     4451#ifdef PUGIXML_HAS_LONG_LONG
     4452        PUGI__FN long long get_value_llong(const char_t* value)
     4453        {
     4454                return string_to_integer<unsigned long long>(value, 0 - static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
     4455        }
     4456
     4457        PUGI__FN unsigned long long get_value_ullong(const char_t* value)
     4458        {
     4459                return string_to_integer<unsigned long long>(value, 0, ULLONG_MAX);
     4460        }
     4461#endif
     4462
     4463        template <typename U>
     4464        PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
     4465        {
     4466                char_t* result = end - 1;
     4467                U rest = negative ? 0 - value : value;
     4468
     4469                do
     4470                {
     4471                        *result-- = static_cast<char_t>('0' + (rest % 10));
     4472                        rest /= 10;
     4473                }
     4474                while (rest);
     4475
     4476                assert(result >= begin);
     4477                (void)begin;
     4478
     4479                *result = '-';
     4480
     4481                return result + !negative;
     4482        }
     4483
    33464484        // set value with conversion functions
    3347         PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
     4485        template <typename String, typename Header>
     4486        PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
    33484487        {
    33494488        #ifdef PUGIXML_WCHAR_MODE
    33504489                char_t wbuf[128];
    3351                 impl::widen_ascii(wbuf, buf);
    3352 
    3353                 return strcpy_insitu(dest, header, header_mask, wbuf);
     4490                assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0]));
     4491
     4492                size_t offset = 0;
     4493                for (; buf[offset]; ++offset) wbuf[offset] = buf[offset];
     4494
     4495                return strcpy_insitu(dest, header, header_mask, wbuf, offset);
    33544496        #else
    3355                 return strcpy_insitu(dest, header, header_mask, buf);
     4497                return strcpy_insitu(dest, header, header_mask, buf, strlen(buf));
    33564498        #endif
    33574499        }
    33584500
    3359         PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
     4501        template <typename String, typename Header>
     4502        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value)
     4503        {
     4504                char_t buf[64];
     4505                char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
     4506                char_t* begin = integer_to_string<unsigned int>(buf, end, value, value < 0);
     4507
     4508                return strcpy_insitu(dest, header, header_mask, begin, end - begin);
     4509        }
     4510
     4511        template <typename String, typename Header>
     4512        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned int value)
     4513        {
     4514                char_t buf[64];
     4515                char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
     4516                char_t* begin = integer_to_string<unsigned int>(buf, end, value, false);
     4517
     4518                return strcpy_insitu(dest, header, header_mask, begin, end - begin);
     4519        }
     4520
     4521        template <typename String, typename Header>
     4522        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
    33604523        {
    33614524                char buf[128];
    3362                 sprintf(buf, "%d", value);
     4525                sprintf(buf, "%.9g", value);
     4526
     4527                return set_value_ascii(dest, header, header_mask, buf);
     4528        }
     4529
     4530        template <typename String, typename Header>
     4531        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
     4532        {
     4533                char buf[128];
     4534                sprintf(buf, "%.17g", value);
     4535
     4536                return set_value_ascii(dest, header, header_mask, buf);
     4537        }
    33634538       
    3364                 return set_value_buffer(dest, header, header_mask, buf);
    3365         }
    3366 
    3367         PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
    3368         {
    3369                 char buf[128];
    3370                 sprintf(buf, "%u", value);
    3371 
    3372                 return set_value_buffer(dest, header, header_mask, buf);
    3373         }
    3374 
    3375         PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
    3376         {
    3377                 char buf[128];
    3378                 sprintf(buf, "%g", value);
    3379 
    3380                 return set_value_buffer(dest, header, header_mask, buf);
    3381         }
    3382        
    3383         PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
    3384         {
    3385                 return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
     4539        template <typename String, typename Header>
     4540        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, bool value)
     4541        {
     4542                return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
     4543        }
     4544
     4545#ifdef PUGIXML_HAS_LONG_LONG
     4546        template <typename String, typename Header>
     4547        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, long long value)
     4548        {
     4549                char_t buf[64];
     4550                char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
     4551                char_t* begin = integer_to_string<unsigned long long>(buf, end, value, value < 0);
     4552
     4553                return strcpy_insitu(dest, header, header_mask, begin, end - begin);
     4554        }
     4555
     4556        template <typename String, typename Header>
     4557        PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned long long value)
     4558        {
     4559                char_t buf[64];
     4560                char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
     4561                char_t* begin = integer_to_string<unsigned long long>(buf, end, value, false);
     4562
     4563                return strcpy_insitu(dest, header, header_mask, begin, end - begin);
     4564        }
     4565#endif
     4566
     4567        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)
     4568        {
     4569                // check input buffer
     4570                if (!contents && size) return make_parse_result(status_io_error);
     4571
     4572                // get actual encoding
     4573                xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
     4574
     4575                // get private buffer
     4576                char_t* buffer = 0;
     4577                size_t length = 0;
     4578
     4579                if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
     4580
     4581                // delete original buffer if we performed a conversion
     4582                if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
     4583
     4584                // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
     4585                if (own || buffer != contents) *out_buffer = buffer;
     4586
     4587                // store buffer for offset_debug
     4588                doc->buffer = buffer;
     4589
     4590                // parse
     4591                xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);
     4592
     4593                // remember encoding
     4594                res.encoding = buffer_encoding;
     4595
     4596                return res;
    33864597        }
    33874598
     
    33964607                length_type length = _ftelli64(file);
    33974608                _fseeki64(file, 0, SEEK_SET);
    3398         #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)
     4609        #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))
    33994610                // there are 64-bit versions of fseek/ftell, let's use them
    34004611                typedef off64_t length_type;
     
    34264637        }
    34274638
    3428         PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
     4639        // This function assumes that buffer has extra sizeof(char_t) writable bytes after size
     4640        PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding)
     4641        {
     4642                // We only need to zero-terminate if encoding conversion does not do it for us
     4643        #ifdef PUGIXML_WCHAR_MODE
     4644                xml_encoding wchar_encoding = get_wchar_encoding();
     4645
     4646                if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))
     4647                {
     4648                        size_t length = size / sizeof(char_t);
     4649
     4650                        static_cast<char_t*>(buffer)[length] = 0;
     4651                        return (length + 1) * sizeof(char_t);
     4652                }
     4653        #else
     4654                if (encoding == encoding_utf8)
     4655                {
     4656                        static_cast<char*>(buffer)[size] = 0;
     4657                        return size + 1;
     4658                }
     4659        #endif
     4660
     4661                return size;
     4662        }
     4663
     4664        PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer)
    34294665        {
    34304666                if (!file) return make_parse_result(status_file_not_found);
     
    34334669                size_t size = 0;
    34344670                xml_parse_status size_status = get_file_size(file, size);
    3435 
    3436                 if (size_status != status_ok)
    3437                 {
    3438                         fclose(file);
    3439                         return make_parse_result(size_status);
    3440                 }
     4671                if (size_status != status_ok) return make_parse_result(size_status);
    34414672               
     4673                size_t max_suffix_size = sizeof(char_t);
     4674
    34424675                // allocate buffer for the whole file
    3443                 char* contents = static_cast<char*>(xml_memory::allocate(size > 0 ? size : 1));
    3444 
    3445                 if (!contents)
    3446                 {
    3447                         fclose(file);
    3448                         return make_parse_result(status_out_of_memory);
    3449                 }
     4676                char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
     4677                if (!contents) return make_parse_result(status_out_of_memory);
    34504678
    34514679                // read file in memory
    34524680                size_t read_size = fread(contents, 1, size, file);
    3453                 fclose(file);
    34544681
    34554682                if (read_size != size)
     
    34584685                        return make_parse_result(status_io_error);
    34594686                }
    3460                
    3461                 return doc.load_buffer_inplace_own(contents, size, options, encoding);
     4687
     4688                xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
     4689
     4690                return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);
    34624691        }
    34634692
     
    34684697                {
    34694698                        void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
     4699                        if (!memory) return 0;
    34704700                       
    34714701                        return new (memory) xml_stream_chunk();
    34724702                }
    34734703
    3474                 static void destroy(void* ptr)
    3475                 {
    3476                         xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);
    3477 
     4704                static void destroy(xml_stream_chunk* chunk)
     4705                {
    34784706                        // free chunk chain
    34794707                        while (chunk)
    34804708                        {
    3481                                 xml_stream_chunk* next = chunk->next;
     4709                                xml_stream_chunk* next_ = chunk->next;
     4710
    34824711                                xml_memory::deallocate(chunk);
    3483                                 chunk = next;
     4712
     4713                                chunk = next_;
    34844714                        }
    34854715                }
     
    34974727        template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
    34984728        {
    3499                 buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
     4729                auto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);
    35004730
    35014731                // read file to a chunk list
     
    35254755                }
    35264756
     4757                size_t max_suffix_size = sizeof(char_t);
     4758
    35274759                // copy chunk list to a contiguous buffer
    3528                 char* buffer = static_cast<char*>(xml_memory::allocate(total));
     4760                char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));
    35294761                if (!buffer) return status_out_of_memory;
    35304762
    35314763                char* write = buffer;
    35324764
    3533                 for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)
     4765                for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)
    35344766                {
    35354767                        assert(write + chunk->size <= buffer + total);
     
    35624794                if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
    35634795
     4796                size_t max_suffix_size = sizeof(char_t);
     4797
    35644798                // read stream data into memory (guard against stream exceptions with buffer holder)
    3565                 buffer_holder buffer(xml_memory::allocate((read_length > 0 ? read_length : 1) * sizeof(T)), xml_memory::deallocate);
     4799                auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
    35664800                if (!buffer.data) return status_out_of_memory;
    35674801
     
    35744808                size_t actual_length = static_cast<size_t>(stream.gcount());
    35754809                assert(actual_length <= read_length);
    3576 
     4810               
    35774811                *out_buffer = buffer.release();
    35784812                *out_size = actual_length * sizeof(T);
     
    35814815        }
    35824816
    3583         template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)
     4817        template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer)
    35844818        {
    35854819                void* buffer = 0;
    35864820                size_t size = 0;
     4821                xml_parse_status status = status_ok;
     4822
     4823                // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)
     4824                if (stream.fail()) return make_parse_result(status_io_error);
    35874825
    35884826                // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
    3589                 xml_parse_status status = (stream.tellg() < 0) ? load_stream_data_noseek(stream, &buffer, &size) : load_stream_data_seek(stream, &buffer, &size);
     4827                if (stream.tellg() < 0)
     4828                {
     4829                        stream.clear(); // clear error flags that could be set by a failing tellg
     4830                        status = load_stream_data_noseek(stream, &buffer, &size);
     4831                }
     4832                else
     4833                        status = load_stream_data_seek(stream, &buffer, &size);
     4834
    35904835                if (status != status_ok) return make_parse_result(status);
    35914836
    3592                 return doc.load_buffer_inplace_own(buffer, size, options, encoding);
     4837                xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);
     4838               
     4839                return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer);
    35934840        }
    35944841#endif
    35954842
    3596 #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
     4843#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
    35974844        PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
    35984845        {
     
    36054852
    36064853                // first pass: get length in utf8 characters
    3607                 size_t length = wcslen(str);
     4854                size_t length = strlength_wide(str);
    36084855                size_t size = as_utf8_begin(str, length);
    36094856
     
    36154862                as_utf8_end(result, size, str, length);
    36164863
     4864                // zero-terminate
     4865                result[size] = 0;
     4866
    36174867                return result;
    36184868        }
     
    36454895                doc.save(writer, indent, flags, encoding);
    36464896
    3647                 int result = ferror(file);
    3648 
    3649                 fclose(file);
    3650 
    3651                 return result == 0;
    3652         }
     4897                return ferror(file) == 0;
     4898        }
     4899
     4900        struct name_null_sentry
     4901        {
     4902                xml_node_struct* node;
     4903                char_t* name;
     4904
     4905                name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name)
     4906                {
     4907                        node->name = 0;
     4908                }
     4909
     4910                ~name_null_sentry()
     4911                {
     4912                        node->name = name;
     4913                }
     4914        };
    36534915PUGI__NS_END
    36544916
     
    37785040        PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
    37795041        {
    3780                 return (_attr && _attr->value) ? _attr->value : def;
     5042                return (_attr && _attr->value) ? _attr->value + 0 : def;
    37815043        }
    37825044
    37835045        PUGI__FN int xml_attribute::as_int(int def) const
    37845046        {
    3785                 return impl::get_value_int(_attr ? _attr->value : 0, def);
     5047                return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
    37865048        }
    37875049
    37885050        PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
    37895051        {
    3790                 return impl::get_value_uint(_attr ? _attr->value : 0, def);
     5052                return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
    37915053        }
    37925054
    37935055        PUGI__FN double xml_attribute::as_double(double def) const
    37945056        {
    3795                 return impl::get_value_double(_attr ? _attr->value : 0, def);
     5057                return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
    37965058        }
    37975059
    37985060        PUGI__FN float xml_attribute::as_float(float def) const
    37995061        {
    3800                 return impl::get_value_float(_attr ? _attr->value : 0, def);
     5062                return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
    38015063        }
    38025064
    38035065        PUGI__FN bool xml_attribute::as_bool(bool def) const
    38045066        {
    3805                 return impl::get_value_bool(_attr ? _attr->value : 0, def);
    3806         }
     5067                return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
     5068        }
     5069
     5070#ifdef PUGIXML_HAS_LONG_LONG
     5071        PUGI__FN long long xml_attribute::as_llong(long long def) const
     5072        {
     5073                return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
     5074        }
     5075
     5076        PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
     5077        {
     5078                return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
     5079        }
     5080#endif
    38075081
    38085082        PUGI__FN bool xml_attribute::empty() const
     
    38135087        PUGI__FN const char_t* xml_attribute::name() const
    38145088        {
    3815                 return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");
     5089                return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
    38165090        }
    38175091
    38185092        PUGI__FN const char_t* xml_attribute::value() const
    38195093        {
    3820                 return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
     5094                return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
    38215095        }
    38225096
     
    38555129        }
    38565130       
    3857         PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
     5131        PUGI__FN xml_attribute& xml_attribute::operator=(float rhs)
    38585132        {
    38595133                set_value(rhs);
     
    38615135        }
    38625136
     5137        PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
     5138        {
     5139                set_value(rhs);
     5140                return *this;
     5141        }
     5142
     5143#ifdef PUGIXML_HAS_LONG_LONG
     5144        PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs)
     5145        {
     5146                set_value(rhs);
     5147                return *this;
     5148        }
     5149
     5150        PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)
     5151        {
     5152                set_value(rhs);
     5153                return *this;
     5154        }
     5155#endif
     5156
    38635157        PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
    38645158        {
    38655159                if (!_attr) return false;
    38665160               
    3867                 return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs);
     5161                return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
    38685162        }
    38695163               
     
    38725166                if (!_attr) return false;
    38735167
    3874                 return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
     5168                return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
    38755169        }
    38765170
     
    38965190        }
    38975191       
     5192        PUGI__FN bool xml_attribute::set_value(float rhs)
     5193        {
     5194                if (!_attr) return false;
     5195
     5196                return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
     5197        }
     5198
    38985199        PUGI__FN bool xml_attribute::set_value(bool rhs)
    38995200        {
     
    39025203                return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
    39035204        }
     5205
     5206#ifdef PUGIXML_HAS_LONG_LONG
     5207        PUGI__FN bool xml_attribute::set_value(long long rhs)
     5208        {
     5209                if (!_attr) return false;
     5210
     5211                return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
     5212        }
     5213
     5214        PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
     5215        {
     5216                if (!_attr) return false;
     5217
     5218                return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
     5219        }
     5220#endif
    39045221
    39055222#ifdef __BORLANDC__
     
    39395256        PUGI__FN xml_node::iterator xml_node::begin() const
    39405257        {
    3941                 return iterator(_root ? _root->first_child : 0, _root);
     5258                return iterator(_root ? _root->first_child + 0 : 0, _root);
    39425259        }
    39435260
     
    39495266        PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
    39505267        {
    3951                 return attribute_iterator(_root ? _root->first_attribute : 0, _root);
     5268                return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
    39525269        }
    39535270
     
    39645281        PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
    39655282        {
    3966                 return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_), name_), xml_named_node_iterator());
     5283                return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
    39675284        }
    39685285
     
    40095326        PUGI__FN const char_t* xml_node::name() const
    40105327        {
    4011                 return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");
     5328                return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
    40125329        }
    40135330
    40145331        PUGI__FN xml_node_type xml_node::type() const
    40155332        {
    4016                 return _root ? static_cast<xml_node_type>((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null;
     5333                return _root ? PUGI__NODETYPE(_root) : node_null;
    40175334        }
    40185335       
    40195336        PUGI__FN const char_t* xml_node::value() const
    40205337        {
    4021                 return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");
     5338                return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
    40225339        }
    40235340       
     
    40555372        PUGI__FN xml_node xml_node::next_sibling() const
    40565373        {
    4057                 if (!_root) return xml_node();
    4058                
    4059                 if (_root->next_sibling) return xml_node(_root->next_sibling);
    4060                 else return xml_node();
     5374                return _root ? xml_node(_root->next_sibling) : xml_node();
    40615375        }
    40625376
     
    40715385        }
    40725386
     5387        PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
     5388        {
     5389                xml_attribute_struct* hint = hint_._attr;
     5390
     5391                // if hint is not an attribute of node, behavior is not defined
     5392                assert(!hint || (_root && impl::is_attribute_of(hint, _root)));
     5393
     5394                if (!_root) return xml_attribute();
     5395
     5396                // optimistically search from hint up until the end
     5397                for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
     5398                        if (i->name && impl::strequal(name_, i->name))
     5399                        {
     5400                                // update hint to maximize efficiency of searching for consecutive attributes
     5401                                hint_._attr = i->next_attribute;
     5402
     5403                                return xml_attribute(i);
     5404                        }
     5405
     5406                // wrap around and search from the first attribute until the hint
     5407                // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
     5408                for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
     5409                        if (j->name && impl::strequal(name_, j->name))
     5410                        {
     5411                                // update hint to maximize efficiency of searching for consecutive attributes
     5412                                hint_._attr = j->next_attribute;
     5413
     5414                                return xml_attribute(j);
     5415                        }
     5416
     5417                return xml_attribute();
     5418        }
     5419
    40735420        PUGI__FN xml_node xml_node::previous_sibling() const
    40745421        {
     
    40865433        PUGI__FN xml_node xml_node::root() const
    40875434        {
    4088                 if (!_root) return xml_node();
    4089 
    4090                 impl::xml_memory_page* page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
    4091 
    4092                 return xml_node(static_cast<impl::xml_document_struct*>(page->allocator));
     5435                return _root ? xml_node(&impl::get_document(_root)) : xml_node();
    40935436        }
    40945437
     
    41035446               
    41045447                for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
    4105                         if (i->value && impl::is_text_node(i))
     5448                        if (impl::is_text_node(i) && i->value)
    41065449                                return i->value;
    41075450
     
    41365479        PUGI__FN bool xml_node::set_name(const char_t* rhs)
    41375480        {
    4138                 switch (type())
    4139                 {
    4140                 case node_pi:
    4141                 case node_declaration:
    4142                 case node_element:
    4143                         return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
    4144 
    4145                 default:
     5481                xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
     5482
     5483                if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
    41465484                        return false;
    4147                 }
     5485
     5486                return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
    41485487        }
    41495488               
    41505489        PUGI__FN bool xml_node::set_value(const char_t* rhs)
    41515490        {
    4152                 switch (type())
    4153                 {
    4154                 case node_pi:
    4155                 case node_cdata:
    4156                 case node_pcdata:
    4157                 case node_comment:
    4158                 case node_doctype:
    4159                         return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
    4160 
    4161                 default:
     5491                xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
     5492
     5493                if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
    41625494                        return false;
    4163                 }
     5495
     5496                return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
    41645497        }
    41655498
    41665499        PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
    41675500        {
    4168                 if (type() != node_element && type() != node_declaration) return xml_attribute();
     5501                if (!impl::allow_insert_attribute(type())) return xml_attribute();
    41695502               
    4170                 xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root)));
     5503                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5504                if (!alloc.reserve()) return xml_attribute();
     5505
     5506                xml_attribute a(impl::allocate_attribute(alloc));
     5507                if (!a) return xml_attribute();
     5508
     5509                impl::append_attribute(a._attr, _root);
     5510
    41715511                a.set_name(name_);
    41725512               
     
    41765516        PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
    41775517        {
    4178                 if (type() != node_element && type() != node_declaration) return xml_attribute();
     5518                if (!impl::allow_insert_attribute(type())) return xml_attribute();
    41795519               
    4180                 xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
     5520                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5521                if (!alloc.reserve()) return xml_attribute();
     5522
     5523                xml_attribute a(impl::allocate_attribute(alloc));
    41815524                if (!a) return xml_attribute();
    41825525
     5526                impl::prepend_attribute(a._attr, _root);
     5527
    41835528                a.set_name(name_);
     5529
     5530                return a;
     5531        }
     5532
     5533        PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
     5534        {
     5535                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5536                if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
    41845537               
    4185                 xml_attribute_struct* head = _root->first_attribute;
    4186 
    4187                 if (head)
    4188                 {
    4189                         a._attr->prev_attribute_c = head->prev_attribute_c;
    4190                         head->prev_attribute_c = a._attr;
    4191                 }
    4192                 else
    4193                         a._attr->prev_attribute_c = a._attr;
     5538                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5539                if (!alloc.reserve()) return xml_attribute();
     5540
     5541                xml_attribute a(impl::allocate_attribute(alloc));
     5542                if (!a) return xml_attribute();
     5543
     5544                impl::insert_attribute_after(a._attr, attr._attr, _root);
     5545
     5546                a.set_name(name_);
     5547
     5548                return a;
     5549        }
     5550
     5551        PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
     5552        {
     5553                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5554                if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
    41945555               
    4195                 a._attr->next_attribute = head;
    4196                 _root->first_attribute = a._attr;
    4197                                
     5556                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5557                if (!alloc.reserve()) return xml_attribute();
     5558
     5559                xml_attribute a(impl::allocate_attribute(alloc));
     5560                if (!a) return xml_attribute();
     5561
     5562                impl::insert_attribute_before(a._attr, attr._attr, _root);
     5563
     5564                a.set_name(name_);
     5565
    41985566                return a;
    41995567        }
    42005568
    4201         PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
    4202         {
    4203                 if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
     5569        PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
     5570        {
     5571                if (!proto) return xml_attribute();
     5572                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5573
     5574                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5575                if (!alloc.reserve()) return xml_attribute();
     5576
     5577                xml_attribute a(impl::allocate_attribute(alloc));
     5578                if (!a) return xml_attribute();
     5579
     5580                impl::append_attribute(a._attr, _root);
     5581                impl::node_copy_attribute(a._attr, proto._attr);
     5582
     5583                return a;
     5584        }
     5585
     5586        PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
     5587        {
     5588                if (!proto) return xml_attribute();
     5589                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5590
     5591                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5592                if (!alloc.reserve()) return xml_attribute();
     5593
     5594                xml_attribute a(impl::allocate_attribute(alloc));
     5595                if (!a) return xml_attribute();
     5596
     5597                impl::prepend_attribute(a._attr, _root);
     5598                impl::node_copy_attribute(a._attr, proto._attr);
     5599
     5600                return a;
     5601        }
     5602
     5603        PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
     5604        {
     5605                if (!proto) return xml_attribute();
     5606                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5607                if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
     5608
     5609                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5610                if (!alloc.reserve()) return xml_attribute();
     5611
     5612                xml_attribute a(impl::allocate_attribute(alloc));
     5613                if (!a) return xml_attribute();
     5614
     5615                impl::insert_attribute_after(a._attr, attr._attr, _root);
     5616                impl::node_copy_attribute(a._attr, proto._attr);
     5617
     5618                return a;
     5619        }
     5620
     5621        PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
     5622        {
     5623                if (!proto) return xml_attribute();
     5624                if (!impl::allow_insert_attribute(type())) return xml_attribute();
     5625                if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
     5626
     5627                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5628                if (!alloc.reserve()) return xml_attribute();
     5629
     5630                xml_attribute a(impl::allocate_attribute(alloc));
     5631                if (!a) return xml_attribute();
     5632
     5633                impl::insert_attribute_before(a._attr, attr._attr, _root);
     5634                impl::node_copy_attribute(a._attr, proto._attr);
     5635
     5636                return a;
     5637        }
     5638
     5639        PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
     5640        {
     5641                if (!impl::allow_insert_child(type(), type_)) return xml_node();
    42045642               
    4205                 // check that attribute belongs to *this
    4206                 xml_attribute_struct* cur = attr._attr;
    4207 
    4208                 while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
    4209 
    4210                 if (cur != _root->first_attribute) return xml_attribute();
    4211 
    4212                 xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
    4213                 if (!a) return xml_attribute();
    4214 
    4215                 a.set_name(name_);
    4216 
    4217                 if (attr._attr->prev_attribute_c->next_attribute)
    4218                         attr._attr->prev_attribute_c->next_attribute = a._attr;
    4219                 else
    4220                         _root->first_attribute = a._attr;
     5643                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5644                if (!alloc.reserve()) return xml_node();
     5645
     5646                xml_node n(impl::allocate_node(alloc, type_));
     5647                if (!n) return xml_node();
     5648
     5649                impl::append_node(n._root, _root);
     5650
     5651                if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
     5652
     5653                return n;
     5654        }
     5655
     5656        PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
     5657        {
     5658                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5659
     5660                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5661                if (!alloc.reserve()) return xml_node();
    42215662               
    4222                 a._attr->prev_attribute_c = attr._attr->prev_attribute_c;
    4223                 a._attr->next_attribute = attr._attr;
    4224                 attr._attr->prev_attribute_c = a._attr;
    4225                                
    4226                 return a;
    4227         }
    4228 
    4229         PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
    4230         {
    4231                 if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
    4232                
    4233                 // check that attribute belongs to *this
    4234                 xml_attribute_struct* cur = attr._attr;
    4235 
    4236                 while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
    4237 
    4238                 if (cur != _root->first_attribute) return xml_attribute();
    4239 
    4240                 xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
    4241                 if (!a) return xml_attribute();
    4242 
    4243                 a.set_name(name_);
    4244 
    4245                 if (attr._attr->next_attribute)
    4246                         attr._attr->next_attribute->prev_attribute_c = a._attr;
    4247                 else
    4248                         _root->first_attribute->prev_attribute_c = a._attr;
    4249                
    4250                 a._attr->next_attribute = attr._attr->next_attribute;
    4251                 a._attr->prev_attribute_c = attr._attr;
    4252                 attr._attr->next_attribute = a._attr;
    4253 
    4254                 return a;
    4255         }
    4256 
    4257         PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
    4258         {
    4259                 if (!proto) return xml_attribute();
    4260 
    4261                 xml_attribute result = append_attribute(proto.name());
    4262                 result.set_value(proto.value());
    4263 
    4264                 return result;
    4265         }
    4266 
    4267         PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
    4268         {
    4269                 if (!proto) return xml_attribute();
    4270 
    4271                 xml_attribute result = prepend_attribute(proto.name());
    4272                 result.set_value(proto.value());
    4273 
    4274                 return result;
    4275         }
    4276 
    4277         PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
    4278         {
    4279                 if (!proto) return xml_attribute();
    4280 
    4281                 xml_attribute result = insert_attribute_after(proto.name(), attr);
    4282                 result.set_value(proto.value());
    4283 
    4284                 return result;
    4285         }
    4286 
    4287         PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
    4288         {
    4289                 if (!proto) return xml_attribute();
    4290 
    4291                 xml_attribute result = insert_attribute_before(proto.name(), attr);
    4292                 result.set_value(proto.value());
    4293 
    4294                 return result;
    4295         }
    4296 
    4297         PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
    4298         {
    4299                 if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
    4300                
    4301                 xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_));
    4302 
    4303                 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
    4304 
    4305                 return n;
    4306         }
    4307 
    4308         PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
    4309         {
    4310                 if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
    4311                
    4312                 xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
     5663                xml_node n(impl::allocate_node(alloc, type_));
    43135664                if (!n) return xml_node();
    43145665
    4315                 n._root->parent = _root;
    4316 
    4317                 xml_node_struct* head = _root->first_child;
    4318 
    4319                 if (head)
    4320                 {
    4321                         n._root->prev_sibling_c = head->prev_sibling_c;
    4322                         head->prev_sibling_c = n._root;
    4323                 }
    4324                 else
    4325                         n._root->prev_sibling_c = n._root;
    4326                
    4327                 n._root->next_sibling = head;
    4328                 _root->first_child = n._root;
     5666                impl::prepend_node(n._root, _root);
    43295667                               
    43305668                if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
     
    43355673        PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
    43365674        {
    4337                 if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
     5675                if (!impl::allow_insert_child(type(), type_)) return xml_node();
    43385676                if (!node._root || node._root->parent != _root) return xml_node();
     5677
     5678                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5679                if (!alloc.reserve()) return xml_node();
    43395680       
    4340                 xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
     5681                xml_node n(impl::allocate_node(alloc, type_));
    43415682                if (!n) return xml_node();
    43425683
    4343                 n._root->parent = _root;
     5684                impl::insert_node_before(n._root, node._root);
     5685
     5686                if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
     5687
     5688                return n;
     5689        }
     5690
     5691        PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
     5692        {
     5693                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5694                if (!node._root || node._root->parent != _root) return xml_node();
     5695
     5696                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5697                if (!alloc.reserve()) return xml_node();
     5698       
     5699                xml_node n(impl::allocate_node(alloc, type_));
     5700                if (!n) return xml_node();
     5701
     5702                impl::insert_node_after(n._root, node._root);
     5703
     5704                if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
     5705
     5706                return n;
     5707        }
     5708
     5709        PUGI__FN xml_node xml_node::append_child(const char_t* name_)
     5710        {
     5711                xml_node result = append_child(node_element);
     5712
     5713                result.set_name(name_);
     5714
     5715                return result;
     5716        }
     5717
     5718        PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
     5719        {
     5720                xml_node result = prepend_child(node_element);
     5721
     5722                result.set_name(name_);
     5723
     5724                return result;
     5725        }
     5726
     5727        PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
     5728        {
     5729                xml_node result = insert_child_after(node_element, node);
     5730
     5731                result.set_name(name_);
     5732
     5733                return result;
     5734        }
     5735
     5736        PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
     5737        {
     5738                xml_node result = insert_child_before(node_element, node);
     5739
     5740                result.set_name(name_);
     5741
     5742                return result;
     5743        }
     5744
     5745        PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
     5746        {
     5747                xml_node_type type_ = proto.type();
     5748                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5749
     5750                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5751                if (!alloc.reserve()) return xml_node();
     5752
     5753                xml_node n(impl::allocate_node(alloc, type_));
     5754                if (!n) return xml_node();
     5755
     5756                impl::append_node(n._root, _root);
     5757                impl::node_copy_tree(n._root, proto._root);
     5758
     5759                return n;
     5760        }
     5761
     5762        PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
     5763        {
     5764                xml_node_type type_ = proto.type();
     5765                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5766
     5767                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5768                if (!alloc.reserve()) return xml_node();
     5769
     5770                xml_node n(impl::allocate_node(alloc, type_));
     5771                if (!n) return xml_node();
     5772
     5773                impl::prepend_node(n._root, _root);
     5774                impl::node_copy_tree(n._root, proto._root);
     5775
     5776                return n;
     5777        }
     5778
     5779        PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
     5780        {
     5781                xml_node_type type_ = proto.type();
     5782                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5783                if (!node._root || node._root->parent != _root) return xml_node();
     5784
     5785                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5786                if (!alloc.reserve()) return xml_node();
     5787
     5788                xml_node n(impl::allocate_node(alloc, type_));
     5789                if (!n) return xml_node();
     5790
     5791                impl::insert_node_after(n._root, node._root);
     5792                impl::node_copy_tree(n._root, proto._root);
     5793
     5794                return n;
     5795        }
     5796
     5797        PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
     5798        {
     5799                xml_node_type type_ = proto.type();
     5800                if (!impl::allow_insert_child(type(), type_)) return xml_node();
     5801                if (!node._root || node._root->parent != _root) return xml_node();
     5802
     5803                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5804                if (!alloc.reserve()) return xml_node();
     5805
     5806                xml_node n(impl::allocate_node(alloc, type_));
     5807                if (!n) return xml_node();
     5808
     5809                impl::insert_node_before(n._root, node._root);
     5810                impl::node_copy_tree(n._root, proto._root);
     5811
     5812                return n;
     5813        }
     5814
     5815        PUGI__FN xml_node xml_node::append_move(const xml_node& moved)
     5816        {
     5817                if (!impl::allow_move(*this, moved)) return xml_node();
     5818
     5819                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5820                if (!alloc.reserve()) return xml_node();
     5821
     5822                // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
     5823                impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
     5824
     5825                impl::remove_node(moved._root);
     5826                impl::append_node(moved._root, _root);
     5827
     5828                return moved;
     5829        }
     5830
     5831        PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved)
     5832        {
     5833                if (!impl::allow_move(*this, moved)) return xml_node();
     5834
     5835                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5836                if (!alloc.reserve()) return xml_node();
     5837
     5838                // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
     5839                impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
     5840
     5841                impl::remove_node(moved._root);
     5842                impl::prepend_node(moved._root, _root);
     5843
     5844                return moved;
     5845        }
     5846
     5847        PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node)
     5848        {
     5849                if (!impl::allow_move(*this, moved)) return xml_node();
     5850                if (!node._root || node._root->parent != _root) return xml_node();
     5851                if (moved._root == node._root) return xml_node();
     5852
     5853                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5854                if (!alloc.reserve()) return xml_node();
     5855
     5856                // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
     5857                impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
     5858
     5859                impl::remove_node(moved._root);
     5860                impl::insert_node_after(moved._root, node._root);
     5861
     5862                return moved;
     5863        }
     5864
     5865        PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node)
     5866        {
     5867                if (!impl::allow_move(*this, moved)) return xml_node();
     5868                if (!node._root || node._root->parent != _root) return xml_node();
     5869                if (moved._root == node._root) return xml_node();
     5870
     5871                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5872                if (!alloc.reserve()) return xml_node();
     5873
     5874                // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
     5875                impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
     5876
     5877                impl::remove_node(moved._root);
     5878                impl::insert_node_before(moved._root, node._root);
     5879
     5880                return moved;
     5881        }
     5882
     5883        PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
     5884        {
     5885                return remove_attribute(attribute(name_));
     5886        }
     5887
     5888        PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
     5889        {
     5890                if (!_root || !a._attr) return false;
     5891                if (!impl::is_attribute_of(a._attr, _root)) return false;
     5892
     5893                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5894                if (!alloc.reserve()) return false;
     5895
     5896                impl::remove_attribute(a._attr, _root);
     5897                impl::destroy_attribute(a._attr, alloc);
     5898
     5899                return true;
     5900        }
     5901
     5902        PUGI__FN bool xml_node::remove_child(const char_t* name_)
     5903        {
     5904                return remove_child(child(name_));
     5905        }
     5906
     5907        PUGI__FN bool xml_node::remove_child(const xml_node& n)
     5908        {
     5909                if (!_root || !n._root || n._root->parent != _root) return false;
     5910
     5911                impl::xml_allocator& alloc = impl::get_allocator(_root);
     5912                if (!alloc.reserve()) return false;
     5913
     5914                impl::remove_node(n._root);
     5915                impl::destroy_node(n._root, alloc);
     5916
     5917                return true;
     5918        }
     5919
     5920        PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
     5921        {
     5922                // append_buffer is only valid for elements/documents
     5923                if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);
     5924
     5925                // get document node
     5926                impl::xml_document_struct* doc = &impl::get_document(_root);
     5927
     5928                // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense
     5929                doc->header |= impl::xml_memory_page_contents_shared_mask;
    43445930               
    4345                 if (node._root->prev_sibling_c->next_sibling)
    4346                         node._root->prev_sibling_c->next_sibling = n._root;
    4347                 else
    4348                         _root->first_child = n._root;
    4349                
    4350                 n._root->prev_sibling_c = node._root->prev_sibling_c;
    4351                 n._root->next_sibling = node._root;
    4352                 node._root->prev_sibling_c = n._root;
    4353 
    4354                 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
    4355 
    4356                 return n;
    4357         }
    4358 
    4359         PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
    4360         {
    4361                 if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
    4362                 if (!node._root || node._root->parent != _root) return xml_node();
    4363        
    4364                 xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
    4365                 if (!n) return xml_node();
    4366 
    4367                 n._root->parent = _root;
    4368        
    4369                 if (node._root->next_sibling)
    4370                         node._root->next_sibling->prev_sibling_c = n._root;
    4371                 else
    4372                         _root->first_child->prev_sibling_c = n._root;
    4373                
    4374                 n._root->next_sibling = node._root->next_sibling;
    4375                 n._root->prev_sibling_c = node._root;
    4376                 node._root->next_sibling = n._root;
    4377 
    4378                 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
    4379 
    4380                 return n;
    4381         }
    4382 
    4383         PUGI__FN xml_node xml_node::append_child(const char_t* name_)
    4384         {
    4385                 xml_node result = append_child(node_element);
    4386 
    4387                 result.set_name(name_);
    4388 
    4389                 return result;
    4390         }
    4391 
    4392         PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
    4393         {
    4394                 xml_node result = prepend_child(node_element);
    4395 
    4396                 result.set_name(name_);
    4397 
    4398                 return result;
    4399         }
    4400 
    4401         PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
    4402         {
    4403                 xml_node result = insert_child_after(node_element, node);
    4404 
    4405                 result.set_name(name_);
    4406 
    4407                 return result;
    4408         }
    4409 
    4410         PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
    4411         {
    4412                 xml_node result = insert_child_before(node_element, node);
    4413 
    4414                 result.set_name(name_);
    4415 
    4416                 return result;
    4417         }
    4418 
    4419         PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
    4420         {
    4421                 xml_node result = append_child(proto.type());
    4422 
    4423                 if (result) impl::recursive_copy_skip(result, proto, result);
    4424 
    4425                 return result;
    4426         }
    4427 
    4428         PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
    4429         {
    4430                 xml_node result = prepend_child(proto.type());
    4431 
    4432                 if (result) impl::recursive_copy_skip(result, proto, result);
    4433 
    4434                 return result;
    4435         }
    4436 
    4437         PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
    4438         {
    4439                 xml_node result = insert_child_after(proto.type(), node);
    4440 
    4441                 if (result) impl::recursive_copy_skip(result, proto, result);
    4442 
    4443                 return result;
    4444         }
    4445 
    4446         PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
    4447         {
    4448                 xml_node result = insert_child_before(proto.type(), node);
    4449 
    4450                 if (result) impl::recursive_copy_skip(result, proto, result);
    4451 
    4452                 return result;
    4453         }
    4454 
    4455         PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
    4456         {
    4457                 return remove_attribute(attribute(name_));
    4458         }
    4459 
    4460         PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
    4461         {
    4462                 if (!_root || !a._attr) return false;
    4463 
    4464                 // check that attribute belongs to *this
    4465                 xml_attribute_struct* attr = a._attr;
    4466 
    4467                 while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c;
    4468 
    4469                 if (attr != _root->first_attribute) return false;
    4470 
    4471                 if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c;
    4472                 else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c;
    4473                
    4474                 if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute;
    4475                 else _root->first_attribute = a._attr->next_attribute;
    4476 
    4477                 impl::destroy_attribute(a._attr, impl::get_allocator(_root));
    4478 
    4479                 return true;
    4480         }
    4481 
    4482         PUGI__FN bool xml_node::remove_child(const char_t* name_)
    4483         {
    4484                 return remove_child(child(name_));
    4485         }
    4486 
    4487         PUGI__FN bool xml_node::remove_child(const xml_node& n)
    4488         {
    4489                 if (!_root || !n._root || n._root->parent != _root) return false;
    4490 
    4491                 if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c;
    4492                 else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c;
    4493                
    4494                 if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling;
    4495                 else _root->first_child = n._root->next_sibling;
    4496                
    4497                 impl::destroy_node(n._root, impl::get_allocator(_root));
    4498 
    4499                 return true;
     5931                // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)
     5932                impl::xml_memory_page* page = 0;
     5933                impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));
     5934                (void)page;
     5935
     5936                if (!extra) return impl::make_parse_result(status_out_of_memory);
     5937
     5938                // add extra buffer to the list
     5939                extra->buffer = 0;
     5940                extra->next = doc->extra_buffers;
     5941                doc->extra_buffers = extra;
     5942
     5943                // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
     5944                impl::name_null_sentry sentry(_root);
     5945
     5946                return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
    45005947        }
    45015948
     
    45085955                        {
    45095956                                for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
    4510                                         if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
     5957                                        if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
    45115958                                                return xml_node(i);
    45125959                        }
     
    45215968                for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
    45225969                        for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
    4523                                 if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
     5970                                if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
    45245971                                        return xml_node(i);
    45255972
     
    45305977        PUGI__FN string_t xml_node::path(char_t delimiter) const
    45315978        {
    4532                 xml_node cursor = *this; // Make a copy.
    4533                
    4534                 string_t result = cursor.name();
    4535 
    4536                 while (cursor.parent())
    4537                 {
    4538                         cursor = cursor.parent();
    4539                        
    4540                         string_t temp = cursor.name();
    4541                         temp += delimiter;
    4542                         temp += result;
    4543                         result.swap(temp);
    4544                 }
     5979                if (!_root) return string_t();
     5980
     5981                size_t offset = 0;
     5982
     5983                for (xml_node_struct* i = _root; i; i = i->parent)
     5984                {
     5985                        offset += (i != _root);
     5986                        offset += i->name ? impl::strlength(i->name) : 0;
     5987                }
     5988
     5989                string_t result;
     5990                result.resize(offset);
     5991
     5992                for (xml_node_struct* j = _root; j; j = j->parent)
     5993                {
     5994                        if (j != _root)
     5995                                result[--offset] = delimiter;
     5996
     5997                        if (j->name && *j->name)
     5998                        {
     5999                                size_t length = impl::strlength(j->name);
     6000
     6001                                offset -= length;
     6002                                memcpy(&result[offset], j->name, length * sizeof(char_t));
     6003                        }
     6004                }
     6005
     6006                assert(offset == 0);
    45456007
    45466008                return result;
     
    46596121                impl::xml_buffered_writer buffered_writer(writer, encoding);
    46606122
    4661                 impl::node_output(buffered_writer, *this, indent, flags, depth);
     6123                impl::node_output(buffered_writer, _root, indent, flags, depth);
     6124
     6125                buffered_writer.flush();
    46626126        }
    46636127
     
    46806144        PUGI__FN ptrdiff_t xml_node::offset_debug() const
    46816145        {
    4682                 xml_node_struct* r = root()._root;
    4683 
    4684                 if (!r) return -1;
    4685 
    4686                 const char_t* buffer = static_cast<impl::xml_document_struct*>(r)->buffer;
    4687 
    4688                 if (!buffer) return -1;
     6146                if (!_root) return -1;
     6147
     6148                impl::xml_document_struct& doc = impl::get_document(_root);
     6149
     6150                // we can determine the offset reliably only if there is exactly once parse buffer
     6151                if (!doc.buffer || doc.extra_buffers) return -1;
    46896152
    46906153                switch (type())
     
    46966159                case node_declaration:
    46976160                case node_pi:
    4698                         return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;
     6161                        return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1;
    46996162
    47006163                case node_pcdata:
     
    47026165                case node_comment:
    47036166                case node_doctype:
    4704                         return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;
     6167                        return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
    47056168
    47066169                default:
     
    47716234                xml_node_struct* d = _data();
    47726235
    4773                 return (d && d->value) ? d->value : PUGIXML_TEXT("");
     6236                return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
    47746237        }
    47756238
     
    47786241                xml_node_struct* d = _data();
    47796242
    4780                 return (d && d->value) ? d->value : def;
     6243                return (d && d->value) ? d->value + 0 : def;
    47816244        }
    47826245
     
    47856248                xml_node_struct* d = _data();
    47866249
    4787                 return impl::get_value_int(d ? d->value : 0, def);
     6250                return (d && d->value) ? impl::get_value_int(d->value) : def;
    47886251        }
    47896252
     
    47926255                xml_node_struct* d = _data();
    47936256
    4794                 return impl::get_value_uint(d ? d->value : 0, def);
     6257                return (d && d->value) ? impl::get_value_uint(d->value) : def;
    47956258        }
    47966259
     
    47996262                xml_node_struct* d = _data();
    48006263
    4801                 return impl::get_value_double(d ? d->value : 0, def);
     6264                return (d && d->value) ? impl::get_value_double(d->value) : def;
    48026265        }
    48036266
     
    48066269                xml_node_struct* d = _data();
    48076270
    4808                 return impl::get_value_float(d ? d->value : 0, def);
     6271                return (d && d->value) ? impl::get_value_float(d->value) : def;
    48096272        }
    48106273
     
    48136276                xml_node_struct* d = _data();
    48146277
    4815                 return impl::get_value_bool(d ? d->value : 0, def);
    4816         }
     6278                return (d && d->value) ? impl::get_value_bool(d->value) : def;
     6279        }
     6280
     6281#ifdef PUGIXML_HAS_LONG_LONG
     6282        PUGI__FN long long xml_text::as_llong(long long def) const
     6283        {
     6284                xml_node_struct* d = _data();
     6285
     6286                return (d && d->value) ? impl::get_value_llong(d->value) : def;
     6287        }
     6288
     6289        PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
     6290        {
     6291                xml_node_struct* d = _data();
     6292
     6293                return (d && d->value) ? impl::get_value_ullong(d->value) : def;
     6294        }
     6295#endif
    48176296
    48186297        PUGI__FN bool xml_text::set(const char_t* rhs)
     
    48206299                xml_node_struct* dn = _data_new();
    48216300
    4822                 return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
     6301                return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;
    48236302        }
    48246303
     
    48376316        }
    48386317
     6318        PUGI__FN bool xml_text::set(float rhs)
     6319        {
     6320                xml_node_struct* dn = _data_new();
     6321
     6322                return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
     6323        }
     6324
    48396325        PUGI__FN bool xml_text::set(double rhs)
    48406326        {
     
    48506336                return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
    48516337        }
     6338
     6339#ifdef PUGIXML_HAS_LONG_LONG
     6340        PUGI__FN bool xml_text::set(long long rhs)
     6341        {
     6342                xml_node_struct* dn = _data_new();
     6343
     6344                return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
     6345        }
     6346
     6347        PUGI__FN bool xml_text::set(unsigned long long rhs)
     6348        {
     6349                xml_node_struct* dn = _data_new();
     6350
     6351                return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
     6352        }
     6353#endif
    48526354
    48536355        PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)
     
    48756377        }
    48766378
    4877         PUGI__FN xml_text& xml_text::operator=(bool rhs)
     6379        PUGI__FN xml_text& xml_text::operator=(float rhs)
    48786380        {
    48796381                set(rhs);
    48806382                return *this;
    48816383        }
     6384
     6385        PUGI__FN xml_text& xml_text::operator=(bool rhs)
     6386        {
     6387                set(rhs);
     6388                return *this;
     6389        }
     6390
     6391#ifdef PUGIXML_HAS_LONG_LONG
     6392        PUGI__FN xml_text& xml_text::operator=(long long rhs)
     6393        {
     6394                set(rhs);
     6395                return *this;
     6396        }
     6397
     6398        PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)
     6399        {
     6400                set(rhs);
     6401                return *this;
     6402        }
     6403#endif
    48826404
    48836405        PUGI__FN xml_node xml_text::data() const
     
    50246546        }
    50256547
    5026         PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _node(node), _name(name)
     6548        PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)
     6549        {
     6550        }
     6551
     6552        PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name)
    50276553        {
    50286554        }
     
    50306556        PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
    50316557        {
    5032                 return _node == rhs._node;
     6558                return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
    50336559        }
    50346560
    50356561        PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const
    50366562        {
    5037                 return _node != rhs._node;
     6563                return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
    50386564        }
    50396565
    50406566        PUGI__FN xml_node& xml_named_node_iterator::operator*() const
    50416567        {
    5042                 assert(_node._root);
    5043                 return _node;
     6568                assert(_wrap._root);
     6569                return _wrap;
    50446570        }
    50456571
    50466572        PUGI__FN xml_node* xml_named_node_iterator::operator->() const
    50476573        {
    5048                 assert(_node._root);
    5049                 return const_cast<xml_node*>(&_node); // BCC32 workaround
     6574                assert(_wrap._root);
     6575                return const_cast<xml_node*>(&_wrap); // BCC32 workaround
    50506576        }
    50516577
    50526578        PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
    50536579        {
    5054                 assert(_node._root);
    5055                 _node = _node.next_sibling(_name);
     6580                assert(_wrap._root);
     6581                _wrap = _wrap.next_sibling(_name);
    50566582                return *this;
    50576583        }
     
    50616587                xml_named_node_iterator temp = *this;
    50626588                ++*this;
     6589                return temp;
     6590        }
     6591
     6592        PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--()
     6593        {
     6594                if (_wrap._root)
     6595                        _wrap = _wrap.previous_sibling(_name);
     6596                else
     6597                {
     6598                        _wrap = _parent.last_child();
     6599
     6600                        if (!impl::strequal(_wrap.name(), _name))
     6601                                _wrap = _wrap.previous_sibling(_name);
     6602                }
     6603
     6604                return *this;
     6605        }
     6606
     6607        PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int)
     6608        {
     6609                xml_named_node_iterator temp = *this;
     6610                --*this;
    50636611                return temp;
    50646612        }
     
    50966644                case status_end_element_mismatch: return "Start-end tags mismatch";
    50976645
     6646                case status_append_invalid_root: return "Unable to append nodes: root is not an element or document";
     6647
     6648                case status_no_document_element: return "No document element found";
     6649
    50986650                default: return "Unknown error";
    50996651                }
     
    51266678        PUGI__FN void xml_document::create()
    51276679        {
     6680                assert(!_root);
     6681
     6682        #ifdef PUGIXML_COMPACT
     6683                const size_t page_offset = sizeof(uint32_t);
     6684        #else
     6685                const size_t page_offset = 0;
     6686        #endif
     6687
    51286688                // initialize sentinel page
    5129                 PUGI__STATIC_ASSERT(offsetof(impl::xml_memory_page, data) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory));
     6689                PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) + page_offset <= sizeof(_memory));
    51306690
    51316691                // align upwards to page boundary
     
    51346694                // prepare page structure
    51356695                impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory);
     6696                assert(page);
    51366697
    51376698                page->busy_size = impl::xml_memory_page_size;
    51386699
     6700                // setup first page marker
     6701        #ifdef PUGIXML_COMPACT
     6702                // round-trip through void* to avoid 'cast increases required alignment of target type' warning
     6703                page->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)));
     6704                *page->compact_page_marker = sizeof(impl::xml_memory_page);
     6705        #endif
     6706
    51396707                // allocate new root
    5140                 _root = new (page->data) impl::xml_document_struct(page);
     6708                _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);
    51416709                _root->prev_sibling_c = _root;
    51426710
    51436711                // setup sentinel page
    51446712                page->allocator = static_cast<impl::xml_document_struct*>(_root);
     6713
     6714                // verify the document allocation
     6715                assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
    51456716        }
    51466717
    51476718        PUGI__FN void xml_document::destroy()
    51486719        {
     6720                assert(_root);
     6721
    51496722                // destroy static storage
    51506723                if (_buffer)
     
    51546727                }
    51556728
     6729                // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)
     6730                for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)
     6731                {
     6732                        if (extra->buffer) impl::xml_memory::deallocate(extra->buffer);
     6733                }
     6734
    51566735                // destroy dynamic storage, leave sentinel page (it's in static memory)
    5157                 if (_root)
    5158                 {
    5159                         impl::xml_memory_page* root_page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
    5160                         assert(root_page && !root_page->prev && !root_page->memory);
    5161 
    5162                         // destroy all pages
    5163                         for (impl::xml_memory_page* page = root_page->next; page; )
    5164                         {
    5165                                 impl::xml_memory_page* next = page->next;
    5166 
    5167                                 impl::xml_allocator::deallocate_page(page);
    5168 
    5169                                 page = next;
    5170                         }
    5171 
    5172                         // cleanup root page
    5173                         root_page->allocator = 0;
    5174                         root_page->next = 0;
    5175                         root_page->busy_size = root_page->freed_size = 0;
    5176 
    5177                         _root = 0;
    5178                 }
     6736                impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
     6737                assert(root_page && !root_page->prev);
     6738                assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
     6739
     6740                for (impl::xml_memory_page* page = root_page->next; page; )
     6741                {
     6742                        impl::xml_memory_page* next = page->next;
     6743
     6744                        impl::xml_allocator::deallocate_page(page);
     6745
     6746                        page = next;
     6747                }
     6748
     6749        #ifdef PUGIXML_COMPACT
     6750                // destroy hash table
     6751                static_cast<impl::xml_document_struct*>(_root)->hash.clear();
     6752        #endif
     6753
     6754                _root = 0;
    51796755        }
    51806756
     
    51846760                reset();
    51856761
    5186                 return impl::load_stream_impl(*this, stream, options, encoding);
     6762                return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);
    51876763        }
    51886764
     
    51916767                reset();
    51926768
    5193                 return impl::load_stream_impl(*this, stream, options, encoding_wchar);
     6769                return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer);
    51946770        }
    51956771#endif
    51966772
    5197         PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
     6773        PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
    51986774        {
    51996775                // Force native encoding (skip autodetection)
     
    52076783        }
    52086784
     6785        PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
     6786        {
     6787                return load_string(contents, options);
     6788        }
     6789
    52096790        PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
    52106791        {
    52116792                reset();
    52126793
    5213                 FILE* file = fopen(path_, "rb");
    5214 
    5215                 return impl::load_file_impl(*this, file, options, encoding);
     6794                using impl::auto_deleter; // MSVC7 workaround
     6795                auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, "rb"), fclose);
     6796
     6797                return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
    52166798        }
    52176799
     
    52206802                reset();
    52216803
    5222                 FILE* file = impl::open_file_wide(path_, L"rb");
    5223 
    5224                 return impl::load_file_impl(*this, file, options, encoding);
    5225         }
    5226 
    5227         PUGI__FN xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own)
     6804                using impl::auto_deleter; // MSVC7 workaround
     6805                auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, L"rb"), fclose);
     6806
     6807                return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
     6808        }
     6809
     6810        PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
    52286811        {
    52296812                reset();
    52306813
    5231                 // check input buffer
    5232                 assert(contents || size == 0);
    5233 
    5234                 // get actual encoding
    5235                 xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
    5236 
    5237                 // get private buffer
    5238                 char_t* buffer = 0;
    5239                 size_t length = 0;
    5240 
    5241                 if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
    5242                
    5243                 // delete original buffer if we performed a conversion
    5244                 if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
    5245 
    5246                 // parse
    5247                 xml_parse_result res = impl::xml_parser::parse(buffer, length, _root, options);
    5248 
    5249                 // remember encoding
    5250                 res.encoding = buffer_encoding;
    5251 
    5252                 // grab onto buffer if it's our buffer, user is responsible for deallocating contens himself
    5253                 if (own || buffer != contents) _buffer = buffer;
    5254 
    5255                 return res;
    5256         }
    5257 
    5258         PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
    5259         {
    5260                 return load_buffer_impl(const_cast<void*>(contents), size, options, encoding, false, false);
     6814                return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);
    52616815        }
    52626816
    52636817        PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
    52646818        {
    5265                 return load_buffer_impl(contents, size, options, encoding, true, false);
    5266         }
    5267                
     6819                reset();
     6820
     6821                return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);
     6822        }
     6823
    52686824        PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
    52696825        {
    5270                 return load_buffer_impl(contents, size, options, encoding, true, true);
     6826                reset();
     6827
     6828                return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);
    52716829        }
    52726830
     
    52866844                }
    52876845
    5288                 if (!(flags & format_no_declaration) && !impl::has_declaration(*this))
    5289                 {
    5290                         buffered_writer.write(PUGIXML_TEXT("<?xml version=\"1.0\""));
    5291                         if (encoding == encoding_latin1) buffered_writer.write(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
     6846                if (!(flags & format_no_declaration) && !impl::has_declaration(_root))
     6847                {
     6848                        buffered_writer.write_string(PUGIXML_TEXT("<?xml version=\"1.0\""));
     6849                        if (encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
    52926850                        buffered_writer.write('?', '>');
    52936851                        if (!(flags & format_raw)) buffered_writer.write('\n');
    52946852                }
    52956853
    5296                 impl::node_output(buffered_writer, *this, indent, flags, 0);
     6854                impl::node_output(buffered_writer, _root, indent, flags, 0);
     6855
     6856                buffered_writer.flush();
    52976857        }
    52986858
     
    53156875        PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
    53166876        {
    5317                 FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
    5318                 return impl::save_file_impl(*this, file, indent, flags, encoding);
     6877                using impl::auto_deleter; // MSVC7 workaround
     6878                auto_deleter<FILE, int(*)(FILE*)> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), fclose);
     6879
     6880                return impl::save_file_impl(*this, file.data, indent, flags, encoding);
    53196881        }
    53206882
    53216883        PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
    53226884        {
    5323                 FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
    5324                 return impl::save_file_impl(*this, file, indent, flags, encoding);
     6885                using impl::auto_deleter; // MSVC7 workaround
     6886                auto_deleter<FILE, int(*)(FILE*)> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), fclose);
     6887
     6888                return impl::save_file_impl(*this, file.data, indent, flags, encoding);
    53256889        }
    53266890
    53276891        PUGI__FN xml_node xml_document::document_element() const
    53286892        {
     6893                assert(_root);
     6894
    53296895                for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
    5330                         if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element)
     6896                        if (PUGI__NODETYPE(i) == node_element)
    53316897                                return xml_node(i);
    53326898
     
    53396905                assert(str);
    53406906
    5341                 return impl::as_utf8_impl(str, wcslen(str));
     6907                return impl::as_utf8_impl(str, impl::strlength_wide(str));
    53426908        }
    53436909
     
    53916957        }
    53926958
    5393         PUGI__FN std::forward_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
    5394         {
    5395                 return std::forward_iterator_tag();
     6959        PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
     6960        {
     6961                return std::bidirectional_iterator_tag();
    53966962        }
    53976963}
     
    54126978        }
    54136979
    5414         PUGI__FN std::forward_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
    5415         {
    5416                 return std::forward_iterator_tag();
     6980        PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
     6981        {
     6982                return std::bidirectional_iterator_tag();
    54176983        }
    54186984}
     
    54206986
    54216987#ifndef PUGIXML_NO_XPATH
    5422 
    54236988// STL replacements
    54246989PUGI__NS_BEGIN
     
    54757040        template <typename I> void reverse(I begin, I end)
    54767041        {
    5477                 while (begin + 1 < end) swap(*begin++, *--end);
     7042                while (end - begin > 1) swap(*begin++, *--end);
    54787043        }
    54797044
     
    54817046        {
    54827047                // fast skip head
    5483                 while (begin + 1 < end && *begin != *(begin + 1)) begin++;
     7048                while (end - begin > 1 && *begin != *(begin + 1)) begin++;
    54847049
    54857050                if (begin == end) return begin;
     
    56507215// Allocator used for AST and evaluation stacks
    56517216PUGI__NS_BEGIN
     7217        static const size_t xpath_memory_page_size =
     7218        #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
     7219                PUGIXML_MEMORY_XPATH_PAGE_SIZE
     7220        #else
     7221                4096
     7222        #endif
     7223                ;
     7224
     7225        static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
     7226
    56527227        struct xpath_memory_block
    56537228        {       
    56547229                xpath_memory_block* next;
    5655 
    5656                 char data[
    5657         #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
    5658                         PUGIXML_MEMORY_XPATH_PAGE_SIZE
    5659         #else
    5660                         4096
    5661         #endif
    5662                 ];
     7230                size_t capacity;
     7231
     7232                union
     7233                {
     7234                        char data[xpath_memory_page_size];
     7235                        double alignment;
     7236                };
    56637237        };
    56647238               
     
    56827256                void* allocate_nothrow(size_t size)
    56837257                {
    5684                         const size_t block_capacity = sizeof(_root->data);
    5685 
    5686                         // align size so that we're able to store pointers in subsequent blocks
    5687                         size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
    5688 
    5689                         if (_root_size + size <= block_capacity)
    5690                         {
    5691                                 void* buf = _root->data + _root_size;
     7258                        // round size up to block alignment boundary
     7259                        size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
     7260
     7261                        if (_root_size + size <= _root->capacity)
     7262                        {
     7263                                void* buf = &_root->data[0] + _root_size;
    56927264                                _root_size += size;
    56937265                                return buf;
     
    56957267                        else
    56967268                        {
    5697                                 size_t block_data_size = (size > block_capacity) ? size : block_capacity;
    5698                                 size_t block_size = block_data_size + offsetof(xpath_memory_block, data);
     7269                                // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests
     7270                                size_t block_capacity_base = sizeof(_root->data);
     7271                                size_t block_capacity_req = size + block_capacity_base / 4;
     7272                                size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req;
     7273
     7274                                size_t block_size = block_capacity + offsetof(xpath_memory_block, data);
    56997275
    57007276                                xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
     
    57027278                               
    57037279                                block->next = _root;
     7280                                block->capacity = block_capacity;
    57047281                               
    57057282                                _root = block;
     
    57297306                void* reallocate(void* ptr, size_t old_size, size_t new_size)
    57307307                {
    5731                         // align size so that we're able to store pointers in subsequent blocks
    5732                         old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
    5733                         new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
     7308                        // round size up to block alignment boundary
     7309                        old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
     7310                        new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
    57347311
    57357312                        // we can only reallocate the last object
    5736                         assert(ptr == 0 || static_cast<char*>(ptr) + old_size == _root->data + _root_size);
     7313                        assert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size);
    57377314
    57387315                        // adjust root size so that we have not allocated the object at all
     
    57497326                        {
    57507327                                // copy old data
    5751                                 assert(new_size > old_size);
     7328                                assert(new_size >= old_size);
    57527329                                memcpy(result, ptr, old_size);
    57537330
     
    58427419                {
    58437420                        blocks[0].next = blocks[1].next = 0;
     7421                        blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);
    58447422
    58457423                        stack.result = &result;
     
    58657443                const char_t* _buffer;
    58667444                bool _uses_heap;
     7445                size_t _length_heap;
    58677446
    58687447                static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
     
    58777456                }
    58787457
    5879                 static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc)
    5880                 {
    5881                         return duplicate_string(string, strlength(string), alloc);
     7458                xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap)
     7459                {
    58827460                }
    58837461
    58847462        public:
    5885                 xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
    5886                 {
    5887                 }
    5888 
    5889                 explicit xpath_string(const char_t* str, xpath_allocator* alloc)
    5890                 {
    5891                         bool empty_ = (*str == 0);
    5892 
    5893                         _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc);
    5894                         _uses_heap = !empty_;
    5895                 }
    5896 
    5897                 explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap)
    5898                 {
    5899                 }
    5900 
    5901                 xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc)
     7463                static xpath_string from_const(const char_t* str)
     7464                {
     7465                        return xpath_string(str, false, 0);
     7466                }
     7467
     7468                static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end)
     7469                {
     7470                        assert(begin <= end && *end == 0);
     7471
     7472                        return xpath_string(begin, true, static_cast<size_t>(end - begin));
     7473                }
     7474
     7475                static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc)
    59027476                {
    59037477                        assert(begin <= end);
    59047478
    5905                         bool empty_ = (begin == end);
    5906 
    5907                         _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast<size_t>(end - begin), alloc);
    5908                         _uses_heap = !empty_;
     7479                        size_t length = static_cast<size_t>(end - begin);
     7480
     7481                        return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length);
     7482                }
     7483
     7484                xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0)
     7485                {
    59097486                }
    59107487
     
    59227499                        {
    59237500                                // need to make heap copy
    5924                                 size_t target_length = strlength(_buffer);
    5925                                 size_t source_length = strlength(o._buffer);
     7501                                size_t target_length = length();
     7502                                size_t source_length = o.length();
    59267503                                size_t result_length = target_length + source_length;
    59277504
     
    59407517                                _buffer = result;
    59417518                                _uses_heap = true;
     7519                                _length_heap = result_length;
    59427520                        }
    59437521                }
     
    59507528                size_t length() const
    59517529                {
    5952                         return strlength(_buffer);
     7530                        return _uses_heap ? _length_heap : strlength(_buffer);
    59537531                }
    59547532               
     
    59587536                        if (!_uses_heap)
    59597537                        {
    5960                                 _buffer = duplicate_string(_buffer, alloc);
     7538                                size_t length_ = strlength(_buffer);
     7539
     7540                                _buffer = duplicate_string(_buffer, length_, alloc);
    59617541                                _uses_heap = true;
     7542                                _length_heap = length_;
    59627543                        }
    59637544
     
    59857566                }
    59867567        };
    5987 
    5988         PUGI__FN xpath_string xpath_string_const(const char_t* str)
    5989         {
    5990                 return xpath_string(str, false);
    5991         }
    59927568PUGI__NS_END
    59937569
     
    60327608        {
    60337609                if (na.attribute())
    6034                         return xpath_string_const(na.attribute().value());
     7610                        return xpath_string::from_const(na.attribute().value());
    60357611                else
    60367612                {
    6037                         const xml_node& n = na.node();
     7613                        xml_node n = na.node();
    60387614
    60397615                        switch (n.type())
     
    60437619                        case node_comment:
    60447620                        case node_pi:
    6045                                 return xpath_string_const(n.value());
     7621                                return xpath_string::from_const(n.value());
    60467622                       
    60477623                        case node_document:
     
    60557631                                {
    60567632                                        if (cur.type() == node_pcdata || cur.type() == node_cdata)
    6057                                                 result.append(xpath_string_const(cur.value()), alloc);
     7633                                                result.append(xpath_string::from_const(cur.value()), alloc);
    60587634
    60597635                                        if (cur.first_child())
     
    60797655        }
    60807656       
    6081         PUGI__FN unsigned int node_height(xml_node n)
    6082         {
    6083                 unsigned int result = 0;
    6084                
    6085                 while (n)
    6086                 {
    6087                         ++result;
    6088                         n = n.parent();
    6089                 }
    6090                
    6091                 return result;
     7657        PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
     7658        {
     7659                assert(ln->parent == rn->parent);
     7660
     7661                // there is no common ancestor (the shared parent is null), nodes are from different documents
     7662                if (!ln->parent) return ln < rn;
     7663
     7664                // determine sibling order
     7665                xml_node_struct* ls = ln;
     7666                xml_node_struct* rs = rn;
     7667
     7668                while (ls && rs)
     7669                {
     7670                        if (ls == rn) return true;
     7671                        if (rs == ln) return false;
     7672
     7673                        ls = ls->next_sibling;
     7674                        rs = rs->next_sibling;
     7675                }
     7676
     7677                // if rn sibling chain ended ln must be before rn
     7678                return !rs;
    60927679        }
    60937680       
    6094         PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh)
    6095         {
    6096                 // normalize heights
    6097                 for (unsigned int i = rh; i < lh; i++) ln = ln.parent();
    6098                 for (unsigned int j = lh; j < rh; j++) rn = rn.parent();
    6099                
     7681        PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)
     7682        {
     7683                // find common ancestor at the same depth, if any
     7684                xml_node_struct* lp = ln;
     7685                xml_node_struct* rp = rn;
     7686
     7687                while (lp && rp && lp->parent != rp->parent)
     7688                {
     7689                        lp = lp->parent;
     7690                        rp = rp->parent;
     7691                }
     7692
     7693                // parents are the same!
     7694                if (lp && rp) return node_is_before_sibling(lp, rp);
     7695
     7696                // nodes are at different depths, need to normalize heights
     7697                bool left_higher = !lp;
     7698
     7699                while (lp)
     7700                {
     7701                        lp = lp->parent;
     7702                        ln = ln->parent;
     7703                }
     7704
     7705                while (rp)
     7706                {
     7707                        rp = rp->parent;
     7708                        rn = rn->parent;
     7709                }
     7710
    61007711                // one node is the ancestor of the other
    6101                 if (ln == rn) return lh < rh;
    6102                
    6103                 // find common ancestor
    6104                 while (ln.parent() != rn.parent())
    6105                 {
    6106                         ln = ln.parent();
    6107                         rn = rn.parent();
    6108                 }
    6109 
    6110                 // there is no common ancestor (the shared parent is null), nodes are from different documents
    6111                 if (!ln.parent()) return ln < rn;
    6112 
    6113                 // determine sibling order
    6114                 for (; ln; ln = ln.next_sibling())
    6115                         if (ln == rn)
    6116                                 return true;
    6117                                
    6118                 return false;
    6119         }
    6120 
    6121         PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node)
    6122         {
    6123                 while (node && node != parent) node = node.parent();
     7712                if (ln == rn) return left_higher;
     7713
     7714                // find common ancestor... again
     7715                while (ln->parent != rn->parent)
     7716                {
     7717                        ln = ln->parent;
     7718                        rn = rn->parent;
     7719                }
     7720
     7721                return node_is_before_sibling(ln, rn);
     7722        }
     7723
     7724        PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)
     7725        {
     7726                while (node && node != parent) node = node->parent;
    61247727
    61257728                return parent && node == parent;
    61267729        }
    61277730
    6128         PUGI__FN const void* document_order(const xpath_node& xnode)
     7731        PUGI__FN const void* document_buffer_order(const xpath_node& xnode)
    61297732        {
    61307733                xml_node_struct* node = xnode.node().internal_object();
     
    61327735                if (node)
    61337736                {
    6134                         if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name;
    6135                         if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value;
     7737                        if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0)
     7738                        {
     7739                                if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name;
     7740                                if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value;
     7741                        }
     7742
    61367743                        return 0;
    61377744                }
     
    61417748                if (attr)
    61427749                {
    6143                         if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name;
    6144                         if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value;
     7750                        if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0)
     7751                        {
     7752                                if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name;
     7753                                if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value;
     7754                        }
     7755
    61457756                        return 0;
    61467757                }
     
    61547765                {
    61557766                        // optimized document order based check
    6156                         const void* lo = document_order(lhs);
    6157                         const void* ro = document_order(rhs);
     7767                        const void* lo = document_buffer_order(lhs);
     7768                        const void* ro = document_buffer_order(rhs);
    61587769
    61597770                        if (lo && ro) return lo < ro;
     
    61967807
    61977808                        if (ln == rn) return false;
     7809
     7810                        if (!ln || !rn) return ln < rn;
    61987811                       
    6199                         unsigned int lh = node_height(ln);
    6200                         unsigned int rh = node_height(rn);
    6201                        
    6202                         return node_is_before(ln, lh, rn, rh);
     7812                        return node_is_before(ln.internal_object(), rn.internal_object());
    62037813                }
    62047814        };
     
    63347944                // try special number conversion
    63357945                const char_t* special = convert_number_to_string_special(value);
    6336                 if (special) return xpath_string_const(special);
     7946                if (special) return xpath_string::from_const(special);
    63377947
    63387948                // get mantissa + exponent form
    6339                 char mantissa_buffer[64];
     7949                char mantissa_buffer[32];
    63407950
    63417951                char* mantissa;
     
    63437953                convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent);
    63447954
     7955                // allocate a buffer of suitable length for the number
     7956                size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;
     7957                char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));
     7958                assert(result);
     7959
    63457960                // make the number!
    6346                 char_t result[512];
    63477961                char_t* s = result;
    63487962
     
    63597973                        while (exponent > 0)
    63607974                        {
    6361                                 assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
     7975                                assert(*mantissa == 0 || static_cast<unsigned int>(static_cast<unsigned int>(*mantissa) - '0') <= 9);
    63627976                                *s++ = *mantissa ? *mantissa++ : '0';
    63637977                                exponent--;
     
    63878001
    63888002                // zero-terminate
    6389                 assert(s < result + sizeof(result) / sizeof(result[0]));
     8003                assert(s < result + result_size);
    63908004                *s = 0;
    63918005
    6392                 return xpath_string(result, alloc);
     8006                return xpath_string::from_heap_preallocated(result, s);
    63938007        }
    63948008       
     
    64328046                return wcstod(string, 0);
    64338047        #else
    6434                 return atof(string);
     8048                return strtod(string, 0);
    64358049        #endif
    64368050        }
    64378051
    6438         PUGI__FN bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result)
    6439         {
    6440                 char_t buffer[32];
    6441 
     8052        PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)
     8053        {
    64428054                size_t length = static_cast<size_t>(end - begin);
    64438055                char_t* scratch = buffer;
     
    65008112                }
    65018113
    6502                 bool operator()(const xml_attribute& a) const
     8114                bool operator()(xml_attribute a) const
    65038115                {
    65048116                        const char_t* name = a.name();
     
    65108122        };
    65118123
    6512         PUGI__FN const char_t* namespace_uri(const xml_node& node)
     8124        PUGI__FN const char_t* namespace_uri(xml_node node)
    65138125        {
    65148126                namespace_uri_predicate pred = node.name();
     
    65288140        }
    65298141
    6530         PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent)
     8142        PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
    65318143        {
    65328144                namespace_uri_predicate pred = attr.name();
     
    65548166        }
    65558167
    6556         PUGI__FN void normalize_space(char_t* buffer)
     8168        PUGI__FN char_t* normalize_space(char_t* buffer)
    65578169        {
    65588170                char_t* write = buffer;
     
    65788190                // zero-terminate
    65798191                *write = 0;
    6580         }
    6581 
    6582         PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to)
    6583         {
    6584                 size_t to_length = strlength(to);
    6585 
     8192
     8193                return write;
     8194        }
     8195
     8196        PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
     8197        {
    65868198                char_t* write = buffer;
    65878199
     
    66008212                // zero-terminate
    66018213                *write = 0;
     8214
     8215                return write;
     8216        }
     8217
     8218        PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
     8219        {
     8220                unsigned char table[128] = {0};
     8221
     8222                while (*from)
     8223                {
     8224                        unsigned int fc = static_cast<unsigned int>(*from);
     8225                        unsigned int tc = static_cast<unsigned int>(*to);
     8226
     8227                        if (fc >= 128 || tc >= 128)
     8228                                return 0;
     8229
     8230                        // code=128 means "skip character"
     8231                        if (!table[fc])
     8232                                table[fc] = static_cast<unsigned char>(tc ? tc : 128);
     8233
     8234                        from++;
     8235                        if (tc) to++;
     8236                }
     8237
     8238                for (int i = 0; i < 128; ++i)
     8239                        if (!table[i])
     8240                                table[i] = static_cast<unsigned char>(i);
     8241
     8242                void* result = alloc->allocate_nothrow(sizeof(table));
     8243
     8244                if (result)
     8245                {
     8246                        memcpy(result, table, sizeof(table));
     8247                }
     8248
     8249                return static_cast<unsigned char*>(result);
     8250        }
     8251
     8252        PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
     8253        {
     8254                char_t* write = buffer;
     8255
     8256                while (*buffer)
     8257                {
     8258                        char_t ch = *buffer++;
     8259                        unsigned int index = static_cast<unsigned int>(ch);
     8260
     8261                        if (index < 128)
     8262                        {
     8263                                unsigned char code = table[index];
     8264
     8265                                // code=128 means "skip character" (table size is 128 so 128 can be a special value)
     8266                                // this code skips these characters without extra branches
     8267                                *write = static_cast<char_t>(code);
     8268                                write += 1 - (code >> 7);
     8269                        }
     8270                        else
     8271                        {
     8272                                *write++ = ch;
     8273                        }
     8274                }
     8275
     8276                // zero-terminate
     8277                *write = 0;
     8278
     8279                return write;
     8280        }
     8281
     8282        inline bool is_xpath_attribute(const char_t* name)
     8283        {
     8284                return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':'));
    66028285        }
    66038286
    66048287        struct xpath_variable_boolean: xpath_variable
    66058288        {
    6606                 xpath_variable_boolean(): value(false)
     8289                xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false)
    66078290                {
    66088291                }
     
    66148297        struct xpath_variable_number: xpath_variable
    66158298        {
    6616                 xpath_variable_number(): value(0)
     8299                xpath_variable_number(): xpath_variable(xpath_type_number), value(0)
    66178300                {
    66188301                }
     
    66248307        struct xpath_variable_string: xpath_variable
    66258308        {
    6626                 xpath_variable_string(): value(0)
     8309                xpath_variable_string(): xpath_variable(xpath_type_string), value(0)
    66278310                {
    66288311                }
     
    66398322        struct xpath_variable_node_set: xpath_variable
    66408323        {
     8324                xpath_variable_node_set(): xpath_variable(xpath_type_node_set)
     8325                {
     8326                }
     8327
    66418328                xpath_node_set value;
    66428329                char_t name[1];
     
    67328419        }
    67338420
    6734         PUGI__FN xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end)
    6735         {
    6736                 char_t buffer[32];
    6737 
     8421        PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
     8422        {
     8423                switch (rhs->type())
     8424                {
     8425                case xpath_type_node_set:
     8426                        return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);
     8427
     8428                case xpath_type_number:
     8429                        return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);
     8430
     8431                case xpath_type_string:
     8432                        return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);
     8433
     8434                case xpath_type_boolean:
     8435                        return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
     8436
     8437                default:
     8438                        assert(!"Invalid variable type");
     8439                        return false;
     8440                }
     8441        }
     8442
     8443        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)
     8444        {
    67388445                size_t length = static_cast<size_t>(end - begin);
    67398446                char_t* scratch = buffer;
     
    67438450                        // need to make dummy on-heap copy
    67448451                        scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
    6745                         if (!scratch) return 0;
     8452                        if (!scratch) return false;
    67468453                }
    67478454
     
    67508457                scratch[length] = 0;
    67518458
    6752                 xpath_variable* result = set->get(scratch);
     8459                *out_result = set->get(scratch);
    67538460
    67548461                // free dummy buffer
    67558462                if (scratch != buffer) xml_memory::deallocate(scratch);
    67568463
    6757                 return result;
     8464                return true;
    67588465        }
    67598466PUGI__NS_END
     
    67618468// Internal node set class
    67628469PUGI__NS_BEGIN
     8470        PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end)
     8471        {
     8472                if (end - begin < 2)
     8473                        return xpath_node_set::type_sorted;
     8474
     8475                document_order_comparator cmp;
     8476
     8477                bool first = cmp(begin[0], begin[1]);
     8478
     8479                for (const xpath_node* it = begin + 1; it + 1 < end; ++it)
     8480                        if (cmp(it[0], it[1]) != first)
     8481                                return xpath_node_set::type_unsorted;
     8482
     8483                return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse;
     8484        }
     8485
    67638486        PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
    67648487        {
     
    67678490                if (type == xpath_node_set::type_unsorted)
    67688491                {
    6769                         sort(begin, end, document_order_comparator());
    6770 
    6771                         type = xpath_node_set::type_sorted;
     8492                        xpath_node_set::type_t sorted = xpath_get_order(begin, end);
     8493
     8494                        if (sorted == xpath_node_set::type_unsorted)
     8495                        {
     8496                                sort(begin, end, document_order_comparator());
     8497
     8498                                type = xpath_node_set::type_sorted;
     8499                        }
     8500                        else
     8501                                type = sorted;
    67728502                }
    67738503               
     
    68368566                }
    68378567
     8568                void push_back_grow(const xpath_node& node, xpath_allocator* alloc);
     8569
    68388570                void push_back(const xpath_node& node, xpath_allocator* alloc)
    68398571                {
    6840                         if (_end == _eos)
    6841                         {
    6842                                 size_t capacity = static_cast<size_t>(_eos - _begin);
    6843 
    6844                                 // get new capacity (1.5x rule)
    6845                                 size_t new_capacity = capacity + capacity / 2 + 1;
    6846 
    6847                                 // reallocate the old array or allocate a new one
    6848                                 xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
    6849                                 assert(data);
    6850 
    6851                                 // finalize
    6852                                 _begin = data;
    6853                                 _end = data + capacity;
    6854                                 _eos = data + new_capacity;
    6855                         }
    6856 
    6857                         *_end++ = node;
     8572                        if (_end != _eos)
     8573                                *_end++ = node;
     8574                        else
     8575                                push_back_grow(node, alloc);
    68588576                }
    68598577
    68608578                void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
    68618579                {
     8580                        if (begin_ == end_) return;
     8581
    68628582                        size_t size_ = static_cast<size_t>(_end - _begin);
    68638583                        size_t capacity = static_cast<size_t>(_eos - _begin);
     
    69108630                }
    69118631        };
     8632
     8633        PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc)
     8634        {
     8635                size_t capacity = static_cast<size_t>(_eos - _begin);
     8636
     8637                // get new capacity (1.5x rule)
     8638                size_t new_capacity = capacity + capacity / 2 + 1;
     8639
     8640                // reallocate the old array or allocate a new one
     8641                xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
     8642                assert(data);
     8643
     8644                // finalize
     8645                _begin = data;
     8646                _end = data + capacity;
     8647                _eos = data + new_capacity;
     8648
     8649                // push
     8650                *_end++ = node;
     8651        }
    69128652PUGI__NS_END
    69138653
     
    72799019        enum ast_type_t
    72809020        {
     9021                ast_unknown,
    72819022                ast_op_or,                                              // left or right
    72829023                ast_op_and,                                             // left and right
     
    72969037                ast_predicate,                                  // apply predicate to set; next points to next predicate
    72979038                ast_filter,                                             // select * from left where right
    7298                 ast_filter_posinv,                              // select * from left where right; proximity position invariant
    72999039                ast_string_constant,                    // string constant
    73009040                ast_number_constant,                    // number constant
     
    73369076                ast_func_round,                                 // round(left)
    73379077                ast_step,                                               // process set left with step
    7338                 ast_step_root                                   // select root node
     9078                ast_step_root,                                  // select root node
     9079
     9080                ast_opt_translate_table,                // translate(left, right, third) where right/third are constants
     9081                ast_opt_compare_attribute               // @name = 'string'
    73399082        };
    73409083
     
    73699112        };
    73709113
     9114        enum predicate_t
     9115        {
     9116                predicate_default,
     9117                predicate_posinv,
     9118                predicate_constant,
     9119                predicate_constant_one
     9120        };
     9121
     9122        enum nodeset_eval_t
     9123        {
     9124                nodeset_eval_all,
     9125                nodeset_eval_any,
     9126                nodeset_eval_first
     9127        };
     9128
    73719129        template <axis_t N> struct axis_to_type
    73729130        {
     
    73839141                char _rettype;
    73849142
    7385                 // for ast_step / ast_predicate
     9143                // for ast_step
    73869144                char _axis;
     9145
     9146                // for ast_step/ast_predicate/ast_filter
    73879147                char _test;
    73889148
     
    74029162                        // node test for ast_step (node name/namespace/node type/pi target)
    74039163                        const char_t* nodetest;
     9164                        // table for ast_opt_translate_table
     9165                        const unsigned char* table;
    74049166                } _data;
    74059167
     
    74319193                                xpath_allocator_capture cr(stack.result);
    74329194
    7433                                 xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
    7434                                 xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
     9195                                xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
     9196                                xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
    74359197
    74369198                                for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
     
    74609222
    74619223                                        double l = lhs->eval_number(c, stack);
    7462                                         xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
     9224                                        xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
    74639225
    74649226                                        for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
     
    74779239
    74789240                                        xpath_string l = lhs->eval_string(c, stack);
    7479                                         xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
     9241                                        xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
    74809242
    74819243                                        for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
     
    74959257                }
    74969258
     9259                static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)
     9260                {
     9261                        return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any;
     9262                }
     9263
    74979264                template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
    74989265                {
     
    75059272                                xpath_allocator_capture cr(stack.result);
    75069273
    7507                                 xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
    7508                                 xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
     9274                                xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
     9275                                xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
    75099276
    75109277                                for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
     
    75309297
    75319298                                double l = lhs->eval_number(c, stack);
    7532                                 xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
     9299                                xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
    75339300
    75349301                                for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
     
    75469313                                xpath_allocator_capture cr(stack.result);
    75479314
    7548                                 xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
     9315                                xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
    75499316                                double r = rhs->eval_number(c, stack);
    75509317
     
    75669333                }
    75679334
    7568                 void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
     9335                static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
    75699336                {
    75709337                        assert(ns.size() >= first);
     9338                        assert(expr->rettype() != xpath_type_number);
    75719339
    75729340                        size_t i = 1;
    75739341                        size_t size = ns.size() - first;
    7574                                
     9342
    75759343                        xpath_node* last = ns.begin() + first;
    7576                                
     9344
    75779345                        // remove_if... or well, sort of
    75789346                        for (xpath_node* it = last; it != ns.end(); ++it, ++i)
    75799347                        {
    75809348                                xpath_context c(*it, i, size);
    7581                        
    7582                                 if (expr->rettype() == xpath_type_number)
    7583                                 {
    7584                                         if (expr->eval_number(c, stack) == i)
    7585                                                 *last++ = *it;
    7586                                 }
    7587                                 else if (expr->eval_boolean(c, stack))
     9349
     9350                                if (expr->eval_boolean(c, stack))
     9351                                {
    75889352                                        *last++ = *it;
    7589                         }
    7590                        
     9353
     9354                                        if (once) break;
     9355                                }
     9356                        }
     9357
    75919358                        ns.truncate(last);
    75929359                }
    75939360
    7594                 void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack)
     9361                static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
     9362                {
     9363                        assert(ns.size() >= first);
     9364                        assert(expr->rettype() == xpath_type_number);
     9365
     9366                        size_t i = 1;
     9367                        size_t size = ns.size() - first;
     9368
     9369                        xpath_node* last = ns.begin() + first;
     9370
     9371                        // remove_if... or well, sort of
     9372                        for (xpath_node* it = last; it != ns.end(); ++it, ++i)
     9373                        {
     9374                                xpath_context c(*it, i, size);
     9375
     9376                                if (expr->eval_number(c, stack) == i)
     9377                                {
     9378                                        *last++ = *it;
     9379
     9380                                        if (once) break;
     9381                                }
     9382                        }
     9383
     9384                        ns.truncate(last);
     9385                }
     9386
     9387                static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
     9388                {
     9389                        assert(ns.size() >= first);
     9390                        assert(expr->rettype() == xpath_type_number);
     9391
     9392                        size_t size = ns.size() - first;
     9393
     9394                        xpath_node* last = ns.begin() + first;
     9395
     9396                        xpath_context c(xpath_node(), 1, size);
     9397
     9398                        double er = expr->eval_number(c, stack);
     9399
     9400                        if (er >= 1.0 && er <= size)
     9401                        {
     9402                                size_t eri = static_cast<size_t>(er);
     9403
     9404                                if (er == eri)
     9405                                {
     9406                                        xpath_node r = last[eri - 1];
     9407
     9408                                        *last++ = r;
     9409                                }
     9410                        }
     9411
     9412                        ns.truncate(last);
     9413                }
     9414
     9415                void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once)
    75959416                {
    75969417                        if (ns.size() == first) return;
    7597                        
     9418
     9419                        assert(_type == ast_filter || _type == ast_predicate);
     9420
     9421                        if (_test == predicate_constant || _test == predicate_constant_one)
     9422                                apply_predicate_number_const(ns, first, _right, stack);
     9423                        else if (_right->rettype() == xpath_type_number)
     9424                                apply_predicate_number(ns, first, _right, stack, once);
     9425                        else
     9426                                apply_predicate_boolean(ns, first, _right, stack, once);
     9427                }
     9428
     9429                void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
     9430                {
     9431                        if (ns.size() == first) return;
     9432
     9433                        bool last_once = eval_once(ns.type(), eval);
     9434
    75989435                        for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
    7599                         {
    7600                                 apply_predicate(ns, first, pred->_left, stack);
    7601                         }
    7602                 }
    7603 
    7604                 void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc)
    7605                 {
    7606                         if (!a) return;
    7607 
    7608                         const char_t* name = a.name();
    7609 
    7610                         // There are no attribute nodes corresponding to attributes that declare namespaces
    7611                         // That is, "xmlns:..." or "xmlns"
    7612                         if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;
    7613                        
     9436                                pred->apply_predicate(ns, first, stack, !pred->_next && last_once);
     9437                }
     9438
     9439                bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)
     9440                {
     9441                        assert(a);
     9442
     9443                        const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT("");
     9444
    76149445                        switch (_test)
    76159446                        {
    76169447                        case nodetest_name:
    7617                                 if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc);
     9448                                if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
     9449                                {
     9450                                        ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
     9451                                        return true;
     9452                                }
    76189453                                break;
    76199454                               
    76209455                        case nodetest_type_node:
    76219456                        case nodetest_all:
    7622                                 ns.push_back(xpath_node(a, parent), alloc);
     9457                                if (is_xpath_attribute(name))
     9458                                {
     9459                                        ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
     9460                                        return true;
     9461                                }
    76239462                                break;
    76249463                               
    76259464                        case nodetest_all_in_namespace:
    7626                                 if (starts_with(name, _data.nodetest))
    7627                                         ns.push_back(xpath_node(a, parent), alloc);
     9465                                if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
     9466                                {
     9467                                        ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
     9468                                        return true;
     9469                                }
    76289470                                break;
    76299471                       
     
    76319473                                ;
    76329474                        }
     9475
     9476                        return false;
    76339477                }
    76349478               
    7635                 void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc)
    7636                 {
    7637                         if (!n) return;
     9479                bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc)
     9480                {
     9481                        assert(n);
     9482
     9483                        xml_node_type type = PUGI__NODETYPE(n);
    76389484
    76399485                        switch (_test)
    76409486                        {
    76419487                        case nodetest_name:
    7642                                 if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc);
     9488                                if (type == node_element && n->name && strequal(n->name, _data.nodetest))
     9489                                {
     9490                                        ns.push_back(xml_node(n), alloc);
     9491                                        return true;
     9492                                }
    76439493                                break;
    76449494                               
    76459495                        case nodetest_type_node:
    7646                                 ns.push_back(n, alloc);
    7647                                 break;
     9496                                ns.push_back(xml_node(n), alloc);
     9497                                return true;
    76489498                               
    76499499                        case nodetest_type_comment:
    7650                                 if (n.type() == node_comment)
    7651                                         ns.push_back(n, alloc);
     9500                                if (type == node_comment)
     9501                                {
     9502                                        ns.push_back(xml_node(n), alloc);
     9503                                        return true;
     9504                                }
    76529505                                break;
    76539506                               
    76549507                        case nodetest_type_text:
    7655                                 if (n.type() == node_pcdata || n.type() == node_cdata)
    7656                                         ns.push_back(n, alloc);
     9508                                if (type == node_pcdata || type == node_cdata)
     9509                                {
     9510                                        ns.push_back(xml_node(n), alloc);
     9511                                        return true;
     9512                                }
    76579513                                break;
    76589514                               
    76599515                        case nodetest_type_pi:
    7660                                 if (n.type() == node_pi)
    7661                                         ns.push_back(n, alloc);
     9516                                if (type == node_pi)
     9517                                {
     9518                                        ns.push_back(xml_node(n), alloc);
     9519                                        return true;
     9520                                }
    76629521                                break;
    76639522                                                                       
    76649523                        case nodetest_pi:
    7665                                 if (n.type() == node_pi && strequal(n.name(), _data.nodetest))
    7666                                         ns.push_back(n, alloc);
     9524                                if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
     9525                                {
     9526                                        ns.push_back(xml_node(n), alloc);
     9527                                        return true;
     9528                                }
    76679529                                break;
    76689530                               
    76699531                        case nodetest_all:
    7670                                 if (n.type() == node_element)
    7671                                         ns.push_back(n, alloc);
     9532                                if (type == node_element)
     9533                                {
     9534                                        ns.push_back(xml_node(n), alloc);
     9535                                        return true;
     9536                                }
    76729537                                break;
    76739538                               
    76749539                        case nodetest_all_in_namespace:
    7675                                 if (n.type() == node_element && starts_with(n.name(), _data.nodetest))
    7676                                         ns.push_back(n, alloc);
     9540                                if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
     9541                                {
     9542                                        ns.push_back(xml_node(n), alloc);
     9543                                        return true;
     9544                                }
    76779545                                break;
    76789546
    76799547                        default:
    76809548                                assert(!"Unknown axis");
    7681                         }
    7682                 }
    7683 
    7684                 template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T)
     9549                        }
     9550
     9551                        return false;
     9552                }
     9553
     9554                template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)
    76859555                {
    76869556                        const axis_t axis = T::axis;
     
    76909560                        case axis_attribute:
    76919561                        {
    7692                                 for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
    7693                                         step_push(ns, a, n, alloc);
     9562                                for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)
     9563                                        if (step_push(ns, a, n, alloc) & once)
     9564                                                return;
    76949565                               
    76959566                                break;
     
    76989569                        case axis_child:
    76999570                        {
    7700                                 for (xml_node c = n.first_child(); c; c = c.next_sibling())
    7701                                         step_push(ns, c, alloc);
     9571                                for (xml_node_struct* c = n->first_child; c; c = c->next_sibling)
     9572                                        if (step_push(ns, c, alloc) & once)
     9573                                                return;
    77029574                                       
    77039575                                break;
     
    77089580                        {
    77099581                                if (axis == axis_descendant_or_self)
    7710                                         step_push(ns, n, alloc);
     9582                                        if (step_push(ns, n, alloc) & once)
     9583                                                return;
    77119584                                       
    7712                                 xml_node cur = n.first_child();
     9585                                xml_node_struct* cur = n->first_child;
    77139586                               
    7714                                 while (cur && cur != n)
    7715                                 {
    7716                                         step_push(ns, cur, alloc);
     9587                                while (cur)
     9588                                {
     9589                                        if (step_push(ns, cur, alloc) & once)
     9590                                                return;
    77179591                                       
    7718                                         if (cur.first_child())
    7719                                                 cur = cur.first_child();
    7720                                         else if (cur.next_sibling())
    7721                                                 cur = cur.next_sibling();
     9592                                        if (cur->first_child)
     9593                                                cur = cur->first_child;
    77229594                                        else
    77239595                                        {
    7724                                                 while (!cur.next_sibling() && cur != n)
    7725                                                         cur = cur.parent();
     9596                                                while (!cur->next_sibling)
     9597                                                {
     9598                                                        cur = cur->parent;
     9599
     9600                                                        if (cur == n) return;
     9601                                                }
    77269602                                       
    7727                                                 if (cur != n) cur = cur.next_sibling();
     9603                                                cur = cur->next_sibling;
    77289604                                        }
    77299605                                }
     
    77349610                        case axis_following_sibling:
    77359611                        {
    7736                                 for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
    7737                                         step_push(ns, c, alloc);
     9612                                for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)
     9613                                        if (step_push(ns, c, alloc) & once)
     9614                                                return;
    77389615                               
    77399616                                break;
     
    77429619                        case axis_preceding_sibling:
    77439620                        {
    7744                                 for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
    7745                                         step_push(ns, c, alloc);
     9621                                for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)
     9622                                        if (step_push(ns, c, alloc) & once)
     9623                                                return;
    77469624                               
    77479625                                break;
     
    77509628                        case axis_following:
    77519629                        {
    7752                                 xml_node cur = n;
     9630                                xml_node_struct* cur = n;
    77539631
    77549632                                // exit from this node so that we don't include descendants
    7755                                 while (cur && !cur.next_sibling()) cur = cur.parent();
    7756                                 cur = cur.next_sibling();
    7757 
    7758                                 for (;;)
    7759                                 {
    7760                                         step_push(ns, cur, alloc);
    7761 
    7762                                         if (cur.first_child())
    7763                                                 cur = cur.first_child();
    7764                                         else if (cur.next_sibling())
    7765                                                 cur = cur.next_sibling();
     9633                                while (!cur->next_sibling)
     9634                                {
     9635                                        cur = cur->parent;
     9636
     9637                                        if (!cur) return;
     9638                                }
     9639
     9640                                cur = cur->next_sibling;
     9641
     9642                                while (cur)
     9643                                {
     9644                                        if (step_push(ns, cur, alloc) & once)
     9645                                                return;
     9646
     9647                                        if (cur->first_child)
     9648                                                cur = cur->first_child;
    77669649                                        else
    77679650                                        {
    7768                                                 while (cur && !cur.next_sibling()) cur = cur.parent();
    7769                                                 cur = cur.next_sibling();
    7770 
    7771                                                 if (!cur) break;
     9651                                                while (!cur->next_sibling)
     9652                                                {
     9653                                                        cur = cur->parent;
     9654
     9655                                                        if (!cur) return;
     9656                                                }
     9657
     9658                                                cur = cur->next_sibling;
    77729659                                        }
    77739660                                }
     
    77789665                        case axis_preceding:
    77799666                        {
    7780                                 xml_node cur = n;
    7781 
    7782                                 while (cur && !cur.previous_sibling()) cur = cur.parent();
    7783                                 cur = cur.previous_sibling();
    7784 
    7785                                 for (;;)
    7786                                 {
    7787                                         if (cur.last_child())
    7788                                                 cur = cur.last_child();
     9667                                xml_node_struct* cur = n;
     9668
     9669                                // exit from this node so that we don't include descendants
     9670                                while (!cur->prev_sibling_c->next_sibling)
     9671                                {
     9672                                        cur = cur->parent;
     9673
     9674                                        if (!cur) return;
     9675                                }
     9676
     9677                                cur = cur->prev_sibling_c;
     9678
     9679                                while (cur)
     9680                                {
     9681                                        if (cur->first_child)
     9682                                                cur = cur->first_child->prev_sibling_c;
    77899683                                        else
    77909684                                        {
    77919685                                                // leaf node, can't be ancestor
    7792                                                 step_push(ns, cur, alloc);
    7793 
    7794                                                 if (cur.previous_sibling())
    7795                                                         cur = cur.previous_sibling();
    7796                                                 else
     9686                                                if (step_push(ns, cur, alloc) & once)
     9687                                                        return;
     9688
     9689                                                while (!cur->prev_sibling_c->next_sibling)
    77979690                                                {
    7798                                                         do
    7799                                                         {
    7800                                                                 cur = cur.parent();
    7801                                                                 if (!cur) break;
    7802 
    7803                                                                 if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);
    7804                                                         }
    7805                                                         while (!cur.previous_sibling());
    7806 
    7807                                                         cur = cur.previous_sibling();
    7808 
    7809                                                         if (!cur) break;
     9691                                                        cur = cur->parent;
     9692
     9693                                                        if (!cur) return;
     9694
     9695                                                        if (!node_is_ancestor(cur, n))
     9696                                                                if (step_push(ns, cur, alloc) & once)
     9697                                                                        return;
    78109698                                                }
     9699
     9700                                                cur = cur->prev_sibling_c;
    78119701                                        }
    78129702                                }
     
    78199709                        {
    78209710                                if (axis == axis_ancestor_or_self)
    7821                                         step_push(ns, n, alloc);
    7822 
    7823                                 xml_node cur = n.parent();
     9711                                        if (step_push(ns, n, alloc) & once)
     9712                                                return;
     9713
     9714                                xml_node_struct* cur = n->parent;
    78249715                               
    78259716                                while (cur)
    78269717                                {
    7827                                         step_push(ns, cur, alloc);
     9718                                        if (step_push(ns, cur, alloc) & once)
     9719                                                return;
    78289720                                       
    7829                                         cur = cur.parent();
     9721                                        cur = cur->parent;
    78309722                                }
    78319723                               
     
    78429734                        case axis_parent:
    78439735                        {
    7844                                 if (n.parent()) step_push(ns, n.parent(), alloc);
     9736                                if (n->parent)
     9737                                        step_push(ns, n->parent, alloc);
    78459738
    78469739                                break;
     
    78529745                }
    78539746               
    7854                 template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v)
     9747                template <class T> void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v)
    78559748                {
    78569749                        const axis_t axis = T::axis;
     
    78629755                        {
    78639756                                if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
    7864                                         step_push(ns, a, p, alloc);
    7865 
    7866                                 xml_node cur = p;
     9757                                        if (step_push(ns, a, p, alloc) & once)
     9758                                                return;
     9759
     9760                                xml_node_struct* cur = p;
    78679761                               
    78689762                                while (cur)
    78699763                                {
    7870                                         step_push(ns, cur, alloc);
     9764                                        if (step_push(ns, cur, alloc) & once)
     9765                                                return;
    78719766                                       
    7872                                         cur = cur.parent();
     9767                                        cur = cur->parent;
    78739768                                }
    78749769                               
     
    78879782                        case axis_following:
    78889783                        {
    7889                                 xml_node cur = p;
     9784                                xml_node_struct* cur = p;
    78909785                               
    7891                                 for (;;)
    7892                                 {
    7893                                         if (cur.first_child())
    7894                                                 cur = cur.first_child();
    7895                                         else if (cur.next_sibling())
    7896                                                 cur = cur.next_sibling();
     9786                                while (cur)
     9787                                {
     9788                                        if (cur->first_child)
     9789                                                cur = cur->first_child;
    78979790                                        else
    78989791                                        {
    7899                                                 while (cur && !cur.next_sibling()) cur = cur.parent();
    7900                                                 cur = cur.next_sibling();
    7901                                                
    7902                                                 if (!cur) break;
     9792                                                while (!cur->next_sibling)
     9793                                                {
     9794                                                        cur = cur->parent;
     9795
     9796                                                        if (!cur) return;
     9797                                                }
     9798
     9799                                                cur = cur->next_sibling;
    79039800                                        }
    79049801
    7905                                         step_push(ns, cur, alloc);
     9802                                        if (step_push(ns, cur, alloc) & once)
     9803                                                return;
    79069804                                }
    79079805
     
    79199817                        {
    79209818                                // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
    7921                                 step_fill(ns, p, alloc, v);
     9819                                step_fill(ns, p, alloc, once, v);
    79229820                                break;
    79239821                        }
     
    79279825                        }
    79289826                }
    7929                
    7930                 template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)
     9827
     9828                template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)
    79319829                {
    79329830                        const axis_t axis = T::axis;
    7933                         bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
     9831                        const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
     9832
     9833                        if (xn.node())
     9834                                step_fill(ns, xn.node().internal_object(), alloc, once, v);
     9835                        else if (axis_has_attributes && xn.attribute() && xn.parent())
     9836                                step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);
     9837                }
     9838
     9839                template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
     9840                {
     9841                        const axis_t axis = T::axis;
     9842                        const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
     9843                        const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
     9844
     9845                        bool once =
     9846                                (axis == axis_attribute && _test == nodetest_name) ||
     9847                                (!_right && eval_once(axis_type, eval)) ||
     9848                                (_right && !_right->_next && _right->_test == predicate_constant_one);
    79349849
    79359850                        xpath_node_set_raw ns;
    7936                         ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
     9851                        ns.set_type(axis_type);
    79379852
    79389853                        if (_left)
    79399854                        {
    7940                                 xpath_node_set_raw s = _left->eval_node_set(c, stack);
     9855                                xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all);
    79419856
    79429857                                // self axis preserves the original order
     
    79509865                                        if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
    79519866                                       
    7952                                         if (it->node())
    7953                                                 step_fill(ns, it->node(), stack.result, v);
    7954                                         else if (attributes)
    7955                                                 step_fill(ns, it->attribute(), it->parent(), stack.result, v);
    7956                                                
    7957                                         apply_predicates(ns, size, stack);
     9867                                        step_fill(ns, *it, stack.result, once, v);
     9868                                        if (_right) apply_predicates(ns, size, stack, eval);
    79589869                                }
    79599870                        }
    79609871                        else
    79619872                        {
    7962                                 if (c.n.node())
    7963                                         step_fill(ns, c.n.node(), stack.result, v);
    7964                                 else if (attributes)
    7965                                         step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);
    7966                                
    7967                                 apply_predicates(ns, 0, stack);
     9873                                step_fill(ns, c.n, stack.result, once, v);
     9874                                if (_right) apply_predicates(ns, 0, stack, eval);
    79689875                        }
    79699876
     
    80069913                        _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
    80079914                {
     9915                        assert(type == ast_step);
    80089916                        _data.nodetest = contents;
     9917                }
     9918
     9919                xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):
     9920                        _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
     9921                {
     9922                        assert(type == ast_filter || type == ast_predicate);
    80099923                }
    80109924
     
    810910023                        }
    811010024
     10025                        case ast_opt_compare_attribute:
     10026                        {
     10027                                const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();
     10028
     10029                                xml_attribute attr = c.n.node().attribute(_left->_data.nodetest);
     10030
     10031                                return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());
     10032                        }
     10033
    811110034                        case ast_variable:
    811210035                        {
     
    813710060                                        xpath_allocator_capture cr(stack.result);
    813810061
    8139                                         return !eval_node_set(c, stack).empty();
     10062                                        return !eval_node_set(c, stack, nodeset_eval_any).empty();
    814010063                                }
    814110064
     
    818310106                                xpath_allocator_capture cr(stack.result);
    818410107
    8185                                 return static_cast<double>(_left->eval_node_set(c, stack).size());
     10108                                return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());
    818610109                        }
    818710110                       
     
    821610139                                double r = 0;
    821710140                               
    8218                                 xpath_node_set_raw ns = _left->eval_node_set(c, stack);
     10141                                xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all);
    821910142                               
    822010143                                for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
     
    833110254                        *ri = 0;
    833210255
    8333                         return xpath_string(result, true);
     10256                        return xpath_string::from_heap_preallocated(result, ri);
    833410257                }
    833510258
     
    833910262                        {
    834010263                        case ast_string_constant:
    8341                                 return xpath_string_const(_data.string);
     10264                                return xpath_string::from_const(_data.string);
    834210265                       
    834310266                        case ast_func_local_name_0:
     
    834510268                                xpath_node na = c.n;
    834610269                               
    8347                                 return xpath_string_const(local_name(na));
     10270                                return xpath_string::from_const(local_name(na));
    834810271                        }
    834910272
     
    835210275                                xpath_allocator_capture cr(stack.result);
    835310276
    8354                                 xpath_node_set_raw ns = _left->eval_node_set(c, stack);
     10277                                xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
    835510278                                xpath_node na = ns.first();
    835610279                               
    8357                                 return xpath_string_const(local_name(na));
     10280                                return xpath_string::from_const(local_name(na));
    835810281                        }
    835910282
     
    836210285                                xpath_node na = c.n;
    836310286                               
    8364                                 return xpath_string_const(qualified_name(na));
     10287                                return xpath_string::from_const(qualified_name(na));
    836510288                        }
    836610289
     
    836910292                                xpath_allocator_capture cr(stack.result);
    837010293
    8371                                 xpath_node_set_raw ns = _left->eval_node_set(c, stack);
     10294                                xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
    837210295                                xpath_node na = ns.first();
    837310296                               
    8374                                 return xpath_string_const(qualified_name(na));
     10297                                return xpath_string::from_const(qualified_name(na));
    837510298                        }
    837610299
     
    837910302                                xpath_node na = c.n;
    838010303                               
    8381                                 return xpath_string_const(namespace_uri(na));
     10304                                return xpath_string::from_const(namespace_uri(na));
    838210305                        }
    838310306
     
    838610309                                xpath_allocator_capture cr(stack.result);
    838710310
    8388                                 xpath_node_set_raw ns = _left->eval_node_set(c, stack);
     10311                                xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
    838910312                                xpath_node na = ns.first();
    839010313                               
    8391                                 return xpath_string_const(namespace_uri(na));
     10314                                return xpath_string::from_const(namespace_uri(na));
    839210315                        }
    839310316
     
    841210335                                const char_t* pos = find_substring(s.c_str(), p.c_str());
    841310336                               
    8414                                 return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string();
     10337                                return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string();
    841510338                        }
    841610339                       
     
    842710350                                if (!pos) return xpath_string();
    842810351
    8429                                 const char_t* result = pos + p.length();
    8430 
    8431                                 return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result);
     10352                                const char_t* rbegin = pos + p.length();
     10353                                const char_t* rend = s.c_str() + s.length();
     10354
     10355                                return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
    843210356                        }
    843310357
     
    845010374
    845110375                                const char_t* rbegin = s.c_str() + (pos - 1);
     10376                                const char_t* rend = s.c_str() + s.length();
    845210377                               
    8453                                 return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin);
     10378                                return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
    845410379                        }
    845510380                       
     
    847810403                                const char_t* rend = s.c_str() + (end - 1);
    847910404
    8480                                 return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result);
     10405                                return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result);
    848110406                        }
    848210407
     
    848510410                                xpath_string s = string_value(c.n, stack.result);
    848610411
    8487                                 normalize_space(s.data(stack.result));
    8488 
    8489                                 return s;
     10412                                char_t* begin = s.data(stack.result);
     10413                                char_t* end = normalize_space(begin);
     10414
     10415                                return xpath_string::from_heap_preallocated(begin, end);
    849010416                        }
    849110417
     
    849410420                                xpath_string s = _left->eval_string(c, stack);
    849510421
    8496                                 normalize_space(s.data(stack.result));
     10422                                char_t* begin = s.data(stack.result);
     10423                                char_t* end = normalize_space(begin);
    849710424                       
    8498                                 return s;
     10425                                return xpath_string::from_heap_preallocated(begin, end);
    849910426                        }
    850010427
     
    850910436                                xpath_string to = _right->_next->eval_string(c, swapped_stack);
    851010437
    8511                                 translate(s.data(stack.result), from.c_str(), to.c_str());
    8512 
    8513                                 return s;
     10438                                char_t* begin = s.data(stack.result);
     10439                                char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
     10440
     10441                                return xpath_string::from_heap_preallocated(begin, end);
     10442                        }
     10443
     10444                        case ast_opt_translate_table:
     10445                        {
     10446                                xpath_string s = _left->eval_string(c, stack);
     10447
     10448                                char_t* begin = s.data(stack.result);
     10449                                char_t* end = translate_table(begin, _data.table);
     10450
     10451                                return xpath_string::from_heap_preallocated(begin, end);
    851410452                        }
    851510453
     
    851910457
    852010458                                if (_rettype == xpath_type_string)
    8521                                         return xpath_string_const(_data.variable->get_string());
     10459                                        return xpath_string::from_const(_data.variable->get_string());
    852210460
    852310461                                // fallthrough to type conversion
     
    852910467                                {
    853010468                                case xpath_type_boolean:
    8531                                         return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
     10469                                        return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
    853210470                                       
    853310471                                case xpath_type_number:
     
    854010478                                        xpath_stack swapped_stack = {stack.temp, stack.result};
    854110479
    8542                                         xpath_node_set_raw ns = eval_node_set(c, swapped_stack);
     10480                                        xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);
    854310481                                        return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
    854410482                                }
     
    855210490                }
    855310491
    8554                 xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)
     10492                xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval)
    855510493                {
    855610494                        switch (_type)
     
    856210500                                xpath_stack swapped_stack = {stack.temp, stack.result};
    856310501
    8564                                 xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack);
    8565                                 xpath_node_set_raw rs = _right->eval_node_set(c, stack);
    8566                                
     10502                                xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
     10503                                xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
     10504
    856710505                                // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
    856810506                                rs.set_type(xpath_node_set::type_unsorted);
     
    857010508                                rs.append(ls.begin(), ls.end(), stack.result);
    857110509                                rs.remove_duplicates();
    8572                                
     10510
    857310511                                return rs;
    857410512                        }
    857510513
    857610514                        case ast_filter:
    8577                         case ast_filter_posinv:
    8578                         {
    8579                                 xpath_node_set_raw set = _left->eval_node_set(c, stack);
     10515                        {
     10516                                xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);
    858010517
    858110518                                // either expression is a number or it contains position() call; sort by document order
    8582                                 if (_type == ast_filter) set.sort_do();
    8583 
    8584                                 apply_predicate(set, 0, _right, stack);
     10519                                if (_test != predicate_posinv) set.sort_do();
     10520
     10521                                bool once = eval_once(set.type(), eval);
     10522
     10523                                apply_predicate(set, 0, stack, once);
    858510524                       
    858610525                                return set;
     
    859510534                                {
    859610535                                case axis_ancestor:
    8597                                         return step_do(c, stack, axis_to_type<axis_ancestor>());
     10536                                        return step_do(c, stack, eval, axis_to_type<axis_ancestor>());
    859810537                                       
    859910538                                case axis_ancestor_or_self:
    8600                                         return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());
     10539                                        return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());
    860110540
    860210541                                case axis_attribute:
    8603                                         return step_do(c, stack, axis_to_type<axis_attribute>());
     10542                                        return step_do(c, stack, eval, axis_to_type<axis_attribute>());
    860410543
    860510544                                case axis_child:
    8606                                         return step_do(c, stack, axis_to_type<axis_child>());
     10545                                        return step_do(c, stack, eval, axis_to_type<axis_child>());
    860710546                               
    860810547                                case axis_descendant:
    8609                                         return step_do(c, stack, axis_to_type<axis_descendant>());
     10548                                        return step_do(c, stack, eval, axis_to_type<axis_descendant>());
    861010549
    861110550                                case axis_descendant_or_self:
    8612                                         return step_do(c, stack, axis_to_type<axis_descendant_or_self>());
     10551                                        return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());
    861310552
    861410553                                case axis_following:
    8615                                         return step_do(c, stack, axis_to_type<axis_following>());
     10554                                        return step_do(c, stack, eval, axis_to_type<axis_following>());
    861610555                               
    861710556                                case axis_following_sibling:
    8618                                         return step_do(c, stack, axis_to_type<axis_following_sibling>());
     10557                                        return step_do(c, stack, eval, axis_to_type<axis_following_sibling>());
    861910558                               
    862010559                                case axis_namespace:
     
    862310562                               
    862410563                                case axis_parent:
    8625                                         return step_do(c, stack, axis_to_type<axis_parent>());
     10564                                        return step_do(c, stack, eval, axis_to_type<axis_parent>());
    862610565                               
    862710566                                case axis_preceding:
    8628                                         return step_do(c, stack, axis_to_type<axis_preceding>());
     10567                                        return step_do(c, stack, eval, axis_to_type<axis_preceding>());
    862910568
    863010569                                case axis_preceding_sibling:
    8631                                         return step_do(c, stack, axis_to_type<axis_preceding_sibling>());
     10570                                        return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());
    863210571                               
    863310572                                case axis_self:
    8634                                         return step_do(c, stack, axis_to_type<axis_self>());
     10573                                        return step_do(c, stack, eval, axis_to_type<axis_self>());
    863510574
    863610575                                default:
     
    867810617                        }
    867910618                }
     10619
     10620                void optimize(xpath_allocator* alloc)
     10621                {
     10622                        if (_left) _left->optimize(alloc);
     10623                        if (_right) _right->optimize(alloc);
     10624                        if (_next) _next->optimize(alloc);
     10625
     10626                        optimize_self(alloc);
     10627                }
     10628
     10629                void optimize_self(xpath_allocator* alloc)
     10630                {
     10631                        // Rewrite [position()=expr] with [expr]
     10632                        // Note that this step has to go before classification to recognize [position()=1]
     10633                        if ((_type == ast_filter || _type == ast_predicate) &&
     10634                                _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
     10635                        {
     10636                                _right = _right->_right;
     10637                        }
     10638
     10639                        // Classify filter/predicate ops to perform various optimizations during evaluation
     10640                        if (_type == ast_filter || _type == ast_predicate)
     10641                        {
     10642                                assert(_test == predicate_default);
     10643
     10644                                if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
     10645                                        _test = predicate_constant_one;
     10646                                else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
     10647                                        _test = predicate_constant;
     10648                                else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
     10649                                        _test = predicate_posinv;
     10650                        }
     10651
     10652                        // Rewrite descendant-or-self::node()/child::foo with descendant::foo
     10653                        // The former is a full form of //foo, the latter is much faster since it executes the node test immediately
     10654                        // Do a similar kind of rewrite for self/descendant/descendant-or-self axes
     10655                        // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
     10656                        if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
     10657                                _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
     10658                                is_posinv_step())
     10659                        {
     10660                                if (_axis == axis_child || _axis == axis_descendant)
     10661                                        _axis = axis_descendant;
     10662                                else
     10663                                        _axis = axis_descendant_or_self;
     10664
     10665                                _left = _left->_left;
     10666                        }
     10667
     10668                        // Use optimized lookup table implementation for translate() with constant arguments
     10669                        if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
     10670                        {
     10671                                unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
     10672
     10673                                if (table)
     10674                                {
     10675                                        _type = ast_opt_translate_table;
     10676                                        _data.table = table;
     10677                                }
     10678                        }
     10679
     10680                        // Use optimized path for @attr = 'value' or @attr = $value
     10681                        if (_type == ast_op_equal &&
     10682                                _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
     10683                                (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
     10684                        {
     10685                                _type = ast_opt_compare_attribute;
     10686                        }
     10687                }
    868010688               
    8681                 bool is_posinv()
     10689                bool is_posinv_expr() const
    868210690                {
    868310691                        switch (_type)
    868410692                        {
    868510693                        case ast_func_position:
     10694                        case ast_func_last:
    868610695                                return false;
    868710696
     
    869710706                        case ast_predicate:
    869810707                        case ast_filter:
    8699                         case ast_filter_posinv:
    870010708                                return true;
    870110709
    870210710                        default:
    8703                                 if (_left && !_left->is_posinv()) return false;
     10711                                if (_left && !_left->is_posinv_expr()) return false;
    870410712                               
    870510713                                for (xpath_ast_node* n = _right; n; n = n->_next)
    8706                                         if (!n->is_posinv()) return false;
     10714                                        if (!n->is_posinv_expr()) return false;
    870710715                                       
    870810716                                return true;
     
    871010718                }
    871110719
     10720                bool is_posinv_step() const
     10721                {
     10722                        assert(_type == ast_step);
     10723
     10724                        for (xpath_ast_node* n = _right; n; n = n->_next)
     10725                        {
     10726                                assert(n->_type == ast_predicate);
     10727
     10728                                if (n->_test != predicate_posinv)
     10729                                        return false;
     10730                        }
     10731
     10732                        return true;
     10733                }
     10734
    871210735                xpath_value_type rettype() const
    871310736                {
     
    872510748
    872610749                xpath_parse_result* _result;
     10750
     10751                char_t _scratch[32];
    872710752
    872810753        #ifdef PUGIXML_NO_EXCEPTIONS
     
    876810793                                char_t* c = static_cast<char_t*>(_alloc->allocate_nothrow((length + 1) * sizeof(char_t)));
    876910794                                if (!c) throw_error_oom();
     10795                                assert(c); // workaround for clang static analysis
    877010796
    877110797                                memcpy(c, value.begin, length * sizeof(char_t));
     
    880310829                                }
    880410830                                else if (name == PUGIXML_TEXT("contains") && argc == 2)
    8805                                         return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]);
     10831                                        return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);
    880610832                                else if (name == PUGIXML_TEXT("concat") && argc >= 2)
    880710833                                        return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]);
     
    886510891                                        return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
    886610892                                else if (name == PUGIXML_TEXT("string-length") && argc <= 1)
    8867                                         return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_string, args[0]);
     10893                                        return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);
    886810894                                else if (name == PUGIXML_TEXT("starts-with") && argc == 2)
    886910895                                        return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
     
    901411040                                        throw_error("Unknown variable: variable set is not provided");
    901511041
    9016                                 xpath_variable* var = get_variable(_variables, name.begin, name.end);
     11042                                xpath_variable* var = 0;
     11043                                if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))
     11044                                        throw_error_oom();
    901711045
    901811046                                if (!var)
     
    905211080                                double value = 0;
    905311081
    9054                                 if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value))
     11082                                if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))
    905511083                                        throw_error_oom();
    905611084
     
    912011148                                if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
    912111149
    9122                                 bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv();
    9123 
    9124                                 n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
     11150                                n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default);
    912511151
    912611152                                if (_lexer.current() != lex_close_square_brace)
     
    926611292                                xpath_ast_node* expr = parse_expression();
    926711293
    9268                                 xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);
     11294                                xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default);
    926911295                               
    927011296                                if (_lexer.current() != lex_close_square_brace)
     
    927711303                                last = pred;
    927811304                        }
    9279                        
     11305
    928011306                        return n;
    928111307                }
     
    933611362                //                              | FilterExpr '/' RelativeLocationPath
    933711363                //                              | FilterExpr '//' RelativeLocationPath
    9338                 xpath_ast_node* parse_path_expression()
     11364                // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
     11365                // UnaryExpr ::= UnionExpr | '-' UnaryExpr
     11366                xpath_ast_node* parse_path_or_unary_expression()
    933911367                {
    934011368                        // Clarification.
     
    938211410                                return n;
    938311411                        }
    9384                         else return parse_location_path();
    9385                 }
    9386 
    9387                 // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
    9388                 xpath_ast_node* parse_union_expression()
    9389                 {
    9390                         xpath_ast_node* n = parse_path_expression();
    9391 
    9392                         while (_lexer.current() == lex_union)
     11412                        else if (_lexer.current() == lex_minus)
    939311413                        {
    939411414                                _lexer.next();
    939511415
    9396                                 xpath_ast_node* expr = parse_union_expression();
    9397 
    9398                                 if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set)
     11416                                // precedence 7+ - only parses union expressions
     11417                                xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7);
     11418
     11419                                return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
     11420                        }
     11421                        else
     11422                                return parse_location_path();
     11423                }
     11424
     11425                struct binary_op_t
     11426                {
     11427                        ast_type_t asttype;
     11428                        xpath_value_type rettype;
     11429                        int precedence;
     11430
     11431                        binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0)
     11432                        {
     11433                        }
     11434
     11435                        binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)
     11436                        {
     11437                        }
     11438
     11439                        static binary_op_t parse(xpath_lexer& lexer)
     11440                        {
     11441                                switch (lexer.current())
     11442                                {
     11443                                case lex_string:
     11444                                        if (lexer.contents() == PUGIXML_TEXT("or"))
     11445                                                return binary_op_t(ast_op_or, xpath_type_boolean, 1);
     11446                                        else if (lexer.contents() == PUGIXML_TEXT("and"))
     11447                                                return binary_op_t(ast_op_and, xpath_type_boolean, 2);
     11448                                        else if (lexer.contents() == PUGIXML_TEXT("div"))
     11449                                                return binary_op_t(ast_op_divide, xpath_type_number, 6);
     11450                                        else if (lexer.contents() == PUGIXML_TEXT("mod"))
     11451                                                return binary_op_t(ast_op_mod, xpath_type_number, 6);
     11452                                        else
     11453                                                return binary_op_t();
     11454
     11455                                case lex_equal:
     11456                                        return binary_op_t(ast_op_equal, xpath_type_boolean, 3);
     11457
     11458                                case lex_not_equal:
     11459                                        return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);
     11460
     11461                                case lex_less:
     11462                                        return binary_op_t(ast_op_less, xpath_type_boolean, 4);
     11463
     11464                                case lex_greater:
     11465                                        return binary_op_t(ast_op_greater, xpath_type_boolean, 4);
     11466
     11467                                case lex_less_or_equal:
     11468                                        return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);
     11469
     11470                                case lex_greater_or_equal:
     11471                                        return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);
     11472
     11473                                case lex_plus:
     11474                                        return binary_op_t(ast_op_add, xpath_type_number, 5);
     11475
     11476                                case lex_minus:
     11477                                        return binary_op_t(ast_op_subtract, xpath_type_number, 5);
     11478
     11479                                case lex_multiply:
     11480                                        return binary_op_t(ast_op_multiply, xpath_type_number, 6);
     11481
     11482                                case lex_union:
     11483                                        return binary_op_t(ast_op_union, xpath_type_node_set, 7);
     11484
     11485                                default:
     11486                                        return binary_op_t();
     11487                                }
     11488                        }
     11489                };
     11490
     11491                xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit)
     11492                {
     11493                        binary_op_t op = binary_op_t::parse(_lexer);
     11494
     11495                        while (op.asttype != ast_unknown && op.precedence >= limit)
     11496                        {
     11497                                _lexer.next();
     11498
     11499                                xpath_ast_node* rhs = parse_path_or_unary_expression();
     11500
     11501                                binary_op_t nextop = binary_op_t::parse(_lexer);
     11502
     11503                                while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)
     11504                                {
     11505                                        rhs = parse_expression_rec(rhs, nextop.precedence);
     11506
     11507                                        nextop = binary_op_t::parse(_lexer);
     11508                                }
     11509
     11510                                if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))
    939911511                                        throw_error("Union operator has to be applied to node sets");
    940011512
    9401                                 n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr);
    9402                         }
    9403 
    9404                         return n;
    9405                 }
    9406 
    9407                 // UnaryExpr ::= UnionExpr | '-' UnaryExpr
    9408                 xpath_ast_node* parse_unary_expression()
    9409                 {
    9410                         if (_lexer.current() == lex_minus)
    9411                         {
    9412                                 _lexer.next();
    9413 
    9414                                 xpath_ast_node* expr = parse_unary_expression();
    9415 
    9416                                 return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
    9417                         }
    9418                         else return parse_union_expression();
    9419                 }
    9420                
    9421                 // MultiplicativeExpr ::= UnaryExpr
    9422                 //                                                | MultiplicativeExpr '*' UnaryExpr
    9423                 //                                                | MultiplicativeExpr 'div' UnaryExpr
    9424                 //                                                | MultiplicativeExpr 'mod' UnaryExpr
    9425                 xpath_ast_node* parse_multiplicative_expression()
    9426                 {
    9427                         xpath_ast_node* n = parse_unary_expression();
    9428 
    9429                         while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string &&
    9430                                    (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div"))))
    9431                         {
    9432                                 ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply :
    9433                                         _lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod;
    9434                                 _lexer.next();
    9435 
    9436                                 xpath_ast_node* expr = parse_unary_expression();
    9437 
    9438                                 n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr);
    9439                         }
    9440 
    9441                         return n;
    9442                 }
    9443 
    9444                 // AdditiveExpr ::= MultiplicativeExpr
    9445                 //                                      | AdditiveExpr '+' MultiplicativeExpr
    9446                 //                                      | AdditiveExpr '-' MultiplicativeExpr
    9447                 xpath_ast_node* parse_additive_expression()
    9448                 {
    9449                         xpath_ast_node* n = parse_multiplicative_expression();
    9450 
    9451                         while (_lexer.current() == lex_plus || _lexer.current() == lex_minus)
    9452                         {
    9453                                 lexeme_t l = _lexer.current();
    9454 
    9455                                 _lexer.next();
    9456 
    9457                                 xpath_ast_node* expr = parse_multiplicative_expression();
    9458 
    9459                                 n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr);
    9460                         }
    9461 
    9462                         return n;
    9463                 }
    9464 
     11513                                lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs);
     11514
     11515                                op = binary_op_t::parse(_lexer);
     11516                        }
     11517
     11518                        return lhs;
     11519                }
     11520
     11521                // Expr ::= OrExpr
     11522                // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
     11523                // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
     11524                // EqualityExpr ::= RelationalExpr
     11525                //                                      | EqualityExpr '=' RelationalExpr
     11526                //                                      | EqualityExpr '!=' RelationalExpr
    946511527                // RelationalExpr ::= AdditiveExpr
    946611528                //                                        | RelationalExpr '<' AdditiveExpr
     
    946811530                //                                        | RelationalExpr '<=' AdditiveExpr
    946911531                //                                        | RelationalExpr '>=' AdditiveExpr
    9470                 xpath_ast_node* parse_relational_expression()
    9471                 {
    9472                         xpath_ast_node* n = parse_additive_expression();
    9473 
    9474                         while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal ||
    9475                                    _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal)
    9476                         {
    9477                                 lexeme_t l = _lexer.current();
    9478                                 _lexer.next();
    9479 
    9480                                 xpath_ast_node* expr = parse_additive_expression();
    9481 
    9482                                 n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater :
    9483                                                                 l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr);
    9484                         }
    9485 
    9486                         return n;
    9487                 }
    9488                
    9489                 // EqualityExpr ::= RelationalExpr
    9490                 //                                      | EqualityExpr '=' RelationalExpr
    9491                 //                                      | EqualityExpr '!=' RelationalExpr
    9492                 xpath_ast_node* parse_equality_expression()
    9493                 {
    9494                         xpath_ast_node* n = parse_relational_expression();
    9495 
    9496                         while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal)
    9497                         {
    9498                                 lexeme_t l = _lexer.current();
    9499 
    9500                                 _lexer.next();
    9501 
    9502                                 xpath_ast_node* expr = parse_relational_expression();
    9503 
    9504                                 n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr);
    9505                         }
    9506 
    9507                         return n;
    9508                 }
    9509                
    9510                 // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
    9511                 xpath_ast_node* parse_and_expression()
    9512                 {
    9513                         xpath_ast_node* n = parse_equality_expression();
    9514 
    9515                         while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and"))
    9516                         {
    9517                                 _lexer.next();
    9518 
    9519                                 xpath_ast_node* expr = parse_equality_expression();
    9520 
    9521                                 n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr);
    9522                         }
    9523 
    9524                         return n;
    9525                 }
    9526 
    9527                 // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
    9528                 xpath_ast_node* parse_or_expression()
    9529                 {
    9530                         xpath_ast_node* n = parse_and_expression();
    9531 
    9532                         while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or"))
    9533                         {
    9534                                 _lexer.next();
    9535 
    9536                                 xpath_ast_node* expr = parse_and_expression();
    9537 
    9538                                 n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr);
    9539                         }
    9540 
    9541                         return n;
    9542                 }
    9543                
    9544                 // Expr ::= OrExpr
     11532                // AdditiveExpr ::= MultiplicativeExpr
     11533                //                                      | AdditiveExpr '+' MultiplicativeExpr
     11534                //                                      | AdditiveExpr '-' MultiplicativeExpr
     11535                // MultiplicativeExpr ::= UnaryExpr
     11536                //                                                | MultiplicativeExpr '*' UnaryExpr
     11537                //                                                | MultiplicativeExpr 'div' UnaryExpr
     11538                //                                                | MultiplicativeExpr 'mod' UnaryExpr
    954511539                xpath_ast_node* parse_expression()
    954611540                {
    9547                         return parse_or_expression();
     11541                        return parse_expression_rec(parse_path_or_unary_expression(), 0);
    954811542                }
    954911543
     
    958411578                {
    958511579                        void* memory = xml_memory::allocate(sizeof(xpath_query_impl));
     11580                        if (!memory) return 0;
    958611581
    958711582                        return new (memory) xpath_query_impl();
    958811583                }
    958911584
    9590                 static void destroy(void* ptr)
    9591                 {
    9592                         if (!ptr) return;
    9593                        
     11585                static void destroy(xpath_query_impl* impl)
     11586                {
    959411587                        // free all allocated pages
    9595                         static_cast<xpath_query_impl*>(ptr)->alloc.release();
     11588                        impl->alloc.release();
    959611589
    959711590                        // free allocator memory (with the first page)
    9598                         xml_memory::deallocate(ptr);
     11591                        xml_memory::deallocate(impl);
    959911592                }
    960011593
     
    960211595                {
    960311596                        block.next = 0;
     11597                        block.capacity = sizeof(block.data);
    960411598                }
    960511599
     
    962111615                return impl->root->eval_string(c, sd.stack);
    962211616        }
     11617
     11618        PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
     11619        {
     11620                if (!impl) return 0;
     11621
     11622                if (impl->root->rettype() != xpath_type_node_set)
     11623                {
     11624                #ifdef PUGIXML_NO_EXCEPTIONS
     11625                        return 0;
     11626                #else
     11627                        xpath_parse_result res;
     11628                        res.error = "Expression does not evaluate to node set";
     11629
     11630                        throw xpath_exception(res);
     11631                #endif
     11632                }
     11633
     11634                return impl->root;
     11635        }
    962311636PUGI__NS_END
    962411637
     
    970511718#endif
    970611719
    9707         PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_)
     11720        PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)
    970811721        {
    970911722                assert(begin_ <= end_);
     
    972111734                        _begin = &_storage;
    972211735                        _end = &_storage + size_;
     11736                        _type = type_;
    972311737                }
    972411738                else
     
    974411758                        _begin = storage;
    974511759                        _end = storage + size_;
    9746                 }
    9747         }
     11760                        _type = type_;
     11761                }
     11762        }
     11763
     11764#if __cplusplus >= 201103
     11765        PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs)
     11766        {
     11767                _type = rhs._type;
     11768                _storage = rhs._storage;
     11769                _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin;
     11770                _end = _begin + (rhs._end - rhs._begin);
     11771
     11772                rhs._type = type_unsorted;
     11773                rhs._begin = &rhs._storage;
     11774                rhs._end = rhs._begin;
     11775        }
     11776#endif
    974811777
    974911778        PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
     
    975111780        }
    975211781
    9753         PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage)
    9754         {
    9755                 _assign(begin_, end_);
     11782        PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage)
     11783        {
     11784                _assign(begin_, end_, type_);
    975611785        }
    975711786
    975811787        PUGI__FN xpath_node_set::~xpath_node_set()
    975911788        {
    9760                 if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
     11789                if (_begin != &_storage)
     11790                        impl::xml_memory::deallocate(_begin);
    976111791        }
    976211792               
    9763         PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage)
    9764         {
    9765                 _assign(ns._begin, ns._end);
     11793        PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage)
     11794        {
     11795                _assign(ns._begin, ns._end, ns._type);
    976611796        }
    976711797       
     
    976911799        {
    977011800                if (this == &ns) return *this;
    9771                
    9772                 _type = ns._type;
    9773                 _assign(ns._begin, ns._end);
     11801
     11802                _assign(ns._begin, ns._end, ns._type);
    977411803
    977511804                return *this;
    977611805        }
     11806
     11807#if __cplusplus >= 201103
     11808        PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage)
     11809        {
     11810                _move(rhs);
     11811        }
     11812
     11813        PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs)
     11814        {
     11815                if (this == &rhs) return *this;
     11816
     11817                if (_begin != &_storage)
     11818                        impl::xml_memory::deallocate(_begin);
     11819
     11820                _move(rhs);
     11821
     11822                return *this;
     11823        }
     11824#endif
    977711825
    977811826        PUGI__FN xpath_node_set::type_t xpath_node_set::type() const
     
    983111879        }
    983211880
    9833         PUGI__FN xpath_variable::xpath_variable()
     11881        PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)
    983411882        {
    983511883        }
     
    993011978        PUGI__FN xpath_variable_set::xpath_variable_set()
    993111979        {
    9932                 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0;
     11980                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     11981                        _data[i] = 0;
    993311982        }
    993411983
     
    993611985        {
    993711986                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
    9938                 {
    9939                         xpath_variable* var = _data[i];
    9940 
    9941                         while (var)
    9942                         {
    9943                                 xpath_variable* next = var->_next;
    9944 
    9945                                 impl::delete_xpath_variable(var->_type, var);
    9946 
    9947                                 var = next;
    9948                         }
    9949                 }
    9950         }
    9951 
    9952         PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const
     11987                        _destroy(_data[i]);
     11988        }
     11989
     11990        PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)
     11991        {
     11992                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     11993                        _data[i] = 0;
     11994
     11995                _assign(rhs);
     11996        }
     11997
     11998        PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
     11999        {
     12000                if (this == &rhs) return *this;
     12001
     12002                _assign(rhs);
     12003
     12004                return *this;
     12005        }
     12006
     12007#if __cplusplus >= 201103
     12008        PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs)
     12009        {
     12010                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     12011                {
     12012                        _data[i] = rhs._data[i];
     12013                        rhs._data[i] = 0;
     12014                }
     12015        }
     12016
     12017        PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs)
     12018        {
     12019                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     12020                {
     12021                        _destroy(_data[i]);
     12022
     12023                        _data[i] = rhs._data[i];
     12024                        rhs._data[i] = 0;
     12025                }
     12026
     12027                return *this;
     12028        }
     12029#endif
     12030
     12031        PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
     12032        {
     12033                xpath_variable_set temp;
     12034
     12035                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     12036                        if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))
     12037                                return;
     12038
     12039                _swap(temp);
     12040        }
     12041
     12042        PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
     12043        {
     12044                for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
     12045                {
     12046                        xpath_variable* chain = _data[i];
     12047
     12048                        _data[i] = rhs._data[i];
     12049                        rhs._data[i] = chain;
     12050                }
     12051        }
     12052
     12053        PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
    995312054        {
    995412055                const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
     
    996312064        }
    996412065
     12066        PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
     12067        {
     12068                xpath_variable* last = 0;
     12069
     12070                while (var)
     12071                {
     12072                        // allocate storage for new variable
     12073                        xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());
     12074                        if (!nvar) return false;
     12075
     12076                        // link the variable to the result immediately to handle failures gracefully
     12077                        if (last)
     12078                                last->_next = nvar;
     12079                        else
     12080                                *out_result = nvar;
     12081
     12082                        last = nvar;
     12083
     12084                        // copy the value; this can fail due to out-of-memory conditions
     12085                        if (!impl::copy_xpath_variable(nvar, var)) return false;
     12086
     12087                        var = var->_next;
     12088                }
     12089
     12090                return true;
     12091        }
     12092
     12093        PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var)
     12094        {
     12095                while (var)
     12096                {
     12097                        xpath_variable* next = var->_next;
     12098
     12099                        impl::delete_xpath_variable(var->_type, var);
     12100
     12101                        var = next;
     12102                }
     12103        }
     12104
    996512105        PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
    996612106        {
     
    997812118                if (result)
    997912119                {
    9980                         result->_type = type;
    998112120                        result->_next = _data[hash];
    998212121
     
    1001312152        PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
    1001412153        {
    10015                 return find(name);
     12154                return _find(name);
    1001612155        }
    1001712156
    1001812157        PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
    1001912158        {
    10020                 return find(name);
     12159                return _find(name);
    1002112160        }
    1002212161
     
    1003512174                else
    1003612175                {
    10037                         impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);
     12176                        using impl::auto_deleter; // MSVC7 workaround
     12177                        auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);
    1003812178
    1003912179                        qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
     
    1004112181                        if (qimpl->root)
    1004212182                        {
    10043                                 _impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());
     12183                                qimpl->root->optimize(&qimpl->alloc);
     12184
     12185                                _impl = impl.release();
    1004412186                                _result.error = 0;
    1004512187                        }
     
    1004712189        }
    1004812190
     12191        PUGI__FN xpath_query::xpath_query(): _impl(0)
     12192        {
     12193        }
     12194
    1004912195        PUGI__FN xpath_query::~xpath_query()
    1005012196        {
    10051                 impl::xpath_query_impl::destroy(_impl);
    10052         }
     12197                if (_impl)
     12198                        impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
     12199        }
     12200
     12201#if __cplusplus >= 201103
     12202        PUGI__FN xpath_query::xpath_query(xpath_query&& rhs)
     12203        {
     12204                _impl = rhs._impl;
     12205                _result = rhs._result;
     12206                rhs._impl = 0;
     12207                rhs._result = xpath_parse_result();
     12208        }
     12209
     12210        PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs)
     12211        {
     12212                if (this == &rhs) return *this;
     12213
     12214                if (_impl)
     12215                        impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
     12216
     12217                _impl = rhs._impl;
     12218                _result = rhs._result;
     12219                rhs._impl = 0;
     12220                rhs._result = xpath_parse_result();
     12221
     12222                return *this;
     12223        }
     12224#endif
    1005312225
    1005412226        PUGI__FN xpath_value_type xpath_query::return_type() const
     
    1009212264                impl::xpath_stack_data sd;
    1009312265
    10094                 return impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd).c_str();
     12266                impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);
     12267
     12268                return string_t(r.c_str(), r.length());
    1009512269        }
    1009612270#endif
     
    1011812292        PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
    1011912293        {
    10120                 if (!_impl) return xpath_node_set();
    10121 
    10122                 impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;
    10123 
    10124                 if (root->rettype() != xpath_type_node_set)
    10125                 {
    10126                 #ifdef PUGIXML_NO_EXCEPTIONS
    10127                         return xpath_node_set();
    10128                 #else
    10129                         xpath_parse_result res;
    10130                         res.error = "Expression does not evaluate to node set";
    10131 
    10132                         throw xpath_exception(res);
    10133                 #endif
    10134                 }
    10135                
     12294                impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
     12295                if (!root) return xpath_node_set();
     12296
    1013612297                impl::xpath_context c(n, 1, 1);
    1013712298                impl::xpath_stack_data sd;
     
    1014112302        #endif
    1014212303
    10143                 impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack);
     12304                impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
    1014412305
    1014512306                return xpath_node_set(r.begin(), r.end(), r.type());
    1014612307        }
    1014712308
     12309        PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const
     12310        {
     12311                impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
     12312                if (!root) return xpath_node();
     12313
     12314                impl::xpath_context c(n, 1, 1);
     12315                impl::xpath_stack_data sd;
     12316
     12317        #ifdef PUGIXML_NO_EXCEPTIONS
     12318                if (setjmp(sd.error_handler)) return xpath_node();
     12319        #endif
     12320
     12321                impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);
     12322
     12323                return r.first();
     12324        }
     12325
    1014812326        PUGI__FN const xpath_parse_result& xpath_query::result() const
    1014912327        {
     
    1016312341        {
    1016412342                return !_impl;
     12343        }
     12344
     12345        PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
     12346        {
     12347                xpath_query q(query, variables);
     12348                return select_node(q);
     12349        }
     12350
     12351        PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
     12352        {
     12353                return query.evaluate_node(*this);
     12354        }
     12355
     12356        PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
     12357        {
     12358                xpath_query q(query, variables);
     12359                return select_nodes(q);
     12360        }
     12361
     12362        PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
     12363        {
     12364                return query.evaluate_node_set(*this);
    1016512365        }
    1016612366
     
    1017312373        PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
    1017412374        {
    10175                 xpath_node_set s = query.evaluate_node_set(*this);
    10176                 return s.empty() ? xpath_node() : s.first();
    10177         }
    10178 
    10179         PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
    10180         {
    10181                 xpath_query q(query, variables);
    10182                 return select_nodes(q);
    10183         }
    10184 
    10185         PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
    10186         {
    10187                 return query.evaluate_node_set(*this);
     12375                return query.evaluate_node(*this);
    1018812376        }
    1018912377}
     
    1020312391// Undefine all local macros (makes sure we're not leaking macros in header-only mode)
    1020412392#undef PUGI__NO_INLINE
     12393#undef PUGI__UNLIKELY
    1020512394#undef PUGI__STATIC_ASSERT
    1020612395#undef PUGI__DMC_VOLATILE
     
    1021012399#undef PUGI__FN
    1021112400#undef PUGI__FN_NO_INLINE
     12401#undef PUGI__GETPAGE_IMPL
     12402#undef PUGI__GETPAGE
     12403#undef PUGI__NODETYPE
    1021212404#undef PUGI__IS_CHARTYPE_IMPL
    1021312405#undef PUGI__IS_CHARTYPE
    1021412406#undef PUGI__IS_CHARTYPEX
     12407#undef PUGI__ENDSWITH
    1021512408#undef PUGI__SKIPWS
    1021612409#undef PUGI__OPTSET
     
    1021912412#undef PUGI__SCANFOR
    1022012413#undef PUGI__SCANWHILE
     12414#undef PUGI__SCANWHILE_UNROLL
    1022112415#undef PUGI__ENDSEG
    1022212416#undef PUGI__THROW_ERROR
     
    1022612420
    1022712421/**
    10228  * Copyright (c) 2006-2012 Arseny Kapoulkine
     12422 * Copyright (c) 2006-2015 Arseny Kapoulkine
    1022912423 *
    1023012424 * Permission is hereby granted, free of charge, to any person
  • XmlTools2/trunk/libs/pugixml.hpp

    r906 r1055  
    11/**
    2  * pugixml parser - version 1.2
     2 * pugixml parser - version 1.7
    33 * --------------------------------------------------------
    4  * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
     4 * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
    55 * Report bugs and download new versions at http://pugixml.org/
    66 *
     
    1414#ifndef PUGIXML_VERSION
    1515// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
    16 #       define PUGIXML_VERSION 120
     16#       define PUGIXML_VERSION 170
    1717#endif
    1818
     
    6464#endif
    6565
     66// If the platform is known to have long long support, enable long long functions
     67#ifndef PUGIXML_HAS_LONG_LONG
     68#       if __cplusplus >= 201103
     69#               define PUGIXML_HAS_LONG_LONG
     70#       elif defined(_MSC_VER) && _MSC_VER >= 1400
     71#               define PUGIXML_HAS_LONG_LONG
     72#       endif
     73#endif
     74
    6675// Character interface macros
    6776#ifdef PUGIXML_WCHAR_MODE
     
    142151        // This flag is off by default; turning it on may result in slower parsing and more memory consumption.
    143152        const unsigned int parse_ws_pcdata_single = 0x0400;
     153
     154        // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
     155        const unsigned int parse_trim_pcdata = 0x0800;
     156
     157        // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
     158        // is a valid document. This flag is off by default.
     159        const unsigned int parse_fragment = 0x1000;
    144160
    145161        // The default parsing mode.
     
    188204        const unsigned int format_save_file_text = 0x20;
    189205
     206        // Write every attribute on a new line with appropriate indentation. This flag is off by default.
     207        const unsigned int format_indent_attributes = 0x40;
     208
    190209        // The default set of formatting flags.
    191210        // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
    192211        const unsigned int format_default = format_indent;
    193                
     212
    194213        // Forward declarations
    195214        struct xml_attribute_struct;
     
    201220
    202221        class xml_tree_walker;
     222
     223        struct xml_parse_result;
    203224
    204225        class xml_node;
     
    218239        public:
    219240                typedef It const_iterator;
     241                typedef It iterator;
    220242
    221243                xml_object_range(It b, It e): _begin(b), _end(e)
     
    318340                float as_float(float def = 0) const;
    319341
     342        #ifdef PUGIXML_HAS_LONG_LONG
     343                long long as_llong(long long def = 0) const;
     344                unsigned long long as_ullong(unsigned long long def = 0) const;
     345        #endif
     346
    320347                // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
    321348                bool as_bool(bool def = false) const;
     
    329356                bool set_value(unsigned int rhs);
    330357                bool set_value(double rhs);
     358                bool set_value(float rhs);
    331359                bool set_value(bool rhs);
     360
     361        #ifdef PUGIXML_HAS_LONG_LONG
     362                bool set_value(long long rhs);
     363                bool set_value(unsigned long long rhs);
     364        #endif
    332365
    333366                // Set attribute value (equivalent to set_value without error checking)
     
    336369                xml_attribute& operator=(unsigned int rhs);
    337370                xml_attribute& operator=(double rhs);
     371                xml_attribute& operator=(float rhs);
    338372                xml_attribute& operator=(bool rhs);
     373
     374        #ifdef PUGIXML_HAS_LONG_LONG
     375                xml_attribute& operator=(long long rhs);
     376                xml_attribute& operator=(unsigned long long rhs);
     377        #endif
    339378
    340379                // Get next/previous attribute in the attribute list of the parent node
     
    394433                xml_node_type type() const;
    395434
    396                 // Get node name/value, or "" if node is empty or it has no name/value
     435                // Get node name, or "" if node is empty or it has no name
    397436                const char_t* name() const;
     437
     438                // Get node value, or "" if node is empty or it has no value
     439                // Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
    398440                const char_t* value() const;
    399441       
     
    425467                xml_node previous_sibling(const char_t* name) const;
    426468
     469                // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast)
     470                xml_attribute attribute(const char_t* name, xml_attribute& hint) const;
     471
    427472                // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
    428473                const char_t* child_value() const;
     
    465510                xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
    466511
     512                // Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
     513                xml_node append_move(const xml_node& moved);
     514                xml_node prepend_move(const xml_node& moved);
     515                xml_node insert_move_after(const xml_node& moved, const xml_node& node);
     516                xml_node insert_move_before(const xml_node& moved, const xml_node& node);
     517
    467518                // Remove specified attribute
    468519                bool remove_attribute(const xml_attribute& a);
     
    472523                bool remove_child(const xml_node& n);
    473524                bool remove_child(const char_t* name);
     525
     526                // Parses buffer as an XML document fragment and appends all nodes as children of the current node.
     527                // Copies/converts the buffer, so it may be deleted or changed after the function returns.
     528                // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
     529                xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
    474530
    475531                // Find attribute using predicate. Returns first attribute for which predicate returned true.
     
    538594        #ifndef PUGIXML_NO_XPATH
    539595                // Select single node by evaluating XPath query. Returns first node from the resulting node set.
    540                 xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
    541                 xpath_node select_single_node(const xpath_query& query) const;
     596                xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
     597                xpath_node select_node(const xpath_query& query) const;
    542598
    543599                // Select node set by evaluating XPath query
    544600                xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
    545601                xpath_node_set select_nodes(const xpath_query& query) const;
     602
     603                // (deprecated: use select_node instead) Select single node by evaluating XPath query.
     604                xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
     605                xpath_node select_single_node(const xpath_query& query) const;
     606
    546607        #endif
    547608               
     
    627688                float as_float(float def = 0) const;
    628689
     690        #ifdef PUGIXML_HAS_LONG_LONG
     691                long long as_llong(long long def = 0) const;
     692                unsigned long long as_ullong(unsigned long long def = 0) const;
     693        #endif
     694
    629695                // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
    630696                bool as_bool(bool def = false) const;
     
    637703                bool set(unsigned int rhs);
    638704                bool set(double rhs);
     705                bool set(float rhs);
    639706                bool set(bool rhs);
     707
     708        #ifdef PUGIXML_HAS_LONG_LONG
     709                bool set(long long rhs);
     710                bool set(unsigned long long rhs);
     711        #endif
    640712
    641713                // Set text (equivalent to set without error checking)
     
    644716                xml_text& operator=(unsigned int rhs);
    645717                xml_text& operator=(double rhs);
     718                xml_text& operator=(float rhs);
    646719                xml_text& operator=(bool rhs);
     720
     721        #ifdef PUGIXML_HAS_LONG_LONG
     722                xml_text& operator=(long long rhs);
     723                xml_text& operator=(unsigned long long rhs);
     724        #endif
    647725
    648726                // Get the data node (node_pcdata or node_cdata) for this object
     
    741819
    742820        // Named node range helper
    743         class xml_named_node_iterator
    744         {
     821        class PUGIXML_CLASS xml_named_node_iterator
     822        {
     823                friend class xml_node;
     824
    745825        public:
    746826                // Iterator traits
     
    751831
    752832        #ifndef PUGIXML_NO_STL
    753                 typedef std::forward_iterator_tag iterator_category;
     833                typedef std::bidirectional_iterator_tag iterator_category;
    754834        #endif
    755835
     
    770850                xml_named_node_iterator operator++(int);
    771851
     852                const xml_named_node_iterator& operator--();
     853                xml_named_node_iterator operator--(int);
     854
    772855        private:
    773                 mutable xml_node _node;
     856                mutable xml_node _wrap;
     857                xml_node _parent;
    774858                const char_t* _name;
     859
     860                xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
    775861        };
    776862
     
    821907                status_bad_attribute,           // Parsing error occurred while parsing element attribute
    822908                status_bad_end_element,         // Parsing error occurred while parsing end element tag
    823                 status_end_element_mismatch // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
     909                status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
     910
     911                status_append_invalid_root,     // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
     912
     913                status_no_document_element      // Parsing resulted in a document without element nodes
    824914        };
    825915
     
    856946                // Non-copyable semantics
    857947                xml_document(const xml_document&);
    858                 const xml_document& operator=(const xml_document&);
     948                xml_document& operator=(const xml_document&);
    859949
    860950                void create();
    861951                void destroy();
    862 
    863                 xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own);
    864952
    865953        public:
     
    882970        #endif
    883971
     972                // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
     973                xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
     974
    884975                // Load document from zero-terminated string. No encoding conversions are applied.
    885                 xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
     976                xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
    886977
    887978                // Load document from file
     
    9561047                xpath_variable* _next;
    9571048
    958                 xpath_variable();
     1049                xpath_variable(xpath_value_type type);
    9591050
    9601051                // Non-copyable semantics
     
    9881079                xpath_variable* _data[64];
    9891080
    990                 // Non-copyable semantics
    991                 xpath_variable_set(const xpath_variable_set&);
    992                 xpath_variable_set& operator=(const xpath_variable_set&);
    993 
    994                 xpath_variable* find(const char_t* name) const;
     1081                void _assign(const xpath_variable_set& rhs);
     1082                void _swap(xpath_variable_set& rhs);
     1083
     1084                xpath_variable* _find(const char_t* name) const;
     1085
     1086                static bool _clone(xpath_variable* var, xpath_variable** out_result);
     1087                static void _destroy(xpath_variable* var);
    9951088
    9961089        public:
     
    9981091                xpath_variable_set();
    9991092                ~xpath_variable_set();
     1093
     1094                // Copy constructor/assignment operator
     1095                xpath_variable_set(const xpath_variable_set& rhs);
     1096                xpath_variable_set& operator=(const xpath_variable_set& rhs);
     1097
     1098        #if __cplusplus >= 201103
     1099                // Move semantics support
     1100                xpath_variable_set(xpath_variable_set&& rhs);
     1101                xpath_variable_set& operator=(xpath_variable_set&& rhs);
     1102        #endif
    10001103
    10011104                // Add a new variable or get the existing one, if the types match
     
    10311134                explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
    10321135
     1136                // Constructor
     1137                xpath_query();
     1138
    10331139                // Destructor
    10341140                ~xpath_query();
     1141
     1142        #if __cplusplus >= 201103
     1143                // Move semantics support
     1144                xpath_query(xpath_query&& rhs);
     1145                xpath_query& operator=(xpath_query&& rhs);
     1146        #endif
    10351147
    10361148                // Get query expression return type
     
    10611173                // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
    10621174                xpath_node_set evaluate_node_set(const xpath_node& n) const;
     1175
     1176                // Evaluate expression as node set in the specified context.
     1177                // Return first node in document order, or empty node if node set is empty.
     1178                // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
     1179                // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
     1180                xpath_node evaluate_node(const xpath_node& n) const;
    10631181
    10641182                // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
     
    11461264                // Constant iterator type
    11471265                typedef const xpath_node* const_iterator;
     1266
     1267                // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
     1268                typedef const xpath_node* iterator;
    11481269       
    11491270                // Default constructor. Constructs empty set.
     
    11601281                xpath_node_set& operator=(const xpath_node_set& ns);
    11611282
     1283        #if __cplusplus >= 201103
     1284                // Move semantics support
     1285                xpath_node_set(xpath_node_set&& rhs);
     1286                xpath_node_set& operator=(xpath_node_set&& rhs);
     1287        #endif
     1288
    11621289                // Get collection type
    11631290                type_t type() const;
     
    11901317                xpath_node* _end;
    11911318
    1192                 void _assign(const_iterator begin, const_iterator end);
     1319                void _assign(const_iterator begin, const_iterator end, type_t type);
     1320                void _move(xpath_node_set& rhs);
    11931321        };
    11941322#endif
     
    12241352        std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
    12251353        std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
    1226         std::forward_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
     1354        std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
    12271355}
    12281356#endif
     
    12341362        std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
    12351363        std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
    1236         std::forward_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
     1364        std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
    12371365}
    12381366#endif
     
    12401368#endif
    12411369
     1370// Make sure implementation is included in header-only mode
     1371// Use macro expansion in #include to work around QMake (QTBUG-11923)
     1372#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)
     1373#       define PUGIXML_SOURCE "pugixml.cpp"
     1374#       include PUGIXML_SOURCE
     1375#endif
     1376
    12421377/**
    1243  * Copyright (c) 2006-2012 Arseny Kapoulkine
     1378 * Copyright (c) 2006-2015 Arseny Kapoulkine
    12441379 *
    12451380 * Permission is hereby granted, free of charge, to any person
  • XmlTools2/trunk/optionsparser.cpp

    r967 r1055  
    1111    parser.setApplicationDescription("Additional documentation can be found at: http://wiki.oni2.net/XmlTools");
    1212
    13     XmlTools *myXmlTools;
     13    std::unique_ptr<XmlTools> myXmlTools;
    1414    QString filesWildCard, patchFilesWildCard, forceTargetFilesWildcard;
    1515    QString currentVal, newVal, diffOldNewVal, positions;
     
    219219
    220220    if(parser.isSet(elementNameOption)){
    221         myXmlTools=new XmlTools(filesWildCard,filters,noBackups,noVerbose);
     221        myXmlTools = std::make_unique<XmlTools>(filesWildCard,filters,noBackups,noVerbose);
    222222    }
    223223    else{
    224         myXmlTools=new XmlTools(filesWildCard,xPathExpression,noBackups,noVerbose);
     224        myXmlTools = std::make_unique<XmlTools>(filesWildCard,xPathExpression,noBackups,noVerbose);
    225225    }
    226226
     
    276276                                          Util::toQString("--patch-files\n--add-values\n--remove-values\n--replace-value\n--replace-all-values\n--update-elements\n--invert-elements"));
    277277    }
    278 
    279     delete myXmlTools;
    280278}
  • XmlTools2/trunk/readme.txt

    r1003 r1055  
    11Readme.txt
    22~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3 XmlTools v2.0b
     3XmlTools v2.0c
    44~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    55
     
    6262Change Log:
    6363----------------------------------
     642.0c, 28-10-2016
     65-Fixed bug in --update-elements operation
     66-Added some exception handling
     67-Started migrating some of the source code to C++14
     68-Upgraded pugixml to latest 1.7 version
     69----------------------------------
    64702.0b, 13-06-2014
    6571-Minor code fixes (use of singleton pattern)
  • XmlTools2/trunk/util.cpp

    r980 r1055  
    88QString AppExecutable="./"+AppName; // Mac OS needs unix like executing
    99#endif
    10 QString AppVersion="2.0b";
     10QString AppVersion="2.0c";
    1111}
    1212
     
    133133
    134134    foreach(QString value, stringList){
    135         intList << value.toInt();
     135        bool ok;
     136
     137        intList << value.toInt(&ok);
     138
     139        if(!ok){
     140            throw std::runtime_error(QString("Impossible to convert string '" + value + "' to int!").toUtf8().constData());
     141        }
    136142    }
    137143
     
    146152
    147153    foreach(QString value, stringList){
    148         doubleList << value.toDouble();
     154        bool ok;
     155
     156        doubleList << value.toDouble(&ok);
     157
     158        if(!ok){
     159            throw std::runtime_error(QString("Impossible to convert string '" + value + "' to double!").toUtf8().constData());
     160        }
    149161    }
    150162
  • XmlTools2/trunk/util.h

    r967 r1055  
    88#include <QVector>
    99#include <iostream> // cout, cin etc.
     10#include <memory>
    1011
    1112namespace GlobalVars{
  • XmlTools2/trunk/xmlpatch.cpp

    r997 r1055  
    5252    pugi::xml_parse_result result;
    5353
    54     filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
    55 
    56     if(filesToProcess.isEmpty()){
    57         UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","No XML files were found for the wildcard: "+filesWildcard);
    58     }
    59 
    60     result=newNode.load(xmlString.toUtf8().constData()); // load xml to insert
    61 
    62     if(result.status!=pugi::status_ok){
    63         UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
    64     }
    65 
    66     // Process all XmlFiles
    67     for(int i=0; i<filesToProcess.size(); i++){
    68 
    69         UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@ADD_INSIDE_NODES");
    70 
    71         // Check how the element will be fetched via element name or xpath expression
    72         if(xPathExpression.isEmpty()){
    73             UtilXmlTools::getAllNamedElements(this->rootNode,nodesToInsertion,filters);
    74         }
    75         else{
    76             UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToInsertion);
    77         }
    78 
    79         if(nodesToInsertion.isEmpty() || nodesToInsertion[0].type()==pugi::node_null){
    80 
    81             QString errMessage;
    82 
     54    try{
     55
     56        filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
     57
     58        if(filesToProcess.isEmpty()){
     59            UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES","No XML files were found for the wildcard: "+filesWildcard);
     60        }
     61
     62        result=newNode.load(xmlString.toUtf8().constData()); // load xml to insert
     63
     64        if(result.status!=pugi::status_ok){
     65            UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", "The xml node to insert is invalid.\n" + Util::toQString(result.description()));
     66        }
     67
     68        // Process all XmlFiles
     69        for(int i=0; i<filesToProcess.size(); i++){
     70
     71            UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@ADD_INSIDE_NODES");
     72
     73            // Check how the element will be fetched via element name or xpath expression
    8374            if(xPathExpression.isEmpty()){
    84                 errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
    85                 if(filters.getParentElementName()!=""){
    86                     errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
    87                 }
    88                 if(filters.getAttributeName()!=""){
    89                     errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
    90                 }
     75                UtilXmlTools::getAllNamedElements(this->rootNode,nodesToInsertion,filters);
    9176            }
    9277            else{
    93                 errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
    94             }
    95 
    96             UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES",errMessage);
    97         }
    98 
    99         for(int j=0; j<nodesToInsertion.size(); j++){
    100             for (pugi::xml_node currNodeToInsert = newNode.first_child(); currNodeToInsert; currNodeToInsert = currNodeToInsert.next_sibling())
    101             {
    102                 nodesToInsertion[j].append_copy(currNodeToInsert); // append the new node
    103             }
    104 
    105         }
    106 
    107         UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODES");
    108 
    109         nodesToInsertion.clear();
     78                UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToInsertion);
     79            }
     80
     81            if(nodesToInsertion.isEmpty() || nodesToInsertion[0].type()==pugi::node_null){
     82
     83                QString errMessage;
     84
     85                if(xPathExpression.isEmpty()){
     86                    errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
     87                    if(filters.getParentElementName()!=""){
     88                        errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
     89                    }
     90                    if(filters.getAttributeName()!=""){
     91                        errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
     92                    }
     93                }
     94                else{
     95                    errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
     96                }
     97
     98                UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES",errMessage);
     99            }
     100
     101            for(int j=0; j<nodesToInsertion.size(); j++){
     102                for (pugi::xml_node currNodeToInsert = newNode.first_child(); currNodeToInsert; currNodeToInsert = currNodeToInsert.next_sibling())
     103                {
     104                    nodesToInsertion[j].append_copy(currNodeToInsert); // append the new node
     105                }
     106
     107            }
     108
     109            UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@ADD_INSIDE_NODES");
     110
     111            nodesToInsertion.clear();
     112        }
     113    }
     114    catch(const std::exception &e){
     115        UtilXmlTools::displayErrorMessage("@ADD_INSIDE_NODES", QString("An exception was caught: ") + e.what());
    110116    }
    111117
     
    119125    QList<pugi::xml_node> nodesToDeletion;
    120126
    121     filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
    122 
    123     if(filesToProcess.isEmpty()){
    124         UtilXmlTools::displayErrorMessage("@REMOVE_NODES","No XML files were found for the wildcard: "+filesWildcard);
    125     }
    126 
    127     // Process all XmlFiles
    128     for(int i=0; i<filesToProcess.size(); i++){
    129 
    130         UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@REMOVE_NODES");
    131 
    132         // Check how the element will be fetched via element name or xpath expression
    133         if(xPathExpression.isEmpty()){
    134             UtilXmlTools::getAllNamedElements(this->rootNode,nodesToDeletion,filters);
    135         }
    136         else{
    137             UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToDeletion);
    138         }
    139 
    140         if(nodesToDeletion.isEmpty() || nodesToDeletion[0].type()==pugi::node_null){
    141 
    142             QString errMessage;
    143 
     127    try{
     128
     129        filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
     130
     131        if(filesToProcess.isEmpty()){
     132            UtilXmlTools::displayErrorMessage("@REMOVE_NODES","No XML files were found for the wildcard: "+filesWildcard);
     133        }
     134
     135        // Process all XmlFiles
     136        for(int i=0; i<filesToProcess.size(); i++){
     137
     138            UtilXmlTools::loadXmlFile(filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"@REMOVE_NODES");
     139
     140            // Check how the element will be fetched via element name or xpath expression
    144141            if(xPathExpression.isEmpty()){
    145                 errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
    146                 if(filters.getParentElementName()!=""){
    147                     errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
    148                 }
    149                 if(filters.getAttributeName()!=""){
    150                     errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
    151                 }
     142                UtilXmlTools::getAllNamedElements(this->rootNode,nodesToDeletion,filters);
    152143            }
    153144            else{
    154                 errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
    155             }
    156 
    157             UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
    158         }
    159 
    160         // Delete all the specified nodes
    161         for(int j=0; j<nodesToDeletion.size(); j++){
    162             if(!nodesToDeletion[j].parent().remove_child(nodesToDeletion[j])){  // remove the node
     145                UtilXmlTools::getAllXpathElements(xPathExpression,this->document,nodesToDeletion);
     146            }
     147
     148            if(nodesToDeletion.isEmpty() || nodesToDeletion[0].type()==pugi::node_null){
    163149
    164150                QString errMessage;
     151
    165152                if(xPathExpression.isEmpty()){
    166                     errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
    167 
     153                    errMessage = "No node was found with an ElementName: '" + filters.getElementName() + "'";
    168154                    if(filters.getParentElementName()!=""){
    169                         errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
     155                        errMessage += " and a ParentElementName: '" + filters.getParentElementName() + "'";
     156                    }
     157                    if(filters.getAttributeName()!=""){
     158                        errMessage += " and an AttributeName: '" + filters.getAttributeName() + "' and an AttributeValue: '" + filters.getAttributeValue() + "'";
    170159                    }
    171160                }
    172161                else{
    173                     errMessage = "Couldn't remove the node with the XPathExpression '" + xPathExpression + "'";
     162                    errMessage = "No node was found with an XPathExpression: '" + xPathExpression + "'";
    174163                }
    175164
    176165                UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
    177166            }
    178         }
    179 
    180         UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODES");
    181 
    182         nodesToDeletion.clear();
     167
     168            // Delete all the specified nodes
     169            for(int j=0; j<nodesToDeletion.size(); j++){
     170                if(!nodesToDeletion[j].parent().remove_child(nodesToDeletion[j])){  // remove the node
     171
     172                    QString errMessage;
     173                    if(xPathExpression.isEmpty()){
     174                        errMessage = "Couldn't remove the node with Element '" + filters.getElementName() + "'";
     175
     176                        if(filters.getParentElementName()!=""){
     177                            errMessage += " and a ParentElement: '" + filters.getParentElementName() + "'";
     178                        }
     179                    }
     180                    else{
     181                        errMessage = "Couldn't remove the node with the XPathExpression '" + xPathExpression + "'";
     182                    }
     183
     184                    UtilXmlTools::displayErrorMessage("@REMOVE_NODES",errMessage);
     185                }
     186            }
     187
     188            UtilXmlTools::saveXmlFile(filesToProcess[i],this->document, "@REMOVE_NODES");
     189
     190            nodesToDeletion.clear();
     191        }
     192    }
     193    catch(const std::exception &e){
     194        UtilXmlTools::displayErrorMessage("@REMOVE_NODES", QString("An exception was caught: ") + e.what());
    183195    }
    184196
     
    216228    QVector<QString> filesToProcess=UtilXmlTools::getAllXmlFilesByWildcard(filesWildcard);
    217229
    218     if(filesToProcess.isEmpty()){
    219         UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","No XML files were found for the wildcard: "+filesWildcard);
    220     }
    221 
    222     XmlCustomCode::getInstance()->executeCustomCode(jsString,filesToProcess,this->backupsEnabled,this->verboseEnabled);
    223 
     230    try{
     231        if(filesToProcess.isEmpty()){
     232            UtilXmlTools::displayErrorMessage("@CUSTOM_CODE","No XML files were found for the wildcard: "+filesWildcard);
     233        }
     234
     235        XmlCustomCode::getInstance()->executeCustomCode(jsString,filesToProcess,this->backupsEnabled,this->verboseEnabled);
     236    }
     237    catch(const std::exception &e){
     238        UtilXmlTools::displayErrorMessage("@CUSTOM_CODE", QString("An exception was caught: ") + e.what());
     239    }
    224240
    225241    UtilXmlTools::displaySuccessMessage(filesToProcess.size(), "@CUSTOM_CODE");
  • XmlTools2/trunk/xmltools.cpp

    r935 r1055  
    2626void XmlTools::addValues(QString newValues){
    2727
    28     // Process all XmlFiles
    29     for(int i=0; i<this->filesToProcess.size(); i++){
    30 
    31         QStringList newValuesList, currValuesList;
    32         QList<pugi::xml_node> elements;
    33 
    34         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"add-values");
    35 
    36         newValuesList=Util::qStringListFromSpacedString(newValues);
    37 
    38         // Check how the elements will be fetched via element name or xpath expression
    39         if(this->xPathExpression==""){
    40             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    41         }
    42         else{
    43             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    44         }
    45 
    46         for(int j=0; j<elements.size(); j++){
    47 
    48             currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
    49 
    50             for(int k=0; k<newValuesList.size(); k++){
    51                 if(currValuesList.contains(newValuesList[k])){ // If the current element already contains this value proceed to the next
    52                     continue;
    53                 }
    54 
    55                 elements[j].text()=QString(Util::toQString(elements[j].text().as_string()) + " " + newValuesList[k]).toUtf8().constData(); // If it doesn't exists yet let's add it
    56             }
    57         }
    58 
    59         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document,"add-values");
     28    try{
     29        // Process all XmlFiles
     30        for(int i=0; i<this->filesToProcess.size(); i++){
     31
     32            QStringList newValuesList, currValuesList;
     33            QList<pugi::xml_node> elements;
     34
     35            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled,"add-values");
     36
     37            newValuesList=Util::qStringListFromSpacedString(newValues);
     38
     39            // Check how the elements will be fetched via element name or xpath expression
     40            if(this->xPathExpression==""){
     41                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     42            }
     43            else{
     44                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     45            }
     46
     47            for(int j=0; j<elements.size(); j++){
     48
     49                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
     50
     51                for(int k=0; k<newValuesList.size(); k++){
     52                    if(currValuesList.contains(newValuesList[k])){ // If the current element already contains this value proceed to the next
     53                        continue;
     54                    }
     55
     56                    elements[j].text()=QString(Util::toQString(elements[j].text().as_string()) + " " + newValuesList[k]).toUtf8().constData(); // If it doesn't exists yet let's add it
     57                }
     58            }
     59
     60            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document,"add-values");
     61        }
     62    }
     63    catch(const std::exception &e){
     64        UtilXmlTools::displayErrorMessage("add-values", QString("An exception was caught: ") + e.what());
    6065    }
    6166
     
    6671void XmlTools::removeValues(QString valuesToRemove){
    6772
    68     // Process all XmlFiles
    69     for(int i=0; i<this->filesToProcess.size(); i++){
    70 
    71         QList<pugi::xml_node> elements;
    72         QStringList valuesToRemoveList, currValuesList;
    73         bool elementChanged=false;
    74 
    75         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "remove-values");
    76 
    77         // Check how the elements will be fetched via element name or xpath expression
    78         if(this->xPathExpression==""){
    79             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    80         }
    81         else{
    82             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    83         }
    84 
    85         valuesToRemoveList=Util::qStringListFromSpacedString(valuesToRemove);
    86 
    87         for(int j=0; j<elements.size(); j++){ // O(3)... Optimization may be necessary.
    88             currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
    89 
    90             for(int k=0; k<currValuesList.size(); k++){
    91                 for(int m=0; m<valuesToRemoveList.size(); m++){
    92                     if(currValuesList[k]==valuesToRemoveList[m]){
    93                         currValuesList[k]=""; // flag to deletion
     73    try{
     74        // Process all XmlFiles
     75        for(int i=0; i<this->filesToProcess.size(); i++){
     76
     77            QList<pugi::xml_node> elements;
     78            QStringList valuesToRemoveList, currValuesList;
     79            bool elementChanged=false;
     80
     81            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "remove-values");
     82
     83            // Check how the elements will be fetched via element name or xpath expression
     84            if(this->xPathExpression==""){
     85                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     86            }
     87            else{
     88                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     89            }
     90
     91            valuesToRemoveList=Util::qStringListFromSpacedString(valuesToRemove);
     92
     93            for(int j=0; j<elements.size(); j++){ // O(3)... Optimization may be necessary.
     94                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
     95
     96                for(int k=0; k<currValuesList.size(); k++){
     97                    for(int m=0; m<valuesToRemoveList.size(); m++){
     98                        if(currValuesList[k]==valuesToRemoveList[m]){
     99                            currValuesList[k]=""; // flag to deletion
     100                            elementChanged=true;
     101                        }
     102                    }
     103                }
     104
     105                if(elementChanged){ // If curr element changed update the XML
     106                    currValuesList.removeAll(""); // remove all files flagged to deletion
     107                    elements[j].text()=currValuesList.join(' ').toUtf8().constData();
     108                    elementChanged=false;
     109                }
     110            }
     111
     112            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "remove-values");
     113        }
     114    }
     115    catch(const std::exception &e){
     116        UtilXmlTools::displayErrorMessage("remove-values", QString("An exception was caught: ") + e.what());
     117    }
     118
     119    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"remove-values");
     120}
     121
     122void XmlTools::replaceValue(QString oldValue, QString newValue){
     123
     124    try{
     125        // Process all XmlFiles
     126        for(int i=0; i<this->filesToProcess.size(); i++){
     127
     128            QList<pugi::xml_node> elements;
     129            QStringList currValuesList;
     130            bool elementChanged=false;
     131
     132            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-value");
     133
     134            // Check how the elements will be fetched via element name or xpath expression
     135            if(this->xPathExpression==""){
     136                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     137            }
     138            else{
     139                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     140            }
     141
     142            for(int j=0; j<elements.size(); j++){
     143                currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
     144
     145                for(int k=0; k<currValuesList.size(); k++){
     146                    if(currValuesList[k]==oldValue){ // Found a match with the old value?
     147                        currValuesList[k]=newValue; // If found replace it with the new value
    94148                        elementChanged=true;
    95149                    }
    96150                }
    97             }
    98 
    99             if(elementChanged){ // If curr element changed update the XML
    100                 currValuesList.removeAll(""); // remove all files flagged to deletion
    101                 elements[j].text()=currValuesList.join(' ').toUtf8().constData();
     151
     152                if(elementChanged){ // If curr element changed update the XML
     153                    elements[j].text()=currValuesList.join(" ").toUtf8().constData();
     154                }
    102155                elementChanged=false;
    103156            }
    104         }
    105 
    106         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "remove-values");
    107     }
    108 
    109 
    110     UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"remove-values");
    111 }
    112 
    113 void XmlTools::replaceValue(QString oldValue, QString newValue){
    114 
    115     // Process all XmlFiles
    116     for(int i=0; i<this->filesToProcess.size(); i++){
    117 
    118         QList<pugi::xml_node> elements;
    119         QStringList currValuesList;
    120         bool elementChanged=false;
    121 
    122         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-value");
    123 
    124         // Check how the elements will be fetched via element name or xpath expression
    125         if(this->xPathExpression==""){
    126             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    127         }
    128         else{
    129             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    130         }
    131 
    132         for(int j=0; j<elements.size(); j++){
    133             currValuesList=Util::qStringListFromSpacedString(Util::toQString(elements[j].text().as_string())); // convert each element in a list (uses space as separator)
    134 
    135             for(int k=0; k<currValuesList.size(); k++){
    136                 if(currValuesList[k]==oldValue){ // Found a match with the old value?
    137                     currValuesList[k]=newValue; // If found replace it with the new value
    138                     elementChanged=true;
    139                 }
    140             }
    141 
    142             if(elementChanged){ // If curr element changed update the XML
    143                 elements[j].text()=currValuesList.join(" ").toUtf8().constData();
    144             }
    145             elementChanged=false;
    146         }
    147 
    148         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-value");
     157
     158            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-value");
     159        }
     160    }
     161    catch(const std::exception &e){
     162        UtilXmlTools::displayErrorMessage("replace-value", QString("An exception was caught: ") + e.what());
    149163    }
    150164
     
    155169void XmlTools::replaceAll(QString value, QString valuePositions){
    156170
    157     // Process all XmlFiles
    158     for(int i=0; i<this->filesToProcess.size(); i++){
    159 
    160         QList<pugi::xml_node> elements;
    161 
    162         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-all");
    163 
    164         // Check how the elements will be fetched via element name or xpath expression
    165         if(this->xPathExpression==""){
    166             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    167         }
    168         else{
    169             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    170         }
    171 
    172 
    173         // Let's start the override
    174         for(int j=0; j<elements.size(); j++){
    175             if(valuePositions!=""){
    176                 elements[j].text()=replaceSpecificPositions(value, Util::toQString(elements[j].text().as_string()),valuePositions).toUtf8().constData();
    177             }
    178             else{
    179                 elements[j].text()=value.toUtf8().constData();
    180             }
    181         }
    182 
    183         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-all");
     171    try{
     172        // Process all XmlFiles
     173        for(int i=0; i<this->filesToProcess.size(); i++){
     174
     175            QList<pugi::xml_node> elements;
     176
     177            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "replace-all");
     178
     179            // Check how the elements will be fetched via element name or xpath expression
     180            if(this->xPathExpression==""){
     181                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     182            }
     183            else{
     184                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     185            }
     186
     187
     188            // Let's start the override
     189            for(int j=0; j<elements.size(); j++){
     190                if(valuePositions!=""){
     191                    elements[j].text()=replaceSpecificPositions(value, Util::toQString(elements[j].text().as_string()),valuePositions).toUtf8().constData();
     192                }
     193                else{
     194                    elements[j].text()=value.toUtf8().constData();
     195                }
     196            }
     197
     198            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "replace-all");
     199        }
     200    }
     201    catch(const std::exception &e){
     202        UtilXmlTools::displayErrorMessage("replace-all", QString("An exception was caught: ") + e.what());
    184203    }
    185204
     
    192211void XmlTools::updateElements(QString diffBetweenOldAndNewValue){
    193212
    194     // Process all XmlFiles
    195     for(int i=0; i<this->filesToProcess.size(); i++){
    196 
    197         QList<pugi::xml_node> elements;
    198         MultiDimVar lastXmlValue(0); // inicialize with any value or dimension
    199         MultiDimVar currXmlValue(0);
    200         MultiDimVar newXmlValue(0); // value that will update currValue
    201 
    202         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled, "update-elements");
    203 
    204         // Check how the elements will be fetched via element name or xpath expression
    205         if(this->xPathExpression==""){
    206             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    207         }
    208         else{
    209             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    210         }
    211 
    212 
    213         if(elements.size()>1){
    214             lastXmlValue=MultiDimVar(Util::toQString(elements[0].text().as_string())); // the lastXmlValue will begin to be the first one of the node
    215             currXmlValue=MultiDimVar(Util::toQString(elements[1].text().as_string())); // the currXmlValue will begin to be the second one of the node
    216             newXmlValue=MultiDimVar::sub(lastXmlValue, MultiDimVar(diffBetweenOldAndNewValue));
    217             elements[0].text() = newXmlValue.toString().toUtf8().constData(); // update the first eblement with the new one already
    218         }
    219 
    220         // Let's start the node update
    221         for(int j=1; j<elements.size()-1; j++){ // We start in 1 because the 0 is already saved in lastXmlValue // -1 because we will also work with the next one in the current
    222 
    223             newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
    224             elements[j].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
    225             lastXmlValue=currXmlValue;
    226             currXmlValue=MultiDimVar(Util::toQString(elements[j+1].text().as_string()));
    227 
    228         }
    229 
    230         // To update too last element (avoid out of bound because i+1)
    231         newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
    232         elements[elements.size()-1].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
    233 
    234         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "update-elements");
    235     }
    236 
    237     UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"updateNode");
     213    try{
     214        // Process all XmlFiles
     215        for(int i=0; i<this->filesToProcess.size(); i++){
     216
     217            QList<pugi::xml_node> elements;
     218            MultiDimVar lastXmlValue(0); // inicialize with any value or dimension
     219            MultiDimVar currXmlValue(0);
     220            MultiDimVar newXmlValue(0); // value that will update currValue
     221
     222            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled,this->verboseEnabled, "update-elements");
     223
     224            // Check how the elements will be fetched via element name or xpath expression
     225            if(this->xPathExpression==""){
     226                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     227            }
     228            else{
     229                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     230            }
     231
     232            if(elements.size() > 0){
     233
     234                lastXmlValue=MultiDimVar(Util::toQString(elements[0].text().as_string())); // the lastXmlValue will begin to be the first one of the node
     235                newXmlValue=MultiDimVar::sub(lastXmlValue, MultiDimVar(diffBetweenOldAndNewValue));
     236                elements[0].text() = newXmlValue.toString().toUtf8().constData(); // update the first element with the new one already
     237
     238                // If we have more than 1 element
     239                if(elements.size()>1){
     240                    currXmlValue=MultiDimVar(Util::toQString(elements[1].text().as_string())); // the currXmlValue will begin to be the second one of the node
     241
     242                    // Let's start the node update
     243                    for(int j=1; j<elements.size()-1; j++){ // We start in 1 because the 0 is already saved in lastXmlValue // -1 because we will also work with the next one in the current
     244
     245                        newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
     246                        elements[j].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
     247                        lastXmlValue=currXmlValue;
     248                        currXmlValue=MultiDimVar(Util::toQString(elements[j+1].text().as_string()));
     249
     250                    }
     251
     252                    // To update too last element (avoid out of bound because i+1)
     253                    newXmlValue=MultiDimVar::sum(newXmlValue,MultiDimVar::sub(currXmlValue,lastXmlValue));
     254                    elements[elements.size()-1].text() = newXmlValue.toString().toUtf8().constData(); // update element with the new value
     255                }
     256            }
     257
     258            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "update-elements");
     259        }
     260    }
     261    catch(const std::exception &e){
     262        UtilXmlTools::displayErrorMessage("update-elements", QString("An exception was caught: ") + e.what());
     263    }
     264
     265    UtilXmlTools::displaySuccessMessage(this->filesToProcess.size(),"update-elements");
    238266
    239267}
     
    242270void XmlTools::invertElements(){
    243271
    244     // Process all XmlFiles
    245     for(int i=0; i<this->filesToProcess.size(); i++){
    246         UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "invert-elements");
    247 
    248         QList<pugi::xml_node> elements;
    249         QStringList invertedElements; //Inverting the element order
    250 
    251         // Check how the elements will be fetched via element name or xpath expression
    252         if(this->xPathExpression==""){
    253             UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
    254         }
    255         else{
    256             UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
    257         }
    258 
    259         // Read all elements and save to the list
    260         for(int j=elements.size()-1; j>=0; j--){
    261             invertedElements << Util::toQString(elements[j].text().as_string());
    262         }
    263 
    264         // Override the tree with the inverted order
    265         for(int j=0; j<elements.size(); j++){
    266             elements[j].text()= invertedElements[j].toUtf8().constData();
    267         }
    268 
    269         UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "invert-elements");
     272    try{
     273        // Process all XmlFiles
     274        for(int i=0; i<this->filesToProcess.size(); i++){
     275            UtilXmlTools::loadXmlFile(this->filesToProcess[i],this->document,this->rootNode,this->backupsEnabled, this->verboseEnabled, "invert-elements");
     276
     277            QList<pugi::xml_node> elements;
     278            QStringList invertedElements; //Inverting the element order
     279
     280            // Check how the elements will be fetched via element name or xpath expression
     281            if(this->xPathExpression==""){
     282                UtilXmlTools::getAllNamedElements(this->rootNode,elements,this->filters);
     283            }
     284            else{
     285                UtilXmlTools::getAllXpathElements(this->xPathExpression,this->document,elements);
     286            }
     287
     288            // Read all elements and save to the list
     289            for(int j=elements.size()-1; j>=0; j--){
     290                invertedElements << Util::toQString(elements[j].text().as_string());
     291            }
     292
     293            // Override the tree with the inverted order
     294            for(int j=0; j<elements.size(); j++){
     295                elements[j].text()= invertedElements[j].toUtf8().constData();
     296            }
     297
     298            UtilXmlTools::saveXmlFile(this->filesToProcess[i],this->document, "invert-elements");
     299        }
     300    }
     301    catch(const std::exception &e){
     302        UtilXmlTools::displayErrorMessage("invert-elements", QString("An exception was caught: ") + e.what());
    270303    }
    271304
Note: See TracChangeset for help on using the changeset viewer.