From b43b39b8b8090008c19e9d6e475245b03395c6d3 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Mon, 25 Mar 2024 16:51:29 +1100 Subject: [PATCH] add newer upstream pugixml --- CMakeLists.txt | 1 + ext/pugixml/CMakeLists.txt | 264 + ext/pugixml/LICENSE.md | 24 + ext/pugixml/scripts/pugixml-config.cmake.in | 12 + ext/pugixml/scripts/pugixml.pc.in | 11 + ext/pugixml/scripts/pugixml_dll.rc | 45 + .../src/detail => pugixml/src}/pugiconfig.hpp | 22 +- .../src/detail => pugixml/src}/pugixml.cpp | 5000 ++++++++++------- ext/pugixml/src/pugixml.hpp | 1516 +++++ ext/tmxlite/CMakeLists.txt | 3 +- ext/tmxlite/src/detail/pugixml.LICENSE | 34 - ext/tmxlite/src/detail/pugixml.hpp | 1401 +---- 12 files changed, 4787 insertions(+), 3546 deletions(-) create mode 100644 ext/pugixml/CMakeLists.txt create mode 100644 ext/pugixml/LICENSE.md create mode 100644 ext/pugixml/scripts/pugixml-config.cmake.in create mode 100644 ext/pugixml/scripts/pugixml.pc.in create mode 100644 ext/pugixml/scripts/pugixml_dll.rc rename ext/{tmxlite/src/detail => pugixml/src}/pugiconfig.hpp (85%) rename ext/{tmxlite/src/detail => pugixml/src}/pugixml.cpp (67%) create mode 100644 ext/pugixml/src/pugixml.hpp delete mode 100644 ext/tmxlite/src/detail/pugixml.LICENSE diff --git a/CMakeLists.txt b/CMakeLists.txt index 27e5d4a..59d06bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ endif() # Libraries set(TMXLITE_STATIC_LIB ON) add_subdirectory(ext/miniz) +add_subdirectory(ext/pugixml) add_subdirectory(ext/tmxlite) # Main tmx2gba sources diff --git a/ext/pugixml/CMakeLists.txt b/ext/pugixml/CMakeLists.txt new file mode 100644 index 0000000..d3f810e --- /dev/null +++ b/ext/pugixml/CMakeLists.txt @@ -0,0 +1,264 @@ +cmake_minimum_required(VERSION 3.5) + +# Policy configuration; this *MUST* be specified before project is defined +if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY +endif() + +project(pugixml VERSION 1.14 LANGUAGES CXX) + +include(CMakePackageConfigHelpers) +include(CMakeDependentOption) +include(GNUInstallDirs) +include(CTest) + +cmake_dependent_option(PUGIXML_USE_VERSIONED_LIBDIR + "Use a private subdirectory to install the headers and libraries" OFF + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + +cmake_dependent_option(PUGIXML_USE_POSTFIX + "Use separate postfix for each configuration to make sure you can install multiple build outputs" OFF + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + +cmake_dependent_option(PUGIXML_STATIC_CRT + "Use static MSVC RT libraries" OFF + "MSVC" OFF) + +cmake_dependent_option(PUGIXML_BUILD_TESTS + "Build pugixml tests" OFF + "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + +# Custom build defines +set(PUGIXML_BUILD_DEFINES CACHE STRING "Build defines for custom options") +separate_arguments(PUGIXML_BUILD_DEFINES) + +# Technically not needed for this file. This is builtin CMAKE global variable. +option(BUILD_SHARED_LIBS "Build shared instead of static library" OFF) + +# Expose option to build PUGIXML as static as well when the global BUILD_SHARED_LIBS variable is set +cmake_dependent_option(PUGIXML_BUILD_SHARED_AND_STATIC_LIBS + "Build both shared and static libraries" OFF + "BUILD_SHARED_LIBS" OFF) + +# Expose options from the pugiconfig.hpp +option(PUGIXML_WCHAR_MODE "Enable wchar_t mode" OFF) +option(PUGIXML_COMPACT "Enable compact mode" OFF) + +# Advanced options from pugiconfig.hpp +option(PUGIXML_NO_XPATH "Disable XPath" OFF) +option(PUGIXML_NO_STL "Disable STL" OFF) +option(PUGIXML_NO_EXCEPTIONS "Disable Exceptions" OFF) +mark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS) + +set(PUGIXML_PUBLIC_DEFINITIONS + $<$:PUGIXML_WCHAR_MODE> + $<$:PUGIXML_COMPACT> + $<$:PUGIXML_NO_XPATH> + $<$:PUGIXML_NO_STL> + $<$:PUGIXML_NO_EXCEPTIONS>) + +# This is used to backport a CMake 3.15 feature, but is also forwards compatible +if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY + MultiThreaded$<$:Debug>$<$>:DLL>) +endif() + +if (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) +endif() + +if (PUGIXML_USE_POSTFIX) + set(CMAKE_RELWITHDEBINFO_POSTFIX _r) + set(CMAKE_MINSIZEREL_POSTFIX _m) + set(CMAKE_DEBUG_POSTFIX _d) +endif() + +if (CMAKE_VERSION VERSION_LESS 3.15) + set(msvc-rt $) + + set(msvc-rt-mtd-shared $) + set(msvc-rt-mtd-static $) + set(msvc-rt-mt-shared $) + set(msvc-rt-mt-static $) + unset(msvc-rt) + + set(msvc-rt-mtd-shared $<${msvc-rt-mtd-shared}:-MDd>) + set(msvc-rt-mtd-static $<${msvc-rt-mtd-static}:-MTd>) + set(msvc-rt-mt-shared $<${msvc-rt-mt-shared}:-MD>) + set(msvc-rt-mt-static $<${msvc-rt-mt-static}:-MT>) +endif() + +set(versioned-dir $<$:/pugixml-${PROJECT_VERSION}>) + +set(libs) + +if (BUILD_SHARED_LIBS) + add_library(pugixml-shared SHARED + ${PROJECT_SOURCE_DIR}/scripts/pugixml_dll.rc + ${PROJECT_SOURCE_DIR}/src/pugixml.cpp) + add_library(pugixml::shared ALIAS pugixml-shared) + list(APPEND libs pugixml-shared) + string(CONCAT pugixml.msvc $, + $ + >) + + set_property(TARGET pugixml-shared PROPERTY EXPORT_NAME shared) + target_include_directories(pugixml-shared + PUBLIC + $) + target_compile_definitions(pugixml-shared + PUBLIC + ${PUGIXML_BUILD_DEFINES} + ${PUGIXML_PUBLIC_DEFINITIONS} + PRIVATE + PUGIXML_API=$ + ) + target_compile_options(pugixml-shared + PRIVATE + ${msvc-rt-mtd-shared} + ${msvc-rt-mtd-static} + ${msvc-rt-mt-shared} + ${msvc-rt-mt-static}) +endif() + +if (NOT BUILD_SHARED_LIBS OR PUGIXML_BUILD_SHARED_AND_STATIC_LIBS) + add_library(pugixml-static STATIC + ${PROJECT_SOURCE_DIR}/src/pugixml.cpp) + add_library(pugixml::static ALIAS pugixml-static) + list(APPEND libs pugixml-static) + + set_property(TARGET pugixml-static PROPERTY EXPORT_NAME static) + target_include_directories(pugixml-static + PUBLIC + $) + target_compile_definitions(pugixml-static + PUBLIC + ${PUGIXML_BUILD_DEFINES} + ${PUGIXML_PUBLIC_DEFINITIONS}) + target_compile_options(pugixml-static + PRIVATE + ${msvc-rt-mtd-shared} + ${msvc-rt-mtd-static} + ${msvc-rt-mt-shared} + ${msvc-rt-mt-static}) +endif() + +if (BUILD_SHARED_LIBS) + set(pugixml-alias pugixml-shared) +else() + set(pugixml-alias pugixml-static) +endif() +add_library(pugixml INTERFACE) +target_link_libraries(pugixml INTERFACE ${pugixml-alias}) +add_library(pugixml::pugixml ALIAS pugixml) + +set_target_properties(${libs} + PROPERTIES + MSVC_RUNTIME_LIBRARY ${CMAKE_MSVC_RUNTIME_LIBRARY} + EXCLUDE_FROM_ALL ON + POSITION_INDEPENDENT_CODE ON + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + OUTPUT_NAME pugixml) + +set_target_properties(${libs} + PROPERTIES + EXCLUDE_FROM_ALL OFF) +set(install-targets pugixml ${libs}) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/scripts/pugixml-config.cmake.in" + "${PROJECT_BINARY_DIR}/pugixml-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR} + NO_CHECK_REQUIRED_COMPONENTS_MACRO + NO_SET_AND_CHECK_MACRO) + +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/pugixml-config-version.cmake" + COMPATIBILITY SameMajorVersion) + +if (PUGIXML_USE_POSTFIX) + if(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) + set(LIB_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX}) + elseif(CMAKE_BUILD_TYPE MATCHES MinSizeRel) + set(LIB_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX}) + elseif(CMAKE_BUILD_TYPE MATCHES Debug) + set(LIB_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + endif() +endif() + +configure_file(scripts/pugixml.pc.in pugixml.pc @ONLY) + +if (NOT DEFINED PUGIXML_RUNTIME_COMPONENT) + set(PUGIXML_RUNTIME_COMPONENT Runtime) +endif() + +if (NOT DEFINED PUGIXML_LIBRARY_COMPONENT) + set(PUGIXML_LIBRARY_COMPONENT Library) +endif() + +if (NOT DEFINED PUGIXML_DEVELOPMENT_COMPONENT) + set(PUGIXML_DEVELOPMENT_COMPONENT Development) +endif() + +set(namelink-component) +if (NOT CMAKE_VERSION VERSION_LESS 3.12) + set(namelink-component NAMELINK_COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}) +endif() +install(TARGETS ${install-targets} + EXPORT pugixml-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${PUGIXML_RUNTIME_COMPONENT} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${PUGIXML_LIBRARY_COMPONENT} ${namelink-component} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}${versioned-dir}) + +install(EXPORT pugixml-targets + NAMESPACE pugixml:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pugixml COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}) + +export(EXPORT pugixml-targets + NAMESPACE pugixml::) + +install(FILES + "${PROJECT_BINARY_DIR}/pugixml-config-version.cmake" + "${PROJECT_BINARY_DIR}/pugixml-config.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pugixml COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}) + +install(FILES ${PROJECT_BINARY_DIR}/pugixml.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}) + +install( + FILES + "${PROJECT_SOURCE_DIR}/src/pugiconfig.hpp" + "${PROJECT_SOURCE_DIR}/src/pugixml.hpp" + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}${versioned-dir} COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}) + +if (PUGIXML_BUILD_TESTS) + set(fuzz-pattern "tests/fuzz_*.cpp") + set(test-pattern "tests/*.cpp") + if (CMAKE_VERSION VERSION_GREATER 3.11) + list(INSERT fuzz-pattern 0 CONFIGURE_DEPENDS) + list(INSERT test-pattern 0 CONFIGURE_DEPENDS) + endif() + file(GLOB test-sources ${test-pattern}) + file(GLOB fuzz-sources ${fuzz-pattern}) + list(REMOVE_ITEM test-sources ${fuzz-sources}) + + add_custom_target(check + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) + + add_executable(pugixml-check ${test-sources}) + add_test(NAME pugixml::test + COMMAND pugixml-check + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_dependencies(check pugixml-check) + target_link_libraries(pugixml-check + PRIVATE + pugixml::pugixml) +endif() diff --git a/ext/pugixml/LICENSE.md b/ext/pugixml/LICENSE.md new file mode 100644 index 0000000..91bdfc1 --- /dev/null +++ b/ext/pugixml/LICENSE.md @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2006-2023 Arseny Kapoulkine + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/ext/pugixml/scripts/pugixml-config.cmake.in b/ext/pugixml/scripts/pugixml-config.cmake.in new file mode 100644 index 0000000..d4282f9 --- /dev/null +++ b/ext/pugixml/scripts/pugixml-config.cmake.in @@ -0,0 +1,12 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/pugixml-targets.cmake") + +# If the user is not requiring 1.11 (either by explicitly requesting an older +# version or not requesting one at all), provide the old imported target name +# for compatibility. +if (NOT TARGET pugixml AND (NOT DEFINED PACKAGE_FIND_VERSION OR PACKAGE_FIND_VERSION VERSION_LESS "1.11")) + add_library(pugixml INTERFACE IMPORTED) + # Equivalent to target_link_libraries INTERFACE, but compatible with CMake 3.10 + set_target_properties(pugixml PROPERTIES INTERFACE_LINK_LIBRARIES pugixml::pugixml) +endif () diff --git a/ext/pugixml/scripts/pugixml.pc.in b/ext/pugixml/scripts/pugixml.pc.in new file mode 100644 index 0000000..7958774 --- /dev/null +++ b/ext/pugixml/scripts/pugixml.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@@INSTALL_SUFFIX@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@@INSTALL_SUFFIX@ + +Name: pugixml +Description: Light-weight, simple and fast XML parser for C++ with XPath support. +URL: https://pugixml.org/ +Version: @pugixml_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lpugixml@LIB_POSTFIX@ diff --git a/ext/pugixml/scripts/pugixml_dll.rc b/ext/pugixml/scripts/pugixml_dll.rc new file mode 100644 index 0000000..e020b54 --- /dev/null +++ b/ext/pugixml/scripts/pugixml_dll.rc @@ -0,0 +1,45 @@ +#include + +#define PUGIXML_VERSION_MAJOR 1 +#define PUGIXML_VERSION_MINOR 14 +#define PUGIXML_VERSION_PATCH 0 +#define PUGIXML_VERSION_NUMBER "1.14.0\0" + +#if defined(GCC_WINDRES) || defined(__MINGW32__) || defined(__CYGWIN__) +VS_VERSION_INFO VERSIONINFO +#else +VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE +#endif + FILEVERSION PUGIXML_VERSION_MAJOR,PUGIXML_VERSION_MINOR,PUGIXML_VERSION_PATCH,0 + PRODUCTVERSION PUGIXML_VERSION_MAJOR,PUGIXML_VERSION_MINOR,PUGIXML_VERSION_PATCH,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS 1 +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + BEGIN + VALUE "CompanyName", "zeux/pugixml\0" + VALUE "FileDescription", "pugixml library\0" + VALUE "FileVersion", PUGIXML_VERSION_NUMBER + VALUE "InternalName", "pugixml.dll\0" + VALUE "LegalCopyright", "Copyright (C) 2006-2023, by Arseny Kapoulkine\0" + VALUE "OriginalFilename", "pugixml.dll\0" + VALUE "ProductName", "pugixml\0" + VALUE "ProductVersion", PUGIXML_VERSION_NUMBER + VALUE "Comments", "For more information visit https://github.com/zeux/pugixml/\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/ext/tmxlite/src/detail/pugiconfig.hpp b/ext/pugixml/src/pugiconfig.hpp similarity index 85% rename from ext/tmxlite/src/detail/pugiconfig.hpp rename to ext/pugixml/src/pugiconfig.hpp index 5ad1a87..3dc839d 100644 --- a/ext/tmxlite/src/detail/pugiconfig.hpp +++ b/ext/pugixml/src/pugiconfig.hpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.7 + * pugixml parser - version 1.14 * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -23,13 +23,12 @@ // Uncomment this to disable XPath // #define PUGIXML_NO_XPATH -#ifdef __ANDROID__ // Uncomment this to disable STL -#define PUGIXML_NO_STL +// #define PUGIXML_NO_STL // Uncomment this to disable exceptions -#define PUGIXML_NO_EXCEPTIONS -#endif //__ANDROID__ +// #define PUGIXML_NO_EXCEPTIONS + // Set this to control attributes for public classes/functions, i.e.: // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL @@ -41,8 +40,11 @@ // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 +// Tune this constant to adjust max nesting for XPath queries +// #define PUGIXML_XPATH_DEPTH_LIMIT 1024 + // Uncomment this to switch to header-only version -//#define PUGIXML_HEADER_ONLY +// #define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG @@ -50,7 +52,7 @@ #endif /** - * Copyright (c) 2006-2015 Arseny Kapoulkine + * Copyright (c) 2006-2023 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -63,7 +65,7 @@ * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND diff --git a/ext/tmxlite/src/detail/pugixml.cpp b/ext/pugixml/src/pugixml.cpp similarity index 67% rename from ext/tmxlite/src/detail/pugixml.cpp rename to ext/pugixml/src/pugixml.cpp index 444648f..89123c7 100644 --- a/ext/tmxlite/src/detail/pugixml.cpp +++ b/ext/pugixml/src/pugixml.cpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.7 + * pugixml parser - version 1.14 * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -15,7 +15,6 @@ #define SOURCE_PUGIXML_CPP #include "pugixml.hpp" -#include #include #include @@ -23,13 +22,6 @@ #include #include -// Fix for mingw, even if it should be in limits.h -#ifndef LLONG_MIN -#define LLONG_MIN (-9223372036854775807LL - 1) -#define LLONG_MAX 9223372036854775807LL -#define ULLONG_MAX 18446744073709551615ULL -#endif - #ifdef PUGIXML_WCHAR_MODE # include #endif @@ -37,9 +29,6 @@ #ifndef PUGIXML_NO_XPATH # include # include -# ifdef PUGIXML_NO_EXCEPTIONS -# include -# endif #endif #ifndef PUGIXML_NO_STL @@ -51,18 +40,26 @@ // For placement new #include +// For load_file +#if defined(__linux__) || defined(__APPLE__) +#include +#endif + #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) -# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable # pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4996) // this function or variable may be unsafe -# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged +#endif + +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe #endif #ifdef __INTEL_COMPILER -# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 177) // function was declared but never referenced # pragma warning(disable: 279) // controlling expression is constant # pragma warning(disable: 1478 1786) // function was declared "deprecated" # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type @@ -84,64 +81,105 @@ # pragma diag_suppress=237 // controlling expression is constant #endif +#ifdef __TI_COMPILER_VERSION__ +# pragma diag_suppress 179 // function was declared but never referenced +#endif + // Inlining controls #if defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGI__NO_INLINE __declspec(noinline) +# define PUGI_IMPL_NO_INLINE __declspec(noinline) #elif defined(__GNUC__) -# define PUGI__NO_INLINE __attribute__((noinline)) +# define PUGI_IMPL_NO_INLINE __attribute__((noinline)) #else -# define PUGI__NO_INLINE +# define PUGI_IMPL_NO_INLINE #endif // Branch weight controls -#if defined(__GNUC__) -# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) +#if defined(__GNUC__) && !defined(__c2__) +# define PUGI_IMPL_UNLIKELY(cond) __builtin_expect(cond, 0) #else -# define PUGI__UNLIKELY(cond) (cond) +# define PUGI_IMPL_UNLIKELY(cond) (cond) #endif // Simple static assertion -#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } +#define PUGI_IMPL_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } // Digital Mars C++ bug workaround for passing char loaded from memory via stack #ifdef __DMC__ -# define PUGI__DMC_VOLATILE volatile +# define PUGI_IMPL_DMC_VOLATILE volatile #else -# define PUGI__DMC_VOLATILE +# define PUGI_IMPL_DMC_VOLATILE +#endif + +// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings +#if defined(__clang__) && defined(__has_attribute) +# if __has_attribute(no_sanitize) +# define PUGI_IMPL_UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) +# else +# define PUGI_IMPL_UNSIGNED_OVERFLOW +# endif +#else +# define PUGI_IMPL_UNSIGNED_OVERFLOW #endif // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; +using std::memset; +#endif + +// Old versions of GCC do not define ::malloc and ::free depending on header include order +#if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)) +using std::malloc; +using std::free; +#endif + +// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations +#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define LLONG_MAX __LONG_LONG_MAX__ +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features -#if defined(_MSC_VER) && !defined(__S3E__) -# define PUGI__MSVC_CRT_VERSION _MSC_VER +#if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE) +# define PUGI_IMPL_MSVC_CRT_VERSION _MSC_VER +#elif defined(_WIN32_WCE) +# define PUGI_IMPL_MSVC_CRT_VERSION 1310 // MSVC7.1 #endif +// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size. +#if __cplusplus >= 201103 +# define PUGI_IMPL_SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) +#elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 +# define PUGI_IMPL_SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) +#elif defined(__APPLE__) && __clang_major__ >= 14 // Xcode 14 marks sprintf as deprecated while still using C++98 by default +# define PUGI_IMPL_SNPRINTF(buf, fmt, arg1, arg2) snprintf(buf, sizeof(buf), fmt, arg1, arg2) +#else +# define PUGI_IMPL_SNPRINTF sprintf +#endif + +// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat. #ifdef PUGIXML_HEADER_ONLY -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } -# define PUGI__FN inline -# define PUGI__FN_NO_INLINE inline +# define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { +# define PUGI_IMPL_NS_END } } +# define PUGI_IMPL_FN inline +# define PUGI_IMPL_FN_NO_INLINE inline #else # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } +# define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { +# define PUGI_IMPL_NS_END } } # else -# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { -# define PUGI__NS_END } } } +# define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { namespace { +# define PUGI_IMPL_NS_END } } } # endif -# define PUGI__FN -# define PUGI__FN_NO_INLINE PUGI__NO_INLINE +# define PUGI_IMPL_FN +# define PUGI_IMPL_FN_NO_INLINE PUGI_IMPL_NO_INLINE #endif // uintptr_t -#if !defined(_MSC_VER) || _MSC_VER >= 1600 -# include -#else +#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) namespace pugi { # ifndef _UINTPTR_T_DEFINED @@ -152,16 +190,18 @@ namespace pugi typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; } +#else +# include #endif // Memory allocation -PUGI__NS_BEGIN - PUGI__FN void* default_allocate(size_t size) +PUGI_IMPL_NS_BEGIN + PUGI_IMPL_FN void* default_allocate(size_t size) { return malloc(size); } - PUGI__FN void default_deallocate(void* ptr) + PUGI_IMPL_FN void default_deallocate(void* ptr) { free(ptr); } @@ -179,12 +219,12 @@ PUGI__NS_BEGIN template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; typedef xml_memory_management_function_storage xml_memory; -PUGI__NS_END +PUGI_IMPL_NS_END // String utilities -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN // Get string length - PUGI__FN size_t strlength(const char_t* s) + PUGI_IMPL_FN size_t strlength(const char_t* s) { assert(s); @@ -196,7 +236,7 @@ PUGI__NS_BEGIN } // Compare two strings - PUGI__FN bool strequal(const char_t* src, const char_t* dst) + PUGI_IMPL_FN bool strequal(const char_t* src, const char_t* dst) { assert(src && dst); @@ -208,17 +248,17 @@ PUGI__NS_BEGIN } // Compare lhs with [rhs_begin, rhs_end) - PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) + PUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) { for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; - + return lhs[count] == 0; } // Get length of wide string, even if CRT lacks wide character support - PUGI__FN size_t strlength_wide(const wchar_t* s) + PUGI_IMPL_FN size_t strlength_wide(const wchar_t* s) { assert(s); @@ -230,12 +270,14 @@ PUGI__NS_BEGIN return static_cast(end - s); #endif } -PUGI__NS_END +PUGI_IMPL_NS_END // auto_ptr-like object for exception recovery -PUGI__NS_BEGIN - template struct auto_deleter +PUGI_IMPL_NS_BEGIN + template struct auto_deleter { + typedef void (*D)(T*); + T* data; D deleter; @@ -255,10 +297,10 @@ PUGI__NS_BEGIN return result; } }; -PUGI__NS_END +PUGI_IMPL_NS_END #ifdef PUGIXML_COMPACT -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN class compact_hash_table { public: @@ -277,67 +319,37 @@ PUGI__NS_BEGIN } } - void** find(const void* key) + void* find(const void* key) { - assert(key); - if (_capacity == 0) return 0; - size_t hashmod = _capacity - 1; - size_t bucket = hash(key) & hashmod; + item_t* item = get_item(key); + assert(item); + assert(item->key == key || (item->key == 0 && item->value == 0)); - for (size_t probe = 0; probe <= hashmod; ++probe) - { - item_t& probe_item = _items[bucket]; - - if (probe_item.key == key) - return &probe_item.value; - - if (probe_item.key == 0) - return 0; - - // hash collision, quadratic probing - bucket = (bucket + probe + 1) & hashmod; - } - - assert(!"Hash table is full"); - return 0; + return item->value; } - void** insert(const void* key) + void insert(const void* key, void* value) { - assert(key); - assert(_count < _capacity * 3 / 4); + assert(_capacity != 0 && _count < _capacity - _capacity / 4); - size_t hashmod = _capacity - 1; - size_t bucket = hash(key) & hashmod; + item_t* item = get_item(key); + assert(item); - for (size_t probe = 0; probe <= hashmod; ++probe) + if (item->key == 0) { - item_t& probe_item = _items[bucket]; - - if (probe_item.key == 0) - { - probe_item.key = key; - _count++; - return &probe_item.value; - } - - if (probe_item.key == key) - return &probe_item.value; - - // hash collision, quadratic probing - bucket = (bucket + probe + 1) & hashmod; + _count++; + item->key = key; } - assert(!"Hash table is full"); - return 0; + item->value = value; } - bool reserve() + bool reserve(size_t extra = 16) { - if (_count + 16 >= _capacity - _capacity / 4) - return rehash(); + if (_count + extra >= _capacity - _capacity / 4) + return rehash(_count + extra); return true; } @@ -354,11 +366,34 @@ PUGI__NS_BEGIN size_t _count; - bool rehash(); + bool rehash(size_t count); - static unsigned int hash(const void* key) + item_t* get_item(const void* key) { - unsigned int h = static_cast(reinterpret_cast(key)); + assert(key); + assert(_capacity > 0); + + size_t hashmod = _capacity - 1; + size_t bucket = hash(key) & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + item_t& probe_item = _items[bucket]; + + if (probe_item.key == key || probe_item.key == 0) + return &probe_item; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return 0; + } + + static PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash(const void* key) + { + unsigned int h = static_cast(reinterpret_cast(key) & 0xffffffff); // MurmurHash3 32-bit finalizer h ^= h >> 16; @@ -371,71 +406,67 @@ PUGI__NS_BEGIN } }; - PUGI__FN_NO_INLINE bool compact_hash_table::rehash() + PUGI_IMPL_FN_NO_INLINE bool compact_hash_table::rehash(size_t count) { + size_t capacity = 32; + while (count >= capacity - capacity / 4) + capacity *= 2; + compact_hash_table rt; - rt._capacity = (_capacity == 0) ? 32 : _capacity * 2; - rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * rt._capacity)); + rt._capacity = capacity; + rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * capacity)); if (!rt._items) return false; - memset(rt._items, 0, sizeof(item_t) * rt._capacity); + memset(rt._items, 0, sizeof(item_t) * capacity); for (size_t i = 0; i < _capacity; ++i) if (_items[i].key) - *rt.insert(_items[i].key) = _items[i].value; + rt.insert(_items[i].key, _items[i].value); if (_items) xml_memory::deallocate(_items); - _capacity = rt._capacity; + _capacity = capacity; _items = rt._items; + assert(_count == rt._count); + return true; } -PUGI__NS_END +PUGI_IMPL_NS_END #endif -PUGI__NS_BEGIN - static const size_t xml_memory_page_size = - #ifdef PUGIXML_MEMORY_PAGE_SIZE - PUGIXML_MEMORY_PAGE_SIZE - #else - 32768 - #endif - ; - +PUGI_IMPL_NS_BEGIN #ifdef PUGIXML_COMPACT static const uintptr_t xml_memory_block_alignment = 4; - - static const uintptr_t xml_memory_page_alignment = sizeof(void*); #else static const uintptr_t xml_memory_block_alignment = sizeof(void*); - - static const uintptr_t xml_memory_page_alignment = 64; - static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); #endif // extra metadata bits - static const uintptr_t xml_memory_page_contents_shared_mask = 32; - static const uintptr_t xml_memory_page_name_allocated_mask = 16; - static const uintptr_t xml_memory_page_value_allocated_mask = 8; - static const uintptr_t xml_memory_page_type_mask = 7; + static const uintptr_t xml_memory_page_contents_shared_mask = 64; + static const uintptr_t xml_memory_page_name_allocated_mask = 32; + static const uintptr_t xml_memory_page_value_allocated_mask = 16; + static const uintptr_t xml_memory_page_type_mask = 15; // combined masks for string uniqueness static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; #ifdef PUGIXML_COMPACT - #define PUGI__GETPAGE_IMPL(header) (header).get_page() + #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) // unused + #define PUGI_IMPL_GETPAGE_IMPL(header) (header).get_page() #else - #define PUGI__GETPAGE_IMPL(header) reinterpret_cast((header) & impl::xml_memory_page_pointer_mask) + #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) + // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + #define PUGI_IMPL_GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) #endif - #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) - #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1) + #define PUGI_IMPL_GETPAGE(n) PUGI_IMPL_GETPAGE_IMPL((n)->header) + #define PUGI_IMPL_NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) struct xml_allocator; @@ -475,6 +506,14 @@ PUGI__NS_BEGIN #endif }; + static const size_t xml_memory_page_size = + #ifdef PUGIXML_MEMORY_PAGE_SIZE + (PUGIXML_MEMORY_PAGE_SIZE) + #else + 32768 + #endif + - sizeof(xml_memory_page); + struct xml_memory_string_header { uint16_t page_offset; // offset from page->data @@ -495,37 +534,29 @@ PUGI__NS_BEGIN size_t size = sizeof(xml_memory_page) + data_size; // allocate block with some alignment, leaving memory for worst-case padding - void* memory = xml_memory::allocate(size + xml_memory_page_alignment); + void* memory = xml_memory::allocate(size); if (!memory) return 0; - // align to next page boundary (note: this guarantees at least 1 usable byte before the page) - char* page_memory = reinterpret_cast((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1)); - // prepare page structure - xml_memory_page* page = xml_memory_page::construct(page_memory); + xml_memory_page* page = xml_memory_page::construct(memory); assert(page); - page->allocator = _root->allocator; - - // record the offset for freeing the memory block - assert(page_memory > memory && page_memory - static_cast(memory) <= 127); - page_memory[-1] = static_cast(page_memory - static_cast(memory)); + assert(this == _root->allocator); + page->allocator = this; return page; } static void deallocate_page(xml_memory_page* page) { - char* page_memory = reinterpret_cast(page); - - xml_memory::deallocate(page_memory - page_memory[-1]); + xml_memory::deallocate(page); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); void* allocate_memory(size_t size, xml_memory_page*& out_page) { - if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size)) + if (PUGI_IMPL_UNLIKELY(_busy_size + size > xml_memory_page_size)) return allocate_memory_oob(size, out_page); void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; @@ -546,7 +577,7 @@ PUGI__NS_BEGIN // adjust for marker ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); - if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) + if (PUGI_IMPL_UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) { // insert new marker uint32_t* marker = static_cast(result); @@ -623,11 +654,11 @@ PUGI__NS_BEGIN { static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; - PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); + PUGI_IMPL_STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); // allocate memory for string and header block size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); - + // round size up to block alignment boundary size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); @@ -689,7 +720,7 @@ PUGI__NS_BEGIN #endif }; - PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) + PUGI_IMPL_FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) { const size_t large_allocation_threshold = xml_memory_page_size / 4; @@ -726,10 +757,10 @@ PUGI__NS_BEGIN return reinterpret_cast(page) + sizeof(xml_memory_page); } -PUGI__NS_END +PUGI_IMPL_NS_END #ifdef PUGIXML_COMPACT -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN static const uintptr_t compact_alignment_log2 = 2; static const uintptr_t compact_alignment = 1 << compact_alignment_log2; @@ -738,7 +769,7 @@ PUGI__NS_BEGIN public: compact_header(xml_memory_page* page, unsigned int flags) { - PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); + PUGI_IMPL_STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); @@ -749,12 +780,12 @@ PUGI__NS_BEGIN void operator&=(uintptr_t mod) { - _flags &= mod; + _flags &= static_cast(mod); } void operator|=(uintptr_t mod) { - _flags |= mod; + _flags |= static_cast(mod); } uintptr_t operator&(uintptr_t mod) const @@ -764,10 +795,11 @@ PUGI__NS_BEGIN xml_memory_page* get_page() const { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); - const char* page = page_marker - *reinterpret_cast(page_marker); + const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); - return const_cast(reinterpret_cast(page)); + return const_cast(reinterpret_cast(static_cast(page))); } private: @@ -775,21 +807,21 @@ PUGI__NS_BEGIN unsigned char _flags; }; - PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset) + PUGI_IMPL_FN xml_memory_page* compact_get_page(const void* object, int header_offset) { const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); return header->get_page(); } - template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) + template PUGI_IMPL_FN_NO_INLINE T* compact_get_value(const void* object) { - return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object)); + return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object)); } - template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value) + template PUGI_IMPL_FN_NO_INLINE void compact_set_value(const void* object, T* value) { - *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value; + compact_get_page(object, header_offset)->allocator->_hash->insert(object, value); } template class compact_pointer @@ -836,7 +868,7 @@ PUGI__NS_BEGIN { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); - return reinterpret_cast(base + ((_data - 1 + start) << compact_alignment_log2)); + return reinterpret_cast(base + (_data - 1 + start) * compact_alignment); } else return compact_get_value(this); @@ -847,7 +879,7 @@ PUGI__NS_BEGIN T* operator->() const { - return operator T*(); + return *this; } private: @@ -885,7 +917,7 @@ PUGI__NS_BEGIN { xml_memory_page* page = compact_get_page(this, header_offset); - if (PUGI__UNLIKELY(page->compact_shared_parent == 0)) + if (PUGI_IMPL_UNLIKELY(page->compact_shared_parent == 0)) page->compact_shared_parent = value; if (page->compact_shared_parent == value) @@ -914,7 +946,7 @@ PUGI__NS_BEGIN { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); - return reinterpret_cast(base + ((_data - 1 - 65533) << compact_alignment_log2)); + return reinterpret_cast(base + (_data - 1 - 65533) * compact_alignment); } else if (_data == 65534) return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); @@ -927,7 +959,7 @@ PUGI__NS_BEGIN T* operator->() const { - return operator T*(); + return *this; } private: @@ -952,14 +984,15 @@ PUGI__NS_BEGIN { xml_memory_page* page = compact_get_page(this, header_offset); - if (PUGI__UNLIKELY(page->compact_string_base == 0)) + if (PUGI_IMPL_UNLIKELY(page->compact_string_base == 0)) page->compact_string_base = value; ptrdiff_t offset = value - page->compact_string_base; if (static_cast(offset) < (65535 << 7)) { - uint16_t* base = reinterpret_cast(reinterpret_cast(this) - base_offset); + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); if (*base == 0) { @@ -1003,7 +1036,8 @@ PUGI__NS_BEGIN { xml_memory_page* page = compact_get_page(this, header_offset); - const uint16_t* base = reinterpret_cast(reinterpret_cast(this) - base_offset); + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); assert(*base); ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); @@ -1022,7 +1056,7 @@ PUGI__NS_BEGIN private: unsigned char _data; }; -PUGI__NS_END +PUGI_IMPL_NS_END #endif #ifdef PUGIXML_COMPACT @@ -1032,7 +1066,7 @@ namespace pugi { xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) { - PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); + PUGI_IMPL_STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); } impl::compact_header header; @@ -1048,9 +1082,9 @@ namespace pugi struct xml_node_struct { - xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type - 1), namevalue_base(0) + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) { - PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); + PUGI_IMPL_STATIC_ASSERT(sizeof(xml_node_struct) == 12); } impl::compact_header header; @@ -1075,8 +1109,9 @@ namespace pugi { struct xml_attribute_struct { - xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) + xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) { + header = PUGI_IMPL_GETHEADER_IMPL(this, page, 0); } uintptr_t header; @@ -1090,8 +1125,9 @@ namespace pugi struct xml_node_struct { - xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { + header = PUGI_IMPL_GETHEADER_IMPL(this, page, type); } uintptr_t header; @@ -1111,7 +1147,7 @@ namespace pugi } #endif -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN struct xml_extra_buffer { char_t* buffer; @@ -1122,9 +1158,6 @@ PUGI__NS_BEGIN { xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) { - #ifdef PUGIXML_COMPACT - _hash = &hash; - #endif } const char_t* buffer; @@ -1140,19 +1173,19 @@ PUGI__NS_BEGIN { assert(object); - return *PUGI__GETPAGE(object)->allocator; + return *PUGI_IMPL_GETPAGE(object)->allocator; } template inline xml_document_struct& get_document(const Object* object) { assert(object); - return *static_cast(PUGI__GETPAGE(object)->allocator); + return *static_cast(PUGI_IMPL_GETPAGE(object)->allocator); } -PUGI__NS_END +PUGI_IMPL_NS_END // Low-level DOM operations -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) { xml_memory_page* page; @@ -1179,7 +1212,7 @@ PUGI__NS_BEGIN if (a->header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); - alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI_IMPL_GETPAGE(a)); } inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) @@ -1208,7 +1241,7 @@ PUGI__NS_BEGIN child = next; } - alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n)); + alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI_IMPL_GETPAGE(n)); } inline void append_node(xml_node_struct* child, xml_node_struct* node) @@ -1256,12 +1289,14 @@ PUGI__NS_BEGIN child->parent = parent; - if (node->next_sibling) - node->next_sibling->prev_sibling_c = child; + xml_node_struct* next = node->next_sibling; + + if (next) + next->prev_sibling_c = child; else parent->first_child->prev_sibling_c = child; - child->next_sibling = node->next_sibling; + child->next_sibling = next; child->prev_sibling_c = node; node->next_sibling = child; @@ -1273,12 +1308,14 @@ PUGI__NS_BEGIN child->parent = parent; - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = child; + xml_node_struct* prev = node->prev_sibling_c; + + if (prev->next_sibling) + prev->next_sibling = child; else parent->first_child = child; - child->prev_sibling_c = node->prev_sibling_c; + child->prev_sibling_c = prev; child->next_sibling = node; node->prev_sibling_c = child; @@ -1288,15 +1325,18 @@ PUGI__NS_BEGIN { xml_node_struct* parent = node->parent; - if (node->next_sibling) - node->next_sibling->prev_sibling_c = node->prev_sibling_c; - else - parent->first_child->prev_sibling_c = node->prev_sibling_c; + xml_node_struct* next = node->next_sibling; + xml_node_struct* prev = node->prev_sibling_c; - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = node->next_sibling; + if (next) + next->prev_sibling_c = prev; else - parent->first_child = node->next_sibling; + parent->first_child->prev_sibling_c = prev; + + if (prev->next_sibling) + prev->next_sibling = next; + else + parent->first_child = next; node->parent = 0; node->prev_sibling_c = 0; @@ -1340,45 +1380,52 @@ PUGI__NS_BEGIN inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { - if (place->next_attribute) - place->next_attribute->prev_attribute_c = attr; + xml_attribute_struct* next = place->next_attribute; + + if (next) + next->prev_attribute_c = attr; else node->first_attribute->prev_attribute_c = attr; - attr->next_attribute = place->next_attribute; + attr->next_attribute = next; attr->prev_attribute_c = place; place->next_attribute = attr; } inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { - if (place->prev_attribute_c->next_attribute) - place->prev_attribute_c->next_attribute = attr; + xml_attribute_struct* prev = place->prev_attribute_c; + + if (prev->next_attribute) + prev->next_attribute = attr; else node->first_attribute = attr; - attr->prev_attribute_c = place->prev_attribute_c; + attr->prev_attribute_c = prev; attr->next_attribute = place; place->prev_attribute_c = attr; } inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) { - if (attr->next_attribute) - attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; - else - node->first_attribute->prev_attribute_c = attr->prev_attribute_c; + xml_attribute_struct* next = attr->next_attribute; + xml_attribute_struct* prev = attr->prev_attribute_c; - if (attr->prev_attribute_c->next_attribute) - attr->prev_attribute_c->next_attribute = attr->next_attribute; + if (next) + next->prev_attribute_c = prev; else - node->first_attribute = attr->next_attribute; + node->first_attribute->prev_attribute_c = prev; + + if (prev->next_attribute) + prev->next_attribute = next; + else + node->first_attribute = next; attr->prev_attribute_c = 0; attr->next_attribute = 0; } - PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + PUGI_IMPL_FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) { if (!alloc.reserve()) return 0; @@ -1390,7 +1437,7 @@ PUGI__NS_BEGIN return child; } - PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) + PUGI_IMPL_FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) { if (!alloc.reserve()) return 0; @@ -1401,10 +1448,10 @@ PUGI__NS_BEGIN return attr; } -PUGI__NS_END +PUGI_IMPL_NS_END // Helper classes for code generation -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN struct opt_false { enum { value = 0 }; @@ -1414,10 +1461,10 @@ PUGI__NS_BEGIN { enum { value = 1 }; }; -PUGI__NS_END +PUGI_IMPL_NS_END // Unicode utilities -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN inline uint16_t endian_swap(uint16_t value) { return static_cast(((value & 0xff) << 8) | (value >> 8)); @@ -1799,15 +1846,15 @@ PUGI__NS_BEGIN }; #ifdef PUGIXML_WCHAR_MODE - PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) + PUGI_IMPL_FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif -PUGI__NS_END +PUGI_IMPL_NS_END -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN enum chartype_t { ct_parse_pcdata = 1, // \0, &, \r, < @@ -1844,18 +1891,18 @@ PUGI__NS_BEGIN enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " + ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . }; - + static const unsigned char chartypex_table[256] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 + 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 @@ -1871,35 +1918,94 @@ PUGI__NS_BEGIN 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 }; - + #ifdef PUGIXML_WCHAR_MODE - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) + #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) + #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif - #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) - #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) + #define PUGI_IMPL_IS_CHARTYPE(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartype_table) + #define PUGI_IMPL_IS_CHARTYPEX(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartypex_table) - PUGI__FN bool is_little_endian() + PUGI_IMPL_FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } - PUGI__FN xml_encoding get_wchar_encoding() + PUGI_IMPL_FN xml_encoding get_wchar_encoding() { - PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + PUGI_IMPL_STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - else + else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } - PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) + PUGI_IMPL_FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) { + #define PUGI_IMPL_SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } + #define PUGI_IMPL_SCANCHARTYPE(ct) { while (offset < size && PUGI_IMPL_IS_CHARTYPE(data[offset], ct)) offset++; } + + // check if we have a non-empty XML declaration + if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI_IMPL_IS_CHARTYPE(data[5], ct_space))) + return false; + + // scan XML declaration until the encoding field + for (size_t i = 6; i + 1 < size; ++i) + { + // declaration can not contain ? in quoted values + if (data[i] == '?') + return false; + + if (data[i] == 'e' && data[i + 1] == 'n') + { + size_t offset = i; + + // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed + PUGI_IMPL_SCANCHAR('e'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('c'); PUGI_IMPL_SCANCHAR('o'); + PUGI_IMPL_SCANCHAR('d'); PUGI_IMPL_SCANCHAR('i'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('g'); + + // S? = S? + PUGI_IMPL_SCANCHARTYPE(ct_space); + PUGI_IMPL_SCANCHAR('='); + PUGI_IMPL_SCANCHARTYPE(ct_space); + + // the only two valid delimiters are ' and " + uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; + + PUGI_IMPL_SCANCHAR(delimiter); + + size_t start = offset; + + out_encoding = data + offset; + + PUGI_IMPL_SCANCHARTYPE(ct_symbol); + + out_length = offset - start; + + PUGI_IMPL_SCANCHAR(delimiter); + + return true; + } + } + + return false; + + #undef PUGI_IMPL_SCANCHAR + #undef PUGI_IMPL_SCANCHARTYPE + } + + PUGI_IMPL_FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) + { + // skip encoding autodetection if input buffer is too small + if (size < 4) return encoding_utf8; + + uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + // look for BOM in first few bytes if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; @@ -1912,17 +2018,36 @@ PUGI__NS_BEGIN if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le; if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be; if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le; - if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8; // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early) if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be; if (d0 == 0x3c && d1 == 0) return encoding_utf16_le; - // no known BOM detected, assume utf8 + // no known BOM detected; parse declaration + const uint8_t* enc = 0; + size_t enc_length = 0; + + if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length)) + { + // iso-8859-1 (case-insensitive) + if (enc_length == 10 + && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o' + && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9' + && enc[8] == '-' && enc[9] == '1') + return encoding_latin1; + + // latin1 (case-insensitive) + if (enc_length == 6 + && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't' + && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n' + && enc[5] == '1') + return encoding_latin1; + } + return encoding_utf8; } - PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size) + PUGI_IMPL_FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); @@ -1936,18 +2061,13 @@ PUGI__NS_BEGIN // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; - // skip encoding autodetection if input buffer is too small - if (size < 4) return encoding_utf8; - // try to guess encoding (based on XML specification, Appendix F.1) const uint8_t* data = static_cast(contents); - PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; - - return guess_buffer_encoding(d0, d1, d2, d3); + return guess_buffer_encoding(data, size); } - PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + PUGI_IMPL_FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { size_t length = size / sizeof(char_t); @@ -1976,13 +2096,13 @@ PUGI__NS_BEGIN } #ifdef PUGIXML_WCHAR_MODE - PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) + PUGI_IMPL_FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) { return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); } - PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + PUGI_IMPL_FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const char_t* data = static_cast(contents); size_t length = size / sizeof(char_t); @@ -2011,7 +2131,7 @@ PUGI__NS_BEGIN return true; } - template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + template PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); @@ -2036,7 +2156,7 @@ PUGI__NS_BEGIN return true; } - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // get native encoding xml_encoding wchar_encoding = get_wchar_encoding(); @@ -2077,11 +2197,11 @@ PUGI__NS_BEGIN if (encoding == encoding_latin1) return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); // unreachable return false; } #else - template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + template PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); @@ -2106,7 +2226,7 @@ PUGI__NS_BEGIN return true; } - PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) + PUGI_IMPL_FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) { for (size_t i = 0; i < size; ++i) if (data[i] > 127) @@ -2115,7 +2235,7 @@ PUGI__NS_BEGIN return size; } - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + PUGI_IMPL_FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const uint8_t* data = static_cast(contents); size_t data_length = size; @@ -2152,7 +2272,7 @@ PUGI__NS_BEGIN return true; } - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required if (encoding == encoding_utf8) @@ -2182,30 +2302,30 @@ PUGI__NS_BEGIN if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); // unreachable return false; } #endif - PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) + PUGI_IMPL_FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters return wchar_decoder::process(str, length, 0, utf8_counter()); } - PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + PUGI_IMPL_FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); - + assert(begin + size == end); (void)!end; (void)!size; } - + #ifndef PUGIXML_NO_STL - PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) + PUGI_IMPL_FN std::string as_utf8_impl(const wchar_t* str, size_t length) { // first pass: get length in utf8 characters size_t size = as_utf8_begin(str, length); @@ -2220,7 +2340,7 @@ PUGI__NS_BEGIN return result; } - PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) + PUGI_IMPL_FN std::basic_string as_wide_impl(const char* str, size_t size) { const uint8_t* data = reinterpret_cast(str); @@ -2263,15 +2383,17 @@ PUGI__NS_BEGIN } template - PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) + PUGI_IMPL_FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) { + assert((header & header_mask) == 0 || dest); // header bit indicates whether dest was previously allocated + if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory - xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; if (header & header_mask) alloc->deallocate_string(dest); - + // mark the string as not allocated dest = 0; header &= ~header_mask; @@ -2283,12 +2405,12 @@ PUGI__NS_BEGIN // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, source_length * sizeof(char_t)); dest[source_length] = 0; - + return true; } else { - xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; if (!alloc->reserve()) return false; @@ -2302,7 +2424,7 @@ PUGI__NS_BEGIN // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); - + // the string is now allocated, so set the flag dest = buf; header |= header_mask; @@ -2315,11 +2437,11 @@ PUGI__NS_BEGIN { char_t* end; size_t size; - + gap(): end(0), size(0) { } - + // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) @@ -2330,14 +2452,14 @@ PUGI__NS_BEGIN assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } - + s += count; // end of current gap - + // "merge" two gaps end = s; size += count; } - + // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { @@ -2352,8 +2474,8 @@ PUGI__NS_BEGIN else return s; } }; - - PUGI__FN char_t* strconv_escape(char_t* s, gap& g) + + PUGI_IMPL_FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; @@ -2384,7 +2506,7 @@ PUGI__NS_BEGIN ch = *++stre; } - + ++stre; } else // &#... (dec code) @@ -2395,7 +2517,7 @@ PUGI__NS_BEGIN for (;;) { - if (static_cast(static_cast(ch) - '0') <= 9) + if (static_cast(ch - '0') <= 9) ucsc = 10 * ucsc + (ch - '0'); else if (ch == ';') break; @@ -2404,7 +2526,7 @@ PUGI__NS_BEGIN ch = *++stre; } - + ++stre; } @@ -2413,7 +2535,7 @@ PUGI__NS_BEGIN #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif - + g.push(s, stre - s); return stre; } @@ -2428,7 +2550,7 @@ PUGI__NS_BEGIN { *s++ = '&'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2453,7 +2575,7 @@ PUGI__NS_BEGIN { *s++ = '>'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2466,7 +2588,7 @@ PUGI__NS_BEGIN { *s++ = '<'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2479,7 +2601,7 @@ PUGI__NS_BEGIN { *s++ = '"'; ++stre; - + g.push(s, stre - s); return stre; } @@ -2489,41 +2611,41 @@ PUGI__NS_BEGIN default: break; } - + return stre; } // Parser utilities - #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) - #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } - #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) - #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } - #define PUGI__POPNODE() { cursor = cursor->parent; } - #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } - #define PUGI__SCANWHILE(X) { while (X) ++s; } - #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; } } - #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } - #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) - #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } + #define PUGI_IMPL_ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + #define PUGI_IMPL_SKIPWS() { while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) ++s; } + #define PUGI_IMPL_OPTSET(OPT) ( optmsk & (OPT) ) + #define PUGI_IMPL_PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); } + #define PUGI_IMPL_POPNODE() { cursor = cursor->parent; } + #define PUGI_IMPL_SCANFOR(X) { while (*s != 0 && !(X)) ++s; } + #define PUGI_IMPL_SCANWHILE(X) { while (X) ++s; } + #define PUGI_IMPL_SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI_IMPL_UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 3; break; } s += 4; } } + #define PUGI_IMPL_ENDSEG() { ch = *s; *s = 0; ++s; } + #define PUGI_IMPL_THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) + #define PUGI_IMPL_CHECK_ERROR(err, m) { if (*s == 0) PUGI_IMPL_THROW_ERROR(err, m); } - PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) + PUGI_IMPL_FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; - + while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_comment)); + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } - else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here + else if (s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; - + return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) @@ -2534,24 +2656,24 @@ PUGI__NS_BEGIN } } - PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) + PUGI_IMPL_FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; - + while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_cdata)); + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } - else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here + else if (s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; - + return s + 1; } else if (*s == 0) @@ -2561,9 +2683,9 @@ PUGI__NS_BEGIN else ++s; } } - + typedef char_t* (*strconv_pcdata_t)(char_t*); - + template struct strconv_pcdata_impl { static char_t* parse(char_t* s) @@ -2574,24 +2696,24 @@ PUGI__NS_BEGIN while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata)); if (*s == '<') // PCDATA ends here { char_t* end = g.flush(s); if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; - + return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a - + if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') @@ -2603,7 +2725,7 @@ PUGI__NS_BEGIN char_t* end = g.flush(s); if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; @@ -2614,12 +2736,12 @@ PUGI__NS_BEGIN } } }; - - PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) - { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) + PUGI_IMPL_FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); + + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; @@ -2629,12 +2751,12 @@ PUGI__NS_BEGIN case 5: return strconv_pcdata_impl::parse; case 6: return strconv_pcdata_impl::parse; case 7: return strconv_pcdata_impl::parse; - default: assert(false); return 0; // should not get here + default: assert(false); return 0; // unreachable } } typedef char_t* (*strconv_attribute_t)(char_t*, char_t); - + template struct strconv_attribute_impl { static char_t* parse_wnorm(char_t* s, char_t end_quote) @@ -2642,38 +2764,38 @@ PUGI__NS_BEGIN gap g; // trim leading whitespaces - if (PUGI__IS_CHARTYPE(*s, ct_space)) + if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { char_t* str = s; - + do ++str; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - + while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); + g.push(s, str - s); } while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); + if (*s == end_quote) { char_t* str = g.flush(s); - + do *str-- = 0; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - + while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); + return s + 1; } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) + else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; - - if (PUGI__IS_CHARTYPE(*s, ct_space)) + + if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; - while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; - + while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)) ++str; + g.push(s, str - s); } } @@ -2695,20 +2817,20 @@ PUGI__NS_BEGIN while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws)); + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) + else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { if (*s == '\r') { *s++ = ' '; - + if (*s == '\n') g.push(s, 1); } else *s++ = ' '; @@ -2731,18 +2853,18 @@ PUGI__NS_BEGIN while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } else if (*s == '\r') { *s++ = '\n'; - + if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') @@ -2763,12 +2885,12 @@ PUGI__NS_BEGIN while (true) { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); + if (*s == end_quote) { *g.flush(s) = 0; - + return s + 1; } else if (opt_escape::value && *s == '&') @@ -2784,11 +2906,11 @@ PUGI__NS_BEGIN } }; - PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) + PUGI_IMPL_FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; @@ -2806,7 +2928,7 @@ PUGI__NS_BEGIN case 13: return strconv_attribute_impl::parse_wnorm; case 14: return strconv_attribute_impl::parse_wnorm; case 15: return strconv_attribute_impl::parse_wnorm; - default: assert(false); return 0; // should not get here + default: assert(false); return 0; // unreachable } } @@ -2821,18 +2943,12 @@ PUGI__NS_BEGIN struct xml_parser { - xml_allocator alloc; - xml_allocator* alloc_state; + xml_allocator* alloc; char_t* error_offset; xml_parse_status error_status; - - xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok) - { - } - ~xml_parser() + xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) { - *alloc_state = alloc; } // DOCTYPE consists of nested sections of the following possible types: @@ -2848,8 +2964,8 @@ PUGI__NS_BEGIN { // quoted string char_t ch = *s++; - PUGI__SCANFOR(*s == ch); - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + PUGI_IMPL_SCANFOR(*s == ch); + if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s++; } @@ -2857,20 +2973,20 @@ PUGI__NS_BEGIN { // s += 2; - PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + PUGI_IMPL_SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s += 3; } - else PUGI__THROW_ERROR(status_bad_doctype, s); + else PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); return s; } @@ -2903,7 +3019,7 @@ PUGI__NS_BEGIN else s++; } - PUGI__THROW_ERROR(status_bad_doctype, s); + PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); } char_t* parse_doctype_group(char_t* s, char_t endch) @@ -2947,7 +3063,7 @@ PUGI__NS_BEGIN else s++; } - if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); + if (depth != 0 || endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); return s; } @@ -2965,31 +3081,31 @@ PUGI__NS_BEGIN { ++s; - if (PUGI__OPTSET(parse_comments)) + if (PUGI_IMPL_OPTSET(parse_comments)) { - PUGI__PUSHNODE(node_comment); // Append a new node on the tree. + PUGI_IMPL_PUSHNODE(node_comment); // Append a new node on the tree. cursor->value = s; // Save the offset. } - if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) + if (PUGI_IMPL_OPTSET(parse_eol) && PUGI_IMPL_OPTSET(parse_comments)) { s = strconv_comment(s, endch); - if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); + if (!s) PUGI_IMPL_THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_comment, s); + PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')); + PUGI_IMPL_CHECK_ERROR(status_bad_comment, s); - if (PUGI__OPTSET(parse_comments)) + if (PUGI_IMPL_OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } - else PUGI__THROW_ERROR(status_bad_comment, s); + else PUGI_IMPL_THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { @@ -2998,22 +3114,22 @@ PUGI__NS_BEGIN { ++s; - if (PUGI__OPTSET(parse_cdata)) + if (PUGI_IMPL_OPTSET(parse_cdata)) { - PUGI__PUSHNODE(node_cdata); // Append a new node on the tree. + PUGI_IMPL_PUSHNODE(node_cdata); // Append a new node on the tree. cursor->value = s; // Save the offset. - if (PUGI__OPTSET(parse_eol)) + if (PUGI_IMPL_OPTSET(parse_eol)) { s = strconv_cdata(s, endch); - if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); + if (!s) PUGI_IMPL_THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); + PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); + PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); *s++ = 0; // Zero-terminate this segment. } @@ -3021,21 +3137,21 @@ PUGI__NS_BEGIN else // Flagged for discard, but we still have to scan for the terminator. { // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); + PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); + PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } - else PUGI__THROW_ERROR(status_bad_cdata, s); + else PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); } - 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')) + else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI_IMPL_ENDSWITH(s[6], 'E')) { s -= 2; - if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); + if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); char_t* mark = s + 9; @@ -3045,18 +3161,18 @@ PUGI__NS_BEGIN assert((*s == 0 && endch == '>') || *s == '>'); if (*s) *s++ = 0; - if (PUGI__OPTSET(parse_doctype)) + if (PUGI_IMPL_OPTSET(parse_doctype)) { - while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; + while (PUGI_IMPL_IS_CHARTYPE(*mark, ct_space)) ++mark; - PUGI__PUSHNODE(node_doctype); + PUGI_IMPL_PUSHNODE(node_doctype); cursor->value = mark; } } - else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); - else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); - else PUGI__THROW_ERROR(status_unrecognized_tag, s); + else if (*s == 0 && endch == '-') PUGI_IMPL_THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); + else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s); return s; } @@ -3073,50 +3189,50 @@ PUGI__NS_BEGIN // read PI target char_t* target = s; - if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); + if (!PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); - PUGI__CHECK_ERROR(status_bad_pi, s); + PUGI_IMPL_SCANWHILE(PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)); + PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); // determine node type; stricmp / strcasecmp is not portable bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; - if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) + if (declaration ? PUGI_IMPL_OPTSET(parse_declaration) : PUGI_IMPL_OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations - if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); + if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); - PUGI__PUSHNODE(node_declaration); + PUGI_IMPL_PUSHNODE(node_declaration); } else { - PUGI__PUSHNODE(node_pi); + PUGI_IMPL_PUSHNODE(node_pi); } cursor->name = target; - PUGI__ENDSEG(); + PUGI_IMPL_ENDSEG(); // parse value/attributes if (ch == '?') { // empty node - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); + if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); s += (*s == '>'); - PUGI__POPNODE(); + PUGI_IMPL_POPNODE(); } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) + else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { - PUGI__SKIPWS(); + PUGI_IMPL_SKIPWS(); // scan for tag end char_t* value = s; - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); + PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); + PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); if (declaration) { @@ -3131,20 +3247,20 @@ PUGI__NS_BEGIN // store value and step over > cursor->value = value; - PUGI__POPNODE(); + PUGI_IMPL_POPNODE(); - PUGI__ENDSEG(); + PUGI_IMPL_ENDSEG(); s += (*s == '>'); } } - else PUGI__THROW_ERROR(status_bad_pi, s); + else PUGI_IMPL_THROW_ERROR(status_bad_pi, s); } else { // scan for tag end - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); + PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); + PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); s += (s[1] == '>' ? 2 : 1); } @@ -3159,10 +3275,11 @@ PUGI__NS_BEGIN { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); - + char_t ch = 0; xml_node_struct* cursor = root; char_t* mark = s; + char_t* merged_pcdata = s; while (*s != 0) { @@ -3171,47 +3288,47 @@ PUGI__NS_BEGIN ++s; LOC_TAG: - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { - PUGI__PUSHNODE(node_element); // Append a new node to the tree. + PUGI_IMPL_PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) + else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { - PUGI__SKIPWS(); // Eat any whitespace. - - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + PUGI_IMPL_SKIPWS(); // Eat any whitespace. + + if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { - xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute. - if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); + xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. + if (!a) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. - if (PUGI__IS_CHARTYPE(ch, ct_space)) + if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { - PUGI__SKIPWS(); // Eat any whitespace. + PUGI_IMPL_SKIPWS(); // Eat any whitespace. ch = *s; ++s; } - + if (ch == '=') // '<... #=...' { - PUGI__SKIPWS(); // Eat any whitespace. + PUGI_IMPL_SKIPWS(); // Eat any whitespace. if (*s == '"' || *s == '\'') // '<... #="...' { @@ -3220,34 +3337,34 @@ PUGI__NS_BEGIN a->value = s; // Save the offset. s = strconv_attribute(s, ch); - - if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); + + if (!s) PUGI_IMPL_THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; // Whitespaces, / and > are ok, symbols and EOF are wrong, // everything else will be detected - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); + if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } - else PUGI__THROW_ERROR(status_bad_attribute, s); + else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } - else PUGI__THROW_ERROR(status_bad_attribute, s); + else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; - + if (*s == '>') { - PUGI__POPNODE(); + PUGI_IMPL_POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { - PUGI__POPNODE(); + PUGI_IMPL_POPNODE(); break; } - else PUGI__THROW_ERROR(status_bad_start_element, s); + else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { @@ -3259,16 +3376,16 @@ PUGI__NS_BEGIN { break; } - else PUGI__THROW_ERROR(status_bad_start_element, s); + else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); + if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); - PUGI__POPNODE(); // Pop. + PUGI_IMPL_POPNODE(); // Pop. s += (*s == '>'); } @@ -3276,40 +3393,42 @@ PUGI__NS_BEGIN { // we stepped over null terminator, backtrack & handle closing tag --s; - - if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); + + if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } - else PUGI__THROW_ERROR(status_bad_start_element, s); + else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; + mark = s; + char_t* name = cursor->name; - if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); - - while (PUGI__IS_CHARTYPE(*s, ct_symbol)) + if (!name) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); + + while (PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)) { - if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); + if (*s++ != *name++) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); } if (*name) { - if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); - else PUGI__THROW_ERROR(status_end_element_mismatch, s); + if (*s == 0 && name[0] == endch && name[1] == 0) PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); + else PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); } - - PUGI__POPNODE(); // Pop. - PUGI__SKIPWS(); + PUGI_IMPL_POPNODE(); // Pop. + + PUGI_IMPL_SKIPWS(); if (*s == 0) { - if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); } else { - if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + if (*s != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); ++s; } } @@ -3319,56 +3438,81 @@ PUGI__NS_BEGIN if (!s) return s; assert(cursor); - if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES; + if (PUGI_IMPL_NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES; } else if (*s == '!') // 'first_child) continue; } } - if (!PUGI__OPTSET(parse_trim_pcdata)) + if (!PUGI_IMPL_OPTSET(parse_trim_pcdata)) s = mark; - - if (cursor->parent || PUGI__OPTSET(parse_fragment)) + + if (cursor->parent || PUGI_IMPL_OPTSET(parse_fragment)) { - PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. - cursor->value = s; // Save the offset. + char_t* parsed_pcdata = s; s = strconv_pcdata(s); - - PUGI__POPNODE(); // Pop since this is a standalone. - + + if (PUGI_IMPL_OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) + { + cursor->value = parsed_pcdata; // Save the offset. + } + else if (PUGI_IMPL_OPTSET(parse_merge_pcdata) && cursor->first_child && PUGI_IMPL_NODETYPE(cursor->first_child->prev_sibling_c) == node_pcdata) + { + assert(merged_pcdata >= cursor->first_child->prev_sibling_c->value); + + // Catch up to the end of last parsed value; only needed for the first fragment. + merged_pcdata += strlength(merged_pcdata); + + size_t length = strlength(parsed_pcdata); + + // Must use memmove instead of memcpy as this move may overlap + memmove(merged_pcdata, parsed_pcdata, (length + 1) * sizeof(char_t)); + merged_pcdata += length; + } + else + { + xml_node_struct* prev_cursor = cursor; + PUGI_IMPL_PUSHNODE(node_pcdata); // Append a new node on the tree. + + cursor->value = parsed_pcdata; // Save the offset. + merged_pcdata = parsed_pcdata; // Used for parse_merge_pcdata above, cheaper to save unconditionally + + cursor = prev_cursor; // Pop since this is a standalone. + } + if (!*s) break; } else { - PUGI__SCANFOR(*s == '<'); // '...<' + PUGI_IMPL_SCANFOR(*s == '<'); // '...<' if (!*s) break; - + ++s; } @@ -3378,7 +3522,7 @@ PUGI__NS_BEGIN } // check that last tag is closed - if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); + if (cursor != root) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, s); return s; } @@ -3400,7 +3544,7 @@ PUGI__NS_BEGIN { while (node) { - if (PUGI__NODETYPE(node) == node_element) return true; + if (PUGI_IMPL_NODETYPE(node) == node_element) return true; node = node->next_sibling; } @@ -3412,18 +3556,18 @@ PUGI__NS_BEGIN { // early-out for empty documents if (length == 0) - return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); + return make_parse_result(PUGI_IMPL_OPTSET(parse_fragment) ? status_ok : status_no_document_element); // get last child of the root before parsing xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; - + // create parser on stack xml_parser parser(static_cast(xmldoc)); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; - + // skip BOM to make sure it does not end up as part of parse output char_t* buffer_data = parse_skip_bom(buffer); @@ -3440,9 +3584,9 @@ PUGI__NS_BEGIN return make_parse_result(status_unrecognized_tag, length - 1); // check if there are any element nodes parsed - xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child; + xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child + 0; - if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) + if (!PUGI_IMPL_OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) return make_parse_result(status_no_document_element, length - 1); } else @@ -3457,7 +3601,7 @@ PUGI__NS_BEGIN }; // Output facilities - PUGI__FN xml_encoding get_write_native_encoding() + PUGI_IMPL_FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); @@ -3466,7 +3610,7 @@ PUGI__NS_BEGIN #endif } - PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) + PUGI_IMPL_FN xml_encoding get_write_encoding(xml_encoding encoding) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); @@ -3484,18 +3628,18 @@ PUGI__NS_BEGIN return encoding_utf8; } - template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) + template PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) { - PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); return static_cast(end - dest) * sizeof(*dest); } - template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) + template PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) { - PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); @@ -3509,15 +3653,15 @@ PUGI__NS_BEGIN } #ifdef PUGIXML_WCHAR_MODE - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 1) return 0; - // discard last character if it's the lead of a surrogate pair + // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } - 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) + PUGI_IMPL_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) { // only endian-swapping is required if (need_endian_swap_utf(encoding, get_wchar_encoding())) @@ -3526,7 +3670,7 @@ PUGI__NS_BEGIN return length * sizeof(char_t); } - + // convert to utf8 if (encoding == encoding_utf8) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); @@ -3551,11 +3695,11 @@ PUGI__NS_BEGIN if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); // unreachable return 0; } #else - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 5) return 0; @@ -3571,7 +3715,7 @@ PUGI__NS_BEGIN return length; } - 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) + PUGI_IMPL_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) { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { @@ -3590,7 +3734,7 @@ PUGI__NS_BEGIN if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); - assert(!"Invalid encoding"); + assert(false && "Invalid encoding"); // unreachable return 0; } #endif @@ -3603,7 +3747,7 @@ PUGI__NS_BEGIN public: xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) { - PUGI__STATIC_ASSERT(bufcapacity >= 8); + PUGI_IMPL_STATIC_ASSERT(bufcapacity >= 8); } size_t flush() @@ -3809,15 +3953,15 @@ PUGI__NS_BEGIN xml_encoding encoding; }; - PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + PUGI_IMPL_FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { while (*s) { const char_t* prev = s; - + // While *s is a usual symbol - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); - + PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPEX(ss, type)); + writer.write_buffer(prev, static_cast(s - prev)); switch (*s) @@ -3836,7 +3980,17 @@ PUGI__NS_BEGIN ++s; break; case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); + if (flags & format_attribute_single_quote) + writer.write('"'); + else + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + case '\'': + if (flags & format_attribute_single_quote) + writer.write('&', 'a', 'p', 'o', 's', ';'); + else + writer.write('\''); ++s; break; default: // s is not a usual symbol @@ -3844,21 +3998,22 @@ PUGI__NS_BEGIN unsigned int ch = static_cast(*s++); assert(ch < 32); - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + if (!(flags & format_skip_control_chars)) + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } } - PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) + PUGI_IMPL_FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { if (flags & format_no_escapes) writer.write_string(s); else - text_output_escaped(writer, s, type); + text_output_escaped(writer, s, type, flags); } - PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) + PUGI_IMPL_FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) { do { @@ -3880,7 +4035,7 @@ PUGI__NS_BEGIN while (*s); } - PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) + PUGI_IMPL_FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) { switch (indent_length) { @@ -3920,7 +4075,7 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) + PUGI_IMPL_FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) { writer.write('<', '!', '-', '-'); @@ -3945,7 +4100,7 @@ PUGI__NS_BEGIN writer.write('-', '-', '>'); } - PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) + PUGI_IMPL_FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) { while (*s) { @@ -3966,9 +4121,10 @@ PUGI__NS_BEGIN } } - 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) + PUGI_IMPL_FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { @@ -3983,20 +4139,20 @@ PUGI__NS_BEGIN writer.write(' '); } - writer.write_string(a->name ? a->name : default_name); - writer.write('=', '"'); + writer.write_string(a->name ? a->name + 0 : default_name); + writer.write('=', enquotation_char); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); - writer.write('"'); + writer.write(enquotation_char); } } - 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) + PUGI_IMPL_FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; + const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<'); writer.write_string(name); @@ -4004,35 +4160,72 @@ PUGI__NS_BEGIN if (node->first_attribute) node_output_attributes(writer, node, indent, indent_length, flags, depth); - if (!node->first_child) + // element nodes can have value if parse_embed_pcdata was used + if (!node->value) { - writer.write(' ', '/', '>'); + if (!node->first_child) + { + if (flags & format_no_empty_element_tags) + { + writer.write('>', '<', '/'); + writer.write_string(name); + writer.write('>'); - return false; + return false; + } + else + { + if ((flags & format_raw) == 0) + writer.write(' '); + + writer.write('/', '>'); + + return false; + } + } + else + { + writer.write('>'); + + return true; + } } else { writer.write('>'); - return true; + text_output(writer, node->value, ctx_special_pcdata, flags); + + if (!node->first_child) + { + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + return true; + } } } - PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) + PUGI_IMPL_FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; + const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<', '/'); writer.write_string(name); writer.write('>'); } - PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + PUGI_IMPL_FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - switch (PUGI__NODETYPE(node)) + switch (PUGI_IMPL_NODETYPE(node)) { case node_pcdata: text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); @@ -4048,7 +4241,7 @@ PUGI__NS_BEGIN case node_pi: writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); + writer.write_string(node->name ? node->name + 0 : default_name); if (node->value) { @@ -4061,7 +4254,7 @@ PUGI__NS_BEGIN case node_declaration: writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); + writer.write_string(node->name ? node->name + 0 : default_name); node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); writer.write('?', '>'); break; @@ -4080,7 +4273,7 @@ PUGI__NS_BEGIN break; default: - assert(!"Invalid node type"); + assert(false && "Invalid node type"); // unreachable } } @@ -4090,7 +4283,7 @@ PUGI__NS_BEGIN indent_indent = 2 }; - PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) + PUGI_IMPL_FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; unsigned int indent_flags = indent_indent; @@ -4102,7 +4295,7 @@ PUGI__NS_BEGIN assert(node); // begin writing current node - if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) + if (PUGI_IMPL_NODETYPE(node) == node_pcdata || PUGI_IMPL_NODETYPE(node) == node_cdata) { node_output_simple(writer, node, flags); @@ -4116,18 +4309,22 @@ PUGI__NS_BEGIN if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); - if (PUGI__NODETYPE(node) == node_element) + if (PUGI_IMPL_NODETYPE(node) == node_element) { indent_flags = indent_newline | indent_indent; if (node_output_start(writer, node, indent, indent_length, flags, depth)) { + // element nodes can have value if parse_embed_pcdata was used + if (node->value) + indent_flags = 0; + node = node->first_child; depth++; continue; } } - else if (PUGI__NODETYPE(node) == node_document) + else if (PUGI_IMPL_NODETYPE(node) == node_document) { indent_flags = indent_indent; @@ -4157,7 +4354,7 @@ PUGI__NS_BEGIN node = node->parent; // write closing node - if (PUGI__NODETYPE(node) == node_element) + if (PUGI_IMPL_NODETYPE(node) == node_element) { depth--; @@ -4179,11 +4376,11 @@ PUGI__NS_BEGIN writer.write('\n'); } - PUGI__FN bool has_declaration(xml_node_struct* node) + PUGI_IMPL_FN bool has_declaration(xml_node_struct* node) { for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) { - xml_node_type type = PUGI__NODETYPE(child); + xml_node_type type = PUGI_IMPL_NODETYPE(child); if (type == node_declaration) return true; if (type == node_element) return false; @@ -4192,7 +4389,7 @@ PUGI__NS_BEGIN return false; } - PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) + PUGI_IMPL_FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) { for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) if (a == attr) @@ -4201,12 +4398,12 @@ PUGI__NS_BEGIN return false; } - PUGI__FN bool allow_insert_attribute(xml_node_type parent) + PUGI_IMPL_FN bool allow_insert_attribute(xml_node_type parent) { return parent == node_element || parent == node_declaration; } - PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) + PUGI_IMPL_FN bool allow_insert_child(xml_node_type parent, xml_node_type child) { if (parent != node_document && parent != node_element) return false; if (child == node_document || child == node_null) return false; @@ -4215,7 +4412,7 @@ PUGI__NS_BEGIN return true; } - PUGI__FN bool allow_move(xml_node parent, xml_node child) + PUGI_IMPL_FN bool allow_move(xml_node parent, xml_node child) { // check that child can be a child of parent if (!allow_insert_child(parent.type(), child.type())) @@ -4240,9 +4437,9 @@ PUGI__NS_BEGIN } template - PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) + PUGI_IMPL_FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) { - assert(!dest && (header & header_mask) == 0); + assert(!dest && (header & header_mask) == 0); // copies are performed into fresh nodes if (source) { @@ -4259,7 +4456,7 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) + PUGI_IMPL_FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) { node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); @@ -4276,7 +4473,7 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) + PUGI_IMPL_FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) { xml_allocator& alloc = get_allocator(dn); xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; @@ -4288,9 +4485,13 @@ PUGI__NS_BEGIN while (sit && sit != sn) { + // loop invariant: dit is inside the subtree rooted at dn + assert(dit); + + // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop if (sit != dn) { - xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); + xml_node_struct* copy = append_new_node(dit, alloc, PUGI_IMPL_NODETYPE(sit)); if (copy) { @@ -4316,12 +4517,17 @@ PUGI__NS_BEGIN sit = sit->parent; dit = dit->parent; + + // loop invariant: dit is inside the subtree rooted at dn while sit is inside sn + assert(sit == sn || dit); } while (sit != sn); } + + assert(!sit || dit == dn->parent); } - PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + PUGI_IMPL_FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) { xml_allocator& alloc = get_allocator(da); xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; @@ -4332,18 +4538,18 @@ PUGI__NS_BEGIN inline bool is_text_node(xml_node_struct* node) { - xml_node_type type = PUGI__NODETYPE(node); + xml_node_type type = PUGI_IMPL_NODETYPE(node); return type == node_pcdata || type == node_cdata; } // get value with conversion functions - template U string_to_integer(const char_t* value, U minneg, U maxpos) + template PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) { U result = 0; const char_t* s = value; - while (PUGI__IS_CHARTYPE(*s, ct_space)) + while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) s++; bool negative = (*s == '-'); @@ -4356,6 +4562,10 @@ PUGI__NS_BEGIN { s += 2; + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + const char_t* start = s; for (;;) @@ -4376,6 +4586,10 @@ PUGI__NS_BEGIN } else { + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + const char_t* start = s; for (;;) @@ -4390,32 +4604,39 @@ PUGI__NS_BEGIN size_t digits = static_cast(s - start); - PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); + PUGI_IMPL_STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; - const char max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; + const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; const size_t high_bit = sizeof(U) * 8 - 1; overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); } if (negative) - return (overflow || result > minneg) ? 0 - minneg : 0 - result; + { + // Workaround for crayc++ CC-3059: Expected no overflow in routine. + #ifdef _CRAYC + return (overflow || result > ~minv + 1) ? minv : ~result + 1; + #else + return (overflow || result > 0 - minv) ? minv : 0 - result; + #endif + } else - return (overflow || result > maxpos) ? maxpos : result; + return (overflow || result > maxv) ? maxv : result; } - PUGI__FN int get_value_int(const char_t* value) + PUGI_IMPL_FN int get_value_int(const char_t* value) { return string_to_integer(value, static_cast(INT_MIN), INT_MAX); } - PUGI__FN unsigned int get_value_uint(const char_t* value) + PUGI_IMPL_FN unsigned int get_value_uint(const char_t* value) { return string_to_integer(value, 0, UINT_MAX); } - PUGI__FN double get_value_double(const char_t* value) + PUGI_IMPL_FN double get_value_double(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); @@ -4424,7 +4645,7 @@ PUGI__NS_BEGIN #endif } - PUGI__FN float get_value_float(const char_t* value) + PUGI_IMPL_FN float get_value_float(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); @@ -4433,7 +4654,7 @@ PUGI__NS_BEGIN #endif } - PUGI__FN bool get_value_bool(const char_t* value) + PUGI_IMPL_FN bool get_value_bool(const char_t* value) { // only look at first char char_t first = *value; @@ -4443,19 +4664,18 @@ PUGI__NS_BEGIN } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long get_value_llong(const char_t* value) + PUGI_IMPL_FN long long get_value_llong(const char_t* value) { return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); } - PUGI__FN unsigned long long get_value_ullong(const char_t* value) + PUGI_IMPL_FN unsigned long long get_value_ullong(const char_t* value) { return string_to_integer(value, 0, ULLONG_MAX); } #endif - template - PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) + template PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) { char_t* result = end - 1; U rest = negative ? 0 - value : value; @@ -4477,87 +4697,56 @@ PUGI__NS_BEGIN // set value with conversion functions template - PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char (&buf)[128]) + PUGI_IMPL_FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; + assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); size_t offset = 0; for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; return strcpy_insitu(dest, header, header_mask, wbuf, offset); #else - return strcpy_insitu(dest, header, header_mask, buf, strlength(buf)); + return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); #endif } - template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value) + template + PUGI_IMPL_FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); - char_t* begin = integer_to_string(buf, end, value, value < 0); + char_t* begin = integer_to_string(buf, end, value, negative); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned int value) - { - char_t buf[64]; - char_t* end = buf + sizeof(buf) / sizeof(buf[0]); - char_t* begin = integer_to_string(buf, end, value, false); - - return strcpy_insitu(dest, header, header_mask, begin, end - begin); - } - - template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) { char buf[128]; - sprintf(buf, "%.9g", value); + PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, double(value)); return set_value_ascii(dest, header, header_mask, buf); } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) { char buf[128]; - sprintf(buf, "%.17g", value); + PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, value); return set_value_ascii(dest, header, header_mask, buf); } - + template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, bool value) + PUGI_IMPL_FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) { return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); } -#ifdef PUGIXML_HAS_LONG_LONG - template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, long long value) - { - char_t buf[64]; - char_t* end = buf + sizeof(buf) / sizeof(buf[0]); - char_t* begin = integer_to_string(buf, end, value, value < 0); - - return strcpy_insitu(dest, header, header_mask, begin, end - begin); - } - - template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned long long value) - { - char_t buf[64]; - char_t* end = buf + sizeof(buf) / sizeof(buf[0]); - char_t* begin = integer_to_string(buf, end, value, false); - - return strcpy_insitu(dest, header, header_mask, begin, end - begin); - } -#endif - - 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) + PUGI_IMPL_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) { // check input buffer if (!contents && size) return make_parse_result(status_io_error); @@ -4565,12 +4754,19 @@ PUGI__NS_BEGIN // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it + auto_deleter contents_guard(own ? contents : 0, xml_memory::deallocate); + // get private buffer char_t* buffer = 0; size_t length = 0; + // coverity[var_deref_model] if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it + contents_guard.release(); + // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); @@ -4590,9 +4786,19 @@ PUGI__NS_BEGIN } // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick - PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) + PUGI_IMPL_FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { - #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + #if defined(__linux__) || defined(__APPLE__) + // this simultaneously retrieves the file size and file mode (to guard against loading non-files) + struct stat st; + if (fstat(fileno(file), &st) != 0) return status_io_error; + + // anything that's not a regular file doesn't have a coherent length + if (!S_ISREG(st.st_mode)) return status_io_error; + + typedef off_t length_type; + length_type length = st.st_size; + #elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 // there are 64-bit versions of fseek/ftell, let's use them typedef __int64 length_type; @@ -4617,7 +4823,7 @@ PUGI__NS_BEGIN // check for I/O errors if (length < 0) return status_io_error; - + // check for overflow size_t result = static_cast(length); @@ -4630,7 +4836,7 @@ PUGI__NS_BEGIN } // This function assumes that buffer has extra sizeof(char_t) writable bytes after size - PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + PUGI_IMPL_FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us #ifdef PUGIXML_WCHAR_MODE @@ -4654,7 +4860,7 @@ PUGI__NS_BEGIN return size; } - PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) + PUGI_IMPL_FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) { if (!file) return make_parse_result(status_file_not_found); @@ -4662,7 +4868,7 @@ PUGI__NS_BEGIN size_t size = 0; xml_parse_status size_status = get_file_size(file, size); if (size_status != status_ok) return make_parse_result(size_status); - + size_t max_suffix_size = sizeof(char_t); // allocate buffer for the whole file @@ -4683,6 +4889,11 @@ PUGI__NS_BEGIN return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); } + PUGI_IMPL_FN void close_file(FILE* file) + { + fclose(file); + } + #ifndef PUGIXML_NO_STL template struct xml_stream_chunk { @@ -4690,7 +4901,7 @@ PUGI__NS_BEGIN { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); if (!memory) return 0; - + return new (memory) xml_stream_chunk(); } @@ -4717,7 +4928,7 @@ PUGI__NS_BEGIN T data[xml_memory_page_size / sizeof(T)]; }; - template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + template PUGI_IMPL_FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { auto_deleter > chunks(0, xml_stream_chunk::destroy); @@ -4771,7 +4982,7 @@ PUGI__NS_BEGIN return status_ok; } - template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + template PUGI_IMPL_FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { // get length of remaining data in stream typename std::basic_istream::pos_type pos = stream.tellg(); @@ -4800,14 +5011,14 @@ PUGI__NS_BEGIN // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); - + *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } - template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) + template PUGI_IMPL_FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) { void* buffer = 0; size_t size = 0; @@ -4828,18 +5039,23 @@ PUGI__NS_BEGIN if (status != status_ok) return make_parse_result(status); xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); - + return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); } #endif -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) +#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) + PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { +#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return _wfopen_s(&file, path, mode) == 0 ? file : 0; +#else return _wfopen(path, mode); +#endif } #else - PUGI__FN char* convert_path_heap(const wchar_t* str) + PUGI_IMPL_FN char* convert_path_heap(const wchar_t* str) { assert(str); @@ -4860,7 +5076,7 @@ PUGI__NS_BEGIN return result; } - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { // there is no standard function to open wide paths, so our best bet is to try utf8 path char* path_utf8 = convert_path_heap(path); @@ -4880,39 +5096,69 @@ PUGI__NS_BEGIN } #endif - PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) + PUGI_IMPL_FN FILE* open_file(const char* path, const char* mode) + { +#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return fopen_s(&file, path, mode) == 0 ? file : 0; +#else + return fopen(path, mode); +#endif + } + + PUGI_IMPL_FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); - return ferror(file) == 0; + return fflush(file) == 0 && ferror(file) == 0; } -PUGI__NS_END + + struct name_null_sentry + { + xml_node_struct* node; + char_t* name; + + name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) + { + node->name = 0; + } + + ~name_null_sentry() + { + node->name = name; + } + }; +PUGI_IMPL_NS_END namespace pugi { - PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) + PUGI_IMPL_FN xml_writer::~xml_writer() { } - PUGI__FN void xml_writer_file::write(const void* data, size_t size) + PUGI_IMPL_FN xml_writer_file::xml_writer_file(void* file_): file(file_) + { + } + + PUGI_IMPL_FN void xml_writer_file::write(const void* data, size_t size) { size_t result = fwrite(data, 1, size, static_cast(file)); (void)!result; // unfortunately we can't do proper error handling here } #ifndef PUGIXML_NO_STL - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } - PUGI__FN void xml_writer_stream::write(const void* data, size_t size) + PUGI_IMPL_FN void xml_writer_stream::write(const void* data, size_t size) { if (narrow_stream) { @@ -4929,439 +5175,535 @@ namespace pugi } #endif - PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) - { - } - - PUGI__FN xml_tree_walker::~xml_tree_walker() + PUGI_IMPL_FN xml_tree_walker::xml_tree_walker(): _depth(0) { } - PUGI__FN int xml_tree_walker::depth() const + PUGI_IMPL_FN xml_tree_walker::~xml_tree_walker() + { + } + + PUGI_IMPL_FN int xml_tree_walker::depth() const { return _depth; } - PUGI__FN bool xml_tree_walker::begin(xml_node&) + PUGI_IMPL_FN bool xml_tree_walker::begin(xml_node&) { return true; } - PUGI__FN bool xml_tree_walker::end(xml_node&) + PUGI_IMPL_FN bool xml_tree_walker::end(xml_node&) { return true; } - PUGI__FN xml_attribute::xml_attribute(): _attr(0) + PUGI_IMPL_FN xml_attribute::xml_attribute(): _attr(0) { } - PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + PUGI_IMPL_FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } - PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) + PUGI_IMPL_FN static void unspecified_bool_xml_attribute(xml_attribute***) { } - PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const + PUGI_IMPL_FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } - PUGI__FN bool xml_attribute::operator!() const + PUGI_IMPL_FN bool xml_attribute::operator!() const { return !_attr; } - PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const + PUGI_IMPL_FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } - - PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const + + PUGI_IMPL_FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } - PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const + PUGI_IMPL_FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } - - PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const + + PUGI_IMPL_FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } - - PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const + + PUGI_IMPL_FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } - - PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const + + PUGI_IMPL_FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } - PUGI__FN xml_attribute xml_attribute::next_attribute() const + PUGI_IMPL_FN xml_attribute xml_attribute::next_attribute() const { - return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + if (!_attr) return xml_attribute(); + return xml_attribute(_attr->next_attribute); } - PUGI__FN xml_attribute xml_attribute::previous_attribute() const + PUGI_IMPL_FN xml_attribute xml_attribute::previous_attribute() const { - return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + if (!_attr) return xml_attribute(); + xml_attribute_struct* prev = _attr->prev_attribute_c; + return prev->next_attribute ? xml_attribute(prev) : xml_attribute(); } - PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const + PUGI_IMPL_FN const char_t* xml_attribute::as_string(const char_t* def) const { - return (_attr && _attr->value) ? _attr->value : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? value : def; } - PUGI__FN int xml_attribute::as_int(int def) const + PUGI_IMPL_FN int xml_attribute::as_int(int def) const { - return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_int(value) : def; } - PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const + PUGI_IMPL_FN unsigned int xml_attribute::as_uint(unsigned int def) const { - return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_uint(value) : def; } - PUGI__FN double xml_attribute::as_double(double def) const + PUGI_IMPL_FN double xml_attribute::as_double(double def) const { - return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_double(value) : def; } - PUGI__FN float xml_attribute::as_float(float def) const + PUGI_IMPL_FN float xml_attribute::as_float(float def) const { - return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_float(value) : def; } - PUGI__FN bool xml_attribute::as_bool(bool def) const + PUGI_IMPL_FN bool xml_attribute::as_bool(bool def) const { - return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_attribute::as_llong(long long def) const + PUGI_IMPL_FN long long xml_attribute::as_llong(long long def) const { - return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_llong(value) : def; } - PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const + PUGI_IMPL_FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { - return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; + if (!_attr) return def; + const char_t* value = _attr->value; + return value ? impl::get_value_ullong(value) : def; } #endif - PUGI__FN bool xml_attribute::empty() const + PUGI_IMPL_FN bool xml_attribute::empty() const { return !_attr; } - PUGI__FN const char_t* xml_attribute::name() const + PUGI_IMPL_FN const char_t* xml_attribute::name() const { - return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); + if (!_attr) return PUGIXML_TEXT(""); + const char_t* name = _attr->name; + return name ? name : PUGIXML_TEXT(""); } - PUGI__FN const char_t* xml_attribute::value() const + PUGI_IMPL_FN const char_t* xml_attribute::value() const { - return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); + if (!_attr) return PUGIXML_TEXT(""); + const char_t* value = _attr->value; + return value ? value : PUGIXML_TEXT(""); } - PUGI__FN size_t xml_attribute::hash_value() const + PUGI_IMPL_FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } - PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const + PUGI_IMPL_FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } - PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } - PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } - PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long rhs) + { + set_value(rhs); + return *this; + } + + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long rhs) + { + set_value(rhs); + return *this; + } + + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(float rhs) + { + set_value(rhs); + return *this; + } + + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long long rhs) { set_value(rhs); return *this; } - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) + PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) { set_value(rhs); return *this; } #endif - PUGI__FN bool xml_attribute::set_name(const char_t* rhs) + PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; - + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } - - PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + + PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs, size_t size) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } - PUGI__FN bool xml_attribute::set_value(int rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs, size_t size) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size); } - PUGI__FN bool xml_attribute::set_value(unsigned int rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } - PUGI__FN bool xml_attribute::set_value(double rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(float rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } - PUGI__FN bool xml_attribute::set_value(bool rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(long rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(double rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(double rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(float rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(float rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); + } + + PUGI_IMPL_FN bool xml_attribute::set_value(bool rhs) + { + if (!_attr) return false; + + return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_attribute::set_value(long long rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(long long rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } - PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) + PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long long rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } #endif #ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) + PUGI_IMPL_FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } - PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) + PUGI_IMPL_FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif - PUGI__FN xml_node::xml_node(): _root(0) + PUGI_IMPL_FN xml_node::xml_node(): _root(0) { } - PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) - { - } - - PUGI__FN static void unspecified_bool_xml_node(xml_node***) + PUGI_IMPL_FN xml_node::xml_node(xml_node_struct* p): _root(p) { } - PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const + PUGI_IMPL_FN static void unspecified_bool_xml_node(xml_node***) + { + } + + PUGI_IMPL_FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } - PUGI__FN bool xml_node::operator!() const + PUGI_IMPL_FN bool xml_node::operator!() const { return !_root; } - PUGI__FN xml_node::iterator xml_node::begin() const + PUGI_IMPL_FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child + 0 : 0, _root); } - PUGI__FN xml_node::iterator xml_node::end() const + PUGI_IMPL_FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } - - PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const + + PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); } - PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const + PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } - - PUGI__FN xml_object_range xml_node::children() const + + PUGI_IMPL_FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } - PUGI__FN xml_object_range xml_node::children(const char_t* name_) const + PUGI_IMPL_FN xml_object_range xml_node::children(const char_t* name_) const { return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); } - PUGI__FN xml_object_range xml_node::attributes() const + PUGI_IMPL_FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } - PUGI__FN bool xml_node::operator==(const xml_node& r) const + PUGI_IMPL_FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } - PUGI__FN bool xml_node::operator!=(const xml_node& r) const + PUGI_IMPL_FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } - PUGI__FN bool xml_node::operator<(const xml_node& r) const + PUGI_IMPL_FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } - - PUGI__FN bool xml_node::operator>(const xml_node& r) const + + PUGI_IMPL_FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } - - PUGI__FN bool xml_node::operator<=(const xml_node& r) const + + PUGI_IMPL_FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } - - PUGI__FN bool xml_node::operator>=(const xml_node& r) const + + PUGI_IMPL_FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } - PUGI__FN bool xml_node::empty() const + PUGI_IMPL_FN bool xml_node::empty() const { return !_root; } - - PUGI__FN const char_t* xml_node::name() const + + PUGI_IMPL_FN const char_t* xml_node::name() const { - return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); + if (!_root) return PUGIXML_TEXT(""); + const char_t* name = _root->name; + return name ? name : PUGIXML_TEXT(""); } - PUGI__FN xml_node_type xml_node::type() const + PUGI_IMPL_FN xml_node_type xml_node::type() const { - return _root ? PUGI__NODETYPE(_root) : node_null; + return _root ? PUGI_IMPL_NODETYPE(_root) : node_null; } - - PUGI__FN const char_t* xml_node::value() const + + PUGI_IMPL_FN const char_t* xml_node::value() const { - return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); + if (!_root) return PUGIXML_TEXT(""); + const char_t* value = _root->value; + return value ? value : PUGIXML_TEXT(""); } - - PUGI__FN xml_node xml_node::child(const char_t* name_) const + + PUGI_IMPL_FN xml_node xml_node::child(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } - PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const + PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_) const { if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) return xml_attribute(i); - + } + return xml_attribute(); } - - PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const + + PUGI_IMPL_FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } - PUGI__FN xml_node xml_node::next_sibling() const + PUGI_IMPL_FN xml_node xml_node::next_sibling() const { return _root ? xml_node(_root->next_sibling) : xml_node(); } - PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const + PUGI_IMPL_FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) + return xml_node(i); + } return xml_node(); } - PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const + PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const { xml_attribute_struct* hint = hint_._attr; @@ -5372,111 +5714,149 @@ namespace pugi // optimistically search from hint up until the end for (xml_attribute_struct* i = hint; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = i->next_attribute; return xml_attribute(i); } + } // wrap around and search from the first attribute until the hint // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) - if (j->name && impl::strequal(name_, j->name)) + { + const char_t* jname = j->name; + if (jname && impl::strequal(name_, jname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = j->next_attribute; return xml_attribute(j); } + } return xml_attribute(); } - PUGI__FN xml_node xml_node::previous_sibling() const + PUGI_IMPL_FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); - - if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); - else return xml_node(); + xml_node_struct* prev = _root->prev_sibling_c; + return prev->next_sibling ? xml_node(prev) : xml_node(); } - PUGI__FN xml_node xml_node::parent() const + PUGI_IMPL_FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } - PUGI__FN xml_node xml_node::root() const + PUGI_IMPL_FN xml_node xml_node::root() const { return _root ? xml_node(&impl::get_document(_root)) : xml_node(); } - PUGI__FN xml_text xml_node::text() const + PUGI_IMPL_FN xml_text xml_node::text() const { return xml_text(_root); } - PUGI__FN const char_t* xml_node::child_value() const + PUGI_IMPL_FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); - + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) + return _root->value; + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (impl::is_text_node(i) && i->value) - return i->value; + { + const char_t* ivalue = i->value; + if (impl::is_text_node(i) && ivalue) + return ivalue; + } return PUGIXML_TEXT(""); } - PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const + PUGI_IMPL_FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } - PUGI__FN xml_attribute xml_node::first_attribute() const + PUGI_IMPL_FN xml_attribute xml_node::first_attribute() const { - return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + if (!_root) return xml_attribute(); + return xml_attribute(_root->first_attribute); } - PUGI__FN xml_attribute xml_node::last_attribute() const + PUGI_IMPL_FN xml_attribute xml_node::last_attribute() const { - return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + if (!_root) return xml_attribute(); + xml_attribute_struct* first = _root->first_attribute; + return first ? xml_attribute(first->prev_attribute_c) : xml_attribute(); } - PUGI__FN xml_node xml_node::first_child() const + PUGI_IMPL_FN xml_node xml_node::first_child() const { - return _root ? xml_node(_root->first_child) : xml_node(); + if (!_root) return xml_node(); + return xml_node(_root->first_child); } - PUGI__FN xml_node xml_node::last_child() const + PUGI_IMPL_FN xml_node xml_node::last_child() const { - return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + if (!_root) return xml_node(); + xml_node_struct* first = _root->first_child; + return first ? xml_node(first->prev_sibling_c) : xml_node(); } - PUGI__FN bool xml_node::set_name(const char_t* rhs) + PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs) { - static const bool has_name[] = { false, false, true, false, false, false, true, true, false }; + xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; - if (!_root || !has_name[PUGI__NODETYPE(_root)]) + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) return false; return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } - - PUGI__FN bool xml_node::set_value(const char_t* rhs) - { - static const bool has_value[] = { false, false, false, true, true, true, true, false, true }; - if (!_root || !has_value[PUGI__NODETYPE(_root)]) + PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs, size_t size) + { + xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; + + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) + return false; + + return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size); + } + + PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) return false; return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } - PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) + PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs, size_t size) + { + xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) + return false; + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size); + } + + PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); - + impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); @@ -5486,14 +5866,14 @@ namespace pugi impl::append_attribute(a._attr, _root); a.set_name(name_); - + return a; } - PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) + PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); - + impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); @@ -5507,11 +5887,11 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) + PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - + impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); @@ -5525,11 +5905,11 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) + PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - + impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); @@ -5543,7 +5923,7 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) + PUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); @@ -5560,7 +5940,7 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + PUGI_IMPL_FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); @@ -5577,7 +5957,7 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + PUGI_IMPL_FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); @@ -5595,7 +5975,7 @@ namespace pugi return a; } - PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + PUGI_IMPL_FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); @@ -5613,10 +5993,10 @@ namespace pugi return a; } - PUGI__FN xml_node xml_node::append_child(xml_node_type type_) + PUGI_IMPL_FN xml_node xml_node::append_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); - + impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); @@ -5630,31 +6010,31 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) + PUGI_IMPL_FN xml_node xml_node::prepend_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); - + xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::prepend_node(n._root, _root); - + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } - PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); - + xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); @@ -5665,14 +6045,14 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); - + xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); @@ -5683,7 +6063,7 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::append_child(const char_t* name_) + PUGI_IMPL_FN xml_node xml_node::append_child(const char_t* name_) { xml_node result = append_child(node_element); @@ -5692,7 +6072,7 @@ namespace pugi return result; } - PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) + PUGI_IMPL_FN xml_node xml_node::prepend_child(const char_t* name_) { xml_node result = prepend_child(node_element); @@ -5701,7 +6081,7 @@ namespace pugi return result; } - PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) { xml_node result = insert_child_after(node_element, node); @@ -5710,7 +6090,7 @@ namespace pugi return result; } - PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) { xml_node result = insert_child_before(node_element, node); @@ -5719,7 +6099,7 @@ namespace pugi return result; } - PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) + PUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); @@ -5736,7 +6116,7 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) + PUGI_IMPL_FN xml_node xml_node::prepend_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); @@ -5753,7 +6133,7 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); @@ -5771,7 +6151,7 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); @@ -5789,7 +6169,7 @@ namespace pugi return n; } - PUGI__FN xml_node xml_node::append_move(const xml_node& moved) + PUGI_IMPL_FN xml_node xml_node::append_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); @@ -5805,7 +6185,7 @@ namespace pugi return moved; } - PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) + PUGI_IMPL_FN xml_node xml_node::prepend_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); @@ -5821,7 +6201,7 @@ namespace pugi return moved; } - PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); @@ -5839,7 +6219,7 @@ namespace pugi return moved; } - PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) + PUGI_IMPL_FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); @@ -5857,12 +6237,12 @@ namespace pugi return moved; } - PUGI__FN bool xml_node::remove_attribute(const char_t* name_) + PUGI_IMPL_FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } - PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) + PUGI_IMPL_FN bool xml_node::remove_attribute(const xml_attribute& a) { if (!_root || !a._attr) return false; if (!impl::is_attribute_of(a._attr, _root)) return false; @@ -5876,12 +6256,33 @@ namespace pugi return true; } - PUGI__FN bool xml_node::remove_child(const char_t* name_) + PUGI_IMPL_FN bool xml_node::remove_attributes() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_attribute_struct* attr = _root->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + impl::destroy_attribute(attr, alloc); + + attr = next; + } + + _root->first_attribute = 0; + + return true; + } + + PUGI_IMPL_FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } - PUGI__FN bool xml_node::remove_child(const xml_node& n) + PUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n) { if (!_root || !n._root || n._root->parent != _root) return false; @@ -5894,74 +6295,111 @@ namespace pugi return true; } - PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN bool xml_node::remove_children() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_node_struct* cur = _root->first_child; cur; ) + { + xml_node_struct* next = cur->next_sibling; + + impl::destroy_node(cur, alloc); + + cur = next; + } + + _root->first_child = 0; + + return true; + } + + PUGI_IMPL_FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); + // append buffer can not merge PCDATA into existing PCDATA nodes + if ((options & parse_merge_pcdata) != 0 && last_child().type() == node_pcdata) return impl::make_parse_result(status_append_invalid_root); + // get document node impl::xml_document_struct* doc = &impl::get_document(_root); // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense doc->header |= impl::xml_memory_page_contents_shared_mask; - + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) impl::xml_memory_page* page = 0; - impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); + impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page)); (void)page; if (!extra) return impl::make_parse_result(status_out_of_memory); + #ifdef PUGIXML_COMPACT + // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned + // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account + extra = reinterpret_cast((reinterpret_cast(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1)); + #endif + // add extra buffer to the list extra->buffer = 0; extra->next = doc->extra_buffers; doc->extra_buffers = extra; // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level - struct name_sentry - { - xml_node_struct* node; - char_t* name; - - ~name_sentry() { node->name = name; } - }; - - name_sentry sentry = { _root, _root->name }; - - sentry.node->name = 0; + impl::name_null_sentry sentry(_root); return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); } - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const + PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); - + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) + { + const char_t* iname = i->name; + if (iname && impl::strequal(name_, iname)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + { + const char_t* aname = a->name; + if (aname && impl::strequal(attr_name, aname)) + { + const char_t* avalue = a->value; + if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) + return xml_node(i); + } + } + } + } + + return xml_node(); + } + + PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + { + const char_t* aname = a->name; + if (aname && impl::strequal(attr_name, aname)) + { + const char_t* avalue = a->value; + if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) return xml_node(i); + } } return xml_node(); } - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) - return xml_node(i); - - return xml_node(); - } - #ifndef PUGIXML_NO_STL - PUGI__FN string_t xml_node::path(char_t delimiter) const + PUGI_IMPL_FN string_t xml_node::path(char_t delimiter) const { if (!_root) return string_t(); @@ -5969,8 +6407,9 @@ namespace pugi for (xml_node_struct* i = _root; i; i = i->parent) { + const char_t* iname = i->name; offset += (i != _root); - offset += i->name ? impl::strlength(i->name) : 0; + offset += iname ? impl::strlength(iname) : 0; } string_t result; @@ -5981,12 +6420,13 @@ namespace pugi if (j != _root) result[--offset] = delimiter; - if (j->name && *j->name) + const char_t* jname = j->name; + if (jname) { - size_t length = impl::strlength(j->name); + size_t length = impl::strlength(jname); offset -= length; - memcpy(&result[offset], j->name, length * sizeof(char_t)); + memcpy(&result[offset], jname, length * sizeof(char_t)); } } @@ -5996,18 +6436,11 @@ namespace pugi } #endif - PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const + PUGI_IMPL_FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { - xml_node found = *this; // Current search context. + xml_node context = path_[0] == delimiter ? root() : *this; - if (!_root || !path_ || !path_[0]) return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } + if (!context._root) return xml_node(); const char_t* path_segment = path_; @@ -6017,21 +6450,22 @@ namespace pugi while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; - if (path_segment == path_segment_end) return found; + if (path_segment == path_segment_end) return context; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); + return context.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); + return context.parent().first_element_by_path(next_segment, delimiter); else { - for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) { - if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) + const char_t* jname = j->name; + if (jname && impl::strequalrange(jname, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); @@ -6043,65 +6477,64 @@ namespace pugi } } - PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) + PUGI_IMPL_FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; - - xml_node arg_begin = *this; + + xml_node arg_begin(_root); if (!walker.begin(arg_begin)) return false; - xml_node cur = first_child(); - + xml_node_struct* cur = _root ? _root->first_child + 0 : 0; + if (cur) { ++walker._depth; - do + do { - xml_node arg_for_each = cur; + xml_node arg_for_each(cur); if (!walker.for_each(arg_for_each)) return false; - - if (cur.first_child()) + + if (cur->first_child) { ++walker._depth; - cur = cur.first_child(); + cur = cur->first_child; } - else if (cur.next_sibling()) - cur = cur.next_sibling(); + else if (cur->next_sibling) + cur = cur->next_sibling; else { - // Borland C++ workaround - while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) + while (!cur->next_sibling && cur != _root && cur->parent) { --walker._depth; - cur = cur.parent(); + cur = cur->parent; } - - if (cur != *this) - cur = cur.next_sibling(); + + if (cur != _root) + cur = cur->next_sibling; } } - while (cur && cur != *this); + while (cur && cur != _root); } assert(walker._depth == -1); - xml_node arg_end = *this; + xml_node arg_end(_root); return walker.end(arg_end); } - PUGI__FN size_t xml_node::hash_value() const + PUGI_IMPL_FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } - PUGI__FN xml_node_struct* xml_node::internal_object() const + PUGI_IMPL_FN xml_node_struct* xml_node::internal_object() const { return _root; } - PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + PUGI_IMPL_FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { if (!_root) return; @@ -6113,14 +6546,14 @@ namespace pugi } #ifndef PUGIXML_NO_STL - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + PUGI_IMPL_FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding, depth); } - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const + PUGI_IMPL_FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const { xml_writer_stream writer(stream); @@ -6128,7 +6561,7 @@ namespace pugi } #endif - PUGI__FN ptrdiff_t xml_node::offset_debug() const + PUGI_IMPL_FN ptrdiff_t xml_node::offset_debug() const { if (!_root) return -1; @@ -6154,30 +6587,35 @@ namespace pugi return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; default: + assert(false && "Invalid node type"); // unreachable return -1; } } #ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) + PUGI_IMPL_FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } - PUGI__FN bool operator||(const xml_node& lhs, bool rhs) + PUGI_IMPL_FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif - PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) + PUGI_IMPL_FN xml_text::xml_text(xml_node_struct* root): _root(root) { } - PUGI__FN xml_node_struct* xml_text::_data() const + PUGI_IMPL_FN xml_node_struct* xml_text::_data() const { if (!_root || impl::is_text_node(_root)) return _root; + // element nodes can have value if parse_embed_pcdata was used + if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) + return _root; + for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; @@ -6185,7 +6623,7 @@ namespace pugi return 0; } - PUGI__FN xml_node_struct* xml_text::_data_new() + PUGI_IMPL_FN xml_node_struct* xml_text::_data_new() { xml_node_struct* d = _data(); if (d) return d; @@ -6193,390 +6631,446 @@ namespace pugi return xml_node(_root).append_child(node_pcdata).internal_object(); } - PUGI__FN xml_text::xml_text(): _root(0) + PUGI_IMPL_FN xml_text::xml_text(): _root(0) { } - PUGI__FN static void unspecified_bool_xml_text(xml_text***) + PUGI_IMPL_FN static void unspecified_bool_xml_text(xml_text***) { } - PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const + PUGI_IMPL_FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } - PUGI__FN bool xml_text::operator!() const + PUGI_IMPL_FN bool xml_text::operator!() const { return !_data(); } - PUGI__FN bool xml_text::empty() const + PUGI_IMPL_FN bool xml_text::empty() const { return _data() == 0; } - PUGI__FN const char_t* xml_text::get() const + PUGI_IMPL_FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); - - return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); + if (!d) return PUGIXML_TEXT(""); + const char_t* value = d->value; + return value ? value : PUGIXML_TEXT(""); } - PUGI__FN const char_t* xml_text::as_string(const char_t* def) const + PUGI_IMPL_FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? d->value : def; + if (!d) return def; + const char_t* value = d->value; + return value ? value : def; } - PUGI__FN int xml_text::as_int(int def) const + PUGI_IMPL_FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_int(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_int(value) : def; } - PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const + PUGI_IMPL_FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_uint(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_uint(value) : def; } - PUGI__FN double xml_text::as_double(double def) const + PUGI_IMPL_FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_double(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_double(value) : def; } - PUGI__FN float xml_text::as_float(float def) const + PUGI_IMPL_FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_float(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_float(value) : def; } - PUGI__FN bool xml_text::as_bool(bool def) const + PUGI_IMPL_FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_bool(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_text::as_llong(long long def) const + PUGI_IMPL_FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_llong(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_llong(value) : def; } - PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const + PUGI_IMPL_FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); - - return (d && d->value) ? impl::get_value_ullong(d->value) : def; + if (!d) return def; + const char_t* value = d->value; + return value ? impl::get_value_ullong(value) : def; } #endif - PUGI__FN bool xml_text::set(const char_t* rhs) + PUGI_IMPL_FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; } - PUGI__FN bool xml_text::set(int rhs) + PUGI_IMPL_FN bool xml_text::set(const char_t* rhs, size_t size) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false; } - PUGI__FN bool xml_text::set(unsigned int rhs) + PUGI_IMPL_FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } - PUGI__FN bool xml_text::set(float rhs) + PUGI_IMPL_FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } - PUGI__FN bool xml_text::set(double rhs) + PUGI_IMPL_FN bool xml_text::set(long rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } - PUGI__FN bool xml_text::set(bool rhs) + PUGI_IMPL_FN bool xml_text::set(unsigned long rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI_IMPL_FN bool xml_text::set(float rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false; + } + + PUGI_IMPL_FN bool xml_text::set(float rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; + } + + PUGI_IMPL_FN bool xml_text::set(double rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false; + } + + PUGI_IMPL_FN bool xml_text::set(double rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; + } + + PUGI_IMPL_FN bool xml_text::set(bool rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_text::set(long long rhs) + PUGI_IMPL_FN bool xml_text::set(long long rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } - PUGI__FN bool xml_text::set(unsigned long long rhs) + PUGI_IMPL_FN bool xml_text::set(unsigned long long rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } #endif - PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(int rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(double rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(long rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(float rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(bool rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(double rhs) + { + set(rhs); + return *this; + } + + PUGI_IMPL_FN xml_text& xml_text::operator=(float rhs) + { + set(rhs); + return *this; + } + + PUGI_IMPL_FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_text& xml_text::operator=(long long rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs) { set(rhs); return *this; } - PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) + PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long long rhs) { set(rhs); return *this; } #endif - PUGI__FN xml_node xml_text::data() const + PUGI_IMPL_FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) + PUGI_IMPL_FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } - PUGI__FN bool operator||(const xml_text& lhs, bool rhs) + PUGI_IMPL_FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif - PUGI__FN xml_node_iterator::xml_node_iterator() + PUGI_IMPL_FN xml_node_iterator::xml_node_iterator() { } - PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } - PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } - PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + PUGI_IMPL_FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } - - PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + + PUGI_IMPL_FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } - PUGI__FN xml_node& xml_node_iterator::operator*() const + PUGI_IMPL_FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } - PUGI__FN xml_node* xml_node_iterator::operator->() const + PUGI_IMPL_FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } - PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() + PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } - PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) + PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } - PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() + PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } - PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) + PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } - PUGI__FN xml_attribute_iterator::xml_attribute_iterator() + PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator() { } - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } - PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + PUGI_IMPL_FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } - - PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + + PUGI_IMPL_FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } - PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const + PUGI_IMPL_FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } - PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const + PUGI_IMPL_FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() + PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) + PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() + PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) + PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) + PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } - 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) + PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) { } - 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) + PUGI_IMPL_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) { } - PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const + PUGI_IMPL_FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } - PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const + PUGI_IMPL_FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } - PUGI__FN xml_node& xml_named_node_iterator::operator*() const + PUGI_IMPL_FN xml_node& xml_named_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } - PUGI__FN xml_node* xml_named_node_iterator::operator->() const + PUGI_IMPL_FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround + return const_cast(&_wrap); // BCC5 workaround } - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() + PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_wrap._root); _wrap = _wrap.next_sibling(_name); return *this; } - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) + PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() + PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator--() { if (_wrap._root) _wrap = _wrap.previous_sibling(_name); @@ -6591,23 +7085,23 @@ namespace pugi return *this; } - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) + PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator--(int) { xml_named_node_iterator temp = *this; --*this; return temp; } - PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + PUGI_IMPL_FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } - PUGI__FN xml_parse_result::operator bool() const + PUGI_IMPL_FN xml_parse_result::operator bool() const { return status == status_ok; } - PUGI__FN const char* xml_parse_result::description() const + PUGI_IMPL_FN const char* xml_parse_result::description() const { switch (status) { @@ -6638,55 +7132,72 @@ namespace pugi } } - PUGI__FN xml_document::xml_document(): _buffer(0) + PUGI_IMPL_FN xml_document::xml_document(): _buffer(0) { - create(); + _create(); } - PUGI__FN xml_document::~xml_document() + PUGI_IMPL_FN xml_document::~xml_document() { - destroy(); + _destroy(); } - PUGI__FN void xml_document::reset() +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0) { - destroy(); - create(); + _create(); + _move(rhs); } - PUGI__FN void xml_document::reset(const xml_document& proto) + PUGI_IMPL_FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + if (this == &rhs) return *this; + + _destroy(); + _create(); + _move(rhs); + + return *this; + } +#endif + + PUGI_IMPL_FN void xml_document::reset() + { + _destroy(); + _create(); + } + + PUGI_IMPL_FN void xml_document::reset(const xml_document& proto) { reset(); - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); + impl::node_copy_tree(_root, proto._root); } - PUGI__FN void xml_document::create() + PUGI_IMPL_FN void xml_document::_create() { assert(!_root); #ifdef PUGIXML_COMPACT - const size_t page_offset = sizeof(uint32_t); + // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit + const size_t page_offset = sizeof(void*); #else const size_t page_offset = 0; #endif // initialize sentinel page - PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) + page_offset <= sizeof(_memory)); - - // align upwards to page boundary - void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); + PUGI_IMPL_STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); // prepare page structure - impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); + impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); assert(page); page->busy_size = impl::xml_memory_page_size; // setup first page marker #ifdef PUGIXML_COMPACT - page->compact_page_marker = reinterpret_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page)); + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); *page->compact_page_marker = sizeof(impl::xml_memory_page); #endif @@ -6697,11 +7208,16 @@ namespace pugi // setup sentinel page page->allocator = static_cast(_root); + // setup hash table pointer in allocator + #ifdef PUGIXML_COMPACT + page->allocator->_hash = &static_cast(_root)->hash; + #endif + // verify the document allocation assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); } - PUGI__FN void xml_document::destroy() + PUGI_IMPL_FN void xml_document::_destroy() { assert(_root); @@ -6719,7 +7235,7 @@ namespace pugi } // destroy dynamic storage, leave sentinel page (it's in static memory) - impl::xml_memory_page* root_page = PUGI__GETPAGE(_root); + impl::xml_memory_page* root_page = PUGI_IMPL_GETPAGE(_root); assert(root_page && !root_page->prev); assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); @@ -6740,15 +7256,126 @@ namespace pugi _root = 0; } +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + impl::xml_document_struct* doc = static_cast(_root); + impl::xml_document_struct* other = static_cast(rhs._root); + + // save first child pointer for later; this needs hash access + xml_node_struct* other_first_child = other->first_child; + + #ifdef PUGIXML_COMPACT + // reserve space for the hash table up front; this is the only operation that can fail + // if it does, we have no choice but to throw (if we have exceptions) + if (other_first_child) + { + size_t other_children = 0; + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + other_children++; + + // in compact mode, each pointer assignment could result in a hash table request + // during move, we have to relocate document first_child and parents of all children + // normally there's just one child and its parent has a pointerless encoding but + // we assume the worst here + if (!other->_hash->reserve(other_children + 1)) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + } + #endif + + // move allocation state + // note that other->_root may point to the embedded document page, in which case we should keep original (empty) state + if (other->_root != PUGI_IMPL_GETPAGE(other)) + { + doc->_root = other->_root; + doc->_busy_size = other->_busy_size; + } + + // move buffer state + doc->buffer = other->buffer; + doc->extra_buffers = other->extra_buffers; + _buffer = rhs._buffer; + + #ifdef PUGIXML_COMPACT + // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child + doc->hash = other->hash; + doc->_hash = &doc->hash; + + // make sure we don't access other hash up until the end when we reinitialize other document + other->_hash = 0; + #endif + + // move page structure + impl::xml_memory_page* doc_page = PUGI_IMPL_GETPAGE(doc); + assert(doc_page && !doc_page->prev && !doc_page->next); + + impl::xml_memory_page* other_page = PUGI_IMPL_GETPAGE(other); + assert(other_page && !other_page->prev); + + // relink pages since root page is embedded into xml_document + if (impl::xml_memory_page* page = other_page->next) + { + assert(page->prev == other_page); + + page->prev = doc_page; + + doc_page->next = page; + other_page->next = 0; + } + + // make sure pages point to the correct document state + for (impl::xml_memory_page* page = doc_page->next; page; page = page->next) + { + assert(page->allocator == other); + + page->allocator = doc; + + #ifdef PUGIXML_COMPACT + // this automatically migrates most children between documents and prevents ->parent assignment from allocating + if (page->compact_shared_parent == other) + page->compact_shared_parent = doc; + #endif + } + + // move tree structure + assert(!doc->first_child); + + doc->first_child = other_first_child; + + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + { + #ifdef PUGIXML_COMPACT + // most children will have migrated when we reassigned compact_shared_parent + assert(node->parent == other || node->parent == doc); + + node->parent = doc; + #else + assert(node->parent == other); + node->parent = doc; + #endif + } + + // reset other document + new (other) impl::xml_document_struct(PUGI_IMPL_GETPAGE(other)); + rhs._buffer = 0; + } +#endif + #ifndef PUGIXML_NO_STL - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) { reset(); return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); } - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); @@ -6756,7 +7383,7 @@ namespace pugi } #endif - PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) + PUGI_IMPL_FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) { // Force native encoding (skip autodetection) #ifdef PUGIXML_WCHAR_MODE @@ -6768,53 +7395,53 @@ namespace pugi return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); } - PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) + PUGI_IMPL_FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { return load_string(contents, options); } - PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, "rb"), fclose); + auto_deleter file(impl::open_file(path_, "rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } - PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(impl::open_file_wide(path_, L"rb"), fclose); + auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } - PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); } - PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); } - PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); } - PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + PUGI_IMPL_FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const { impl::xml_buffered_writer buffered_writer(writer, encoding); @@ -6843,14 +7470,14 @@ namespace pugi } #ifndef PUGIXML_NO_STL - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const + PUGI_IMPL_FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding); } - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const + PUGI_IMPL_FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const { xml_writer_stream writer(stream); @@ -6858,71 +7485,71 @@ namespace pugi } #endif - PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + PUGI_IMPL_FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), fclose); + auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); - return impl::save_file_impl(*this, file.data, indent, flags, encoding); + return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } - PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + PUGI_IMPL_FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), fclose); + auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); - return impl::save_file_impl(*this, file.data, indent, flags, encoding); + return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } - PUGI__FN xml_node xml_document::document_element() const + PUGI_IMPL_FN xml_node xml_document::document_element() const { assert(_root); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (PUGI__NODETYPE(i) == node_element) + if (PUGI_IMPL_NODETYPE(i) == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, impl::strlength_wide(str)); } - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) + PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) + + PUGI_IMPL_FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) + + PUGI_IMPL_FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif - PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + PUGI_IMPL_FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) { impl::xml_memory::allocate = allocate; impl::xml_memory::deallocate = deallocate; } - PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + PUGI_IMPL_FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } - PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + PUGI_IMPL_FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() { return impl::xml_memory::deallocate; } @@ -6932,17 +7559,17 @@ namespace pugi namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } @@ -6953,17 +7580,17 @@ namespace std namespace std { // Workarounds for (non-standard) iterator category detection - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) + PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } @@ -6972,7 +7599,7 @@ namespace std #ifndef PUGIXML_NO_XPATH // STL replacements -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN struct equal_to { template bool operator()(const T& lhs, const T& rhs) const @@ -7005,14 +7632,14 @@ PUGI__NS_BEGIN } }; - template void swap(T& lhs, T& rhs) + template inline void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } - template I min_element(I begin, I end, const Pred& pred) + template PUGI_IMPL_FN I min_element(I begin, I end, const Pred& pred) { I result = begin; @@ -7023,20 +7650,23 @@ PUGI__NS_BEGIN return result; } - template void reverse(I begin, I end) + template PUGI_IMPL_FN void reverse(I begin, I end) { - while (end - begin > 1) swap(*begin++, *--end); + while (end - begin > 1) + swap(*begin++, *--end); } - template I unique(I begin, I end) + template PUGI_IMPL_FN I unique(I begin, I end) { // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) begin++; + while (end - begin > 1 && *begin != *(begin + 1)) + begin++; - if (begin == end) return begin; + if (begin == end) + return begin; // last written element - I write = begin++; + I write = begin++; // merge unique elements while (begin != end) @@ -7051,134 +7681,79 @@ PUGI__NS_BEGIN return write + 1; } - template void copy_backwards(I begin, I end, I target) + template PUGI_IMPL_FN void insertion_sort(T* begin, T* end, const Pred& pred) { - while (begin != end) *--target = *--end; - } + if (begin == end) + return; - template void insertion_sort(I begin, I end, const Pred& pred, T*) - { - assert(begin != end); - - for (I it = begin + 1; it != end; ++it) + for (T* it = begin + 1; it != end; ++it) { T val = *it; + T* hole = it; - if (pred(val, *begin)) + // move hole backwards + while (hole > begin && pred(val, *(hole - 1))) { - // move to front - copy_backwards(begin, it, it + 1); - *begin = val; + *hole = *(hole - 1); + hole--; } + + // fill hole with element + *hole = val; + } + } + + template inline I median3(I first, I middle, I last, const Pred& pred) + { + if (pred(*middle, *first)) + swap(middle, first); + if (pred(*last, *middle)) + swap(last, middle); + if (pred(*middle, *first)) + swap(middle, first); + + return middle; + } + + template PUGI_IMPL_FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) + { + // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) + T* eq = begin; + T* lt = begin; + T* gt = end; + + while (lt < gt) + { + if (pred(*lt, pivot)) + lt++; + else if (*lt == pivot) + swap(*eq++, *lt++); else - { - I hole = it; - - // move hole backwards - while (pred(val, *(hole - 1))) - { - *hole = *(hole - 1); - hole--; - } - - // fill hole with element - *hole = val; - } + swap(*lt, *--gt); } + + // we now have just 4 groups: = < >; move equal elements to the middle + T* eqbeg = gt; + + for (T* it = begin; it != eq; ++it) + swap(*it, *--eqbeg); + + *out_eqbeg = eqbeg; + *out_eqend = gt; } - // std variant for elements with == - template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) - { - I eqbeg = middle, eqend = middle + 1; - - // expand equal range - while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; - while (eqend != end && *eqend == *eqbeg) ++eqend; - - // process outer elements - I ltend = eqbeg, gtbeg = eqend; - - for (;;) - { - // find the element from the right side that belongs to the left one - for (; gtbeg != end; ++gtbeg) - if (!pred(*eqbeg, *gtbeg)) - { - if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); - else break; - } - - // find the element from the left side that belongs to the right one - for (; ltend != begin; --ltend) - if (!pred(*(ltend - 1), *eqbeg)) - { - if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); - else break; - } - - // scanned all elements - if (gtbeg == end && ltend == begin) - { - *out_eqbeg = eqbeg; - *out_eqend = eqend; - return; - } - - // make room for elements by moving equal area - if (gtbeg == end) - { - if (--ltend != --eqbeg) swap(*ltend, *eqbeg); - swap(*eqbeg, *--eqend); - } - else if (ltend == begin) - { - if (eqend != gtbeg) swap(*eqbeg, *eqend); - ++eqend; - swap(*gtbeg++, *eqbeg++); - } - else swap(*gtbeg++, *--ltend); - } - } - - template void median3(I first, I middle, I last, const Pred& pred) - { - if (pred(*middle, *first)) swap(*middle, *first); - if (pred(*last, *middle)) swap(*last, *middle); - if (pred(*middle, *first)) swap(*middle, *first); - } - - template void median(I first, I middle, I last, const Pred& pred) - { - if (last - first <= 40) - { - // median of three for small chunks - median3(first, middle, last, pred); - } - else - { - // median of nine - size_t step = (last - first + 1) / 8; - - median3(first, first + step, first + 2 * step, pred); - median3(middle - step, middle, middle + step, pred); - median3(last - 2 * step, last - step, last, pred); - median3(first + step, middle, last - step, pred); - } - } - - template void sort(I begin, I end, const Pred& pred) + template PUGI_IMPL_FN void sort(I begin, I end, const Pred& pred) { // sort large chunks - while (end - begin > 32) + while (end - begin > 16) { // find median element I middle = begin + (end - begin) / 2; - median(begin, middle, end - 1, pred); + I median = median3(begin, middle, end - 1, pred); // partition in three chunks (< = >) I eqbeg, eqend; - partition(begin, middle, end, pred, &eqbeg, &eqend); + partition3(begin, end, *median, pred, &eqbeg, &eqend); // loop on larger half if (eqbeg - begin > end - eqend) @@ -7194,12 +7769,47 @@ PUGI__NS_BEGIN } // insertion sort small chunk - if (begin != end) insertion_sort(begin, end, pred, &*begin); + insertion_sort(begin, end, pred); } -PUGI__NS_END + + PUGI_IMPL_FN bool hash_insert(const void** table, size_t size, const void* key) + { + assert(key); + + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + size_t hashmod = size - 1; + size_t bucket = h & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + if (table[bucket] == 0) + { + table[bucket] = key; + return true; + } + + if (table[bucket] == key) + return false; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return false; + } +PUGI_IMPL_NS_END // Allocator used for AST and evaluation stacks -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN static const size_t xpath_memory_page_size = #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE PUGIXML_MEMORY_XPATH_PAGE_SIZE @@ -7211,7 +7821,7 @@ PUGI__NS_BEGIN static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); struct xpath_memory_block - { + { xpath_memory_block* next; size_t capacity; @@ -7221,25 +7831,18 @@ PUGI__NS_BEGIN double alignment; }; }; - - class xpath_allocator + + struct xpath_allocator { xpath_memory_block* _root; size_t _root_size; + bool* _error; - public: - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf* error_handler; - #endif - - xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) + xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error) { - #ifdef PUGIXML_NO_EXCEPTIONS - error_handler = 0; - #endif } - - void* allocate_nothrow(size_t size) + + void* allocate(size_t size) { // round size up to block alignment boundary size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); @@ -7260,35 +7863,22 @@ PUGI__NS_BEGIN size_t block_size = block_capacity + offsetof(xpath_memory_block, data); xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); - if (!block) return 0; - + if (!block) + { + if (_error) *_error = true; + return 0; + } + block->next = _root; block->capacity = block_capacity; - + _root = block; _root_size = size; - + return block->data; } } - void* allocate(size_t size) - { - void* result = allocate_nothrow(size); - - if (!result) - { - #ifdef PUGIXML_NO_EXCEPTIONS - assert(error_handler); - longjmp(*error_handler, 1); - #else - throw std::bad_alloc(); - #endif - } - - return result; - } - void* reallocate(void* ptr, size_t old_size, size_t new_size) { // round size up to block alignment boundary @@ -7298,33 +7888,35 @@ PUGI__NS_BEGIN // we can only reallocate the last object assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); - // adjust root size so that we have not allocated the object at all - bool only_object = (_root_size == old_size); + // try to reallocate the object inplace + if (ptr && _root_size - old_size + new_size <= _root->capacity) + { + _root_size = _root_size - old_size + new_size; + return ptr; + } - if (ptr) _root_size -= old_size; - - // allocate a new version (this will obviously reuse the memory if possible) + // allocate a new block void* result = allocate(new_size); - assert(result); + if (!result) return 0; // we have a new block - if (result != ptr && ptr) + if (ptr) { - // copy old data + // copy old data (we only support growing) assert(new_size >= old_size); memcpy(result, ptr, old_size); // free the previous page if it had no other objects - if (only_object) - { - assert(_root->data == result); - assert(_root->next); + assert(_root->data == result); + assert(_root->next); + if (_root->next->data == ptr) + { + // deallocate the whole page, unless it was the first one xpath_memory_block* next = _root->next->next; if (next) { - // deallocate the whole page, unless it was the first one xml_memory::deallocate(_root->next); _root->next = next; } @@ -7396,22 +7988,15 @@ PUGI__NS_BEGIN xpath_allocator result; xpath_allocator temp; xpath_stack stack; + bool oom; - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf error_handler; - #endif - - xpath_stack_data(): result(blocks + 0), temp(blocks + 1) + xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false) { blocks[0].next = blocks[1].next = 0; blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); stack.result = &result; stack.temp = &temp; - - #ifdef PUGIXML_NO_EXCEPTIONS - result.error_handler = temp.error_handler = &error_handler; - #endif } ~xpath_stack_data() @@ -7420,10 +8005,10 @@ PUGI__NS_BEGIN temp.release(); } }; -PUGI__NS_END +PUGI_IMPL_NS_END // String class -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN class xpath_string { const char_t* _buffer; @@ -7433,7 +8018,7 @@ PUGI__NS_BEGIN static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) { char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); - assert(result); + if (!result) return 0; memcpy(result, string, length * sizeof(char_t)); result[length] = 0; @@ -7462,9 +8047,13 @@ PUGI__NS_BEGIN { assert(begin <= end); - size_t length = static_cast(end - begin); + if (begin == end) + return xpath_string(); - return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); + size_t length = static_cast(end - begin); + const char_t* data = duplicate_string(begin, length, alloc); + + return data ? xpath_string(data, true, length) : xpath_string(); } xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) @@ -7490,7 +8079,7 @@ PUGI__NS_BEGIN // allocate new buffer char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); - assert(result); + if (!result) return; // append first string to the new buffer in case there was no reallocation if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); @@ -7515,15 +8104,18 @@ PUGI__NS_BEGIN { return _uses_heap ? _length_heap : strlength(_buffer); } - + char_t* data(xpath_allocator* alloc) { // make private heap copy if (!_uses_heap) { size_t length_ = strlength(_buffer); + const char_t* data_ = duplicate_string(_buffer, length_, alloc); - _buffer = duplicate_string(_buffer, length_, alloc); + if (!data_) return 0; + + _buffer = data_; _uses_heap = true; _length_heap = length_; } @@ -7551,10 +8143,10 @@ PUGI__NS_BEGIN return _uses_heap; } }; -PUGI__NS_END +PUGI_IMPL_NS_END -PUGI__NS_BEGIN - PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) +PUGI_IMPL_NS_BEGIN + PUGI_IMPL_FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { @@ -7565,7 +8157,7 @@ PUGI__NS_BEGIN return *pattern == 0; } - PUGI__FN const char_t* find_char(const char_t* s, char_t c) + PUGI_IMPL_FN const char_t* find_char(const char_t* s, char_t c) { #ifdef PUGIXML_WCHAR_MODE return wcschr(s, c); @@ -7574,7 +8166,7 @@ PUGI__NS_BEGIN #endif } - PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) + PUGI_IMPL_FN const char_t* find_substring(const char_t* s, const char_t* p) { #ifdef PUGIXML_WCHAR_MODE // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) @@ -7585,12 +8177,12 @@ PUGI__NS_BEGIN } // Converts symbol to lower case, if it is an ASCII one - PUGI__FN char_t tolower_ascii(char_t ch) + PUGI_IMPL_FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } - PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + PUGI_IMPL_FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) { if (na.attribute()) return xpath_string::from_const(na.attribute().value()); @@ -7605,14 +8197,18 @@ PUGI__NS_BEGIN case node_comment: case node_pi: return xpath_string::from_const(n.value()); - + case node_document: case node_element: { xpath_string result; + // element nodes can have value if parse_embed_pcdata was used + if (n.value()[0]) + result.append(xpath_string::from_const(n.value()), alloc); + xml_node cur = n.first_child(); - + while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) @@ -7630,17 +8226,17 @@ PUGI__NS_BEGIN if (cur != n) cur = cur.next_sibling(); } } - + return result; } - + default: return xpath_string(); } } } - - PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) + + PUGI_IMPL_FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) { assert(ln->parent == rn->parent); @@ -7663,8 +8259,8 @@ PUGI__NS_BEGIN // if rn sibling chain ended ln must be before rn return !rs; } - - PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) + + PUGI_IMPL_FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) { // find common ancestor at the same depth, if any xml_node_struct* lp = ln; @@ -7707,14 +8303,14 @@ PUGI__NS_BEGIN return node_is_before_sibling(ln, rn); } - PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) + PUGI_IMPL_FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) { while (node && node != parent) node = node->parent; return parent && node == parent; } - PUGI__FN const void* document_buffer_order(const xpath_node& xnode) + PUGI_IMPL_FN const void* document_buffer_order(const xpath_node& xnode) { xml_node_struct* node = xnode.node().internal_object(); @@ -7744,7 +8340,7 @@ PUGI__NS_BEGIN return 0; } - + struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const @@ -7768,10 +8364,10 @@ PUGI__NS_BEGIN for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; - + return false; } - + // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); @@ -7780,50 +8376,43 @@ PUGI__NS_BEGIN { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; - + ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; - + rn = rhs.parent(); } if (ln == rn) return false; if (!ln || !rn) return ln < rn; - + return node_is_before(ln.internal_object(), rn.internal_object()); } }; - struct duplicate_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else return rhs.attribute() ? false : lhs.node() < rhs.node(); - } - }; - - PUGI__FN double gen_nan() + PUGI_IMPL_FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) - union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; - u[0].i = 0x7fc00000; - return u[0].f; + PUGI_IMPL_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); + typedef uint32_t UI; // BCC5 workaround + union { float f; UI i; } u; + u.i = 0x7fc00000; + return double(u.f); #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } - - PUGI__FN bool is_nan(double value) + + PUGI_IMPL_FN bool is_nan(double value) { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) return !!_isnan(value); #elif defined(fpclassify) && defined(FP_NAN) return fpclassify(value) == FP_NAN; @@ -7833,10 +8422,10 @@ PUGI__NS_BEGIN return v != v; #endif } - - PUGI__FN const char_t* convert_number_to_string_special(double value) + + PUGI_IMPL_FN const char_t* convert_number_to_string_special(double value) { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; if (_isnan(value)) return PUGIXML_TEXT("NaN"); return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); @@ -7865,13 +8454,13 @@ PUGI__NS_BEGIN return 0; #endif } - - PUGI__FN bool convert_number_to_boolean(double value) + + PUGI_IMPL_FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } - - PUGI__FN void truncate_zeros(char* begin, char* end) + + PUGI_IMPL_FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; @@ -7879,12 +8468,12 @@ PUGI__NS_BEGIN } // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent -#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) +#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 + PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) { // get base values int sign, exponent; - _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); + _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign); // truncate redundant zeros truncate_zeros(buffer, buffer + strlen(buffer)); @@ -7894,12 +8483,10 @@ PUGI__NS_BEGIN *out_exponent = exponent; } #else - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) { // get a scientific notation value with IEEE DBL_DIG decimals - sprintf(buffer, "%.*e", DBL_DIG, value); - assert(strlen(buffer) < buffer_size); - (void)!buffer_size; + PUGI_IMPL_SNPRINTF(buffer, "%.*e", DBL_DIG, value); // get the exponent (possibly negative) char* exponent_string = strchr(buffer, 'e'); @@ -7909,7 +8496,7 @@ PUGI__NS_BEGIN // extract mantissa string: skip sign char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; - assert(mantissa[0] != '0' && mantissa[1] == '.'); + assert(mantissa[0] != '0' && (mantissa[1] == '.' || mantissa[1] == ',')); // divide mantissa by 10 to eliminate integer part mantissa[1] = mantissa[0]; @@ -7925,7 +8512,7 @@ PUGI__NS_BEGIN } #endif - PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) + PUGI_IMPL_FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) { // try special number conversion const char_t* special = convert_number_to_string_special(value); @@ -7936,12 +8523,12 @@ PUGI__NS_BEGIN char* mantissa; int exponent; - convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); + convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent); // allocate a buffer of suitable length for the number size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); - assert(result); + if (!result) return xpath_string(); // make the number! char_t* s = result; @@ -7958,7 +8545,7 @@ PUGI__NS_BEGIN { while (exponent > 0) { - assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); + assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9); *s++ = *mantissa ? *mantissa++ : '0'; exponent--; } @@ -7991,11 +8578,11 @@ PUGI__NS_BEGIN return xpath_string::from_heap_preallocated(result, s); } - - PUGI__FN bool check_string_to_number_format(const char_t* string) + + PUGI_IMPL_FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; // parse sign if (*string == '-') ++string; @@ -8003,26 +8590,26 @@ PUGI__NS_BEGIN if (!*string) return false; // if there is no integer part, there should be a decimal part with at least one digit - if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; + if (!PUGI_IMPL_IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI_IMPL_IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } - PUGI__FN double convert_string_to_number(const char_t* string) + PUGI_IMPL_FN double convert_string_to_number(const char_t* string) { // check string format if (!check_string_to_number_format(string)) return gen_nan(); @@ -8035,7 +8622,7 @@ PUGI__NS_BEGIN #endif } - PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) + PUGI_IMPL_FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; @@ -8058,29 +8645,29 @@ PUGI__NS_BEGIN return true; } - - PUGI__FN double round_nearest(double value) + + PUGI_IMPL_FN double round_nearest(double value) { return floor(value + 0.5); } - PUGI__FN double round_nearest_nzero(double value) + PUGI_IMPL_FN double round_nearest_nzero(double value) { // same as round_nearest, but returns -0 for [-0.5, -0] // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } - - PUGI__FN const char_t* qualified_name(const xpath_node& node) + + PUGI_IMPL_FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } - - PUGI__FN const char_t* local_name(const xpath_node& node) + + PUGI_IMPL_FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); - + return p ? p + 1 : name; } @@ -8107,51 +8694,51 @@ PUGI__NS_BEGIN } }; - PUGI__FN const char_t* namespace_uri(xml_node node) + PUGI_IMPL_FN const char_t* namespace_uri(xml_node node) { namespace_uri_predicate pred = node.name(); - + xml_node p = node; - + while (p) { xml_attribute a = p.find_attribute(pred); - + if (a) return a.value(); - + p = p.parent(); } - + return PUGIXML_TEXT(""); } - PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) + PUGI_IMPL_FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) { namespace_uri_predicate pred = attr.name(); - + // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); - + xml_node p = parent; - + while (p) { xml_attribute a = p.find_attribute(pred); - + if (a) return a.value(); - + p = p.parent(); } - + return PUGIXML_TEXT(""); } - PUGI__FN const char_t* namespace_uri(const xpath_node& node) + PUGI_IMPL_FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } - PUGI__FN char_t* normalize_space(char_t* buffer) + PUGI_IMPL_FN char_t* normalize_space(char_t* buffer) { char_t* write = buffer; @@ -8159,10 +8746,10 @@ PUGI__NS_BEGIN { char_t ch = *it++; - if (PUGI__IS_CHARTYPE(ch, ct_space)) + if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space - while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; + while (PUGI_IMPL_IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; @@ -8171,7 +8758,7 @@ PUGI__NS_BEGIN } // remove trailing space - if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; + if (write != buffer && PUGI_IMPL_IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; @@ -8179,13 +8766,13 @@ PUGI__NS_BEGIN return write; } - PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) + PUGI_IMPL_FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) { char_t* write = buffer; while (*buffer) { - PUGI__DMC_VOLATILE char_t ch = *buffer++; + PUGI_IMPL_DMC_VOLATILE char_t ch = *buffer++; const char_t* pos = find_char(from, ch); @@ -8201,7 +8788,7 @@ PUGI__NS_BEGIN return write; } - PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) + PUGI_IMPL_FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) { unsigned char table[128] = {0}; @@ -8225,17 +8812,15 @@ PUGI__NS_BEGIN if (!table[i]) table[i] = static_cast(i); - void* result = alloc->allocate_nothrow(sizeof(table)); + void* result = alloc->allocate(sizeof(table)); + if (!result) return 0; - if (result) - { - memcpy(result, table, sizeof(table)); - } + memcpy(result, table, sizeof(table)); return static_cast(result); } - PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) + PUGI_IMPL_FN char_t* translate_table(char_t* buffer, const unsigned char* table) { char_t* write = buffer; @@ -8317,7 +8902,7 @@ PUGI__NS_BEGIN static const xpath_node_set dummy_node_set; - PUGI__FN unsigned int hash_string(const char_t* str) + PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str) { // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) unsigned int result = 0; @@ -8328,15 +8913,15 @@ PUGI__NS_BEGIN result += result << 10; result ^= result >> 6; } - + result += result << 3; result ^= result >> 11; result += result << 15; - + return result; } - template PUGI__FN T* new_xpath_variable(const char_t* name) + template PUGI_IMPL_FN T* new_xpath_variable(const char_t* name) { size_t length = strlength(name); if (length == 0) return 0; // empty variable names are invalid @@ -8352,7 +8937,7 @@ PUGI__NS_BEGIN return result; } - PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) + PUGI_IMPL_FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) { switch (type) { @@ -8373,13 +8958,13 @@ PUGI__NS_BEGIN } } - template PUGI__FN void delete_xpath_variable(T* var) + template PUGI_IMPL_FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } - PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) + PUGI_IMPL_FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) { switch (type) { @@ -8400,11 +8985,11 @@ PUGI__NS_BEGIN break; default: - assert(!"Invalid variable type"); + assert(false && "Invalid variable type"); // unreachable } } - PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + PUGI_IMPL_FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) { switch (rhs->type()) { @@ -8421,12 +9006,12 @@ PUGI__NS_BEGIN return lhs->set(static_cast(rhs)->value); default: - assert(!"Invalid variable type"); + assert(false && "Invalid variable type"); // unreachable return false; } } - 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) + PUGI_IMPL_FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; @@ -8449,11 +9034,11 @@ PUGI__NS_BEGIN return true; } -PUGI__NS_END +PUGI_IMPL_NS_END // Internal node set class -PUGI__NS_BEGIN - PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) +PUGI_IMPL_NS_BEGIN + PUGI_IMPL_FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) { if (end - begin < 2) return xpath_node_set::type_sorted; @@ -8469,7 +9054,7 @@ PUGI__NS_BEGIN return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; } - PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) + PUGI_IMPL_FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) { xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; @@ -8486,13 +9071,13 @@ PUGI__NS_BEGIN else type = sorted; } - + if (type != order) reverse(begin, end); - + return order; } - PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) + PUGI_IMPL_FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) { if (begin == end) return xpath_node(); @@ -8508,7 +9093,7 @@ PUGI__NS_BEGIN return *min_element(begin, end, document_order_comparator()); default: - assert(!"Invalid node set type"); + assert(false && "Invalid node set type"); // unreachable return xpath_node(); } } @@ -8573,7 +9158,7 @@ PUGI__NS_BEGIN { // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); - assert(data); + if (!data) return; // finalize _begin = data; @@ -8597,12 +9182,42 @@ PUGI__NS_BEGIN _end = pos; } - void remove_duplicates() + void remove_duplicates(xpath_allocator* alloc) { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); - - _end = unique(_begin, _end); + if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) + { + xpath_allocator_capture cr(alloc); + + size_t size_ = static_cast(_end - _begin); + + size_t hash_size = 1; + while (hash_size < size_ + size_ / 2) hash_size *= 2; + + const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**))); + if (!hash_data) return; + + memset(hash_data, 0, hash_size * sizeof(const void**)); + + xpath_node* write = _begin; + + for (xpath_node* it = _begin; it != _end; ++it) + { + const void* attr = it->attribute().internal_object(); + const void* node = it->node().internal_object(); + const void* key = attr ? attr : node; + + if (key && hash_insert(hash_data, hash_size, key)) + { + *write++ = *it; + } + } + + _end = write; + } + else + { + _end = unique(_begin, _end); + } } xpath_node_set::type_t type() const @@ -8616,7 +9231,7 @@ PUGI__NS_BEGIN } }; - PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) + PUGI_IMPL_FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) { size_t capacity = static_cast(_eos - _begin); @@ -8625,7 +9240,7 @@ PUGI__NS_BEGIN // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); - assert(data); + if (!data) return; // finalize _begin = data; @@ -8635,9 +9250,9 @@ PUGI__NS_BEGIN // push *_end++ = node; } -PUGI__NS_END +PUGI_IMPL_NS_END -PUGI__NS_BEGIN +PUGI_IMPL_NS_BEGIN struct xpath_context { xpath_node n; @@ -8709,17 +9324,17 @@ PUGI__NS_BEGIN { next(); } - + const char_t* state() const { return _cur; } - + void next() { const char_t* cur = _cur; - while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; + while (PUGI_IMPL_IS_CHARTYPE(*cur, ct_space)) ++cur; // save lexeme position for error reporting _cur_lexeme_pos = cur; @@ -8729,7 +9344,7 @@ PUGI__NS_BEGIN case 0: _cur_lexeme = lex_eof; break; - + case '>': if (*(cur+1) == '=') { @@ -8773,7 +9388,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_equal; break; - + case '+': cur += 1; _cur_lexeme = lex_plus; @@ -8797,25 +9412,25 @@ PUGI__NS_BEGIN _cur_lexeme = lex_union; break; - + case '$': cur += 1; - if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + if (cur[0] == ':' && PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_var_ref; } else @@ -8836,7 +9451,7 @@ PUGI__NS_BEGIN _cur_lexeme = lex_close_brace; break; - + case '[': cur += 1; _cur_lexeme = lex_open_square_brace; @@ -8867,23 +9482,23 @@ PUGI__NS_BEGIN _cur_lexeme = lex_slash; } break; - + case '.': if (*(cur+1) == '.') { cur += 2; _cur_lexeme = lex_double_dot; } - else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) + else if (PUGI_IMPL_IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_number; } else @@ -8909,7 +9524,7 @@ PUGI__NS_BEGIN _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; - + if (!*cur) _cur_lexeme = lex_none; else @@ -8934,28 +9549,28 @@ PUGI__NS_BEGIN break; default: - if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) + if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; + if (*cur == '.') { cur++; - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } - else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + else if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { @@ -8963,16 +9578,16 @@ PUGI__NS_BEGIN { cur += 2; // :* } - else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + else if (PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } } _cur_lexeme_contents.end = cur; - + _cur_lexeme = lex_string; } else @@ -9083,7 +9698,7 @@ PUGI__NS_BEGIN axis_preceding_sibling, axis_self }; - + enum nodetest_t { nodetest_none, @@ -9118,7 +9733,7 @@ PUGI__NS_BEGIN }; template const axis_t axis_to_type::axis = N; - + class xpath_ast_node { private: @@ -9238,7 +9853,7 @@ PUGI__NS_BEGIN } } - assert(!"Wrong types"); + assert(false && "Wrong types"); // unreachable return false; } @@ -9313,7 +9928,7 @@ PUGI__NS_BEGIN } else { - assert(!"Wrong types"); + assert(false && "Wrong types"); // unreachable return false; } } @@ -9359,7 +9974,7 @@ PUGI__NS_BEGIN { xpath_context c(*it, i, size); - if (expr->eval_number(c, stack) == i) + if (expr->eval_number(c, stack) == static_cast(i)) { *last++ = *it; @@ -9379,15 +9994,16 @@ PUGI__NS_BEGIN xpath_node* last = ns.begin() + first; - xpath_context c(xpath_node(), 1, size); + xpath_node cn; + xpath_context c(cn, 1, size); double er = expr->eval_number(c, stack); - if (er >= 1.0 && er <= size) + if (er >= 1.0 && er <= static_cast(size)) { size_t eri = static_cast(er); - if (er == eri) + if (er == static_cast(eri)) { xpath_node r = last[eri - 1]; @@ -9437,7 +10053,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_node: case nodetest_all: if (is_xpath_attribute(name)) @@ -9446,7 +10062,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) { @@ -9454,19 +10070,19 @@ PUGI__NS_BEGIN return true; } break; - + default: ; } return false; } - + bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) { assert(n); - xml_node_type type = PUGI__NODETYPE(n); + xml_node_type type = PUGI_IMPL_NODETYPE(n); switch (_test) { @@ -9477,11 +10093,11 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_node: ns.push_back(xml_node(n), alloc); return true; - + case nodetest_type_comment: if (type == node_comment) { @@ -9489,7 +10105,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_text: if (type == node_pcdata || type == node_cdata) { @@ -9497,7 +10113,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_type_pi: if (type == node_pi) { @@ -9505,7 +10121,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_pi: if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) { @@ -9513,7 +10129,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all: if (type == node_element) { @@ -9521,7 +10137,7 @@ PUGI__NS_BEGIN return true; } break; - + case nodetest_all_in_namespace: if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) { @@ -9531,7 +10147,7 @@ PUGI__NS_BEGIN break; default: - assert(!"Unknown axis"); + assert(false && "Unknown axis"); // unreachable } return false; @@ -9548,33 +10164,33 @@ PUGI__NS_BEGIN for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) if (step_push(ns, a, n, alloc) & once) return; - + break; } - + case axis_child: { for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) if (step_push(ns, n, alloc) & once) return; - + xml_node_struct* cur = n->first_child; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + if (cur->first_child) cur = cur->first_child; else @@ -9585,32 +10201,32 @@ PUGI__NS_BEGIN if (cur == n) return; } - + cur = cur->next_sibling; } } - + break; } - + case axis_following_sibling: { for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_preceding_sibling: { for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) if (step_push(ns, c, alloc) & once) return; - + break; } - + case axis_following: { xml_node_struct* cur = n; @@ -9689,7 +10305,7 @@ PUGI__NS_BEGIN break; } - + case axis_ancestor: case axis_ancestor_or_self: { @@ -9698,15 +10314,15 @@ PUGI__NS_BEGIN return; xml_node_struct* cur = n->parent; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + cur = cur->parent; } - + break; } @@ -9724,12 +10340,12 @@ PUGI__NS_BEGIN break; } - + default: - assert(!"Unimplemented axis"); + assert(false && "Unimplemented axis"); // unreachable } } - + template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; @@ -9744,15 +10360,15 @@ PUGI__NS_BEGIN return; xml_node_struct* cur = p; - + while (cur) { if (step_push(ns, cur, alloc) & once) return; - + cur = cur->parent; } - + break; } @@ -9768,7 +10384,7 @@ PUGI__NS_BEGIN case axis_following: { xml_node_struct* cur = p; - + while (cur) { if (cur->first_child) @@ -9805,9 +10421,9 @@ PUGI__NS_BEGIN step_fill(ns, p, alloc, once, v); break; } - + default: - assert(!"Unimplemented axis"); + assert(false && "Unimplemented axis"); // unreachable } } @@ -9831,6 +10447,7 @@ PUGI__NS_BEGIN bool once = (axis == axis_attribute && _test == nodetest_name) || (!_right && eval_once(axis_type, eval)) || + // coverity[mixed_enums] (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; @@ -9849,7 +10466,7 @@ PUGI__NS_BEGIN // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); - + step_fill(ns, *it, stack.result, once, v); if (_right) apply_predicates(ns, size, stack, eval); } @@ -9863,11 +10480,11 @@ PUGI__NS_BEGIN // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); + ns.remove_duplicates(stack.temp); return ns; } - + public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) @@ -9882,14 +10499,14 @@ PUGI__NS_BEGIN assert(type == ast_number_constant); _data.number = value; } - + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } - + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { @@ -9924,25 +10541,25 @@ PUGI__NS_BEGIN { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); - + case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); - + case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); - + case ast_op_less: return compare_rel(_left, _right, c, stack, less()); - + case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); - + case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); @@ -9968,43 +10585,43 @@ PUGI__NS_BEGIN case ast_func_boolean: return _left->eval_boolean(c, stack); - + case ast_func_not: return !_left->eval_boolean(c, stack); - + case ast_func_true: return true; - + case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; - + xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); - + for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); - + if (a) { const char_t* value = a.value(); - + // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } - + return *value == 0 || *value == '-'; } } - + return false; } @@ -10024,35 +10641,37 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); - // fallthrough to type conversion + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack, nodeset_eval_any).empty(); - } - - default: - assert(!"Wrong expression for return type boolean"); - return false; - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); // unreachable + return false; } } @@ -10062,7 +10681,7 @@ PUGI__NS_BEGIN { case ast_op_add: return _left->eval_number(c, stack) + _right->eval_number(c, stack); - + case ast_op_subtract: return _left->eval_number(c, stack) - _right->eval_number(c, stack); @@ -10083,7 +10702,7 @@ PUGI__NS_BEGIN case ast_func_last: return static_cast(c.size); - + case ast_func_position: return static_cast(c.position); @@ -10093,28 +10712,28 @@ PUGI__NS_BEGIN return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); } - + case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } - + case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } - + case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } - + case ast_func_number_1: return _left->eval_number(c, stack); @@ -10123,36 +10742,36 @@ PUGI__NS_BEGIN xpath_allocator_capture cr(stack.result); double r = 0; - + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); - + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } - + return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); - + return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); - + return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); - + case ast_variable: { assert(_rettype == _data.variable->type()); @@ -10160,39 +10779,40 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_number) return _data.variable->get_number(); - // fallthrough to type conversion + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(!"Wrong expression for return type number"); - return 0; - } - + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); // unreachable + return 0; } } - + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) { assert(_type == ast_func_concat); @@ -10203,16 +10823,9 @@ PUGI__NS_BEGIN size_t count = 1; for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; - // gather all strings - xpath_string static_buffer[4]; - xpath_string* buffer = static_buffer; - - // allocate on-heap for large concats - if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) - { - buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); - assert(buffer); - } + // allocate a buffer for temporary string objects + xpath_string* buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + if (!buffer) return xpath_string(); // evaluate all strings to temporary stack xpath_stack swapped_stack = {stack.temp, stack.result}; @@ -10229,7 +10842,7 @@ PUGI__NS_BEGIN // create final string char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); - assert(result); + if (!result) return xpath_string(); char_t* ri = result; @@ -10248,11 +10861,11 @@ PUGI__NS_BEGIN { case ast_string_constant: return xpath_string::from_const(_data.string); - + case ast_func_local_name_0: { xpath_node na = c.n; - + return xpath_string::from_const(local_name(na)); } @@ -10262,14 +10875,14 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; - + return xpath_string::from_const(qualified_name(na)); } @@ -10279,14 +10892,14 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; - + return xpath_string::from_const(namespace_uri(na)); } @@ -10296,7 +10909,7 @@ PUGI__NS_BEGIN xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); - + return xpath_string::from_const(namespace_uri(na)); } @@ -10319,10 +10932,10 @@ PUGI__NS_BEGIN xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); - + return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); } - + case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); @@ -10331,7 +10944,7 @@ PUGI__NS_BEGIN xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); - + const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); @@ -10351,19 +10964,19 @@ PUGI__NS_BEGIN size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); - + if (is_nan(first)) return xpath_string(); // NaN - else if (first >= s_length + 1) return xpath_string(); - + else if (first >= static_cast(s_length + 1)) return xpath_string(); + size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + s.length(); - + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } - + case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); @@ -10375,14 +10988,14 @@ PUGI__NS_BEGIN double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); - + if (is_nan(first) || is_nan(last)) return xpath_string(); - else if (first >= s_length + 1) return xpath_string(); + else if (first >= static_cast(s_length + 1)) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); - + size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + size_t end = last >= static_cast(s_length + 1) ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); @@ -10396,6 +11009,8 @@ PUGI__NS_BEGIN xpath_string s = string_value(c.n, stack.result); char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + char_t* end = normalize_space(begin); return xpath_string::from_heap_preallocated(begin, end); @@ -10406,8 +11021,10 @@ PUGI__NS_BEGIN xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + char_t* end = normalize_space(begin); - + return xpath_string::from_heap_preallocated(begin, end); } @@ -10422,6 +11039,8 @@ PUGI__NS_BEGIN xpath_string to = _right->_next->eval_string(c, swapped_stack); char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); return xpath_string::from_heap_preallocated(begin, end); @@ -10432,6 +11051,8 @@ PUGI__NS_BEGIN xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + char_t* end = translate_table(begin, _data.table); return xpath_string::from_heap_preallocated(begin, end); @@ -10444,34 +11065,36 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_string) return xpath_string::from_const(_data.variable->get_string()); - // fallthrough to type conversion + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(!"Wrong expression for return type string"); - return xpath_string(); - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); // unreachable + return xpath_string(); } } @@ -10485,16 +11108,16 @@ PUGI__NS_BEGIN xpath_stack swapped_stack = {stack.temp, stack.result}; - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); - xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); + xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); + ls.set_type(xpath_node_set::type_unsorted); - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); + ls.append(rs.begin(), rs.end(), stack.result); + ls.remove_duplicates(stack.temp); - return rs; + return ls; } case ast_filter: @@ -10507,20 +11130,20 @@ PUGI__NS_BEGIN bool once = eval_once(set.type(), eval); apply_predicate(set, 0, stack, once); - + return set; } - + case ast_func_id: return xpath_node_set_raw(); - + case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, eval, axis_to_type()); - + case axis_ancestor_or_self: return step_do(c, stack, eval, axis_to_type()); @@ -10529,7 +11152,7 @@ PUGI__NS_BEGIN case axis_child: return step_do(c, stack, eval, axis_to_type()); - + case axis_descendant: return step_do(c, stack, eval, axis_to_type()); @@ -10538,28 +11161,28 @@ PUGI__NS_BEGIN case axis_following: return step_do(c, stack, eval, axis_to_type()); - + case axis_following_sibling: return step_do(c, stack, eval, axis_to_type()); - + case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); - + case axis_parent: return step_do(c, stack, eval, axis_to_type()); - + case axis_preceding: return step_do(c, stack, eval, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, eval, axis_to_type()); - + case axis_self: return step_do(c, stack, eval, axis_to_type()); default: - assert(!"Unknown axis"); + assert(false && "Unknown axis"); // unreachable return xpath_node_set_raw(); } } @@ -10594,21 +11217,31 @@ PUGI__NS_BEGIN return ns; } - // fallthrough to type conversion + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } default: - assert(!"Wrong expression for return type node set"); - return xpath_node_set_raw(); + ; } + + // none of the ast types that return the value directly matched, but conversions to node set are invalid + assert(false && "Wrong expression for return type node set"); // unreachable + return xpath_node_set_raw(); } void optimize(xpath_allocator* alloc) { - if (_left) _left->optimize(alloc); - if (_right) _right->optimize(alloc); - if (_next) _next->optimize(alloc); + if (_left) + _left->optimize(alloc); + if (_right) + _right->optimize(alloc); + + if (_next) + _next->optimize(alloc); + + // coverity[var_deref_model] optimize_self(alloc); } @@ -10617,13 +11250,14 @@ PUGI__NS_BEGIN // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && + _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) { _right = _right->_right; } // Classify filter/predicate ops to perform various optimizations during evaluation - if (_type == ast_filter || _type == ast_predicate) + if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) { assert(_test == predicate_default); @@ -10639,8 +11273,8 @@ PUGI__NS_BEGIN // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of rewrite for self/descendant/descendant-or-self axes // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) - if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && - _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && + _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) { if (_axis == axis_child || _axis == axis_descendant) @@ -10652,7 +11286,9 @@ PUGI__NS_BEGIN } // Use optimized lookup table implementation for translate() with constant arguments - if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + if (_type == ast_func_translate && + _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) + _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); @@ -10665,13 +11301,15 @@ PUGI__NS_BEGIN // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && + _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) + // coverity[mixed_enums] _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { _type = ast_opt_compare_attribute; } } - + bool is_posinv_expr() const { switch (_type) @@ -10695,10 +11333,10 @@ PUGI__NS_BEGIN default: if (_left && !_left->is_posinv_expr()) return false; - + for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv_expr()) return false; - + return true; } } @@ -10724,6 +11362,14 @@ PUGI__NS_BEGIN } }; + static const size_t xpath_ast_depth_limit = + #ifdef PUGIXML_XPATH_DEPTH_LIMIT + PUGIXML_XPATH_DEPTH_LIMIT + #else + 1024 + #endif + ; + struct xpath_parser { xpath_allocator* _alloc; @@ -10736,65 +11382,84 @@ PUGI__NS_BEGIN char_t _scratch[32]; - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf _error_handler; - #endif + size_t _depth; - void throw_error(const char* message) + xpath_ast_node* error(const char* message) { _result->error = message; _result->offset = _lexer.current_pos() - _query; - #ifdef PUGIXML_NO_EXCEPTIONS - longjmp(_error_handler, 1); - #else - throw xpath_exception(*_result); - #endif + return 0; } - void throw_error_oom() + xpath_ast_node* error_oom() { - #ifdef PUGIXML_NO_EXCEPTIONS - throw_error("Out of memory"); - #else - throw std::bad_alloc(); - #endif + assert(_alloc->_error); + *_alloc->_error = true; + + return 0; + } + + xpath_ast_node* error_rec() + { + return error("Exceeded maximum allowed query depth"); } void* alloc_node() { - void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); + return _alloc->allocate(sizeof(xpath_ast_node)); + } - if (!result) throw_error_oom(); + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } - return result; + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0; } const char_t* alloc_string(const xpath_lexer_string& value) { - if (value.begin) - { - size_t length = static_cast(value.end - value.begin); + if (!value.begin) + return PUGIXML_TEXT(""); - char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); - if (!c) throw_error_oom(); - assert(c); // workaround for clang static analysis + size_t length = static_cast(value.end - value.begin); - memcpy(c, value.begin, length * sizeof(char_t)); - c[length] = 0; + char_t* c = static_cast(_alloc->allocate((length + 1) * sizeof(char_t))); + if (!c) return 0; - return c; - } - else return 0; - } + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; - xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) - { - assert(argc <= 1); - - if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - - return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); + return c; } xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) @@ -10803,111 +11468,118 @@ PUGI__NS_BEGIN { case 'b': if (name == PUGIXML_TEXT("boolean") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); - + return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]); + break; - + case 'c': if (name == PUGIXML_TEXT("count") && argc == 1) { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_count, xpath_type_number, args[0]); } else if (name == PUGIXML_TEXT("contains") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); + return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("concat") && argc >= 2) - return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); + return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("ceiling") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); - - break; - - case 'f': - if (name == PUGIXML_TEXT("false") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); - else if (name == PUGIXML_TEXT("floor") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); - - break; - - case 'i': - if (name == PUGIXML_TEXT("id") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); - - break; - - case 'l': - if (name == PUGIXML_TEXT("last") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); - else if (name == PUGIXML_TEXT("lang") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("local-name") && argc <= 1) - return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); - - break; - - case 'n': - if (name == PUGIXML_TEXT("name") && argc <= 1) - return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); - else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) - return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); - else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("not") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("number") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); - - break; - - case 'p': - if (name == PUGIXML_TEXT("position") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); - - break; - - case 'r': - if (name == PUGIXML_TEXT("round") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); + return alloc_node(ast_func_ceiling, xpath_type_number, args[0]); break; - - case 's': - if (name == PUGIXML_TEXT("string") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); - else if (name == PUGIXML_TEXT("string-length") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); - else if (name == PUGIXML_TEXT("starts-with") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-before") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-after") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) - return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("sum") && argc == 1) + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return alloc_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return alloc_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return alloc_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return alloc_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return alloc_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); } break; - + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("not") && argc == 1) + return alloc_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); + + break; + + case 'p': + if (name == PUGIXML_TEXT("position") && argc == 0) + return alloc_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return alloc_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("string-length") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("sum") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + case 't': if (name == PUGIXML_TEXT("translate") && argc == 3) - return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); + return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("true") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); - + return alloc_node(ast_func_true, xpath_type_boolean); + break; default: break; } - throw_error("Unrecognized function or wrong parameter count"); - - return 0; + return error("Unrecognized function or wrong parameter count"); } axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) @@ -10923,37 +11595,37 @@ PUGI__NS_BEGIN return axis_ancestor_or_self; else if (name == PUGIXML_TEXT("attribute")) return axis_attribute; - + break; - + case 'c': if (name == PUGIXML_TEXT("child")) return axis_child; - + break; - + case 'd': if (name == PUGIXML_TEXT("descendant")) return axis_descendant; else if (name == PUGIXML_TEXT("descendant-or-self")) return axis_descendant_or_self; - + break; - + case 'f': if (name == PUGIXML_TEXT("following")) return axis_following; else if (name == PUGIXML_TEXT("following-sibling")) return axis_following_sibling; - + break; - + case 'n': if (name == PUGIXML_TEXT("namespace")) return axis_namespace; - + break; - + case 'p': if (name == PUGIXML_TEXT("parent")) return axis_parent; @@ -10961,13 +11633,13 @@ PUGI__NS_BEGIN return axis_preceding; else if (name == PUGIXML_TEXT("preceding-sibling")) return axis_preceding_sibling; - + break; - + case 's': if (name == PUGIXML_TEXT("self")) return axis_self; - + break; default: @@ -11005,7 +11677,7 @@ PUGI__NS_BEGIN return nodetest_type_text; break; - + default: break; } @@ -11023,18 +11695,18 @@ PUGI__NS_BEGIN xpath_lexer_string name = _lexer.contents(); if (!_variables) - throw_error("Unknown variable: variable set is not provided"); + return error("Unknown variable: variable set is not provided"); xpath_variable* var = 0; if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) - throw_error_oom(); + return error_oom(); if (!var) - throw_error("Unknown variable: variable set does not contain the given name"); + return error("Unknown variable: variable set does not contain the given name"); _lexer.next(); - return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); + return alloc_node(ast_variable, var->type(), var); } case lex_open_brace: @@ -11042,9 +11714,10 @@ PUGI__NS_BEGIN _lexer.next(); xpath_ast_node* n = parse_expression(); + if (!n) return 0; if (_lexer.current() != lex_close_brace) - throw_error("Unmatched braces"); + return error("Expected ')' to match an opening '('"); _lexer.next(); @@ -11054,11 +11727,11 @@ PUGI__NS_BEGIN case lex_quoted_string: { const char_t* value = alloc_string(_lexer.contents()); + if (!value) return 0; - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); _lexer.next(); - return n; + return alloc_node(ast_string_constant, xpath_type_string, value); } case lex_number: @@ -11066,84 +11739,100 @@ PUGI__NS_BEGIN double value = 0; if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) - throw_error_oom(); + return error_oom(); - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); _lexer.next(); - return n; + return alloc_node(ast_number_constant, xpath_type_number, value); } case lex_string: { xpath_ast_node* args[2] = {0}; size_t argc = 0; - + xpath_lexer_string function = _lexer.contents(); _lexer.next(); - + xpath_ast_node* last_arg = 0; - + if (_lexer.current() != lex_open_brace) - throw_error("Unrecognized function call"); + return error("Unrecognized function call"); _lexer.next(); - if (_lexer.current() != lex_close_brace) - args[argc++] = parse_expression(); + size_t old_depth = _depth; while (_lexer.current() != lex_close_brace) { - if (_lexer.current() != lex_comma) - throw_error("No comma between function arguments"); - _lexer.next(); - + if (argc > 0) + { + if (_lexer.current() != lex_comma) + return error("No comma between function arguments"); + _lexer.next(); + } + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_expression(); - + if (!n) return 0; + if (argc < 2) args[argc] = n; else last_arg->set_next(n); argc++; last_arg = n; } - + _lexer.next(); + _depth = old_depth; + return parse_function(function, argc, args); } default: - throw_error("Unrecognizable primary expression"); - - return 0; + return error("Unrecognizable primary expression"); } } - + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate // Predicate ::= '[' PredicateExpr ']' // PredicateExpr ::= Expr xpath_ast_node* parse_filter_expression() { xpath_ast_node* n = parse_primary_expression(); + if (!n) return 0; + + size_t old_depth = _depth; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + + if (n->rettype() != xpath_type_node_set) + return error("Predicate has to be applied to node set"); + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; - if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); + n = alloc_node(ast_filter, n, expr, predicate_default); + if (!n) return 0; if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - + return error("Expected ']' to match an opening '['"); + _lexer.next(); } - + + _depth = old_depth; + return n; } - + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep // AxisSpecifier ::= AxisName '::' | '@'? // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' @@ -11152,7 +11841,7 @@ PUGI__NS_BEGIN xpath_ast_node* parse_step(xpath_ast_node* set) { if (set && set->rettype() != xpath_type_node_set) - throw_error("Step has to be applied to node set"); + return error("Step has to be applied to node set"); bool axis_specified = false; axis_t axis = axis_child; // implied child axis @@ -11161,25 +11850,31 @@ PUGI__NS_BEGIN { axis = axis_attribute; axis_specified = true; - + _lexer.next(); } else if (_lexer.current() == lex_dot) { _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0); } else if (_lexer.current() == lex_double_dot) { _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0); } - + nodetest_t nt_type = nodetest_none; xpath_lexer_string nt_name; - + if (_lexer.current() == lex_string) { // node name test @@ -11190,11 +11885,13 @@ PUGI__NS_BEGIN if (_lexer.current() == lex_double_colon) { // parse axis name - if (axis_specified) throw_error("Two axis specifiers in one step"); + if (axis_specified) + return error("Two axis specifiers in one step"); axis = parse_axis_name(nt_name, axis_specified); - if (!axis_specified) throw_error("Unknown axis"); + if (!axis_specified) + return error("Unknown axis"); // read actual node test _lexer.next(); @@ -11210,42 +11907,47 @@ PUGI__NS_BEGIN nt_name = _lexer.contents(); _lexer.next(); } - else throw_error("Unrecognized node test"); + else + { + return error("Unrecognized node test"); + } } - + if (nt_type == nodetest_none) { // node type test or processing-instruction if (_lexer.current() == lex_open_brace) { _lexer.next(); - + if (_lexer.current() == lex_close_brace) { _lexer.next(); nt_type = parse_node_test_type(nt_name); - if (nt_type == nodetest_none) throw_error("Unrecognized node type"); - + if (nt_type == nodetest_none) + return error("Unrecognized node type"); + nt_name = xpath_lexer_string(); } else if (nt_name == PUGIXML_TEXT("processing-instruction")) { if (_lexer.current() != lex_quoted_string) - throw_error("Only literals are allowed as arguments to processing-instruction()"); - + return error("Only literals are allowed as arguments to processing-instruction()"); + nt_type = nodetest_pi; nt_name = _lexer.contents(); _lexer.next(); - + if (_lexer.current() != lex_close_brace) - throw_error("Unmatched brace near processing-instruction()"); + return error("Unmatched brace near processing-instruction()"); _lexer.next(); } else - throw_error("Unmatched brace near node type test"); - + { + return error("Unmatched brace near node type test"); + } } // QName or NCName:* else @@ -11253,10 +11955,13 @@ PUGI__NS_BEGIN if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* { nt_name.end--; // erase * - + nt_type = nodetest_all_in_namespace; } - else nt_type = nodetest_name; + else + { + nt_type = nodetest_name; + } } } } @@ -11265,52 +11970,82 @@ PUGI__NS_BEGIN nt_type = nodetest_all; _lexer.next(); } - else throw_error("Unrecognized node test"); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); - + else + { + return error("Unrecognized node test"); + } + + const char_t* nt_name_copy = alloc_string(nt_name); + if (!nt_name_copy) return 0; + + xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); + if (!n) return 0; + + size_t old_depth = _depth; + xpath_ast_node* last = 0; - + while (_lexer.current() == lex_open_square_brace) { _lexer.next(); - - xpath_ast_node* expr = parse_expression(); - xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); - + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; + + xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default); + if (!pred) return 0; + if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); + return error("Expected ']' to match an opening '['"); _lexer.next(); - + if (last) last->set_next(pred); else n->set_right(pred); - + last = pred; } + _depth = old_depth; + return n; } - + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) { xpath_ast_node* n = parse_step(set); - + if (!n) return 0; + + size_t old_depth = _depth; + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - + { + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + + ++_depth; + } + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + n = parse_step(n); + if (!n) return 0; } - + + _depth = old_depth; + return n; } - + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath xpath_ast_node* parse_location_path() @@ -11318,8 +12053,9 @@ PUGI__NS_BEGIN if (_lexer.current() == lex_slash) { _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path lexeme_t l = _lexer.current(); @@ -11332,17 +12068,20 @@ PUGI__NS_BEGIN else if (_lexer.current() == lex_double_slash) { _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; + + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + return parse_relative_location_path(n); } // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 return parse_relative_location_path(0); } - + // PathExpr ::= LocationPath // | FilterExpr // | FilterExpr '/' RelativeLocationPath @@ -11357,8 +12096,7 @@ PUGI__NS_BEGIN // PrimaryExpr begins with '$' in case of it being a variable reference, // '(' in case of it being an expression, string literal, number constant or // function call. - - if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { @@ -11366,29 +12104,34 @@ PUGI__NS_BEGIN { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); - - while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; - - if (*state != '(') return parse_location_path(); + + while (PUGI_IMPL_IS_CHARTYPE(*state, ct_space)) ++state; + + if (*state != '(') + return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. - if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); + if (parse_node_test_type(_lexer.contents()) != nodetest_none) + return parse_location_path(); } - + xpath_ast_node* n = parse_filter_expression(); + if (!n) return 0; if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); - + if (l == lex_double_slash) { - if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); + if (n->rettype() != xpath_type_node_set) + return error("Step has to be applied to node set"); - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; } - + // select from location path return parse_relative_location_path(n); } @@ -11400,12 +12143,15 @@ PUGI__NS_BEGIN _lexer.next(); // precedence 7+ - only parses union expressions - xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); + xpath_ast_node* n = parse_expression(7); + if (!n) return 0; - return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); + return alloc_node(ast_op_negate, xpath_type_number, n); } else + { return parse_location_path(); + } } struct binary_op_t @@ -11482,21 +12228,27 @@ PUGI__NS_BEGIN { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* rhs = parse_path_or_unary_expression(); + if (!rhs) return 0; binary_op_t nextop = binary_op_t::parse(_lexer); while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) { rhs = parse_expression_rec(rhs, nextop.precedence); + if (!rhs) return 0; nextop = binary_op_t::parse(_lexer); } if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) - throw_error("Union operator has to be applied to node sets"); + return error("Union operator has to be applied to node sets"); - lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); + lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); + if (!lhs) return 0; op = binary_op_t::parse(_lexer); } @@ -11522,39 +12274,46 @@ PUGI__NS_BEGIN // | MultiplicativeExpr '*' UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr // | MultiplicativeExpr 'mod' UnaryExpr - xpath_ast_node* parse_expression() + xpath_ast_node* parse_expression(int limit = 0) { - return parse_expression_rec(parse_path_or_unary_expression(), 0); + size_t old_depth = _depth; + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + + xpath_ast_node* n = parse_path_or_unary_expression(); + if (!n) return 0; + + n = parse_expression_rec(n, limit); + + _depth = old_depth; + + return n; } - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) { } xpath_ast_node* parse() { - xpath_ast_node* result = parse_expression(); - + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + assert(_depth == 0); + + // check if there are unparsed tokens left if (_lexer.current() != lex_eof) - { - // there are still unparsed tokens left, error - throw_error("Incorrect query"); - } - - return result; + return error("Incorrect query"); + + return n; } static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) { xpath_parser parser(query, variables, alloc, result); - #ifdef PUGIXML_NO_EXCEPTIONS - int error = setjmp(parser._error_handler); - - return (error == 0) ? parser.parse() : 0; - #else return parser.parse(); - #endif } }; @@ -11577,7 +12336,7 @@ PUGI__NS_BEGIN xml_memory::deallocate(impl); } - xpath_query_impl(): root(0), alloc(&block) + xpath_query_impl(): root(0), alloc(&block, &oom), oom(false) { block.next = 0; block.capacity = sizeof(block.data); @@ -11586,22 +12345,10 @@ PUGI__NS_BEGIN xpath_ast_node* root; xpath_allocator alloc; xpath_memory_block block; + bool oom; }; - PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) - { - if (!impl) return xpath_string(); - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_string(); - #endif - - xpath_context c(n, 1, 1); - - return impl->root->eval_string(c, sd.stack); - } - - PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) + PUGI_IMPL_FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) { if (!impl) return 0; @@ -11619,169 +12366,156 @@ PUGI__NS_BEGIN return impl->root; } -PUGI__NS_END +PUGI_IMPL_NS_END namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS - PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) + PUGI_IMPL_FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } - - PUGI__FN const char* xpath_exception::what() const throw() + + PUGI_IMPL_FN const char* xpath_exception::what() const throw() { return _result.error; } - PUGI__FN const xpath_parse_result& xpath_exception::result() const + PUGI_IMPL_FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif - - PUGI__FN xpath_node::xpath_node() - { - } - - PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) - { - } - - PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) + + PUGI_IMPL_FN xpath_node::xpath_node() { } - PUGI__FN xml_node xpath_node::node() const + PUGI_IMPL_FN xpath_node::xpath_node(const xml_node& node_): _node(node_) + { + } + + PUGI_IMPL_FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) + { + } + + PUGI_IMPL_FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } - - PUGI__FN xml_attribute xpath_node::attribute() const + + PUGI_IMPL_FN xml_attribute xpath_node::attribute() const { return _attribute; } - - PUGI__FN xml_node xpath_node::parent() const + + PUGI_IMPL_FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } - PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) + PUGI_IMPL_FN static void unspecified_bool_xpath_node(xpath_node***) { } - PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const + PUGI_IMPL_FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } - - PUGI__FN bool xpath_node::operator!() const + + PUGI_IMPL_FN bool xpath_node::operator!() const { return !(_node || _attribute); } - PUGI__FN bool xpath_node::operator==(const xpath_node& n) const + PUGI_IMPL_FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } - - PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const + + PUGI_IMPL_FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) + PUGI_IMPL_FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } - PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) + PUGI_IMPL_FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif - PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) + PUGI_IMPL_FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) { assert(begin_ <= end_); size_t size_ = static_cast(end_ - begin_); - if (size_ <= 1) + // use internal buffer for 0 or 1 elements, heap buffer otherwise + xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) { - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - _type = type_; + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif } - else - { - // make heap copy - xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - if (!storage) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return; - #else - throw std::bad_alloc(); - #endif - } + // deallocate old buffer + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB + if (size_) memcpy(storage, begin_, size_ * sizeof(xpath_node)); - - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - // finalize - _begin = storage; - _end = storage + size_; - _type = type_; - } + _begin = storage; + _end = storage + size_; + _type = type_; } -#if __cplusplus >= 201103 - PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT { _type = rhs._type; - _storage = rhs._storage; - _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _storage[0] = rhs._storage[0]; + _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; _end = _begin + (rhs._end - rhs._begin); rhs._type = type_unsorted; - rhs._begin = &rhs._storage; - rhs._end = rhs._begin; + rhs._begin = rhs._storage; + rhs._end = rhs._storage; } #endif - PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI_IMPL_FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) { } - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI_IMPL_FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(begin_, end_, type_); } - PUGI__FN xpath_node_set::~xpath_node_set() + PUGI_IMPL_FN xpath_node_set::~xpath_node_set() { - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); } - - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) + + PUGI_IMPL_FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(ns._begin, ns._end, ns._type); } - - PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + + PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; @@ -11790,17 +12524,17 @@ namespace pugi return *this; } -#if __cplusplus >= 201103 - PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage) +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) { _move(rhs); } - PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) + PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT { if (this == &rhs) return *this; - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); _move(rhs); @@ -11809,66 +12543,66 @@ namespace pugi } #endif - PUGI__FN xpath_node_set::type_t xpath_node_set::type() const + PUGI_IMPL_FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } - - PUGI__FN size_t xpath_node_set::size() const + + PUGI_IMPL_FN size_t xpath_node_set::size() const { return _end - _begin; } - - PUGI__FN bool xpath_node_set::empty() const + + PUGI_IMPL_FN bool xpath_node_set::empty() const { return _begin == _end; } - - PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const + + PUGI_IMPL_FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } - PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const + PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } - - PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const + + PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } - - PUGI__FN void xpath_node_set::sort(bool reverse) + + PUGI_IMPL_FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } - PUGI__FN xpath_node xpath_node_set::first() const + PUGI_IMPL_FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } - PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + PUGI_IMPL_FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } - PUGI__FN xpath_parse_result::operator bool() const + PUGI_IMPL_FN xpath_parse_result::operator bool() const { return error == 0; } - PUGI__FN const char* xpath_parse_result::description() const + PUGI_IMPL_FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } - PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) + PUGI_IMPL_FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } - PUGI__FN const char_t* xpath_variable::name() const + PUGI_IMPL_FN const char_t* xpath_variable::name() const { switch (_type) { @@ -11885,38 +12619,38 @@ namespace pugi return static_cast(this)->name; default: - assert(!"Invalid variable type"); + assert(false && "Invalid variable type"); // unreachable return 0; } } - PUGI__FN xpath_value_type xpath_variable::type() const + PUGI_IMPL_FN xpath_value_type xpath_variable::type() const { return _type; } - PUGI__FN bool xpath_variable::get_boolean() const + PUGI_IMPL_FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } - PUGI__FN double xpath_variable::get_number() const + PUGI_IMPL_FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } - PUGI__FN const char_t* xpath_variable::get_string() const + PUGI_IMPL_FN const char_t* xpath_variable::get_string() const { const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; return value ? value : PUGIXML_TEXT(""); } - PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const + PUGI_IMPL_FN const xpath_node_set& xpath_variable::get_node_set() const { return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; } - PUGI__FN bool xpath_variable::set(bool value) + PUGI_IMPL_FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; @@ -11924,7 +12658,7 @@ namespace pugi return true; } - PUGI__FN bool xpath_variable::set(double value) + PUGI_IMPL_FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; @@ -11932,7 +12666,7 @@ namespace pugi return true; } - PUGI__FN bool xpath_variable::set(const char_t* value) + PUGI_IMPL_FN bool xpath_variable::set(const char_t* value) { if (_type != xpath_type_string) return false; @@ -11953,7 +12687,7 @@ namespace pugi return true; } - PUGI__FN bool xpath_variable::set(const xpath_node_set& value) + PUGI_IMPL_FN bool xpath_variable::set(const xpath_node_set& value) { if (_type != xpath_type_node_set) return false; @@ -11961,19 +12695,19 @@ namespace pugi return true; } - PUGI__FN xpath_variable_set::xpath_variable_set() + PUGI_IMPL_FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } - PUGI__FN xpath_variable_set::~xpath_variable_set() + PUGI_IMPL_FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _destroy(_data[i]); } - PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; @@ -11981,7 +12715,7 @@ namespace pugi _assign(rhs); } - PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) { if (this == &rhs) return *this; @@ -11990,8 +12724,8 @@ namespace pugi return *this; } -#if __cplusplus >= 201103 - PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { @@ -12000,7 +12734,7 @@ namespace pugi } } - PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) + PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { @@ -12014,7 +12748,7 @@ namespace pugi } #endif - PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + PUGI_IMPL_FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) { xpath_variable_set temp; @@ -12025,7 +12759,7 @@ namespace pugi _swap(temp); } - PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + PUGI_IMPL_FN void xpath_variable_set::_swap(xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { @@ -12036,7 +12770,7 @@ namespace pugi } } - PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const + PUGI_IMPL_FN xpath_variable* xpath_variable_set::_find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; @@ -12049,7 +12783,7 @@ namespace pugi return 0; } - PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + PUGI_IMPL_FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) { xpath_variable* last = 0; @@ -12076,7 +12810,7 @@ namespace pugi return true; } - PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + PUGI_IMPL_FN void xpath_variable_set::_destroy(xpath_variable* var) { while (var) { @@ -12088,7 +12822,7 @@ namespace pugi } } - PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) + PUGI_IMPL_FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; @@ -12111,41 +12845,41 @@ namespace pugi return result; } - PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) + PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, bool value) { xpath_variable* var = add(name, xpath_type_boolean); return var ? var->set(value) : false; } - PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) + PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, double value) { xpath_variable* var = add(name, xpath_type_number); return var ? var->set(value) : false; } - PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) + PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const char_t* value) { xpath_variable* var = add(name, xpath_type_string); return var ? var->set(value) : false; } - PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) + PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) { xpath_variable* var = add(name, xpath_type_node_set); return var ? var->set(value) : false; } - PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) + PUGI_IMPL_FN xpath_variable* xpath_variable_set::get(const char_t* name) { return _find(name); } - PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const + PUGI_IMPL_FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return _find(name); } - PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + PUGI_IMPL_FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) { impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); @@ -12171,27 +12905,38 @@ namespace pugi _impl = impl.release(); _result.error = 0; } + else + { + #ifdef PUGIXML_NO_EXCEPTIONS + if (qimpl->oom) _result.error = "Out of memory"; + #else + if (qimpl->oom) throw std::bad_alloc(); + throw xpath_exception(_result); + #endif + } } } - PUGI__FN xpath_query::xpath_query(): _impl(0) + PUGI_IMPL_FN xpath_query::xpath_query(): _impl(0) { } - PUGI__FN xpath_query::~xpath_query() + PUGI_IMPL_FN xpath_query::~xpath_query() { if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); } -#if __cplusplus >= 201103 - PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) +#ifdef PUGIXML_HAS_MOVE + PUGI_IMPL_FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT { _impl = rhs._impl; + _result = rhs._result; rhs._impl = 0; + rhs._result = xpath_parse_result(); } - PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) + PUGI_IMPL_FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT { if (this == &rhs) return *this; @@ -12199,66 +12944,104 @@ namespace pugi impl::xpath_query_impl::destroy(static_cast(_impl)); _impl = rhs._impl; + _result = rhs._result; rhs._impl = 0; + rhs._result = xpath_parse_result(); return *this; } #endif - PUGI__FN xpath_value_type xpath_query::return_type() const + PUGI_IMPL_FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } - PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const + PUGI_IMPL_FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; - + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return false; - #endif - - return static_cast(_impl)->root->eval_boolean(c, sd.stack); + bool r = static_cast(_impl)->root->eval_boolean(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return false; + #else + throw std::bad_alloc(); + #endif + } + + return r; } - - PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const + + PUGI_IMPL_FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); - + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return impl::gen_nan(); - #endif + double r = static_cast(_impl)->root->eval_number(c, sd.stack); - return static_cast(_impl)->root->eval_number(c, sd.stack); + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return impl::gen_nan(); + #else + throw std::bad_alloc(); + #endif + } + + return r; } #ifndef PUGIXML_NO_STL - PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const + PUGI_IMPL_FN string_t xpath_query::evaluate_string(const xpath_node& n) const { + if (!_impl) return string_t(); + + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); + impl::xpath_string r = static_cast(_impl)->root->eval_string(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return string_t(); + #else + throw std::bad_alloc(); + #endif + } return string_t(r.c_str(), r.length()); } #endif - PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + PUGI_IMPL_FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const { + impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); + impl::xpath_string r = _impl ? static_cast(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string(); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + r = impl::xpath_string(); + #else + throw std::bad_alloc(); + #endif + } size_t full_size = r.length() + 1; - + if (capacity > 0) { size_t size = (full_size < capacity) ? full_size : capacity; @@ -12267,11 +13050,11 @@ namespace pugi memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); buffer[size - 1] = 0; } - + return full_size; } - PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + PUGI_IMPL_FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node_set(); @@ -12279,16 +13062,21 @@ namespace pugi impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node_set(); - #endif - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node_set(); + #else + throw std::bad_alloc(); + #endif + } + return xpath_node_set(r.begin(), r.end(), r.type()); } - PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const + PUGI_IMPL_FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node(); @@ -12296,63 +13084,68 @@ namespace pugi impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node(); - #endif - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node(); + #else + throw std::bad_alloc(); + #endif + } + return r.first(); } - PUGI__FN const xpath_parse_result& xpath_query::result() const + PUGI_IMPL_FN const xpath_parse_result& xpath_query::result() const { return _result; } - PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) + PUGI_IMPL_FN static void unspecified_bool_xpath_query(xpath_query***) { } - PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const + PUGI_IMPL_FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } - PUGI__FN bool xpath_query::operator!() const + PUGI_IMPL_FN bool xpath_query::operator!() const { return !_impl; } - PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const + PUGI_IMPL_FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); - return select_node(q); + return q.evaluate_node(*this); } - PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const + PUGI_IMPL_FN xpath_node xml_node::select_node(const xpath_query& query) const { return query.evaluate_node(*this); } - PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); - return select_nodes(q); + return q.evaluate_node_set(*this); } - PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const + PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } - PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + PUGI_IMPL_FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); - return select_single_node(q); + return q.evaluate_node(*this); } - PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const + PUGI_IMPL_FN xpath_node xml_node::select_single_node(const xpath_query& query) const { return query.evaluate_node(*this); } @@ -12370,38 +13163,45 @@ namespace pugi # pragma warning(pop) #endif +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic pop +#endif + // Undefine all local macros (makes sure we're not leaking macros in header-only mode) -#undef PUGI__NO_INLINE -#undef PUGI__UNLIKELY -#undef PUGI__STATIC_ASSERT -#undef PUGI__DMC_VOLATILE -#undef PUGI__MSVC_CRT_VERSION -#undef PUGI__NS_BEGIN -#undef PUGI__NS_END -#undef PUGI__FN -#undef PUGI__FN_NO_INLINE -#undef PUGI__GETPAGE_IMPL -#undef PUGI__GETPAGE -#undef PUGI__NODETYPE -#undef PUGI__IS_CHARTYPE_IMPL -#undef PUGI__IS_CHARTYPE -#undef PUGI__IS_CHARTYPEX -#undef PUGI__ENDSWITH -#undef PUGI__SKIPWS -#undef PUGI__OPTSET -#undef PUGI__PUSHNODE -#undef PUGI__POPNODE -#undef PUGI__SCANFOR -#undef PUGI__SCANWHILE -#undef PUGI__SCANWHILE_UNROLL -#undef PUGI__ENDSEG -#undef PUGI__THROW_ERROR -#undef PUGI__CHECK_ERROR +#undef PUGI_IMPL_NO_INLINE +#undef PUGI_IMPL_UNLIKELY +#undef PUGI_IMPL_STATIC_ASSERT +#undef PUGI_IMPL_DMC_VOLATILE +#undef PUGI_IMPL_UNSIGNED_OVERFLOW +#undef PUGI_IMPL_MSVC_CRT_VERSION +#undef PUGI_IMPL_SNPRINTF +#undef PUGI_IMPL_NS_BEGIN +#undef PUGI_IMPL_NS_END +#undef PUGI_IMPL_FN +#undef PUGI_IMPL_FN_NO_INLINE +#undef PUGI_IMPL_GETHEADER_IMPL +#undef PUGI_IMPL_GETPAGE_IMPL +#undef PUGI_IMPL_GETPAGE +#undef PUGI_IMPL_NODETYPE +#undef PUGI_IMPL_IS_CHARTYPE_IMPL +#undef PUGI_IMPL_IS_CHARTYPE +#undef PUGI_IMPL_IS_CHARTYPEX +#undef PUGI_IMPL_ENDSWITH +#undef PUGI_IMPL_SKIPWS +#undef PUGI_IMPL_OPTSET +#undef PUGI_IMPL_PUSHNODE +#undef PUGI_IMPL_POPNODE +#undef PUGI_IMPL_SCANFOR +#undef PUGI_IMPL_SCANWHILE +#undef PUGI_IMPL_SCANWHILE_UNROLL +#undef PUGI_IMPL_ENDSEG +#undef PUGI_IMPL_THROW_ERROR +#undef PUGI_IMPL_CHECK_ERROR #endif /** - * Copyright (c) 2006-2015 Arseny Kapoulkine + * Copyright (c) 2006-2023 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -12414,7 +13214,7 @@ namespace pugi * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND diff --git a/ext/pugixml/src/pugixml.hpp b/ext/pugixml/src/pugixml.hpp new file mode 100644 index 0000000..d17a7e6 --- /dev/null +++ b/ext/pugixml/src/pugixml.hpp @@ -0,0 +1,1516 @@ +/** + * pugixml parser - version 1.14 + * -------------------------------------------------------- + * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +// Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons +// Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits +#ifndef PUGIXML_VERSION +# define PUGIXML_VERSION 1140 // 1.14 +#endif + +// Include user configuration file (this can define various configuration macros) +#include "pugiconfig.hpp" + +#ifndef HEADER_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +// Include stddef.h for size_t and ptrdiff_t +#include + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#endif + +// Include STL headers +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// Macro for deprecated features +#ifndef PUGIXML_DEPRECATED +# if defined(__GNUC__) +# define PUGIXML_DEPRECATED __attribute__((deprecated)) +# elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_DEPRECATED __declspec(deprecated) +# else +# define PUGIXML_DEPRECATED +# endif +#endif + +// If no API is defined, assume default +#ifndef PUGIXML_API +# define PUGIXML_API +#endif + +// If no API for classes is defined, assume default +#ifndef PUGIXML_CLASS +# define PUGIXML_CLASS PUGIXML_API +#endif + +// If no API for functions is defined, assume default +#ifndef PUGIXML_FUNCTION +# define PUGIXML_FUNCTION PUGIXML_API +#endif + +// If the platform is known to have long long support, enable long long functions +#ifndef PUGIXML_HAS_LONG_LONG +# if __cplusplus >= 201103 +# define PUGIXML_HAS_LONG_LONG +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define PUGIXML_HAS_LONG_LONG +# endif +#endif + +// If the platform is known to have move semantics support, compile move ctor/operator implementation +#ifndef PUGIXML_HAS_MOVE +# if __cplusplus >= 201103 +# define PUGIXML_HAS_MOVE +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_HAS_MOVE +# endif +#endif + +// If C++ is 2011 or higher, add 'noexcept' specifiers +#ifndef PUGIXML_NOEXCEPT +# if __cplusplus >= 201103 +# define PUGIXML_NOEXCEPT noexcept +# elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define PUGIXML_NOEXCEPT noexcept +# else +# define PUGIXML_NOEXCEPT +# endif +#endif + +// Some functions can not be noexcept in compact mode +#ifdef PUGIXML_COMPACT +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT +#else +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT +#endif + +// If C++ is 2011 or higher, add 'override' qualifiers +#ifndef PUGIXML_OVERRIDE +# if __cplusplus >= 201103 +# define PUGIXML_OVERRIDE override +# elif defined(_MSC_VER) && _MSC_VER >= 1700 +# define PUGIXML_OVERRIDE override +# else +# define PUGIXML_OVERRIDE +# endif +#endif + +// If C++ is 2011 or higher, use 'nullptr' +#ifndef PUGIXML_NULL +# if __cplusplus >= 201103 +# define PUGIXML_NULL nullptr +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_NULL nullptr +# else +# define PUGIXML_NULL 0 +# endif +#endif + +// Character interface macros +#ifdef PUGIXML_WCHAR_MODE +# define PUGIXML_TEXT(t) L ## t +# define PUGIXML_CHAR wchar_t +#else +# define PUGIXML_TEXT(t) t +# define PUGIXML_CHAR char +#endif + +namespace pugi +{ + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE + typedef PUGIXML_CHAR char_t; + +#ifndef PUGIXML_NO_STL + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE + typedef std::basic_string, std::allocator > string_t; +#endif +} + +// The PugiXML namespace +namespace pugi +{ + // Tree node types + enum xml_node_type + { + node_null, // Empty (null) node handle + node_document, // A document tree's absolute root + node_element, // Element tag, i.e. '' + node_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' + }; + + // Parsing options + + // Minimal parsing mode (equivalent to turning all other flags off). + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. + const unsigned int parse_minimal = 0x0000; + + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. + const unsigned int parse_pi = 0x0001; + + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. + const unsigned int parse_comments = 0x0002; + + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. + const unsigned int parse_cdata = 0x0004; + + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata = 0x0008; + + // This flag determines if character and entity references are expanded during parsing. This flag is on by default. + const unsigned int parse_escapes = 0x0010; + + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. + const unsigned int parse_eol = 0x0020; + + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. + const unsigned int parse_wconv_attribute = 0x0040; + + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. + const unsigned int parse_wnorm_attribute = 0x0080; + + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. + const unsigned int parse_declaration = 0x0100; + + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. + const unsigned int parse_doctype = 0x0200; + + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only + // of whitespace is added to the DOM tree. + // This flag is off by default; turning it on may result in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata_single = 0x0400; + + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. + const unsigned int parse_trim_pcdata = 0x0800; + + // 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 + // is a valid document. This flag is off by default. + const unsigned int parse_fragment = 0x1000; + + // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of + // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. + // This flag is off by default. + const unsigned int parse_embed_pcdata = 0x2000; + + // This flag determines whether determines whether the the two pcdata should be merged or not, if no intermediatory data are parsed in the document. + // This flag is off by default. + const unsigned int parse_merge_pcdata = 0x4000; + + // The default parsing mode. + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + + // The full parsing mode. + // Nodes of all types are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; + + // These flags determine the encoding of input data for XML document + enum xml_encoding + { + encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range + { + public: + typedef It const_iterator; + typedef It iterator; + + xml_object_range(It b, It e): _begin(b), _end(e) + { + } + + It begin() const { return _begin; } + It end() const { return _end; } + + bool empty() const { return _begin == _end; } + + private: + It _begin, _end; + }; + + // Writer interface for node printing (see xml_node::print) + class PUGIXML_CLASS xml_writer + { + public: + virtual ~xml_writer(); + + // Write memory chunk into stream/file/whatever + virtual void write(const void* data, size_t size) = 0; + }; + + // xml_writer implementation for FILE* + class PUGIXML_CLASS xml_writer_file: public xml_writer + { + public: + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio + xml_writer_file(void* file); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + void* file; + }; + + #ifndef PUGIXML_NO_STL + // xml_writer implementation for streams + class PUGIXML_CLASS xml_writer_stream: public xml_writer + { + public: + // Construct writer from an output stream object + xml_writer_stream(std::basic_ostream >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + std::basic_ostream >* narrow_stream; + std::basic_ostream >* wide_stream; + }; + #endif + + // A light-weight handle for manipulating attributes in DOM tree + class PUGIXML_CLASS xml_attribute + { + friend class xml_attribute_iterator; + friend class xml_node; + + private: + xml_attribute_struct* _attr; + + typedef void (*unspecified_bool_type)(xml_attribute***); + + public: + // Default constructor. Constructs an empty attribute. + xml_attribute(); + + // Constructs attribute from internal pointer + explicit xml_attribute(xml_attribute_struct* attr); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped attribute pointers) + bool operator==(const xml_attribute& r) const; + bool operator!=(const xml_attribute& r) const; + bool operator<(const xml_attribute& r) const; + bool operator>(const xml_attribute& r) const; + bool operator<=(const xml_attribute& r) const; + bool operator>=(const xml_attribute& r) const; + + // Check if attribute is empty + bool empty() const; + + // Get attribute name/value, or "" if attribute is empty + const char_t* name() const; + const char_t* value() const; + + // Get attribute value, or the default value if attribute is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty + bool as_bool(bool def = false) const; + + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) + bool set_name(const char_t* rhs); + bool set_name(const char_t* rhs, size_t size); + bool set_value(const char_t* rhs); + bool set_value(const char_t* rhs, size_t size); + + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set_value(int rhs); + bool set_value(unsigned int rhs); + bool set_value(long rhs); + bool set_value(unsigned long rhs); + bool set_value(double rhs); + bool set_value(double rhs, int precision); + bool set_value(float rhs); + bool set_value(float rhs, int precision); + bool set_value(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set_value(long long rhs); + bool set_value(unsigned long long rhs); + #endif + + // Set attribute value (equivalent to set_value without error checking) + xml_attribute& operator=(const char_t* rhs); + xml_attribute& operator=(int rhs); + xml_attribute& operator=(unsigned int rhs); + xml_attribute& operator=(long rhs); + xml_attribute& operator=(unsigned long rhs); + xml_attribute& operator=(double rhs); + xml_attribute& operator=(float rhs); + xml_attribute& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_attribute& operator=(long long rhs); + xml_attribute& operator=(unsigned long long rhs); + #endif + + // Get next/previous attribute in the attribute list of the parent node + xml_attribute next_attribute() const; + xml_attribute previous_attribute() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_attribute_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); +#endif + + // A light-weight handle for manipulating nodes in DOM tree + class PUGIXML_CLASS xml_node + { + friend class xml_attribute_iterator; + friend class xml_node_iterator; + friend class xml_named_node_iterator; + + protected: + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_node***); + + public: + // Default constructor. Constructs an empty node. + xml_node(); + + // Constructs node from internal pointer + explicit xml_node(xml_node_struct* p); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped node pointers) + bool operator==(const xml_node& r) const; + bool operator!=(const xml_node& r) const; + bool operator<(const xml_node& r) const; + bool operator>(const xml_node& r) const; + bool operator<=(const xml_node& r) const; + bool operator>=(const xml_node& r) const; + + // Check if node is empty. + bool empty() const; + + // Get node type + xml_node_type type() const; + + // Get node name, or "" if node is empty or it has no name + const char_t* name() const; + + // Get node value, or "" if node is empty or it has no value + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. + const char_t* value() const; + + // Get attribute list + xml_attribute first_attribute() const; + xml_attribute last_attribute() const; + + // Get children list + xml_node first_child() const; + xml_node last_child() const; + + // Get next/previous sibling in the children list of the parent node + xml_node next_sibling() const; + xml_node previous_sibling() const; + + // Get parent node + xml_node parent() const; + + // Get root of DOM tree this node belongs to + xml_node root() const; + + // Get text object for the current node + xml_text text() const; + + // Get child, attribute or next/previous sibling with the specified name + xml_node child(const char_t* name) const; + xml_attribute attribute(const char_t* name) const; + xml_node next_sibling(const char_t* name) const; + xml_node previous_sibling(const char_t* name) const; + + // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) + xml_attribute attribute(const char_t* name, xml_attribute& hint) const; + + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA + const char_t* child_value() const; + + // Get child value of child with specified name. Equivalent to child(name).child_value(). + const char_t* child_value(const char_t* name) const; + + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) + bool set_name(const char_t* rhs); + bool set_name(const char_t* rhs, size_t size); + bool set_value(const char_t* rhs); + bool set_value(const char_t* rhs, size_t size); + + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. + xml_attribute append_attribute(const char_t* name); + xml_attribute prepend_attribute(const char_t* name); + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); + + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. + xml_attribute append_copy(const xml_attribute& proto); + xml_attribute prepend_copy(const xml_attribute& proto); + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); + + // Add child node with specified type. Returns added node, or empty node on errors. + xml_node append_child(xml_node_type type = node_element); + xml_node prepend_child(xml_node_type type = node_element); + xml_node insert_child_after(xml_node_type type, const xml_node& node); + xml_node insert_child_before(xml_node_type type, const xml_node& node); + + // Add child element with specified name. Returns added node, or empty node on errors. + xml_node append_child(const char_t* name); + xml_node prepend_child(const char_t* name); + xml_node insert_child_after(const char_t* name, const xml_node& node); + xml_node insert_child_before(const char_t* name, const xml_node& node); + + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. + xml_node append_copy(const xml_node& proto); + xml_node prepend_copy(const xml_node& proto); + xml_node insert_copy_after(const xml_node& proto, const xml_node& node); + xml_node insert_copy_before(const xml_node& proto, const xml_node& node); + + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. + xml_node append_move(const xml_node& moved); + xml_node prepend_move(const xml_node& moved); + xml_node insert_move_after(const xml_node& moved, const xml_node& node); + xml_node insert_move_before(const xml_node& moved, const xml_node& node); + + // Remove specified attribute + bool remove_attribute(const xml_attribute& a); + bool remove_attribute(const char_t* name); + + // Remove all attributes + bool remove_attributes(); + + // Remove specified child + bool remove_child(const xml_node& n); + bool remove_child(const char_t* name); + + // Remove all children + bool remove_children(); + + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Find attribute using predicate. Returns first attribute for which predicate returned true. + template xml_attribute find_attribute(Predicate pred) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) + if (pred(attrib)) + return attrib; + + return xml_attribute(); + } + + // Find child node using predicate. Returns first child for which predicate returned true. + template xml_node find_child(Predicate pred) const + { + if (!_root) return xml_node(); + + for (xml_node node = first_child(); node; node = node.next_sibling()) + if (pred(node)) + return node; + + return xml_node(); + } + + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. + template xml_node find_node(Predicate pred) const + { + if (!_root) return xml_node(); + + xml_node cur = first_child(); + + while (cur._root && cur._root != _root) + { + if (pred(cur)) return cur; + + if (cur.first_child()) cur = cur.first_child(); + else if (cur.next_sibling()) cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); + + if (cur._root != _root) cur = cur.next_sibling(); + } + } + + return xml_node(); + } + + // Find child node by attribute name/value + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; + + #ifndef PUGIXML_NO_STL + // Get the absolute node path from root as a text string. + string_t path(char_t delimiter = '/') const; + #endif + + // Search for a node by path consisting of node names and . or .. elements. + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; + + // Recursively traverse subtree with xml_tree_walker + bool traverse(xml_tree_walker& walker); + + #ifndef PUGIXML_NO_XPATH + // Select single node by evaluating XPath query. Returns first node from the resulting node set. + xpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; + xpath_node select_node(const xpath_query& query) const; + + // Select node set by evaluating XPath query + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; + xpath_node_set select_nodes(const xpath_query& query) const; + + // (deprecated: use select_node instead) Select single node by evaluating XPath query. + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; + + #endif + + // Print subtree using a writer object + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + + #ifndef PUGIXML_NO_STL + // Print subtree to stream + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; + #endif + + // Child nodes iterators + typedef xml_node_iterator iterator; + + iterator begin() const; + iterator end() const; + + // Attribute iterators + typedef xml_attribute_iterator attribute_iterator; + + attribute_iterator attributes_begin() const; + attribute_iterator attributes_end() const; + + // Range-based for support + xml_object_range children() const; + xml_object_range attributes() const; + + // Range-based for support for all children with the specified name + // Note: name pointer must have a longer lifetime than the returned object; be careful with passing temporaries! + xml_object_range children(const char_t* name) const; + + // Get node offset in parsed file/string (in char_t units) for debugging purposes + ptrdiff_t offset_debug() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_node_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); +#endif + + // A helper for working with text inside PCDATA nodes + class PUGIXML_CLASS xml_text + { + friend class xml_node; + + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_text***); + + explicit xml_text(xml_node_struct* root); + + xml_node_struct* _data_new(); + xml_node_struct* _data() const; + + public: + // Default constructor. Constructs an empty object. + xml_text(); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Check if text object is empty + bool empty() const; + + // Get text, or "" if object is empty + const char_t* get() const; + + // Get text, or the default value if object is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get text as a number, or the default value if conversion did not succeed or object is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty + bool as_bool(bool def = false) const; + + // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t* rhs); + bool set(const char_t* rhs, size_t size); + + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set(int rhs); + bool set(unsigned int rhs); + bool set(long rhs); + bool set(unsigned long rhs); + bool set(double rhs); + bool set(double rhs, int precision); + bool set(float rhs); + bool set(float rhs, int precision); + bool set(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set(long long rhs); + bool set(unsigned long long rhs); + #endif + + // Set text (equivalent to set without error checking) + xml_text& operator=(const char_t* rhs); + xml_text& operator=(int rhs); + xml_text& operator=(unsigned int rhs); + xml_text& operator=(long rhs); + xml_text& operator=(unsigned long rhs); + xml_text& operator=(double rhs); + xml_text& operator=(float rhs); + xml_text& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_text& operator=(long long rhs); + xml_text& operator=(unsigned long long rhs); + #endif + + // Get the data node (node_pcdata or node_cdata) for this object + xml_node data() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); +#endif + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + mutable xml_node _wrap; + xml_node _parent; + + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_node_iterator(); + + // Construct an iterator which points to the specified node + xml_node_iterator(const xml_node& node); + + // Iterator operators + bool operator==(const xml_node_iterator& rhs) const; + bool operator!=(const xml_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + xml_node_iterator& operator++(); + xml_node_iterator operator++(int); + + xml_node_iterator& operator--(); + xml_node_iterator operator--(int); + }; + + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) + class PUGIXML_CLASS xml_attribute_iterator + { + friend class xml_node; + + private: + mutable xml_attribute _wrap; + xml_node _parent; + + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_attribute value_type; + typedef xml_attribute* pointer; + typedef xml_attribute& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_attribute_iterator(); + + // Construct an iterator which points to the specified attribute + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); + + // Iterator operators + bool operator==(const xml_attribute_iterator& rhs) const; + bool operator!=(const xml_attribute_iterator& rhs) const; + + xml_attribute& operator*() const; + xml_attribute* operator->() const; + + xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // Named node range helper + class PUGIXML_CLASS xml_named_node_iterator + { + friend class xml_node; + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_named_node_iterator(); + + // Construct an iterator which points to the specified node + // Note: name pointer is stored in the iterator and must have a longer lifetime than iterator itself + xml_named_node_iterator(const xml_node& node, const char_t* name); + + // Iterator operators + bool operator==(const xml_named_node_iterator& rhs) const; + bool operator!=(const xml_named_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + xml_named_node_iterator& operator++(); + xml_named_node_iterator operator++(int); + + xml_named_node_iterator& operator--(); + xml_named_node_iterator operator--(int); + + private: + mutable xml_node _wrap; + xml_node _parent; + const char_t* _name; + + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); + }; + + // Abstract tree walker class (see xml_node::traverse) + class PUGIXML_CLASS xml_tree_walker + { + friend class xml_node; + + private: + int _depth; + + protected: + // Get current traversal depth + int depth() const; + + public: + xml_tree_walker(); + virtual ~xml_tree_walker(); + + // Callback that is called when traversal begins + virtual bool begin(xml_node& node); + + // Callback that is called for each node traversed + virtual bool for_each(xml_node& node) = 0; + + // Callback that is called when traversal ends + virtual bool end(xml_node& node); + }; + + // Parsing status, returned as part of xml_parse_result object + enum xml_parse_status + { + status_ok = 0, // No error + + status_file_not_found, // File was not found during load_file() + status_io_error, // Error reading from file/stream + status_out_of_memory, // Could not allocate memory + status_internal_error, // Internal error occurred + + status_unrecognized_tag, // Parser could not determine tag type + + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction + status_bad_comment, // Parsing error occurred while parsing comment + status_bad_cdata, // Parsing error occurred while parsing CDATA section + status_bad_doctype, // Parsing error occurred while parsing document type declaration + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section + status_bad_start_element, // Parsing error occurred while parsing start element tag + status_bad_attribute, // Parsing error occurred while parsing element attribute + status_bad_end_element, // Parsing error occurred while parsing end element tag + 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) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) + + status_no_document_element // Parsing resulted in a document without element nodes + }; + + // Parsing result + struct PUGIXML_CLASS xml_parse_result + { + // Parsing status (see xml_parse_status) + xml_parse_status status; + + // Last parsed offset (in char_t units from start of input data) + ptrdiff_t offset; + + // Source document encoding + xml_encoding encoding; + + // Default constructor, initializes object to failed state + xml_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // Document class (DOM tree root) + class PUGIXML_CLASS xml_document: public xml_node + { + private: + char_t* _buffer; + + char _memory[192]; + + // Non-copyable semantics + xml_document(const xml_document&); + xml_document& operator=(const xml_document&); + + void _create(); + void _destroy(); + void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + #endif + + // Removes all nodes, leaving the empty document + void reset(); + + // Removes all nodes, then copies the entire contents of the specified document + void reset(const xml_document& proto); + + #ifndef PUGIXML_NO_STL + // Load document from stream. + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); + #endif + + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. + PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default); + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); + + // Load document from file + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + #ifndef PUGIXML_NO_STL + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; + #endif + + // Save XML to file + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + // Get document element + xml_node document_element() const; + }; + +#ifndef PUGIXML_NO_XPATH + // XPath query return type + enum xpath_value_type + { + xpath_type_none, // Unknown type (query failed to compile) + xpath_type_node_set, // Node set (xpath_node_set) + xpath_type_number, // Number + xpath_type_string, // String + xpath_type_boolean // Boolean + }; + + // XPath parsing result + struct PUGIXML_CLASS xpath_parse_result + { + // Error message (0 if no error) + const char* error; + + // Last parsed offset (in char_t units from string start) + ptrdiff_t offset; + + // Default constructor, initializes object to failed state + xpath_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // A single XPath variable + class PUGIXML_CLASS xpath_variable + { + friend class xpath_variable_set; + + protected: + xpath_value_type _type; + xpath_variable* _next; + + xpath_variable(xpath_value_type type); + + // Non-copyable semantics + xpath_variable(const xpath_variable&); + xpath_variable& operator=(const xpath_variable&); + + public: + // Get variable name + const char_t* name() const; + + // Get variable type + xpath_value_type type() const; + + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error + bool get_boolean() const; + double get_number() const; + const char_t* get_string() const; + const xpath_node_set& get_node_set() const; + + // Set variable value; no type conversion is performed, false is returned on type mismatch error + bool set(bool value); + bool set(double value); + bool set(const char_t* value); + bool set(const xpath_node_set& value); + }; + + // A set of XPath variables + class PUGIXML_CLASS xpath_variable_set + { + private: + xpath_variable* _data[64]; + + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); + + xpath_variable* _find(const char_t* name) const; + + static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Add a new variable or get the existing one, if the types match + xpath_variable* add(const char_t* name, xpath_value_type type); + + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch + bool set(const char_t* name, bool value); + bool set(const char_t* name, double value); + bool set(const char_t* name, const char_t* value); + bool set(const char_t* name, const xpath_node_set& value); + + // Get existing variable by name + xpath_variable* get(const char_t* name); + const xpath_variable* get(const char_t* name) const; + }; + + // A compiled XPath query object + class PUGIXML_CLASS xpath_query + { + private: + void* _impl; + xpath_parse_result _result; + + typedef void (*unspecified_bool_type)(xpath_query***); + + // Non-copyable semantics + xpath_query(const xpath_query&); + xpath_query& operator=(const xpath_query&); + + public: + // Construct a compiled object from XPath expression. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. + explicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL); + + // Constructor + xpath_query(); + + // Destructor + ~xpath_query(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT; + xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get query expression return type + xpath_value_type return_type() const; + + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + bool evaluate_boolean(const xpath_node& n) const; + + // Evaluate expression as double value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + double evaluate_number(const xpath_node& n) const; + + #ifndef PUGIXML_NO_STL + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + string_t evaluate_string(const xpath_node& n) const; + #endif + + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. + xpath_node_set evaluate_node_set(const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // Return first node in document order, or empty node if node set is empty. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. + xpath_node evaluate_node(const xpath_node& n) const; + + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) + const xpath_parse_result& result() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + }; + + #ifndef PUGIXML_NO_EXCEPTIONS + #if defined(_MSC_VER) + // C4275 can be ignored in Visual C++ if you are deriving + // from a type in the Standard C++ Library + #pragma warning(push) + #pragma warning(disable: 4275) + #endif + // XPath exception class + class PUGIXML_CLASS xpath_exception: public std::exception + { + private: + xpath_parse_result _result; + + public: + // Construct exception from parse result + explicit xpath_exception(const xpath_parse_result& result); + + // Get error message + virtual const char* what() const throw() PUGIXML_OVERRIDE; + + // Get parse result + const xpath_parse_result& result() const; + }; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + #endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef void (*unspecified_bool_type)(xpath_node***); + + public: + // Default constructor; constructs empty XPath node + xpath_node(); + + // Construct XPath node from XML node/attribute + xpath_node(const xml_node& node); + xpath_node(const xml_attribute& attribute, const xml_node& parent); + + // Get node/attribute, if any + xml_node node() const; + xml_attribute attribute() const; + + // Get parent of contained node/attribute + xml_node parent() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators + bool operator==(const xpath_node& n) const; + bool operator!=(const xpath_node& n) const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); +#endif + + // A fixed-size collection of XPath nodes + class PUGIXML_CLASS xpath_node_set + { + public: + // Collection type + enum type_t + { + type_unsorted, // Not ordered + type_sorted, // Sorted by document order (ascending) + type_sorted_reverse // Sorted by document order (descending) + }; + + // Constant iterator type + typedef const xpath_node* const_iterator; + + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work + typedef const xpath_node* iterator; + + // Default constructor. Constructs empty set. + xpath_node_set(); + + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); + + // Destructor + ~xpath_node_set(); + + // Copy constructor/assignment operator + xpath_node_set(const xpath_node_set& ns); + xpath_node_set& operator=(const xpath_node_set& ns); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get collection type + type_t type() const; + + // Get collection size + size_t size() const; + + // Indexing operator + const xpath_node& operator[](size_t index) const; + + // Collection iterators + const_iterator begin() const; + const_iterator end() const; + + // Sort the collection in ascending/descending order by document order + void sort(bool reverse = false); + + // Get first node in the collection by document order + xpath_node first() const; + + // Check if collection is empty + bool empty() const; + + private: + type_t _type; + + xpath_node _storage[1]; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT; + }; +#endif + +#ifndef PUGIXML_NO_STL + // Convert wide string to UTF8 + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); + + // Convert UTF8 to wide string + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); +#endif + + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure + typedef void* (*allocation_function)(size_t size); + + // Memory deallocation function interface + typedef void (*deallocation_function)(void* ptr); + + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); + + // Get current memory management functions + allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); +} +#endif + +#endif + +// Make sure implementation is included in header-only mode +// Use macro expansion in #include to work around QMake (QTBUG-11923) +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) +# define PUGIXML_SOURCE "pugixml.cpp" +# include PUGIXML_SOURCE +#endif + +/** + * Copyright (c) 2006-2023 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/ext/tmxlite/CMakeLists.txt b/ext/tmxlite/CMakeLists.txt index 1be7263..643577a 100644 --- a/ext/tmxlite/CMakeLists.txt +++ b/ext/tmxlite/CMakeLists.txt @@ -77,8 +77,7 @@ if(USE_EXTLIBS) else() # add miniz and pugixml from source - target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_DIR}/detail/pugixml.cpp) - target_link_libraries(${PROJECT_NAME} External::miniz) + target_link_libraries(${PROJECT_NAME} pugixml::static External::miniz) if(USE_ZSTD) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ZSTD) diff --git a/ext/tmxlite/src/detail/pugixml.LICENSE b/ext/tmxlite/src/detail/pugixml.LICENSE deleted file mode 100644 index 55beffb..0000000 --- a/ext/tmxlite/src/detail/pugixml.LICENSE +++ /dev/null @@ -1,34 +0,0 @@ -/** - * pugixml parser - version 1.7 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - * - * - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/ext/tmxlite/src/detail/pugixml.hpp b/ext/tmxlite/src/detail/pugixml.hpp index 9f7c3fb..fd95160 100644 --- a/ext/tmxlite/src/detail/pugixml.hpp +++ b/ext/tmxlite/src/detail/pugixml.hpp @@ -1,1400 +1 @@ -/** - * pugixml parser - version 1.7 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 170 -#endif - -// Include user configuration file (this can define various configuration macros) -#include "pugiconfig.hpp" - -#ifndef HEADER_PUGIXML_HPP -#define HEADER_PUGIXML_HPP - -// Include stddef.h for size_t and ptrdiff_t -#include - -// Include exception header for XPath -#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) -# include -#endif - -// Include STL headers -#ifndef PUGIXML_NO_STL -# include -# include -# include -#endif - -// Macro for deprecated features -#ifndef PUGIXML_DEPRECATED -# if defined(__GNUC__) -# define PUGIXML_DEPRECATED __attribute__((deprecated)) -# elif defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGIXML_DEPRECATED __declspec(deprecated) -# else -# define PUGIXML_DEPRECATED -# endif -#endif - -// If no API is defined, assume default -#ifndef PUGIXML_API -# define PUGIXML_API -#endif - -// If no API for classes is defined, assume default -#ifndef PUGIXML_CLASS -# define PUGIXML_CLASS PUGIXML_API -#endif - -// If no API for functions is defined, assume default -#ifndef PUGIXML_FUNCTION -# define PUGIXML_FUNCTION PUGIXML_API -#endif - -// If the platform is known to have long long support, enable long long functions -#ifndef PUGIXML_HAS_LONG_LONG -# if __cplusplus >= 201103 -# define PUGIXML_HAS_LONG_LONG -# elif defined(_MSC_VER) && _MSC_VER >= 1400 -# define PUGIXML_HAS_LONG_LONG -# endif -#endif - -// Character interface macros -#ifdef PUGIXML_WCHAR_MODE -# define PUGIXML_TEXT(t) L ## t -# define PUGIXML_CHAR wchar_t -#else -# define PUGIXML_TEXT(t) t -# define PUGIXML_CHAR char -#endif - -namespace pugi -{ - // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE - typedef PUGIXML_CHAR char_t; - -#ifndef PUGIXML_NO_STL - // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE - typedef std::basic_string, std::allocator > string_t; -#endif -} - -// The PugiXML namespace -namespace pugi -{ - // Tree node types - enum xml_node_type - { - node_null, // Empty (null) node handle - node_document, // A document tree's absolute root - node_element, // Element tag, i.e. '' - node_pcdata, // Plain character data, i.e. 'text' - node_cdata, // Character data, i.e. '' - node_comment, // Comment tag, i.e. '' - node_pi, // Processing instruction, i.e. '' - node_declaration, // Document declaration, i.e. '' - node_doctype // Document type declaration, i.e. '' - }; - - // Parsing options - - // Minimal parsing mode (equivalent to turning all other flags off). - // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. - const unsigned int parse_minimal = 0x0000; - - // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. - const unsigned int parse_pi = 0x0001; - - // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. - const unsigned int parse_comments = 0x0002; - - // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. - const unsigned int parse_cdata = 0x0004; - - // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. - // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata = 0x0008; - - // This flag determines if character and entity references are expanded during parsing. This flag is on by default. - const unsigned int parse_escapes = 0x0010; - - // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. - const unsigned int parse_eol = 0x0020; - - // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. - const unsigned int parse_wconv_attribute = 0x0040; - - // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. - const unsigned int parse_wnorm_attribute = 0x0080; - - // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. - const unsigned int parse_declaration = 0x0100; - - // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. - const unsigned int parse_doctype = 0x0200; - - // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only - // of whitespace is added to the DOM tree. - // This flag is off by default; turning it on may result in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata_single = 0x0400; - - // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. - const unsigned int parse_trim_pcdata = 0x0800; - - // 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 - // is a valid document. This flag is off by default. - const unsigned int parse_fragment = 0x1000; - - // The default parsing mode. - // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; - - // The full parsing mode. - // Nodes of all types are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; - - // These flags determine the encoding of input data for XML document - enum xml_encoding - { - encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range - { - public: - typedef It const_iterator; - typedef It iterator; - - xml_object_range(It b, It e): _begin(b), _end(e) - { - } - - It begin() const { return _begin; } - It end() const { return _end; } - - private: - It _begin, _end; - }; - - // Writer interface for node printing (see xml_node::print) - class PUGIXML_CLASS xml_writer - { - public: - virtual ~xml_writer() {} - - // Write memory chunk into stream/file/whatever - virtual void write(const void* data, size_t size) = 0; - }; - - // xml_writer implementation for FILE* - class PUGIXML_CLASS xml_writer_file: public xml_writer - { - public: - // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio - xml_writer_file(void* file); - - virtual void write(const void* data, size_t size); - - private: - void* file; - }; - - #ifndef PUGIXML_NO_STL - // xml_writer implementation for streams - class PUGIXML_CLASS xml_writer_stream: public xml_writer - { - public: - // Construct writer from an output stream object - xml_writer_stream(std::basic_ostream >& stream); - xml_writer_stream(std::basic_ostream >& stream); - - virtual void write(const void* data, size_t size); - - private: - std::basic_ostream >* narrow_stream; - std::basic_ostream >* wide_stream; - }; - #endif - - // A light-weight handle for manipulating attributes in DOM tree - class PUGIXML_CLASS xml_attribute - { - friend class xml_attribute_iterator; - friend class xml_node; - - private: - xml_attribute_struct* _attr; - - typedef void (*unspecified_bool_type)(xml_attribute***); - - public: - // Default constructor. Constructs an empty attribute. - xml_attribute(); - - // Constructs attribute from internal pointer - explicit xml_attribute(xml_attribute_struct* attr); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped attribute pointers) - bool operator==(const xml_attribute& r) const; - bool operator!=(const xml_attribute& r) const; - bool operator<(const xml_attribute& r) const; - bool operator>(const xml_attribute& r) const; - bool operator<=(const xml_attribute& r) const; - bool operator>=(const xml_attribute& r) const; - - // Check if attribute is empty - bool empty() const; - - // Get attribute name/value, or "" if attribute is empty - const char_t* name() const; - const char_t* value() const; - - // Get attribute value, or the default value if attribute is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty - bool as_bool(bool def = false) const; - - // Set attribute name/value (returns false if attribute is empty or there is not enough memory) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set_value(int rhs); - bool set_value(unsigned int rhs); - bool set_value(double rhs); - bool set_value(float rhs); - bool set_value(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set_value(long long rhs); - bool set_value(unsigned long long rhs); - #endif - - // Set attribute value (equivalent to set_value without error checking) - xml_attribute& operator=(const char_t* rhs); - xml_attribute& operator=(int rhs); - xml_attribute& operator=(unsigned int rhs); - xml_attribute& operator=(double rhs); - xml_attribute& operator=(float rhs); - xml_attribute& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_attribute& operator=(long long rhs); - xml_attribute& operator=(unsigned long long rhs); - #endif - - // Get next/previous attribute in the attribute list of the parent node - xml_attribute next_attribute() const; - xml_attribute previous_attribute() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_attribute_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); -#endif - - // A light-weight handle for manipulating nodes in DOM tree - class PUGIXML_CLASS xml_node - { - friend class xml_attribute_iterator; - friend class xml_node_iterator; - friend class xml_named_node_iterator; - - protected: - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_node***); - - public: - // Default constructor. Constructs an empty node. - xml_node(); - - // Constructs node from internal pointer - explicit xml_node(xml_node_struct* p); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped node pointers) - bool operator==(const xml_node& r) const; - bool operator!=(const xml_node& r) const; - bool operator<(const xml_node& r) const; - bool operator>(const xml_node& r) const; - bool operator<=(const xml_node& r) const; - bool operator>=(const xml_node& r) const; - - // Check if node is empty. - bool empty() const; - - // Get node type - xml_node_type type() const; - - // Get node name, or "" if node is empty or it has no name - const char_t* name() const; - - // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. - const char_t* value() const; - - // Get attribute list - xml_attribute first_attribute() const; - xml_attribute last_attribute() const; - - // Get children list - xml_node first_child() const; - xml_node last_child() const; - - // Get next/previous sibling in the children list of the parent node - xml_node next_sibling() const; - xml_node previous_sibling() const; - - // Get parent node - xml_node parent() const; - - // Get root of DOM tree this node belongs to - xml_node root() const; - - // Get text object for the current node - xml_text text() const; - - // Get child, attribute or next/previous sibling with the specified name - xml_node child(const char_t* name) const; - xml_attribute attribute(const char_t* name) const; - xml_node next_sibling(const char_t* name) const; - xml_node previous_sibling(const char_t* name) const; - - // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) - xml_attribute attribute(const char_t* name, xml_attribute& hint) const; - - // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA - const char_t* child_value() const; - - // Get child value of child with specified name. Equivalent to child(name).child_value(). - const char_t* child_value(const char_t* name) const; - - // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Add attribute with specified name. Returns added attribute, or empty attribute on errors. - xml_attribute append_attribute(const char_t* name); - xml_attribute prepend_attribute(const char_t* name); - xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); - xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); - - // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. - xml_attribute append_copy(const xml_attribute& proto); - xml_attribute prepend_copy(const xml_attribute& proto); - xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); - xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); - - // Add child node with specified type. Returns added node, or empty node on errors. - xml_node append_child(xml_node_type type = node_element); - xml_node prepend_child(xml_node_type type = node_element); - xml_node insert_child_after(xml_node_type type, const xml_node& node); - xml_node insert_child_before(xml_node_type type, const xml_node& node); - - // Add child element with specified name. Returns added node, or empty node on errors. - xml_node append_child(const char_t* name); - xml_node prepend_child(const char_t* name); - xml_node insert_child_after(const char_t* name, const xml_node& node); - xml_node insert_child_before(const char_t* name, const xml_node& node); - - // Add a copy of the specified node as a child. Returns added node, or empty node on errors. - xml_node append_copy(const xml_node& proto); - xml_node prepend_copy(const xml_node& proto); - xml_node insert_copy_after(const xml_node& proto, const xml_node& node); - xml_node insert_copy_before(const xml_node& proto, const xml_node& node); - - // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. - xml_node append_move(const xml_node& moved); - xml_node prepend_move(const xml_node& moved); - xml_node insert_move_after(const xml_node& moved, const xml_node& node); - xml_node insert_move_before(const xml_node& moved, const xml_node& node); - - // Remove specified attribute - bool remove_attribute(const xml_attribute& a); - bool remove_attribute(const char_t* name); - - // Remove specified child - bool remove_child(const xml_node& n); - bool remove_child(const char_t* name); - - // Parses buffer as an XML document fragment and appends all nodes as children of the current node. - // Copies/converts the buffer, so it may be deleted or changed after the function returns. - // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. - xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Find attribute using predicate. Returns first attribute for which predicate returned true. - template xml_attribute find_attribute(Predicate pred) const - { - if (!_root) return xml_attribute(); - - for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) - if (pred(attrib)) - return attrib; - - return xml_attribute(); - } - - // Find child node using predicate. Returns first child for which predicate returned true. - template xml_node find_child(Predicate pred) const - { - if (!_root) return xml_node(); - - for (xml_node node = first_child(); node; node = node.next_sibling()) - if (pred(node)) - return node; - - return xml_node(); - } - - // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. - template xml_node find_node(Predicate pred) const - { - if (!_root) return xml_node(); - - xml_node cur = first_child(); - - while (cur._root && cur._root != _root) - { - if (pred(cur)) return cur; - - if (cur.first_child()) cur = cur.first_child(); - else if (cur.next_sibling()) cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); - - if (cur._root != _root) cur = cur.next_sibling(); - } - } - - return xml_node(); - } - - // Find child node by attribute name/value - xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; - xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; - - #ifndef PUGIXML_NO_STL - // Get the absolute node path from root as a text string. - string_t path(char_t delimiter = '/') const; - #endif - - // Search for a node by path consisting of node names and . or .. elements. - xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; - - // Recursively traverse subtree with xml_tree_walker - bool traverse(xml_tree_walker& walker); - - #ifndef PUGIXML_NO_XPATH - // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_node(const xpath_query& query) const; - - // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node_set select_nodes(const xpath_query& query) const; - - // (deprecated: use select_node instead) Select single node by evaluating XPath query. - xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_single_node(const xpath_query& query) const; - - #endif - - // Print subtree using a writer object - void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - - #ifndef PUGIXML_NO_STL - // Print subtree to stream - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; - #endif - - // Child nodes iterators - typedef xml_node_iterator iterator; - - iterator begin() const; - iterator end() const; - - // Attribute iterators - typedef xml_attribute_iterator attribute_iterator; - - attribute_iterator attributes_begin() const; - attribute_iterator attributes_end() const; - - // Range-based for support - xml_object_range children() const; - xml_object_range children(const char_t* name) const; - xml_object_range attributes() const; - - // Get node offset in parsed file/string (in char_t units) for debugging purposes - ptrdiff_t offset_debug() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_node_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); -#endif - - // A helper for working with text inside PCDATA nodes - class PUGIXML_CLASS xml_text - { - friend class xml_node; - - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_text***); - - explicit xml_text(xml_node_struct* root); - - xml_node_struct* _data_new(); - xml_node_struct* _data() const; - - public: - // Default constructor. Constructs an empty object. - xml_text(); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Check if text object is empty - bool empty() const; - - // Get text, or "" if object is empty - const char_t* get() const; - - // Get text, or the default value if object is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get text as a number, or the default value if conversion did not succeed or object is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty - bool as_bool(bool def = false) const; - - // Set text (returns false if object is empty or there is not enough memory) - bool set(const char_t* rhs); - - // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set(int rhs); - bool set(unsigned int rhs); - bool set(double rhs); - bool set(float rhs); - bool set(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set(long long rhs); - bool set(unsigned long long rhs); - #endif - - // Set text (equivalent to set without error checking) - xml_text& operator=(const char_t* rhs); - xml_text& operator=(int rhs); - xml_text& operator=(unsigned int rhs); - xml_text& operator=(double rhs); - xml_text& operator=(float rhs); - xml_text& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_text& operator=(long long rhs); - xml_text& operator=(unsigned long long rhs); - #endif - - // Get the data node (node_pcdata or node_cdata) for this object - xml_node data() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); -#endif - - // Child node iterator (a bidirectional iterator over a collection of xml_node) - class PUGIXML_CLASS xml_node_iterator - { - friend class xml_node; - - private: - mutable xml_node _wrap; - xml_node _parent; - - xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_node_iterator(); - - // Construct an iterator which points to the specified node - xml_node_iterator(const xml_node& node); - - // Iterator operators - bool operator==(const xml_node_iterator& rhs) const; - bool operator!=(const xml_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_node_iterator& operator++(); - xml_node_iterator operator++(int); - - const xml_node_iterator& operator--(); - xml_node_iterator operator--(int); - }; - - // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) - class PUGIXML_CLASS xml_attribute_iterator - { - friend class xml_node; - - private: - mutable xml_attribute _wrap; - xml_node _parent; - - xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_attribute value_type; - typedef xml_attribute* pointer; - typedef xml_attribute& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_attribute_iterator(); - - // Construct an iterator which points to the specified attribute - xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); - - // Iterator operators - bool operator==(const xml_attribute_iterator& rhs) const; - bool operator!=(const xml_attribute_iterator& rhs) const; - - xml_attribute& operator*() const; - xml_attribute* operator->() const; - - const xml_attribute_iterator& operator++(); - xml_attribute_iterator operator++(int); - - const xml_attribute_iterator& operator--(); - xml_attribute_iterator operator--(int); - }; - - // Named node range helper - class PUGIXML_CLASS xml_named_node_iterator - { - friend class xml_node; - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_named_node_iterator(); - - // Construct an iterator which points to the specified node - xml_named_node_iterator(const xml_node& node, const char_t* name); - - // Iterator operators - bool operator==(const xml_named_node_iterator& rhs) const; - bool operator!=(const xml_named_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_named_node_iterator& operator++(); - xml_named_node_iterator operator++(int); - - const xml_named_node_iterator& operator--(); - xml_named_node_iterator operator--(int); - - private: - mutable xml_node _wrap; - xml_node _parent; - const char_t* _name; - - xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); - }; - - // Abstract tree walker class (see xml_node::traverse) - class PUGIXML_CLASS xml_tree_walker - { - friend class xml_node; - - private: - int _depth; - - protected: - // Get current traversal depth - int depth() const; - - public: - xml_tree_walker(); - virtual ~xml_tree_walker(); - - // Callback that is called when traversal begins - virtual bool begin(xml_node& node); - - // Callback that is called for each node traversed - virtual bool for_each(xml_node& node) = 0; - - // Callback that is called when traversal ends - virtual bool end(xml_node& node); - }; - - // Parsing status, returned as part of xml_parse_result object - enum xml_parse_status - { - status_ok = 0, // No error - - status_file_not_found, // File was not found during load_file() - status_io_error, // Error reading from file/stream - status_out_of_memory, // Could not allocate memory - status_internal_error, // Internal error occurred - - status_unrecognized_tag, // Parser could not determine tag type - - status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction - status_bad_comment, // Parsing error occurred while parsing comment - status_bad_cdata, // Parsing error occurred while parsing CDATA section - status_bad_doctype, // Parsing error occurred while parsing document type declaration - status_bad_pcdata, // Parsing error occurred while parsing PCDATA section - status_bad_start_element, // Parsing error occurred while parsing start element tag - status_bad_attribute, // Parsing error occurred while parsing element attribute - status_bad_end_element, // Parsing error occurred while parsing end element tag - 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) - - status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) - - status_no_document_element // Parsing resulted in a document without element nodes - }; - - // Parsing result - struct PUGIXML_CLASS xml_parse_result - { - // Parsing status (see xml_parse_status) - xml_parse_status status; - - // Last parsed offset (in char_t units from start of input data) - ptrdiff_t offset; - - // Source document encoding - xml_encoding encoding; - - // Default constructor, initializes object to failed state - xml_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // Document class (DOM tree root) - class PUGIXML_CLASS xml_document: public xml_node - { - private: - char_t* _buffer; - - char _memory[192]; - - // Non-copyable semantics - xml_document(const xml_document&); - xml_document& operator=(const xml_document&); - - void create(); - void destroy(); - - public: - // Default constructor, makes empty document - xml_document(); - - // Destructor, invalidates all node/attribute handles to this document - ~xml_document(); - - // Removes all nodes, leaving the empty document - void reset(); - - // Removes all nodes, then copies the entire contents of the specified document - void reset(const xml_document& proto); - - #ifndef PUGIXML_NO_STL - // Load document from stream. - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); - #endif - - // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load(const char_t* contents, unsigned int options = parse_default); - - // Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); - - // Load document from file - xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. - xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. - xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). - xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). - void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - #ifndef PUGIXML_NO_STL - // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; - #endif - - // Save XML to file - bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - // Get document element - xml_node document_element() const; - }; - -#ifndef PUGIXML_NO_XPATH - // XPath query return type - enum xpath_value_type - { - xpath_type_none, // Unknown type (query failed to compile) - xpath_type_node_set, // Node set (xpath_node_set) - xpath_type_number, // Number - xpath_type_string, // String - xpath_type_boolean // Boolean - }; - - // XPath parsing result - struct PUGIXML_CLASS xpath_parse_result - { - // Error message (0 if no error) - const char* error; - - // Last parsed offset (in char_t units from string start) - ptrdiff_t offset; - - // Default constructor, initializes object to failed state - xpath_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // A single XPath variable - class PUGIXML_CLASS xpath_variable - { - friend class xpath_variable_set; - - protected: - xpath_value_type _type; - xpath_variable* _next; - - xpath_variable(xpath_value_type type); - - // Non-copyable semantics - xpath_variable(const xpath_variable&); - xpath_variable& operator=(const xpath_variable&); - - public: - // Get variable name - const char_t* name() const; - - // Get variable type - xpath_value_type type() const; - - // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error - bool get_boolean() const; - double get_number() const; - const char_t* get_string() const; - const xpath_node_set& get_node_set() const; - - // Set variable value; no type conversion is performed, false is returned on type mismatch error - bool set(bool value); - bool set(double value); - bool set(const char_t* value); - bool set(const xpath_node_set& value); - }; - - // A set of XPath variables - class PUGIXML_CLASS xpath_variable_set - { - private: - xpath_variable* _data[64]; - - void _assign(const xpath_variable_set& rhs); - void _swap(xpath_variable_set& rhs); - - xpath_variable* _find(const char_t* name) const; - - static bool _clone(xpath_variable* var, xpath_variable** out_result); - static void _destroy(xpath_variable* var); - - public: - // Default constructor/destructor - xpath_variable_set(); - ~xpath_variable_set(); - - // Copy constructor/assignment operator - xpath_variable_set(const xpath_variable_set& rhs); - xpath_variable_set& operator=(const xpath_variable_set& rhs); - - #if __cplusplus >= 201103 - // Move semantics support - xpath_variable_set(xpath_variable_set&& rhs); - xpath_variable_set& operator=(xpath_variable_set&& rhs); - #endif - - // Add a new variable or get the existing one, if the types match - xpath_variable* add(const char_t* name, xpath_value_type type); - - // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch - bool set(const char_t* name, bool value); - bool set(const char_t* name, double value); - bool set(const char_t* name, const char_t* value); - bool set(const char_t* name, const xpath_node_set& value); - - // Get existing variable by name - xpath_variable* get(const char_t* name); - const xpath_variable* get(const char_t* name) const; - }; - - // A compiled XPath query object - class PUGIXML_CLASS xpath_query - { - private: - void* _impl; - xpath_parse_result _result; - - typedef void (*unspecified_bool_type)(xpath_query***); - - // Non-copyable semantics - xpath_query(const xpath_query&); - xpath_query& operator=(const xpath_query&); - - public: - // Construct a compiled object from XPath expression. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); - - // Constructor - xpath_query(); - - // Destructor - ~xpath_query(); - - #if __cplusplus >= 201103 - // Move semantics support - xpath_query(xpath_query&& rhs); - xpath_query& operator=(xpath_query&& rhs); - #endif - - // Get query expression return type - xpath_value_type return_type() const; - - // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - bool evaluate_boolean(const xpath_node& n) const; - - // Evaluate expression as double value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - double evaluate_number(const xpath_node& n) const; - - #ifndef PUGIXML_NO_STL - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - string_t evaluate_string(const xpath_node& n) const; - #endif - - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. - size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. - xpath_node_set evaluate_node_set(const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // Return first node in document order, or empty node if node set is empty. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. - xpath_node evaluate_node(const xpath_node& n) const; - - // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) - const xpath_parse_result& result() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - }; - - #ifndef PUGIXML_NO_EXCEPTIONS - // XPath exception class - class PUGIXML_CLASS xpath_exception: public std::exception - { - private: - xpath_parse_result _result; - - public: - // Construct exception from parse result - explicit xpath_exception(const xpath_parse_result& result); - - // Get error message - virtual const char* what() const throw(); - - // Get parse result - const xpath_parse_result& result() const; - }; - #endif - - // XPath node class (either xml_node or xml_attribute) - class PUGIXML_CLASS xpath_node - { - private: - xml_node _node; - xml_attribute _attribute; - - typedef void (*unspecified_bool_type)(xpath_node***); - - public: - // Default constructor; constructs empty XPath node - xpath_node(); - - // Construct XPath node from XML node/attribute - xpath_node(const xml_node& node); - xpath_node(const xml_attribute& attribute, const xml_node& parent); - - // Get node/attribute, if any - xml_node node() const; - xml_attribute attribute() const; - - // Get parent of contained node/attribute - xml_node parent() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators - bool operator==(const xpath_node& n) const; - bool operator!=(const xpath_node& n) const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); -#endif - - // A fixed-size collection of XPath nodes - class PUGIXML_CLASS xpath_node_set - { - public: - // Collection type - enum type_t - { - type_unsorted, // Not ordered - type_sorted, // Sorted by document order (ascending) - type_sorted_reverse // Sorted by document order (descending) - }; - - // Constant iterator type - typedef const xpath_node* const_iterator; - - // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work - typedef const xpath_node* iterator; - - // Default constructor. Constructs empty set. - xpath_node_set(); - - // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful - xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); - - // Destructor - ~xpath_node_set(); - - // Copy constructor/assignment operator - xpath_node_set(const xpath_node_set& ns); - xpath_node_set& operator=(const xpath_node_set& ns); - - #if __cplusplus >= 201103 - // Move semantics support - xpath_node_set(xpath_node_set&& rhs); - xpath_node_set& operator=(xpath_node_set&& rhs); - #endif - - // Get collection type - type_t type() const; - - // Get collection size - size_t size() const; - - // Indexing operator - const xpath_node& operator[](size_t index) const; - - // Collection iterators - const_iterator begin() const; - const_iterator end() const; - - // Sort the collection in ascending/descending order by document order - void sort(bool reverse = false); - - // Get first node in the collection by document order - xpath_node first() const; - - // Check if collection is empty - bool empty() const; - - private: - type_t _type; - - xpath_node _storage; - - xpath_node* _begin; - xpath_node* _end; - - void _assign(const_iterator begin, const_iterator end, type_t type); - void _move(xpath_node_set& rhs); - }; -#endif - -#ifndef PUGIXML_NO_STL - // Convert wide string to UTF8 - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); - - // Convert UTF8 to wide string - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); -#endif - - // Memory allocation function interface; returns pointer to allocated memory or NULL on failure - typedef void* (*allocation_function)(size_t size); - - // Memory deallocation function interface - typedef void (*deallocation_function)(void* ptr); - - // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. - void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - - // Get current memory management functions - allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); - deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); -} - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); -} -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ - // Workarounds for (non-standard) iterator category detection - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); -} -#endif - -#endif - -// Make sure implementation is included in header-only mode -// Use macro expansion in #include to work around QMake (QTBUG-11923) -#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) -# define PUGIXML_SOURCE "pugixml.cpp" -# include PUGIXML_SOURCE -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ +#include