diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2ae5b70c5..fb86fabe1 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -10,38 +10,6 @@ jobs:
steps:
- checkout
- # Download and cache dependencies
- # - restore_cache:
- # keys:
- # - iqcache-{{ checksum ".circleci/cache-lock" }}-{{ checksum "iqtestdata/.cache-lock" }}
- # # fallback to using the latest cache if no exact match is found
- # - iqcache-
-
- # - run: yarn install --cache-folder ~/yarn-cache
-
- # - run:
- # name: Install Git LFS
- # command: |
- # curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
- # sudo apt-get update
- # sudo apt-get install -y git-lfs openssh-client
- # git lfs install
- # mkdir -p ~/.ssh
- # ssh-keyscan -H github.com >> ~/.ssh/known_hosts
- # ssh git@github.com git-lfs-authenticate "${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" download
- # git lfs pull
-
- # - save_cache:
- # paths:
- # - node_modules
- # - iqtestdata
- # - web/node_modules
- # - iqbak/node_modules
- # - iqshared/node_modules
- # - ~/yarn-cache
-
- # key: iqcache-{{ checksum ".circleci/cache-lock" }}-{{ checksum "iqtestdata/.cache-lock" }}
-
- run:
name: Cmake
command: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 470b34716..8a16ce745 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,7 +23,7 @@ file(GLOB_RECURSE SRC_MX_CORE ${PRIVATE_DIR}/mx/core/*.c* ${PRIVATE_DIR}/mx/core
file(GLOB_RECURSE SRC_MX_IMPL ${PRIVATE_DIR}/mx/impl/*.cpp ${PRIVATE_DIR}/mx/impl/*.h)
file(GLOB_RECURSE SRC_MX_PUGIXML ${PRIVATE_DIR}/mx/pugixml/*.cpp ${PRIVATE_DIR}/mx/pugixml/*.hpp)
file(GLOB_RECURSE SRC_MX_UTILITY ${PRIVATE_DIR}/mx/utility/*.cpp ${PRIVATE_DIR}/mx/utility/*.h)
-file(GLOB_RECURSE SRC_MX_XML ${PRIVATE_DIR}/mx/xml/*.cpp ${PRIVATE_DIR}/mx/xml/*.h)
+file(GLOB_RECURSE SRC_MX_EXTERN ${PRIVATE_DIR}/extern/*.cpp ${PRIVATE_DIR}/extern/*.h)
file(GLOB_RECURSE SRC_MX_TEST_API ${PRIVATE_DIR}/mxtest/api/*.cpp ${PRIVATE_DIR}/mxtest/api/*.h)
file(GLOB_RECURSE SRC_MX_TEST_CONTROL ${PRIVATE_DIR}/mxtest/control/*.cpp ${PRIVATE_DIR}/mxtest/control/*.h)
@@ -32,23 +32,25 @@ file(GLOB_RECURSE SRC_MX_TEST_IMPL ${PRIVATE_DIR}/mxtest/impl/*.cpp ${PRIVATE_DI
file(GLOB_RECURSE SRC_MX_TEST_CORE ${PRIVATE_DIR}/mxtest/core/*.cpp ${PRIVATE_DIR}/mxtest/core/*.h)
file(GLOB_RECURSE SRC_MX_TEST_IMPORT ${PRIVATE_DIR}/mxtest/import/*.cpp ${PRIVATE_DIR}/mxtest/import/*.h)
file(GLOB_RECURSE SRC_MX_TEST_UTILITY ${PRIVATE_DIR}/mxtest/utility/*.cpp ${PRIVATE_DIR}/mxtest/utility/*.h)
-file(GLOB_RECURSE SRC_MX_TEST_XML ${PRIVATE_DIR}/mxtest/xml/*.cpp ${PRIVATE_DIR}/mxtest/xml/*.h)
+#file(GLOB_RECURSE SRC_MX_TEST_XML ${PRIVATE_DIR}/mxtest/xml/*.cpp ${PRIVATE_DIR}/mxtest/xml/*.h)
file(GLOB_RECURSE SRC_CPUL ${PRIVATE_DIR}/cpul/*.cpp ${SOURCE}/cpul/*.h)
# Mx Library
-add_library(Mx STATIC ${SRC_MX_API} ${SRC_MX_CORE} ${SRC_MX_IMPL} ${SRC_MX_PUGIXML} ${SRC_MX_UTILITY} ${SRC_MX_XML})
+add_library(Mx STATIC ${SRC_MX_API} ${SRC_MX_CORE} ${SRC_MX_IMPL} ${SRC_MX_PUGIXML} ${SRC_MX_UTILITY})
source_group( "api-public" FILES ${HEADERS_MX_API})
source_group( "api" FILES ${SRC_MX_API} )
source_group( "core" FILES ${SRC_MX_CORE} )
source_group( "impl" FILES ${SRC_MX_IMPL} )
source_group( "pugixml" FILES ${SRC_MX_PUGIXML} )
source_group( "utility" FILES ${SRC_MX_UTILITY} )
-source_group( "xml" FILES ${SRC_MX_XML} )
+source_group( "extern" FILES ${SRC_MX_EXTERN} )
set_property(TARGET Mx PROPERTY CXX_STANDARD 14)
target_include_directories(Mx PUBLIC ${PUBLIC_DIR})
target_include_directories(Mx PRIVATE ${PRIVATE_DIR})
-
+add_subdirectory("${PRIVATE_DIR}/extern/ezxml")
+target_link_libraries(Mx ezxml)
+target_include_directories(Mx PRIVATE "${PRIVATE_DIR}/extern/ezxml/src/include")
file(WRITE ${PRIVATE_DIR}/mxtest/file/PathRoot.h
"// This file is auto generated by CMake
@@ -80,6 +82,7 @@ if(MX_BUILD_TESTS)
find_package( Threads )
add_executable(MxTest ${SRC_MX_TEST_API} ${SRC_MX_TEST_CONTROL} ${SRC_MX_TEST_CORE} ${SRC_MX_TEST_FILE} ${SRC_MX_TEST_IMPL} ${SRC_MX_TEST_IMPORT} ${SRC_MX_TEST_UTILITY} ${SRC_MX_TEST_XML} ${SRC_CPUL})
target_include_directories(MxTest PRIVATE ${PRIVATE_DIR})
+ target_include_directories(MxTest PRIVATE "${PRIVATE_DIR}/extern/ezxml/src/include")
target_link_libraries(MxTest Mx)
target_link_libraries(MxTest ${CMAKE_THREAD_LIBS_INIT})
set_property(TARGET MxTest PROPERTY CXX_STANDARD 14)
diff --git a/README.md b/README.md
index 12c7363b2..d83563183 100755
--- a/README.md
+++ b/README.md
@@ -293,14 +293,14 @@ The MusicXML classes in `mx::core` are tightly bound to the `musicxml.xsd` speci
##### Namespaces
```
-using namespace mx::core;
-using namespace mx::xml;
+using namespace ezxml;
using namespace mx::api;
+using namespace mx::core;
```
The `mx::core` namespace contains the MusicXML representation objects such as elements and attributes. In the musicxml.xsd there are many cases of 'xs:choice' or 'xs:group' being used. These constructs are typically represented in the mx::core class structure the same way that they are found in the musicxml.xsd specification. The interfaces in this namespace are relatively stable, however they are tightly bound to MusicXML's specification and thus they will change when it comes time to support a future version of MusicXML.
-The `mx::xml` namespace contains generic XML DOM functionality. Under the hood [pugixml](http://pugixml.org/) is being used. See the XML DOM section for more information.
+The `::ezxml::` namespace contains generic XML DOM functionality. Under the hood [pugixml](http://pugixml.org/) is being used. See the XML DOM section for more information.
The `mx::utility` namespace contains the beginnings of an api for simplifying the interactions with MusicXML. This namespace and its interfaces will change completely and should be avoided for now.
@@ -528,20 +528,20 @@ private:
When `getChoice() == BendChoice::Choice::preBend` then we will see `` in the XML, but when `getChoice() == BendChoice::Choice::postBend` then we will see `` in the XML.
-### XML DOM (mx::xml)
+### XML DOM (::ezxml::)
-Any XML document can be read and manipulated with the classes in the `mx::xml` namespace. Most notably, look at the following pure virtual interfaces XDoc, XElement, XAttribute. Also look at the STL-compliant iterators XElementIterator and XAttributeIterator.
+Any XML document can be read and manipulated with the classes in the `::ezxml::` namespace. Most notably, look at the following pure virtual interfaces XDoc, XElement, XAttribute. Also look at the STL-compliant iterators XElementIterator and XAttributeIterator.
These interfaces are designed to wrap any underlying XML DOM software so that `mx::core` does not care or know about the XML DOM code. A set of implementation classes wrapping pugixml are provided, but if you need to use, say Xerces or RapidXML, you can look at the PugiElement, PugiDoc, etc classes and wrap whatever library you need.
-Here's how you can read a MusicXML document into `mx::core` classes by way of `mx::xml`.
+Here's how you can read a MusicXML document into `mx::core` classes by way of `::ezxml::`.
```
#include "mx/core/Document.h"
#include "mx/utility/Utility.h"
#include "functions.h"
-#include "mx/xml/XFactory.h"
-#include "mx/xml/XDoc.h"
+#include "ezxml/XFactory.h"
+#include "ezxml/XDoc.h"
#include
#include
@@ -551,7 +551,7 @@ int main(int argc, const char *argv[])
{
// allocate the objects
mx::core::DocumentPtr mxDoc = makeDocument();
- mx::xml::XDocPtr xmlDoc = mx::xml::XFactory::makeXDoc();
+ ::ezxml::::XDocPtr xmlDoc = ::ezxml::::XFactory::makeXDoc();
// read a MusicXML file into the XML DOM structure
xmlDoc->loadFile( "music.xml" );
@@ -684,7 +684,7 @@ Each of these test input files has been "scrubbed" using the XDoc classes (i.e.
Currently this tester is a "wire-up". All 263 of these round-trip import/export tests fail because the implementation does not yet exist in mx::core. The next body of work will be the mx::core implementation.
-**Historical Note: June 20, 2016:** A simple interface to for XML DOM has been added in the mx::xml namespace. The key classes (pure virtual) are XDoc, XElement, XAttribute, XElementIterator, XAttributeIterator. These are implemented by concrete classes PugiDoc, PugiElement, etc. which serve as a wrapper for the pugixml library (http://pugixml.org/). Although this is a static library, a class XFactory can be used to create a Pugi instance of the XDoc interface.
+**Historical Note: June 20, 2016:** A simple interface to for XML DOM has been added in the ::ezxml:: namespace. The key classes (pure virtual) are XDoc, XElement, XAttribute, XElementIterator, XAttributeIterator. These are implemented by concrete classes PugiDoc, PugiElement, etc. which serve as a wrapper for the pugixml library (http://pugixml.org/). Although this is a static library, a class XFactory can be used to create a Pugi instance of the XDoc interface.
The idea behind using a pure virtual interface is that the client of the Music XML Class Library can, in theory choose a different XML DOM library (Xerces, TinyXML, etc) and wrap with instances of the XDoc interfaces and the Music XML core classes will not know the difference.
diff --git a/Sourcecode/private/extern/ezxml/.gitattributes b/Sourcecode/private/extern/ezxml/.gitattributes
new file mode 100644
index 000000000..ae12e6ea9
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/.gitattributes
@@ -0,0 +1,12 @@
+*.a filter=lfs diff=lfs merge=lfs -text
+*.dylib filter=lfs diff=lfs merge=lfs -text
+*.zip filter=lfs diff=lfs merge=lfs -text
+*.gz filter=lfs diff=lfs merge=lfs -text
+*.tar filter=lfs diff=lfs merge=lfs -text
+*.lfs.* filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs -text
+*.jpeg filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+*.png filter=lfs diff=lfs merge=lfs -text
+*.svg filter=lfs diff=lfs merge=lfs -text
+*.pdf filter=lfs diff=lfs merge=lfs -text
diff --git a/Sourcecode/private/extern/ezxml/.gitignore b/Sourcecode/private/extern/ezxml/.gitignore
new file mode 100644
index 000000000..5e892f97c
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/.gitignore
@@ -0,0 +1,236 @@
+# generated
+# Created by https://www.gitignore.io/api/c++,clion,xcode,macos,linux,cmake,windows
+# Edit at https://www.gitignore.io/?templates=c++,clion,xcode,macos,linux,cmake,windows
+
+### C++ ###
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+### CLion ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### CLion Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+.idea/sonarlint
+
+### CMake ###
+CMakeLists.txt.user
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+Makefile
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+_deps
+
+### CMake Patch ###
+# External projects
+*-prefix/
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+### Xcode ###
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## User settings
+xcuserdata/
+
+## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
+*.xcscmblueprint
+*.xccheckout
+
+## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+build/
+DerivedData/
+*.moved-aside
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+
+### Xcode Patch ###
+*.xcodeproj/*
+!*.xcodeproj/project.pbxproj
+!*.xcodeproj/xcshareddata/
+!*.xcworkspace/contents.xcworkspacedata
+/*.gcno
+**/xcshareddata/WorkspaceSettings.xcsettings
+
+# End of https://www.gitignore.io/api/c++,clion,xcode,macos,linux,cmake,windows
\ No newline at end of file
diff --git a/Sourcecode/private/extern/ezxml/CMakeLists.txt b/Sourcecode/private/extern/ezxml/CMakeLists.txt
new file mode 100644
index 000000000..59c046231
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/CMakeLists.txt
@@ -0,0 +1,57 @@
+cmake_minimum_required(VERSION 3.7.2)
+project(ezxml
+ VERSION 0.0.0
+ LANGUAGES CXX)
+
+option(EZXML_BUILD_TESTS "build or skip the test suite" OFF)
+option(EZXML_BUILD_EXAMPLES "build or skip the examples" OFF)
+
+find_package( Threads )
+set(SOURCE_ROOT "./src")
+set(PRIVATE_DIR "${SOURCE_ROOT}/private")
+set(PUBLIC_DIR "${SOURCE_ROOT}/include")
+set(TEST_DIR "${SOURCE_ROOT}/test")
+
+file(GLOB_RECURSE PUBLIC_HEADERS ${PUBLIC_DIR}/ezxml/*.h)
+file(GLOB_RECURSE PRIVATE_HEADERS ${PRIVATE_DIR}/ezx/*.h)
+file(GLOB_RECURSE PRIVATE_CXX ${PRIVATE_DIR}/private/*.cpp)
+
+add_library(ezxml STATIC ${PRIVATE_CXX})
+source_group( "ezxml-public" FILES ${PUBLIC_HEADERS})
+source_group( "ezxml-private" FILES ${PRIVATE_HEADERS} ${PRIVATE_CXX})
+set_property(TARGET ezxml PROPERTY CXX_STANDARD 14)
+target_include_directories(ezxml PUBLIC ${PUBLIC_DIR})
+target_include_directories(ezxml PRIVATE ${PRIVATE_DIR})
+
+file(WRITE ${TEST_DIR}/Path.h
+"// ezxml, Copyright 2019 by Matthew James Briggs
+// This file is auto generated by CMake
+#pragma once
+
+// The absolute path to the root of the repository.
+#ifndef EZXML_ROOT
+#define EZXML_ROOT \"${CMAKE_CURRENT_SOURCE_DIR}\"
+#endif
+
+// The absolute path to the binary output directory.
+#ifndef EZXML_BIN
+#define EZXML_BIN \"${CMAKE_BINARY_DIR}\"
+#endif
+")
+
+if(EZXML_BUILD_TESTS)
+ message("tests will be compiled")
+ target_compile_options(ezxml PRIVATE -Werror -Wall -Wextra)
+ file(GLOB_RECURSE TEST_CXX ${TEST_DIR}/*.cpp)
+ file(GLOB_RECURSE TEST_HEADERS ${TEST_CXX}/*.h)
+ source_group( "ezxml-test" FILES ${TEST_CXX} ${TEST_HEADERS})
+ add_executable(ezxml_test ${TEST_CXX} ${TEST_HEADERS})
+ target_include_directories(ezxml_test PRIVATE ${PRIVATE_DIR})
+ target_include_directories(ezxml_test PRIVATE ${PUBLIC_DIR})
+ target_include_directories(ezxml_test PRIVATE ${PRIVATE_DIR})
+ target_link_libraries(ezxml_test ezxml)
+ target_link_libraries(ezxml_test ${CMAKE_THREAD_LIBS_INIT})
+ set_property(TARGET ezxml_test PROPERTY CXX_STANDARD 14)
+else()
+ message("tests will not be compiled")
+endif()
\ No newline at end of file
diff --git a/Sourcecode/private/extern/ezxml/license.txt b/Sourcecode/private/extern/ezxml/license.txt
new file mode 100755
index 000000000..2d9c94606
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/license.txt
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright (c) 2017 Matthew James Briggs
+
+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.
\ No newline at end of file
diff --git a/Sourcecode/private/extern/ezxml/other/ezxml.sublime-project b/Sourcecode/private/extern/ezxml/other/ezxml.sublime-project
new file mode 100644
index 000000000..1d0c0fef8
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/other/ezxml.sublime-project
@@ -0,0 +1,8 @@
+{
+ "folders":
+ [
+ {
+ "path": ".."
+ }
+ ]
+}
diff --git a/Sourcecode/private/extern/ezxml/other/ezxml.sublime-workspace.example copy b/Sourcecode/private/extern/ezxml/other/ezxml.sublime-workspace.example copy
new file mode 100644
index 000000000..cc51b99b5
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/other/ezxml.sublime-workspace.example copy
@@ -0,0 +1,172 @@
+{
+ "auto_complete":
+ {
+ "selected_items":
+ [
+ [
+ "FONT_S",
+ "FONT_SEMIBOLD"
+ ]
+ ]
+ },
+ "buffers":
+ [
+ ],
+ "build_system": "",
+ "build_system_choices":
+ [
+ [
+
+ ],
+ [
+
+ ],
+ [
+
+ ]
+ ],
+ "build_varint": "",
+
+
+ "expanded_folders":
+ [
+ "..",
+ ],
+ "file_history":
+ [
+
+ ],
+ "find":
+ {
+ "height": 43.0
+ },
+ "find_in_files":
+ {
+ "height": 119.0,
+ "where_history":
+ [
+ ]
+ },
+ "find_state":
+ {
+ "case_sensitive": false,
+ "find_history":
+ [
+ ],
+ "highlight": true,
+ "in_selection": false,
+ "preserve_case": false,
+ "regex": false,
+ "replace_history":
+ [
+ ],
+ "reverse": false,
+ "show_context": true,
+ "use_buffer2": true,
+ "whole_word": false,
+ "wrap": true
+ },
+ "groups":
+ [
+ {
+ "sheets":
+ [
+ ]
+ }
+ ],
+ "incremental_find":
+ {
+ "height": 31.0
+ },
+ "input":
+ {
+ "height": 53.0
+ },
+ "layout":
+ {
+ "cells":
+ [
+ [
+ 0,
+ 0,
+ 1,
+ 1
+ ]
+ ],
+ "cols":
+ [
+ 0.0,
+ 1.0
+ ],
+ "rows":
+ [
+ 0.0,
+ 1.0
+ ]
+ },
+ "menu_visible": true,
+ "output.SublimeLinter":
+ {
+ "height": 0.0
+ },
+ "output.exec":
+ {
+ "height": 264.0
+ },
+ "output.find_results":
+ {
+ "height": 126.0
+ },
+ "output.gofmt":
+ {
+ "height": 0.0
+ },
+ "pinned_build_system": "",
+ "project": "xlripper.sublime-project",
+ "replace":
+ {
+ "height": 58.0
+ },
+ "save_all_on_build": true,
+ "select_file":
+ {
+ "height": 0.0,
+ "last_filter": "",
+ "selected_items":
+ [
+
+ ],
+ "width": 0.0
+ },
+ "select_project":
+ {
+ "height": 500.0,
+ "last_filter": "",
+ "selected_items":
+ [
+ ],
+ "width": 380.0
+ },
+ "select_symbol":
+ {
+ "height": 0.0,
+ "last_filter": "",
+ "selected_items":
+ [
+ ],
+ "width": 0.0
+ },
+ "selected_group": 0,
+ "settings":
+ {
+ },
+ "show_minimap": true,
+ "show_open_files": false,
+ "show_tabs": true,
+ "side_bar_visible": true,
+ "side_bar_width": 304.0,
+ "status_bar_visible": true,
+ "template_settings":
+ {
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/readme.md b/Sourcecode/private/extern/ezxml/readme.md
new file mode 100644
index 000000000..2a7d50fbd
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/readme.md
@@ -0,0 +1,5 @@
+ezxml
+=====
+
+[![CircleCI](https://circleci.com/gh/webern/ezxml.svg?style=svg)](https://circleci.com/gh/webern/ezxml)
+
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttribute.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttribute.h
new file mode 100755
index 000000000..0c2180862
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttribute.h
@@ -0,0 +1,37 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include
+#include
+
+namespace ezxml
+{
+ class XAttribute;
+ using XAttributePtr = std::shared_ptr;
+
+ class XAttribute;
+ using XAttributePtr = std::shared_ptr;
+
+ class XAttributeIterator;
+ using XAttributeIteratorPtr = std::shared_ptr;
+
+ class XAttributeIterator;
+ using XAttributeIteratorPtr = std::shared_ptr;
+
+ class XElement;
+ using XElementPtr = std::shared_ptr;
+
+ class XAttribute
+ {
+ public:
+ virtual ~XAttribute() = default;
+
+ virtual bool getIsNull() const = 0;
+ virtual std::string getName() const = 0;
+ virtual std::string getValue() const = 0;
+
+ virtual void setName( const std::string& name ) = 0;
+ virtual void setValue( const std::string& name ) = 0;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterImpl.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterImpl.h
new file mode 100755
index 000000000..1e51ea345
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterImpl.h
@@ -0,0 +1,32 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XAttribute.h"
+
+#include
+
+namespace ezxml
+{
+ class XAttributeIterImpl;
+ using XAttributeIterImplUP = std::unique_ptr;
+
+ class XAttributeIterImpl
+ {
+ public:
+
+ virtual ~XAttributeIterImpl() = default;
+
+ virtual bool getIsPayloadNull() const = 0;
+ virtual bool getIsEndIter() const = 0;
+
+ virtual XAttributeIterImplUP clone() const = 0;
+ virtual bool equals( const XAttributeIterator& other ) const = 0;
+
+ virtual XAttribute& getRef() const = 0;
+ virtual XAttribute* getPtr() const = 0;
+
+ virtual const XAttributeIterImpl& increment() = 0;
+ virtual const XAttributeIterImpl& decrement() = 0;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterator.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterator.h
new file mode 100755
index 000000000..53230aa3e
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XAttributeIterator.h
@@ -0,0 +1,53 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XAttribute.h"
+#include "ezxml/XAttributeIterImpl.h"
+
+#include
+#include
+#include
+
+namespace ezxml
+{
+ class XAttributeIterator final
+ {
+
+ public:
+ XAttributeIterator();
+ explicit XAttributeIterator( const XAttributeIterImpl& impl );
+ XAttributeIterator( const XAttributeIterator& other );
+ XAttributeIterator( XAttributeIterator&& other ) = default;
+ XAttributeIterator& operator=( const XAttributeIterator& other );
+ XAttributeIterator& operator=( XAttributeIterator&& other ) = default;
+ ~XAttributeIterator() = default;
+
+ // STL Iterator Compliance
+ typedef ptrdiff_t difference_type; // ???
+ typedef XAttribute value_type;
+ typedef XAttribute* pointer;
+ typedef XAttribute& reference;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ // Iterator operators
+ bool operator==( const XAttributeIterator& rhs ) const;
+ bool operator!=( const XAttributeIterator& rhs ) const;
+
+ XAttribute& operator*() const;
+ XAttribute* operator->() const;
+
+ const XAttributeIterator& operator++();
+ XAttributeIterator operator++( int );
+
+ const XAttributeIterator& operator--();
+ XAttributeIterator operator--( int );
+
+ // Reveal the private data member, violates
+ // encapsulation but could not find a better way
+ const XAttributeIterImplUP& reveal() const;
+
+ private:
+ XAttributeIterImplUP myImpl;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XDoc.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XDoc.h
new file mode 100755
index 000000000..be111173f
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XDoc.h
@@ -0,0 +1,53 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XDocSpec.h"
+#include "ezxml/XElement.h"
+
+#include
+#include
+
+namespace ezxml
+{
+ class XDoc;
+ using XDocPtr = std::shared_ptr;
+ using XDocCPtr = std::shared_ptr;
+
+ class XDoc : public std::enable_shared_from_this
+ {
+ public:
+ virtual ~XDoc() = default;
+
+ // these can throw std::runtime_error
+ virtual void loadStream( std::istream& is ) = 0;
+ virtual void saveStream( std::ostream& os ) const = 0;
+
+ // these can throw std::runtime_error
+ virtual void loadFile( const std::string& filename ) = 0;
+ virtual void saveFile( const std::string& filename ) const = 0;
+
+ // Xml Declaration
+ virtual XmlVersion getXmlVersion() const = 0;
+ virtual void setXmlVersion( XmlVersion value ) = 0;
+ virtual Encoding getEncoding() const = 0;
+ virtual void setEncoding( Encoding value ) = 0;
+ virtual bool getHasStandaloneAttribute() const = 0;
+ virtual void setHasStandaloneAttribute( bool value ) = 0;
+ virtual bool getIsStandalone() const = 0;
+ virtual void setIsStandalone( bool value ) = 0;
+
+ // Doctype Declaration
+ virtual bool getHasDoctypeDeclaration() const = 0;
+ virtual void setHasDoctypeDeclaration( bool value ) = 0;
+ virtual std::string getDoctypeValue() const = 0;
+ virtual void setDoctypeValue( const std::string& value ) = 0;
+
+ // when calling the write function, this value will
+ // determine whether or not to include the BOM
+ virtual void setDoWriteByteOrderMark( bool value ) = 0;
+
+ // Node Access
+ virtual XElementPtr getRoot() const = 0;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XDocSpec.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XDocSpec.h
new file mode 100755
index 000000000..798861d1a
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XDocSpec.h
@@ -0,0 +1,119 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace ezxml
+{
+ enum class XmlVersion
+ {
+ unknown,
+ onePointZero,
+ onePointOne
+ };
+
+ const XmlVersion DEFAULT_XML_VERSION = XmlVersion::onePointZero;
+
+
+ inline std::ostream&
+ toStream( std::ostream& os, const XmlVersion value )
+ {
+ switch( value )
+ {
+ case XmlVersion::unknown:os << "unknown";
+ break;
+ case XmlVersion::onePointZero:os << "1.0";
+ break;
+ case XmlVersion::onePointOne:os << "1.1";
+ break;
+ default:os << "error";
+ break;
+ }
+ return os;
+ }
+
+
+ inline std::string
+ toString( const XmlVersion value )
+ {
+ std::stringstream ss;
+ toStream( ss, value );
+ return ss.str();
+ }
+
+
+ inline XmlVersion
+ parseXmlVersion( const std::string& str )
+ {
+ if( str == "1.0" )
+ {
+ return XmlVersion::onePointZero;
+ }
+
+ if( str == "1.1" )
+ {
+ return XmlVersion::onePointOne;
+ }
+
+ return DEFAULT_XML_VERSION;
+ }
+
+
+ enum class Encoding
+ {
+ unknown,
+ utfEight,
+ utfSixteen
+ };
+
+ const Encoding DEFAULT_ENCODING = Encoding::utfEight;
+
+
+ inline std::ostream&
+ toStream( std::ostream& os, const Encoding value )
+ {
+ switch( value )
+ {
+ case Encoding::unknown:os << "unknown";
+ break;
+ case Encoding::utfEight:os << "UTF-8";
+ break;
+ case Encoding::utfSixteen:os << "UTF-16";
+ break;
+ default:os << "error";
+ break;
+ }
+ return os;
+ }
+
+
+ inline std::string
+ toString( const Encoding value )
+ {
+ std::stringstream ss;
+ toStream( ss, value );
+ return ss.str();
+ }
+
+
+ inline Encoding
+ parseEncoding( const std::string& str )
+ {
+ auto lowerStr = str;
+ std::transform( lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower );
+
+ if( lowerStr == "utf-8" )
+ {
+ return Encoding::utfEight;
+ }
+ if( lowerStr == "utf-16" )
+ {
+ return Encoding::utfSixteen;
+ }
+ return DEFAULT_ENCODING;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XElement.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElement.h
new file mode 100755
index 000000000..eb25d8a67
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElement.h
@@ -0,0 +1,147 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace ezxml
+{
+ enum class XElementType
+ {
+ null, // The element is in a bad, unusable state and getIsNull will return true
+ empty, // The element has no text and no child elements
+ element, // The element has child elements
+ text, // The element has text data
+ };
+
+ class XDoc;
+ using XDocCPtr = std::shared_ptr;
+
+ class XElement;
+ using XElementPtr = std::shared_ptr;
+
+ class XAttribute;
+ using XAttributePtr = std::shared_ptr;
+
+ class XElementIterator;
+ using XElementIteratorPtr = std::shared_ptr;
+
+ class XAttributeIterator;
+ using XAttributeIteratorPtr = std::shared_ptr;
+
+ class XElement
+ {
+ public:
+ virtual ~XElement() = default;
+ virtual XElementPtr clone() const = 0;
+
+ virtual XElementType getType() const = 0;
+ virtual bool getIsNull() const = 0;
+
+ virtual bool getIsProcessingInstruction() const = 0;
+
+ virtual std::string getName() const = 0;
+ virtual std::string getValue() const = 0;
+
+ virtual void setName( const std::string& name ) = 0;
+ virtual void setValue( const std::string& name ) = 0;
+
+ // get the XDoc from which this
+ // entire XML tree descends
+ virtual XDocCPtr getDoc() const = 0;
+
+ // get the element that contains this element
+ // returns an XElement with type == null if this
+ // element is the root of the entire XML tree
+ virtual XElementPtr getParent() const = 0;
+
+ // return the next element after this one, can
+ // return nullptr if there are no more siblings
+ virtual XElementPtr getNextSibling() const = 0;
+
+ // STL compliant iterators to the elements
+ // which are children of this element. If
+ // this element has text or has no children
+ // then begin() == end()
+ virtual XElementIterator begin() const = 0;
+ virtual XElementIterator end() const = 0;
+
+ // if you want iterations to include processing
+ // instructions then use this function.
+ virtual XElementIterator beginWithProcessingInstructions() const = 0;
+
+ // STL compliant iterators to the attributes
+ // of this element. If this element has no
+ // attributes then begin() == end()
+ virtual XAttributeIterator attributesBegin() const = 0;
+ virtual XAttributeIterator attributesEnd() const = 0;
+
+ // add an element as the last child of this element.
+ // throws if this element's type == text
+ virtual XElementPtr appendChild( const std::string& name ) = 0;
+
+ // add an element as the first child of this element.
+ // throws if this element's type == text
+ virtual XElementPtr prependChild( const std::string& name ) = 0;
+
+ // removes the first occurence of a child element with the given name
+ virtual bool removeChild( const std::string& elementName ) = 0;
+
+ // adds an element after this element, at this element's
+ // same level in the tree. throws if this element is the
+ // root of the entire XML tree
+ virtual XElementPtr insertSiblingAfter( const std::string& newElementName ) = 0;
+
+ virtual XAttributePtr appendAttribute( const std::string& name ) = 0;
+ virtual XAttributePtr prependAttribute( const std::string& name ) = 0;
+ virtual void removeAttribute( const XAttributeIterator& iter ) = 0;
+ };
+
+
+
+ // utils for the XElementType enum
+
+
+ constexpr const char* const xElementNullString = "XELEMENT_NULL";
+ constexpr const char* const xElementEmptyString = "XELEMENT_EMPTY";
+ constexpr const char* const xElementElementString = "XELEMENT_ELEMENT";
+ constexpr const char* const xElementTextString = "XELEMENT_TEXT";
+ constexpr const char* const xElementUnknownString = "XELEMENT_UNKNOWN";
+
+
+ inline std::string
+ toString( XElementType t )
+ {
+ switch( t )
+ {
+ case XElementType::null:return xElementNullString;
+ case XElementType::empty:return xElementEmptyString;
+ case XElementType::element:return xElementElementString;
+ case XElementType::text:return xElementTextString;
+ default: break;
+ }
+
+ return xElementUnknownString;
+ }
+
+
+ inline XElementType
+ xElementTypeFromString( const std::string& inString )
+ {
+ if( inString == xElementNullString )
+ {
+ return XElementType::null;
+ }
+ else if( inString == xElementEmptyString )
+ {
+ return XElementType::empty;
+ }
+ else if( inString == xElementElementString )
+ {
+ return XElementType::element;
+ }
+
+ return XElementType::null;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterImpl.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterImpl.h
new file mode 100755
index 000000000..88e9809f5
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterImpl.h
@@ -0,0 +1,35 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XElementIterator.h"
+
+#include
+
+namespace ezxml
+{
+ class XElementIterImpl;
+ using XElementIterImplUP = std::unique_ptr;
+
+ class XElementIterImpl
+ {
+ public:
+
+ virtual ~XElementIterImpl() = default;;
+
+ virtual bool getIsPayloadNull() const = 0;
+ virtual bool getIsEndIter() const = 0;
+ virtual bool getIsProcessingInstruction() const = 0;
+ virtual bool getSkipProcessingInstructions() const = 0;
+ virtual void setSkipProcessingInstructions( bool inValue ) = 0;
+
+ virtual XElementIterImplUP clone() const = 0;
+ virtual bool equals( const XElementIterator& other ) const = 0;
+
+ virtual XElement& getRef() const = 0;
+ virtual XElement* getPtr() const = 0;
+
+ virtual const XElementIterImpl& increment() = 0;
+ virtual const XElementIterImpl& decrement() = 0;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterator.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterator.h
new file mode 100755
index 000000000..5a7d5d770
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XElementIterator.h
@@ -0,0 +1,58 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XElement.h"
+#include "ezxml/XElementIterImpl.h"
+
+#include
+#include
+#include
+
+namespace ezxml
+{
+ class XElementIterator final
+ {
+
+ public:
+ XElementIterator();
+ explicit XElementIterator( const XElementIterImpl& impl );
+ XElementIterator( const XElementIterator& other );
+ XElementIterator( XElementIterator&& other ) = default;
+ XElementIterator& operator=( const XElementIterator& other );
+ XElementIterator& operator=( XElementIterator&& other ) = default;
+ ~XElementIterator() = default;
+
+ // STL Iterator Compliance
+ typedef ptrdiff_t difference_type; // ???
+ typedef XElement value_type;
+ typedef XElement* pointer;
+ typedef XElement& reference;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ // Iterator operators
+ bool operator==( const XElementIterator& rhs ) const;
+ bool operator!=( const XElementIterator& rhs ) const;
+
+ XElement& operator*() const;
+ XElement* operator->() const;
+
+ const XElementIterator& operator++();
+ const XElementIterator operator++( int );
+
+ const XElementIterator& operator--();
+ const XElementIterator operator--( int );
+
+ bool getIsPayloadNull() const;
+
+ bool getSkipProcessingInstructions() const;
+ void setSkipProcessingInstructions( bool inValue );
+
+ // Reveal the private data member, violates
+ // encapsulation but could not find a better way
+ const XElementIterImplUP& reveal() const;
+
+ private:
+ XElementIterImplUP myImpl;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XFactory.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XFactory.h
new file mode 100755
index 000000000..005273984
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XFactory.h
@@ -0,0 +1,16 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XDoc.h"
+#include
+
+namespace ezxml
+{
+ class XFactory
+ {
+ public:
+ static XDocPtr makeXDoc();
+ static XElementPtr makeXElement();
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/XForwardDeclare.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/XForwardDeclare.h
new file mode 100755
index 000000000..66ed5ebd7
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/XForwardDeclare.h
@@ -0,0 +1,13 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#ifndef EZXML_XML_FORWARD_DECLARE_ELEMENT
+#define EZXML_XML_FORWARD_DECLARE_ELEMENT \
+namespace ezxml \
+{ \
+ class XElement; \
+ using XElementPtr ## Ptr = std::shared_ptr; \
+} \
+
+#endif
diff --git a/Sourcecode/private/extern/ezxml/src/include/ezxml/ezxml.h b/Sourcecode/private/extern/ezxml/src/include/ezxml/ezxml.h
new file mode 100644
index 000000000..c8b4752b6
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/include/ezxml/ezxml.h
@@ -0,0 +1,11 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XAttribute.h"
+#include "ezxml/XAttributeIterator.h"
+#include "ezxml/XDoc.h"
+#include "ezxml/XDocSpec.h"
+#include "ezxml/XElement.h"
+#include "ezxml/XElementIterator.h"
+#include "ezxml/XFactory.h"
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/Parse.h b/Sourcecode/private/extern/ezxml/src/private/private/Parse.h
new file mode 100755
index 000000000..3f31d2837
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/Parse.h
@@ -0,0 +1,93 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ezxml
+{
+ inline bool
+ charCompareCaseInsensitive( char a, char b )
+ {
+ return std::tolower( a ) == std::tolower( b );
+ }
+
+
+ inline bool
+ charCompareCaseSensitive( char a, char b )
+ {
+ return a == b;
+ }
+
+
+ inline bool
+ compareCaseInsensitive( const std::string& a, const std::string& b )
+ {
+ if( a.length() == b.length() )
+ {
+ return std::equal( b.begin(), b.end(), a.begin(), charCompareCaseInsensitive );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ // http://stackoverflow.com/a/3152296/2779792
+ // templated version of my_equal so it could work with both char and wchar_t
+ template< typename charT >
+ struct my_equal
+ {
+ explicit my_equal( const std::locale& loc )
+ : myLoc( loc )
+ {
+
+ }
+
+
+ bool operator()( charT ch1, charT ch2 )
+ {
+ return std::toupper( ch1, myLoc ) == std::toupper( ch2, myLoc );
+ }
+
+
+ private:
+ const std::locale& myLoc;
+ };
+
+
+ // find substring (case insensitive)
+ template< typename T >
+ inline int
+ findCaseInsensitive( const T& str1, const T& str2, const std::locale& loc = std::locale() )
+ {
+ typename T::const_iterator it =
+ std::search(
+ str1.begin(), str1.end(),
+ str2.begin(), str2.end(),
+ my_equal( loc )
+ );
+
+ if( it != str1.end() )
+ {
+ return static_cast( it - str1.begin() );
+ }
+ return -1;
+ }
+
+
+ inline bool
+ containsCaseInsensitive(
+ const std::string& substringToFind,
+ const std::string& stringToFindItIn
+ )
+ {
+ auto position = findCaseInsensitive( stringToFindItIn, substringToFind );
+ return position > -1;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.cpp b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.cpp
new file mode 100755
index 000000000..27a7a4f57
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) Matthew James Briggs
+
+#include "private/PugiAttribute.h"
+#include "ezxml/XDoc.h"
+#include "private/PugiAttributeIterImpl.h"
+
+namespace ezxml
+{
+ PugiAttribute::PugiAttribute()
+ : myAttribute(), myParentElement(), myXDoc()
+ {
+
+ }
+
+
+ PugiAttribute::PugiAttribute(
+ const pugi::xml_attribute& attribute,
+ const pugi::xml_node& parentElement,
+ const XDocCPtr& parentXDoc
+ )
+ : myAttribute( attribute ), myParentElement( parentElement ), myXDoc( parentXDoc )
+ {
+
+ }
+
+
+ bool
+ PugiAttribute::getIsNull() const
+ {
+ auto ptr = myXDoc.lock();
+
+ if( !ptr )
+ {
+ return true;
+ }
+
+ if( myParentElement.type() != pugi::node_element )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ std::string
+ PugiAttribute::getName() const
+ {
+ return myAttribute.name();
+ }
+
+
+ std::string
+ PugiAttribute::getValue() const
+ {
+ return myAttribute.value();
+ }
+
+
+ void
+ PugiAttribute::setName( const std::string& name )
+ {
+ myAttribute.set_name( name.c_str() );
+ }
+
+
+ void
+ PugiAttribute::setValue( const std::string& value )
+ {
+ myAttribute.set_value( value.c_str() );
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.h b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.h
new file mode 100755
index 000000000..13bb44e9d
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttribute.h
@@ -0,0 +1,47 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XAttribute.h"
+#include "private/pugixml.hpp"
+
+#include
+
+namespace ezxml
+{
+ class PugiAttribute;
+ using PugiAttributePtr = std::shared_ptr;
+
+ class XDoc;
+ using XDocCPtr = std::shared_ptr;
+ using XDocCWPtr = std::weak_ptr;
+
+ class PugiAttribute : public XAttribute
+ {
+ public:
+ PugiAttribute();
+
+ PugiAttribute(
+ const pugi::xml_attribute& attribute,
+ const pugi::xml_node& parentElement,
+ const XDocCPtr& parentXDoc
+ );
+
+ PugiAttribute( const PugiAttribute& other ) = default;
+ PugiAttribute( PugiAttribute&& other ) = default;
+ PugiAttribute& operator=( const PugiAttribute& other ) = default;
+ PugiAttribute& operator=( PugiAttribute&& other ) = default;
+
+ virtual bool getIsNull() const override;
+ virtual std::string getName() const override;
+ virtual std::string getValue() const override;
+
+ virtual void setName( const std::string& name ) override;
+ virtual void setValue( const std::string& name ) override;
+
+ private:
+ pugi::xml_attribute myAttribute;
+ pugi::xml_node myParentElement;
+ XDocCWPtr myXDoc;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.cpp b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.cpp
new file mode 100755
index 000000000..f4f5a6b80
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.cpp
@@ -0,0 +1,137 @@
+// Copyright (c) Matthew James Briggs
+
+#include "private/PugiAttributeIterImpl.h"
+#include "ezxml/XDoc.h"
+#include "private/XThrow.h"
+
+#define EZXML_CHECK_NODE_STATE if( myParentElement.type() != pugi::node_element ) { EZXML_THROW_XNULL; }
+
+namespace ezxml
+{
+ PugiAttributeIterImpl::PugiAttributeIterImpl()
+ : myIter(), myParentElement(), myXDoc(), myReturnableAttribute()
+ {
+
+ }
+
+
+ PugiAttributeIterImpl::PugiAttributeIterImpl(
+ const pugi::xml_attribute_iterator& iter,
+ const pugi::xml_node& iterParentElement,
+ const XDocCPtr& parentXDoc
+ )
+ : myIter( iter ), myParentElement( iterParentElement ), myXDoc( parentXDoc ), myReturnableAttribute()
+ {
+ EZXML_CHECK_NODE_STATE;
+ if( myParentElement.type() != pugi::node_element )
+ {
+ EZXML_THROW( "improperly constructed, parent should be of element type" );
+ }
+ }
+
+
+ bool
+ PugiAttributeIterImpl::getIsPayloadNull() const
+ {
+ auto ptr = myXDoc.lock();
+
+ if( !ptr )
+ {
+ return true;
+ }
+ else if( myParentElement.type() != pugi::node_element )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiAttributeIterImpl::getIsEndIter() const
+ {
+ if( myIter == myParentElement.attributes_end() )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ XAttributeIterImplUP
+ PugiAttributeIterImpl::clone() const
+ {
+ return XAttributeIterImplUP{ new PugiAttributeIterImpl{ *this } };
+ }
+
+
+ bool
+ PugiAttributeIterImpl::equals( const XAttributeIterator& other ) const
+ {
+ const auto& otherImpl = other.reveal();
+ if( !otherImpl )
+ {
+ return false;
+ }
+ auto pugi = dynamic_cast( otherImpl.get() );
+ if( !pugi )
+ {
+ throw std::runtime_error( "PugiAttributeIterImpl::equals - a downcast failed" );
+ }
+ return this->myIter == pugi->myIter;
+ }
+
+
+ XAttribute&
+ PugiAttributeIterImpl::getRef() const
+ {
+ if( getIsPayloadNull() )
+ {
+ EZXML_THROW_XNULL;
+ }
+
+ if( getIsEndIter() )
+ {
+ EZXML_THROW( "XAttributeIterator attempted to dereference an 'end' iterator" );
+ }
+
+ myReturnableAttribute = PugiAttribute{ *myIter, myParentElement, myXDoc.lock() };
+ return myReturnableAttribute;
+ }
+
+
+ XAttribute*
+ PugiAttributeIterImpl::getPtr() const
+ {
+ if( getIsPayloadNull() )
+ {
+ EZXML_THROW_XNULL;
+ }
+
+ if( getIsEndIter() )
+ {
+ EZXML_THROW( "XAttributeIterator attempted to dereference an 'end' iterator" );
+ }
+
+ myReturnableAttribute = PugiAttribute{ *myIter, myParentElement, myXDoc.lock() };
+ return &myReturnableAttribute;
+ }
+
+
+ const PugiAttributeIterImpl&
+ PugiAttributeIterImpl::increment()
+ {
+ ++myIter;
+ return *this;
+ }
+
+
+ const PugiAttributeIterImpl&
+ PugiAttributeIterImpl::decrement()
+ {
+ --myIter;
+ return *this;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.h b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.h
new file mode 100755
index 000000000..c8dffb361
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiAttributeIterImpl.h
@@ -0,0 +1,47 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XAttributeIterator.h"
+#include "ezxml/XAttributeIterImpl.h"
+#include "private/PugiAttribute.h"
+#include "private/pugixml.hpp"
+
+namespace ezxml
+{
+ class XDoc;
+ using XDocCPtr = std::shared_ptr;
+ using XDocCWPtr = std::weak_ptr;
+
+ class PugiAttributeIterImpl : public XAttributeIterImpl
+ {
+ public:
+
+ PugiAttributeIterImpl();
+
+ PugiAttributeIterImpl(
+ const pugi::xml_attribute_iterator& iter,
+ const pugi::xml_node& iterParentElement,
+ const XDocCPtr& parentXDoc );
+
+ virtual ~PugiAttributeIterImpl() = default;
+
+ virtual bool getIsPayloadNull() const override;
+ virtual bool getIsEndIter() const override;
+
+ virtual XAttributeIterImplUP clone() const override;
+ virtual bool equals( const XAttributeIterator& other ) const override;
+
+ virtual XAttribute& getRef() const override;
+ virtual XAttribute* getPtr() const override;
+
+ virtual const PugiAttributeIterImpl& increment() override;
+ virtual const PugiAttributeIterImpl& decrement() override;
+
+ public:
+ pugi::xml_attribute_iterator myIter;
+ pugi::xml_node myParentElement;
+ XDocCWPtr myXDoc;
+ mutable PugiAttribute myReturnableAttribute;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.cpp b/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.cpp
new file mode 100755
index 000000000..95522fdc5
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.cpp
@@ -0,0 +1,597 @@
+// Copyright (c) Matthew James Briggs
+
+#include "private/PugiDoc.h"
+#include "private/PugiElement.h"
+#include "private/Throw.h"
+#include "private/Parse.h"
+
+#include
+
+namespace ezxml
+{
+
+ static constexpr unsigned int standardOptions =
+ pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_pi;
+
+
+ PugiDoc::PugiDoc()
+ : myDoc(),
+ myXmlVersion( DEFAULT_XML_VERSION ),
+ myEncoding( DEFAULT_ENCODING ),
+ myIsStandalone( false ),
+ myDoWriteBom( false )
+ {
+ std::istringstream is( R"()" );
+ auto options = pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype;
+ myDoc.load( is, options );
+ }
+
+ // Don't look at me, I'm ugly.
+
+ void
+ PugiDoc::loadStream( std::istream& is )
+ {
+ auto parseResult = myDoc.load( is, standardOptions );
+ if( parseResult.status != pugi::status_ok )
+ {
+ std::stringstream ss;
+ ss << "pugixml parsing failed - '";
+ switch( parseResult.status )
+ {
+ case pugi::status_file_not_found:ss << "status_file_not_found'";
+ break;
+
+ case pugi::status_io_error:ss << "status_io_error'";
+ break;
+
+ case pugi::status_out_of_memory:ss << "status_out_of_memory'";
+ break;
+
+ case pugi::status_internal_error:ss << "status_internal_error'";
+ break;
+
+ case pugi::status_unrecognized_tag:ss << "status_unrecognized_tag'";
+ break;
+
+ case pugi::status_bad_pi:ss << "status_bad_pi'";
+ break;
+
+ case pugi::status_bad_comment:ss << "status_bad_comment'";
+ break;
+
+ case pugi::status_bad_cdata:ss << "status_bad_cdata'";
+ break;
+
+ case pugi::status_bad_doctype:ss << "status_bad_doctype'";
+ break;
+
+ case pugi::status_bad_pcdata:ss << "status_bad_pcdata'";
+ break;
+
+ case pugi::status_bad_start_element:ss << "status_bad_start_element'";
+ break;
+
+ case pugi::status_bad_attribute:ss << "status_bad_attribute'";
+ break;
+
+ case pugi::status_end_element_mismatch:ss << "status_end_element_mismatch'";
+ break;
+
+ case pugi::status_append_invalid_root:ss << "status_append_invalid_root'";
+ break;
+
+ case pugi::status_no_document_element:ss << "status_no_document_element'";
+ break;
+
+ default:break;
+ }
+ ss << " - " << parseResult.description();
+ EZXML_THROW( ss.str() );
+ }
+ else if( myDoc.begin() == myDoc.end() )
+ {
+ EZXML_THROW( "pugixml parse created an empty document" );
+ }
+ else if( myDoc.begin()->type() != pugi::node_declaration )
+ {
+ auto xmlDeclaration = myDoc.prepend_child( pugi::node_declaration );
+ xmlDeclaration.set_name( "xml" );
+ auto ver = xmlDeclaration.append_attribute( "version" );
+ ver.set_value( "1.0" );
+ ver = xmlDeclaration.append_attribute( "encoding" );
+ ver.set_value( "UTF-8" );
+ }
+ else
+ {
+ auto xmlDec = *( myDoc.begin() );
+ if( xmlDec.attributes_begin() == xmlDec.attributes_end() )
+ {
+ auto attr = xmlDec.append_attribute( "version" );
+ attr.set_value( "1.0" );
+ attr = xmlDec.append_attribute( "encoding" );
+ attr.set_value( "UTF-8" );
+ }
+ else
+ {
+ auto it = xmlDec.attributes_begin();
+ auto end = xmlDec.attributes_end();
+ if( !compareCaseInsensitive( "version", it->name() ) )
+ {
+ auto attr = xmlDec.prepend_attribute( "version" );
+ attr.set_value( "1.0" );
+ }
+ auto verAttr = xmlDec.attributes_begin();
+ it = xmlDec.attributes_begin();
+ ++it;
+ if( it == end || !compareCaseInsensitive( "encoding", it->name() ) )
+ {
+ auto encodingAttr = xmlDec.insert_attribute_after( "encoding", *verAttr );
+ encodingAttr.set_value( "UTF-8" );
+ }
+ }
+ }
+ parseXmlDeclarationValues();
+
+ // if the detected encoding doesn't match the encoding tag, take the actual encoding ??
+ switch( parseResult.encoding )
+ {
+ case pugi::encoding_auto:
+ case pugi::encoding_utf8:
+ case pugi::encoding_utf16_le:
+ case pugi::encoding_utf16_be:setEncoding( Encoding::utfEight );
+ break;
+ case pugi::encoding_utf16:setEncoding( Encoding::utfSixteen );
+ break;
+ case pugi::encoding_utf32_le:
+ case pugi::encoding_utf32_be:
+ case pugi::encoding_utf32:
+ case pugi::encoding_wchar:
+ case pugi::encoding_latin1:
+ default:setEncoding( Encoding::utfEight );
+ break;
+ }
+ }
+
+
+ void
+ PugiDoc::saveStream( std::ostream& os ) const
+ {
+ auto pugiEncoding = pugi::encoding_utf8;
+ switch( myEncoding )
+ {
+ case Encoding::utfEight:pugiEncoding = pugi::encoding_utf8;
+ break;
+ case Encoding::utfSixteen:pugiEncoding = pugi::encoding_utf16;
+ break;
+ default:pugiEncoding = pugi::encoding_utf8;
+ break;
+ }
+ auto flags = pugi::format_indent;
+
+ if( myDoWriteBom )
+ {
+ flags = pugi::format_indent | pugi::format_write_bom;
+ }
+
+ pugi::xml_writer_stream writer( os );
+ myDoc.save( writer, " ", flags, pugiEncoding );
+ }
+
+
+ void
+ PugiDoc::loadFile( const std::string& filename )
+ {
+ std::ifstream infile( filename.c_str() );
+ if( !infile.is_open() )
+ {
+ std::string message = std::string{ "error opening input file: " } + filename;
+ throw std::runtime_error( message );
+ }
+
+ loadStream( infile );
+ infile.close();
+ }
+
+
+ void
+ PugiDoc::saveFile( const std::string& filename ) const
+ {
+ std::ofstream outfile( filename.c_str() );
+ if( !outfile.is_open() )
+ {
+ std::string message = std::string{ "error opening file for writing: " } + filename;
+ throw std::runtime_error( message );
+ }
+
+ saveStream( outfile );
+ outfile.close();
+ }
+
+
+ XmlVersion
+ PugiDoc::getXmlVersion() const
+ {
+ return myXmlVersion;
+ }
+
+
+ void
+ PugiDoc::setXmlVersion( XmlVersion value )
+ {
+ if( value == XmlVersion::unknown )
+ {
+ EZXML_THROW( "cannot set the XmlVersion to 'unknown'" );
+ }
+ if( myDoc.begin() == myDoc.end() )
+ {
+ EZXML_THROW( "the xml document does not have an xml declaration - bad state" );
+ }
+ else if( myDoc.begin()->type() != pugi::node_declaration )
+ {
+ EZXML_THROW( "the first node in the xml document should be an xml declaration - bad state" );
+ }
+ else
+ {
+ auto node = *( myDoc.begin() );
+ if( node.attributes_begin() == node.attributes_end() )
+ {
+ node.prepend_attribute( "version" );
+ }
+ auto attr = node.attributes_begin();
+ if( attr == node.attributes_end() )
+ {
+ EZXML_THROW( "something bad happened - this line should be unreachable" );
+ }
+ if( !compareCaseInsensitive( "version", attr->name() ) )
+ {
+ EZXML_THROW( "the first attribute of the xml declaration must be 'version'" );
+ }
+ attr->set_value( toString( value ).c_str() );
+ }
+ myXmlVersion = value;
+ }
+
+
+ Encoding
+ PugiDoc::getEncoding() const
+ {
+ return myEncoding;
+ }
+
+
+ void
+ PugiDoc::setEncoding( Encoding value )
+ {
+ if( value == Encoding::unknown )
+ {
+ EZXML_THROW( "the encoding cannot be unknown" );
+ }
+ auto attr = getXmlDeclarationAttribute( "encoding" );
+ if( !compareCaseInsensitive( "encoding", attr.name() ) )
+ {
+ EZXML_THROW( "the 'encoding' attribute could not be found" );
+ }
+ attr.set_value( toString( value ).c_str() );
+ myEncoding = value;
+ }
+
+
+ bool
+ PugiDoc::getHasStandaloneAttribute() const
+ {
+ auto standalone = getXmlDeclarationAttribute( "standalone" );
+ if( compareCaseInsensitive( standalone.name(), "standalone" ) )
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ void
+ PugiDoc::setHasStandaloneAttribute( bool value )
+ {
+ bool isStandaloneCurrentlyPresent = getHasStandaloneAttribute();
+ if( isStandaloneCurrentlyPresent && value )
+ {
+ return;
+ }
+ else if( !isStandaloneCurrentlyPresent && !value )
+ {
+ return;
+ }
+ else if( value )
+ {
+ auto xmlDeclarationNode = getXmlDeclarationNode();
+ auto it = xmlDeclarationNode.attributes_begin();
+ if( it == xmlDeclarationNode.attributes_end() )
+ {
+ EZXML_THROW( "the xml declaration node must have attributes" );
+ }
+ if( !compareCaseInsensitive( "version", it->name() ) )
+ {
+ EZXML_THROW( "the first xml declaration attribute must be 'version'" );
+ }
+ ++it;
+ if( it == xmlDeclarationNode.attributes_end() )
+ {
+ EZXML_THROW( "the xml declaration node must have an 'encoding' attribute" );
+ }
+ if( !compareCaseInsensitive( "encoding", it->name() ) )
+ {
+ EZXML_THROW( "the second attribute of the xml declaration node must be 'encoding'" );
+ }
+ auto attr = xmlDeclarationNode.insert_attribute_after( "standalone", *it );
+ if( myIsStandalone )
+ {
+ attr.set_value( "yes" );
+ }
+ else
+ {
+ attr.set_value( "no" );
+ }
+ return;
+ }
+ else if( !value )
+ {
+ auto xmlDeclarationNode = getXmlDeclarationNode();
+ for( auto it = xmlDeclarationNode.attributes_begin();
+ it != xmlDeclarationNode.attributes_end(); ++it )
+ {
+ if( compareCaseInsensitive( "standalone", it->name() ) )
+ {
+ xmlDeclarationNode.remove_attribute( *it );
+ myIsStandalone = false;
+ return;
+ }
+ }
+ }
+ }
+
+
+ bool
+ PugiDoc::getIsStandalone() const
+ {
+ return myIsStandalone;
+ }
+
+
+ void
+ PugiDoc::setIsStandalone( bool value )
+ {
+ setHasStandaloneAttribute( true );
+ auto attr = getXmlDeclarationAttribute( "standalone" );
+ if( value )
+ {
+ attr.set_value( "yes" );
+ }
+ else
+ {
+ attr.set_value( "no" );
+ }
+ myIsStandalone = value;
+ }
+
+
+ bool
+ PugiDoc::getHasDoctypeDeclaration() const
+ {
+ auto doctype = getDoctypeNode();
+ if( doctype.type() != pugi::node_doctype )
+ {
+ return false;
+ }
+ return true;
+ }
+
+
+ void
+ PugiDoc::setHasDoctypeDeclaration( bool value )
+ {
+ bool isDoctypeCurrentlyPresent = getHasDoctypeDeclaration();
+ if( value && isDoctypeCurrentlyPresent )
+ {
+ // is requested and already exists, nothing to do
+ return;
+ }
+ else if( !isDoctypeCurrentlyPresent && !value )
+ {
+ // is not requested and doesn't exist, nothing to do
+ return;
+ }
+ else if( value )
+ {
+ // add doctype
+ if( myDoc.begin() == myDoc.end() )
+ {
+ EZXML_THROW( "empty documet" );
+ }
+ auto xmlDeclarationNode = *( myDoc.begin() );
+ if( xmlDeclarationNode.type() != pugi::node_declaration )
+ {
+ EZXML_THROW( "bad xml document state" );
+ }
+ myDoc.insert_child_after( pugi::node_doctype, xmlDeclarationNode );
+ return;
+ }
+ else if( !value )
+ {
+ // delete doctype
+ for( auto it = myDoc.begin(); it != myDoc.end(); ++it )
+ {
+ if( it->type() == pugi::node_doctype )
+ {
+ myDoc.remove_child( *it );
+ return;
+ }
+ }
+ }
+ }
+
+
+ std::string
+ PugiDoc::getDoctypeValue() const
+ {
+ auto doctypeNode = getDoctypeNode();
+ return doctypeNode.value();
+ }
+
+
+ void
+ PugiDoc::setDoctypeValue( const std::string& value )
+ {
+ setHasDoctypeDeclaration( true );
+ auto doctypeNode = getDoctypeNode();
+ doctypeNode.set_value( value.c_str() );
+ }
+
+
+ XElementPtr
+ PugiDoc::getRoot() const
+ {
+ for( auto it = myDoc.begin();
+ it != myDoc.end(); ++it )
+ {
+ if( it->type() == pugi::node_element )
+ {
+ return std::make_shared( *it, XDoc::shared_from_this() );
+ }
+ }
+ return XElementPtr{};
+ }
+
+
+ void
+ PugiDoc::parseXmlDeclarationValues()
+ {
+ parseXmlVersionFromDoc();
+ parseEncodingFromDoc();
+ parseStandalone();
+ }
+
+
+ void
+ PugiDoc::parseXmlVersionFromDoc()
+ {
+ auto xmlDeclaration = getXmlDeclarationNode();
+ if( xmlDeclaration.type() != pugi::node_declaration )
+ {
+ EZXML_THROW( "the xml document must have an xml declaration as its first node" );
+ }
+
+ auto version = xmlDeclaration.attribute( "version" );
+ std::string value{ version.value() };
+ if( value == "" )
+ {
+ myXmlVersion = DEFAULT_XML_VERSION;
+ setXmlVersion( DEFAULT_XML_VERSION );
+ return;
+ }
+
+ myXmlVersion = parseXmlVersion( value );
+ }
+
+
+ void
+ PugiDoc::parseEncodingFromDoc()
+ {
+ auto attr = getXmlDeclarationAttribute( "encoding" );
+ if( !compareCaseInsensitive( "encoding", attr.name() ) )
+ {
+ EZXML_THROW( "encoding value was not found" );
+ }
+ myEncoding = parseEncoding( attr.value() );
+ attr.set_value( toString( myEncoding ).c_str() );
+ }
+
+
+ void
+ PugiDoc::parseStandalone()
+ {
+
+ if( !getHasStandaloneAttribute() )
+ {
+ myIsStandalone = false;
+ return;
+ }
+ auto standalone = getXmlDeclarationAttribute( "standalone" );
+ if( std::string{ standalone.value() } == std::string{ "yes" } )
+ {
+ myIsStandalone = true;
+ return;
+ }
+ myIsStandalone = false;
+ }
+
+
+ pugi::xml_node
+ PugiDoc::getDoctypeNode() const
+ {
+ if( myDoc.empty() )
+ {
+ return pugi::xml_node{};
+ }
+
+ auto childrenIter = myDoc.begin();
+ if( childrenIter == myDoc.end() )
+ {
+ return pugi::xml_node{};
+ }
+ if( childrenIter->type() == pugi::node_declaration )
+ {
+ ++childrenIter;
+ }
+ if( childrenIter == myDoc.end() ||
+ childrenIter->type() != pugi::node_doctype )
+ {
+ return pugi::xml_node{};
+ }
+ else
+ {
+ return *childrenIter;
+ }
+ }
+
+
+ pugi::xml_node
+ PugiDoc::getXmlDeclarationNode() const
+ {
+ if( myDoc.empty() )
+ {
+ return pugi::xml_node{};
+ }
+
+ auto firstNode = myDoc.first_child();
+
+ if( firstNode.type() != pugi::node_declaration )
+ {
+ return pugi::xml_node{};
+ }
+ std::string nodeName{ firstNode.name() };
+ if( !compareCaseInsensitive( nodeName, "xml" ) )
+ {
+ return pugi::xml_node{};
+ }
+
+ return firstNode;
+ }
+
+
+ pugi::xml_attribute
+ PugiDoc::getXmlDeclarationAttribute( const char* const name ) const
+ {
+ auto declaration = getXmlDeclarationNode();
+ if( declaration.type() != pugi::node_declaration )
+ {
+ return pugi::xml_attribute{};
+ }
+ return declaration.attribute( name );
+ }
+
+
+ void
+ PugiDoc::setDoWriteByteOrderMark( bool value )
+ {
+ myDoWriteBom = value;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.h b/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.h
new file mode 100755
index 000000000..f07b38511
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiDoc.h
@@ -0,0 +1,65 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include "ezxml/XDoc.h"
+#include "private/pugixml.hpp"
+
+namespace ezxml
+{
+ class PugiDoc;
+ using PugiDocPtr = std::shared_ptr;
+
+ class PugiDoc : public XDoc
+ {
+ public:
+ virtual ~PugiDoc() = default;
+ PugiDoc();
+ PugiDoc( const PugiDoc& other ) = delete;
+ PugiDoc( PugiDoc&& other ) = default;
+ PugiDoc& operator=( const PugiDoc& other ) = delete;
+ PugiDoc& operator=( PugiDoc&& other ) = default;
+
+ virtual void loadStream( std::istream& is ) override;
+ virtual void saveStream( std::ostream& os ) const override;
+
+ virtual void loadFile( const std::string& filename ) override;
+ virtual void saveFile( const std::string& filename ) const override;
+
+ // Xml Declaration
+ virtual XmlVersion getXmlVersion() const override;
+ virtual void setXmlVersion( XmlVersion value ) override;
+ virtual Encoding getEncoding() const override;
+ virtual void setEncoding( Encoding value ) override;
+ virtual bool getHasStandaloneAttribute() const override;
+ virtual void setHasStandaloneAttribute( bool value ) override;
+ virtual bool getIsStandalone() const override;
+ virtual void setIsStandalone( bool value ) override;
+
+ // Doctype Declaration
+ virtual bool getHasDoctypeDeclaration() const override;
+ virtual void setHasDoctypeDeclaration( bool value ) override;
+ virtual std::string getDoctypeValue() const override;
+ virtual void setDoctypeValue( const std::string& value ) override;
+
+ // Node Access
+ virtual XElementPtr getRoot() const override;
+
+ virtual void setDoWriteByteOrderMark( bool value ) override;
+
+ private:
+ pugi::xml_document myDoc;
+ XmlVersion myXmlVersion;
+ Encoding myEncoding;
+ bool myIsStandalone;
+ bool myDoWriteBom;
+
+ void parseXmlDeclarationValues();
+ void parseXmlVersionFromDoc();
+ void parseEncodingFromDoc();
+ void parseStandalone();
+ pugi::xml_node getDoctypeNode() const;
+ pugi::xml_node getXmlDeclarationNode() const;
+ pugi::xml_attribute getXmlDeclarationAttribute( const char* const name ) const;
+ };
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.cpp b/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.cpp
new file mode 100755
index 000000000..00ec2eac1
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.cpp
@@ -0,0 +1,378 @@
+// MusicXML Class Library
+// Copyright (c) by Matthew James Briggs
+// Distributed under the MIT License
+
+#include "private/PugiElement.h"
+#include "private/PugiElementIterImpl.h"
+#include "ezxml/XAttributeIterImpl.h"
+#include "private/PugiAttributeIterImpl.h"
+#include "private/XThrow.h"
+
+#define EZXML_CHECK_NULL_NODE if( getIsNull() ) { EZXML_THROW_XNULL; }
+#define EZXML_CHECK_NODE_ELEMENT if ( myNodeType != pugi::node_element && myNodeType != pugi::node_pi ) { EZXML_THROW( "bad internal state, node should be an element" ); }
+
+namespace ezxml
+{
+
+ PugiElement::PugiElement()
+ : myNode(), myXDoc( XDocCPtr{ nullptr } ), myNodeType{ pugi::xml_node_type::node_null }, myEndIter{}
+ {
+ update();
+ }
+
+
+ PugiElement::PugiElement(
+ const pugi::xml_node& node,
+ const XDocCPtr& xdoc
+ )
+ : myNode( node ), myXDoc( xdoc ), myNodeType{ pugi::xml_node_type::node_null }, myEndIter{}
+ {
+ update();
+ const bool isElement = myNodeType == pugi::node_element;
+ const bool isProcessingInstruction = myNodeType == pugi::node_pi;
+
+ if( ( !isElement ) && ( !isProcessingInstruction ) )
+ {
+ EZXML_THROW( "bad internal state, node should be an element" );
+ }
+ }
+
+
+ XElementPtr
+ PugiElement::clone() const
+ {
+ return XElementPtr( new PugiElement{ myNode, myXDoc.lock() } );
+ }
+
+
+ XElementType
+ PugiElement::getType() const
+ {
+ if( getIsNull() )
+ {
+ return XElementType::null;
+ }
+
+ if( std::string{ myNode.text().as_string() }.length() > 0 )
+ {
+ return XElementType::text;
+ }
+
+ if( begin() == end() )
+ {
+ return XElementType::empty;
+ }
+
+ return XElementType::element;
+ }
+
+
+ bool
+ PugiElement::getIsNull() const
+ {
+ auto ptr = myXDoc.lock();
+
+ if( !ptr )
+ {
+ return true;
+ }
+ else if( myNodeType != pugi::node_element )
+ {
+ if( myNodeType != pugi::node_pi )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiElement::getIsProcessingInstruction() const
+ {
+ if( myNodeType == pugi::node_pi )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ std::string
+ PugiElement::getName() const
+ {
+ if( getIsNull() )
+ {
+ return "";
+ }
+ return std::string{ myNode.name() };
+ }
+
+
+ std::string
+ PugiElement::getValue() const
+ {
+ if( getIsNull() )
+ {
+ return std::string{};
+ }
+
+ if( getIsProcessingInstruction() )
+ {
+ return std::string{ myNode.value() };
+ }
+ else
+ {
+ return std::string{ myNode.text().as_string() };
+ }
+ }
+
+
+ void
+ PugiElement::setName( const std::string& name )
+ {
+ if( getIsNull() )
+ {
+ return;
+ }
+ myNode.set_name( name.c_str() );
+ update();
+ }
+
+
+ void
+ PugiElement::setValue( const std::string& value )
+ {
+ if( getIsNull() )
+ {
+ return;
+ }
+ XElementType xetype = getType();
+
+ if( xetype == XElementType::element )
+ {
+ EZXML_THROW( "the object cannot hold both elements and text" );
+ }
+ else if( xetype == XElementType::empty )
+ {
+ auto newnode = myNode.prepend_child( pugi::node_pcdata );
+ newnode.set_value( value.c_str() );
+ update();
+ return;
+ }
+ else if( xetype == XElementType::text )
+ {
+ auto it = myNode.begin();
+ it->set_value( value.c_str() );
+ update();
+ return;
+ }
+ }
+
+
+ XDocCPtr
+ PugiElement::getDoc() const
+ {
+ return myXDoc.lock();
+ }
+
+
+ XElementPtr
+ PugiElement::getParent() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ return XElementPtr{ new PugiElement{ myNode.parent(), myXDoc.lock() } };
+ }
+
+
+ XElementPtr
+ PugiElement::getNextSibling() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto nextSibling = myNode.next_sibling();
+
+ if( nextSibling.type() == pugi::node_null )
+ {
+ return XElementPtr{};
+ }
+
+ if( nextSibling.type() == pugi::node_element )
+ {
+ return XElementPtr{ new PugiElement{ nextSibling, myXDoc.lock() } };
+ }
+
+ if( nextSibling.type() == pugi::node_pi )
+ {
+ return XElementPtr{ new PugiElement{ nextSibling, myXDoc.lock() } };
+ }
+
+ return XElementPtr{};
+ }
+
+
+ XElementIterator
+ PugiElement::begin() const
+ {
+ auto iter = beginWithProcessingInstructions();
+ iter.setSkipProcessingInstructions( true );
+
+ if( iter.getSkipProcessingInstructions() &&
+ iter != end() &&
+ iter->getIsProcessingInstruction() )
+ {
+ ++iter;
+ }
+
+ return iter;
+ }
+
+
+ XElementIterator
+ PugiElement::beginWithProcessingInstructions() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto beginIter = myNode.begin();
+
+ if( beginIter == myNode.end() )
+ {
+ return this->end();
+ }
+
+ const auto type = beginIter->type();
+
+ if( type == pugi::node_element ||
+ type == pugi::node_pi )
+ {
+ auto result = XElementIterator( PugiElementIterImpl{ myNode.begin(), myNode, myXDoc.lock() } );
+ result.setSkipProcessingInstructions( false );
+ return result;
+ }
+
+ return this->end();
+ }
+
+
+ XElementIterator
+ PugiElement::end() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ return myEndIter;
+ }
+
+
+ XAttributeIterator
+ PugiElement::attributesBegin() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ return XAttributeIterator( PugiAttributeIterImpl{ myNode.attributes_begin(), myNode, myXDoc.lock() } );
+ }
+
+
+ XAttributeIterator
+ PugiElement::attributesEnd() const
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ return XAttributeIterator( PugiAttributeIterImpl{ myNode.attributes_end(), myNode, myXDoc.lock() } );
+ }
+
+
+ XElementPtr
+ PugiElement::appendChild( const std::string& name )
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto result = XElementPtr{ new PugiElement{ myNode.append_child( name.c_str() ), myXDoc.lock() } };
+ update();
+ return result;
+ }
+
+
+ XElementPtr
+ PugiElement::prependChild( const std::string& name )
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto result = XElementPtr{ new PugiElement{ myNode.prepend_child( name.c_str() ), myXDoc.lock() } };
+ update();
+ return result;
+ }
+
+
+ XElementPtr
+ PugiElement::insertSiblingAfter( const std::string& newElementName )
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ auto newNode = myNode.parent().insert_child_after( newElementName.c_str(), myNode );
+ const auto result = XElementPtr{ new PugiElement{ newNode, myXDoc.lock() } };
+ update();
+ return result;
+ }
+
+
+ bool
+ PugiElement::removeChild( const std::string& elementName )
+ {
+ const auto result = myNode.remove_child( elementName.c_str() );
+ update();
+ return result;
+ }
+
+
+ XAttributePtr
+ PugiElement::appendAttribute( const std::string& name )
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto result = XAttributePtr{
+ new PugiAttribute{ myNode.append_attribute( name.c_str() ), myNode, myXDoc.lock() } };
+ update();
+ return result;
+ }
+
+
+ XAttributePtr
+ PugiElement::prependAttribute( const std::string& name )
+ {
+ EZXML_CHECK_NULL_NODE;
+ EZXML_CHECK_NODE_ELEMENT;
+ const auto result = XAttributePtr{
+ new PugiAttribute{ myNode.prepend_attribute( name.c_str() ), myNode, myXDoc.lock() } };
+ update();
+ return result;
+ }
+
+
+ void
+ PugiElement::removeAttribute( const XAttributeIterator& iter )
+ {
+ auto it = myNode.attributes_begin();
+ auto e = myNode.attributes_end();
+ for( ; it != e; ++it )
+ {
+ if( iter->getName() == it->name() )
+ {
+ myNode.remove_attribute( *it );
+ update();
+ return;
+ }
+ }
+ }
+
+
+ void
+ PugiElement::update()
+ {
+ myNodeType = myNode.type();
+ myEndIter = XElementIterator( PugiElementIterImpl{ myNode.end(), myNode, myXDoc.lock() } );
+ }
+}
+
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.h b/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.h
new file mode 100755
index 000000000..46083ca4c
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiElement.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "ezxml/XElement.h"
+#include "private/pugixml.hpp"
+#include "ezxml/XElementIterator.h"
+
+#include
+
+namespace ezxml
+{
+ class PugiElement;
+ using PugiElementPtr = std::shared_ptr;
+
+ class XDoc;
+ using XDocCPtr = std::shared_ptr;
+ using XDocCWPtr = std::weak_ptr;
+
+ class PugiElement : public XElement
+ {
+ public:
+ PugiElement();
+
+ PugiElement(
+ const pugi::xml_node& node,
+ const XDocCPtr& xdoc
+ );
+
+ // copy
+ PugiElement( const PugiElement& other ) = default;
+ PugiElement& operator=( const PugiElement& other ) = default;
+
+ // move
+ PugiElement( PugiElement&& other ) noexcept = default;
+ PugiElement& operator=( PugiElement&& other ) noexcept = default;
+
+ virtual XElementPtr clone() const override;
+
+ virtual XElementType getType() const override;
+ virtual bool getIsNull() const override;
+
+ virtual bool getIsProcessingInstruction() const override;
+
+ virtual std::string getName() const override;
+ virtual std::string getValue() const override;
+
+ virtual void setName( const std::string& name ) override;
+ virtual void setValue( const std::string& name ) override;
+
+ virtual XDocCPtr getDoc() const override;
+ virtual XElementPtr getParent() const override;
+ virtual XElementPtr getNextSibling() const override;
+
+ virtual XElementIterator begin() const override;
+ virtual XElementIterator beginWithProcessingInstructions() const override;
+ virtual XElementIterator end() const override;
+
+ virtual XAttributeIterator attributesBegin() const override;
+ virtual XAttributeIterator attributesEnd() const override;
+
+ virtual XElementPtr appendChild( const std::string& name ) override;
+ virtual XElementPtr prependChild( const std::string& name ) override;
+
+ virtual XElementPtr insertSiblingAfter( const std::string& newElementName ) override;
+ virtual bool removeChild( const std::string& elementName ) override;
+
+ virtual XAttributePtr appendAttribute( const std::string& name ) override;
+ virtual XAttributePtr prependAttribute( const std::string& name ) override;
+ virtual void removeAttribute( const XAttributeIterator& iter ) override;
+
+ private:
+ pugi::xml_node myNode;
+ XDocCWPtr myXDoc;
+ pugi::xml_node_type myNodeType;
+ XElementIterator myEndIter;
+
+ private:
+ void update();
+ };
+}
+
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.cpp b/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.cpp
new file mode 100755
index 000000000..2225522c4
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.cpp
@@ -0,0 +1,266 @@
+#include "private/PugiElementIterImpl.h"
+#include "private/XThrow.h"
+
+namespace ezxml
+{
+ PugiElementIterImpl::PugiElementIterImpl()
+ : myIter(),
+ myIterParent(),
+ myXDoc(),
+ mySkipProcessingInstructions{ true },
+ myReturnableElement{ nullptr }
+ {
+
+ }
+
+
+ PugiElementIterImpl::PugiElementIterImpl(
+ const pugi::xml_node_iterator& iter,
+ const pugi::xml_node& iterParent,
+ const XDocCPtr& parentDoc
+ )
+ : myIter( iter ),
+ myIterParent( iterParent ),
+ myXDoc( parentDoc ),
+ mySkipProcessingInstructions{ true },
+ myReturnableElement{ nullptr }
+ {
+
+ }
+
+
+ PugiElementIterImpl::PugiElementIterImpl( const PugiElementIterImpl& inOther )
+ : myIter( inOther.myIter ),
+ myIterParent( inOther.myIterParent ),
+ myXDoc( inOther.myXDoc ),
+ mySkipProcessingInstructions{ inOther.mySkipProcessingInstructions },
+ myReturnableElement{ inOther.myReturnableElement == nullptr ? nullptr : std::make_unique(
+ *inOther.myReturnableElement
+ ) }
+ {
+
+ }
+
+
+ PugiElementIterImpl&
+ PugiElementIterImpl::operator=( const PugiElementIterImpl& inOther )
+ {
+ myIter = inOther.myIter;
+ myIterParent = inOther.myIterParent;
+ myXDoc = inOther.myXDoc;
+ mySkipProcessingInstructions = inOther.mySkipProcessingInstructions;
+ myReturnableElement = inOther.myReturnableElement == nullptr ? nullptr
+ : std::make_unique( *inOther.myReturnableElement );
+ return *this;
+ }
+
+
+ XElementIterImplUP
+ PugiElementIterImpl::clone() const
+ {
+ return XElementIterImplUP{ new PugiElementIterImpl{ *this } };
+ }
+
+
+ bool
+ PugiElementIterImpl::getIsPayloadNull() const
+ {
+ auto ptr = myXDoc.lock();
+
+ if( !ptr )
+ {
+ return true;
+ }
+
+ const auto type = myIterParent.type();
+ const auto isElement = type == pugi::node_element;
+ const auto isProcessingInstruction = type == pugi::node_pi;
+
+ if( ( !isElement ) && ( !isProcessingInstruction ) )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiElementIterImpl::getIsEndIter() const
+ {
+ if( myIter == myIterParent.end() )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiElementIterImpl::getIsBeginIter() const
+ {
+ if( myIter == myIterParent.begin() )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiElementIterImpl::getIsProcessingInstruction() const
+ {
+ if( this->getIsEndIter() )
+ {
+ return false;
+ }
+ else if( this->getIsPayloadNull() )
+ {
+ return false;
+ }
+
+ const auto type = myIter->type();
+
+ if( type == pugi::node_pi )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ PugiElementIterImpl::getSkipProcessingInstructions() const
+ {
+ return mySkipProcessingInstructions;
+ }
+
+
+ void
+ PugiElementIterImpl::setSkipProcessingInstructions( bool inValue )
+ {
+ mySkipProcessingInstructions = inValue;
+ }
+
+
+ bool
+ PugiElementIterImpl::equals( const XElementIterator& other ) const
+ {
+
+ auto& otherXImplPtr = other.reveal();
+
+ if( !otherXImplPtr )
+ {
+ return false;
+ }
+
+ auto otherPtr = dynamic_cast( otherXImplPtr.get() );
+
+ return myIter == otherPtr->myIter;
+ }
+
+
+ XElement&
+ PugiElementIterImpl::getRef() const
+ {
+ if( getIsPayloadNull() )
+ {
+ EZXML_THROW_XNULL;
+ }
+
+ if( getIsEndIter() )
+ {
+ EZXML_THROW( "XElementIterator attempted to dereference an 'end' iterator" );
+ }
+
+ myReturnableElement = std::make_unique( *myIter, myXDoc.lock() );
+ return *myReturnableElement;
+ }
+
+
+ XElement*
+ PugiElementIterImpl::getPtr() const
+ {
+ if( getIsPayloadNull() )
+ {
+ EZXML_THROW_XNULL;
+ }
+
+ if( getIsEndIter() )
+ {
+ EZXML_THROW( "XElementIterator attempted to dereference an 'end' iterator" );
+ }
+
+ myReturnableElement = std::make_unique( *myIter, myXDoc.lock() );
+ return myReturnableElement.get();
+ }
+
+
+ const PugiElementIterImpl&
+ PugiElementIterImpl::increment()
+ {
+ ++myIter;
+
+ if( mySkipProcessingInstructions )
+ {
+ bool isEnd = getIsEndIter();
+ bool isPi = getIsProcessingInstruction();
+
+ while( !isEnd && isPi )
+ {
+ ++myIter;
+ isEnd = getIsEndIter();
+ isPi = getIsProcessingInstruction();
+ }
+ }
+ return *this;
+ }
+
+
+ const PugiElementIterImpl&
+ PugiElementIterImpl::decrement()
+ {
+ --myIter;
+
+ if( mySkipProcessingInstructions )
+ {
+ bool isBegin = getIsBeginIter();
+ bool isPi = getIsProcessingInstruction();
+
+ while( !isBegin && isPi )
+ {
+ --myIter;
+ isBegin = getIsBeginIter();
+ isPi = getIsProcessingInstruction();
+ }
+ }
+ return *this;
+ }
+
+
+ pugi::xml_node_type
+ PugiElementIterImpl::getPugiXmlNodeType() const
+ {
+ if( getIsPayloadNull() )
+ {
+ return pugi::node_null;
+ }
+ return myIter->type();
+ }
+
+
+ bool
+ PugiElementIterImpl::hasTextData() const
+ {
+ if( getIsPayloadNull() )
+ {
+ return false;
+ }
+ return std::string{ myIter->child_value() }.size() > 0;
+ }
+
+}
+
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.h b/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.h
new file mode 100755
index 000000000..cad2ac443
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/PugiElementIterImpl.h
@@ -0,0 +1,65 @@
+// MusicXML Class Library
+// Copyright (c) by Matthew James Briggs
+// Distributed under the MIT License
+
+#pragma once
+
+#include "ezxml/XElementIterator.h"
+#include "ezxml/XElementIterImpl.h"
+#include "ezxml/XDoc.h"
+#include "private/PugiElement.h"
+#include "private/pugixml.hpp"
+
+namespace ezxml
+{
+
+ class PugiElementIterImpl : public XElementIterImpl
+ {
+ public:
+ ~PugiElementIterImpl() = default;
+
+ PugiElementIterImpl();
+
+ PugiElementIterImpl(
+ const pugi::xml_node_iterator& iter,
+ const pugi::xml_node& iterParent,
+ const XDocCPtr& parentDoc
+ );
+
+ PugiElementIterImpl( const PugiElementIterImpl& );
+ PugiElementIterImpl( PugiElementIterImpl&& ) = default;
+
+ PugiElementIterImpl& operator=( const PugiElementIterImpl& );
+ PugiElementIterImpl& operator=( PugiElementIterImpl&& ) = default;
+
+ virtual bool getIsPayloadNull() const override;
+ virtual bool getIsEndIter() const override;
+ virtual bool getIsProcessingInstruction() const override;
+ virtual bool getSkipProcessingInstructions() const override;
+ virtual void setSkipProcessingInstructions( bool inValue ) override;
+
+ virtual XElementIterImplUP clone() const override;
+ virtual bool equals( const XElementIterator& other ) const override;
+
+ virtual XElement& getRef() const override;
+ virtual XElement* getPtr() const override;
+
+ virtual const PugiElementIterImpl& increment() override;
+ virtual const PugiElementIterImpl& decrement() override;
+
+ // not part of inherited interface
+ virtual pugi::xml_node_type getPugiXmlNodeType() const final;
+ virtual bool hasTextData() const final;
+
+ public:
+ pugi::xml_node_iterator myIter;
+ pugi::xml_node myIterParent;
+ XDocCWPtr myXDoc;
+ bool mySkipProcessingInstructions;
+ mutable std::unique_ptr myReturnableElement;
+
+ private:
+ bool getIsBeginIter() const;
+ };
+}
+
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/Throw.h b/Sourcecode/private/extern/ezxml/src/private/private/Throw.h
new file mode 100755
index 000000000..6f2c40001
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/Throw.h
@@ -0,0 +1,60 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+
+#include
+#include
+
+#ifndef __FILENAME__
+
+ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
+
+ #define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
+
+ #else
+
+ #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+ #endif
+
+#endif
+
+#ifndef EZXML_THROW
+#define EZXML_THROW(throw_error_message) \
+throw std::runtime_error ( \
+ std::string( "error in " ) \
++ std::string( __FILENAME__ ) \
++ std::string(" (line ") \
++ std::string( std::to_string(__LINE__ ) ) \
++ std::string(") ") \
++ std::string( __FUNCTION__ ) \
++ std::string(": '") \
++ std::string( throw_error_message ) );
+#endif
+
+#ifndef EZXML_THROW_NULL
+#define THROW_NULL THROW("null pointer encountered")
+#endif
+
+#ifndef EZXML_THROW_IF_NULL
+#define THROW_IF_NULL(pointerVariable) \
+if ( pointerVariable == nullptr ) { THROW_NULL }
+#endif
+
+#ifndef EZXML_THROW_IF_BAD_VALUE
+#define THROW_IF_BAD_VALUE( VALUE, MIN_VAL, MAX_VAL ) \
+if( VALUE < MIN_VAL || VALUE > MAX_VAL ) { \
+std::stringstream BADVALUEMESSAGE; \
+BADVALUEMESSAGE << "value out of range. " << \
+#VALUE << " = " << VALUE << ", min = " << MIN_VAL << ", max = " << MAX_VAL; \
+THROW( BADVALUEMESSAGE.str() ) }
+#endif
+
+#ifndef EZXML_BUG
+#define EZXML_BUG EZXML_THROW( "this exception is due to a coding error, please report the bug https://github.com/Webern/MusicXML-Class-Library/issues" );
+#endif
+
+#ifndef EZXML_ASSERT
+#define EZXML_ASSERT(expectedTrueStatement) \
+if ( ! (expectedTrueStatement) ) { EZXML_THROW("assertion failed '" #expectedTrueStatement "'"); }
+#endif
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/XAttributeIterator.cpp b/Sourcecode/private/extern/ezxml/src/private/private/XAttributeIterator.cpp
new file mode 100755
index 000000000..20de4bc7a
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/XAttributeIterator.cpp
@@ -0,0 +1,139 @@
+// Copyright (c) Matthew James Briggs
+
+#include "ezxml/XAttributeIterator.h"
+
+namespace ezxml
+{
+ XAttributeIterator::XAttributeIterator()
+ : myImpl( nullptr )
+ {
+
+ }
+
+
+ XAttributeIterator::XAttributeIterator( const XAttributeIterImpl& impl )
+ : myImpl( impl.clone() )
+ {
+
+ }
+
+
+ XAttributeIterator::XAttributeIterator( const XAttributeIterator& other )
+ : myImpl( nullptr )
+ {
+ if( other.myImpl )
+ {
+ myImpl = other.myImpl->clone();
+ }
+ }
+
+
+ XAttributeIterator&
+ XAttributeIterator::operator=( const XAttributeIterator& other )
+ {
+ if( other.myImpl )
+ {
+ myImpl = other.myImpl->clone();
+ }
+ return *this;
+ }
+
+
+ bool
+ XAttributeIterator::operator==( const XAttributeIterator& rhs ) const
+ {
+ if( !myImpl )
+ {
+ return false;
+ }
+ return myImpl->equals( rhs );
+ }
+
+
+ bool
+ XAttributeIterator::operator!=( const XAttributeIterator& rhs ) const
+ {
+ if( !myImpl )
+ {
+ return false;
+ }
+ return !myImpl->equals( rhs );
+ }
+
+
+ XAttribute&
+ XAttributeIterator::operator*() const
+ {
+ if( !myImpl )
+ {
+ throw std::runtime_error( "XAttributeIterator::operator*() - null dereference attempted" );
+ }
+ return myImpl->getRef();
+ }
+
+
+ XAttribute*
+ XAttributeIterator::operator->() const
+ {
+ if( !myImpl )
+ {
+ throw std::runtime_error( "XAttributeIterator::operator->() - null dereference attempted" );
+ }
+ return myImpl->getPtr();
+ }
+
+
+ const XAttributeIterator&
+ XAttributeIterator::operator++()
+ {
+ if( myImpl )
+ {
+ myImpl->increment();
+ }
+ return *this;
+ }
+
+
+ XAttributeIterator
+ XAttributeIterator::operator++( int )
+ {
+ if( !myImpl )
+ {
+ return XAttributeIterator{};
+ }
+ XAttributeIterImplUP temp = myImpl->clone();
+ myImpl->increment();
+ return XAttributeIterator{ *temp };
+ }
+
+
+ const XAttributeIterator&
+ XAttributeIterator::operator--()
+ {
+ if( myImpl )
+ {
+ myImpl->decrement();
+ }
+ return *this;
+ }
+
+
+ XAttributeIterator
+ XAttributeIterator::operator--( int )
+ {
+ if( !myImpl )
+ {
+ return XAttributeIterator{};
+ }
+ XAttributeIterImplUP temp = myImpl->clone();
+ myImpl->decrement();
+ return XAttributeIterator{ *temp };
+ }
+
+
+ const XAttributeIterImplUP&
+ XAttributeIterator::reveal() const
+ {
+ return myImpl;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/XElementIterator.cpp b/Sourcecode/private/extern/ezxml/src/private/private/XElementIterator.cpp
new file mode 100755
index 000000000..d5b25501a
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/XElementIterator.cpp
@@ -0,0 +1,170 @@
+// Copyright (c) Matthew James Briggs
+
+#include "ezxml/XElementIterator.h"
+
+namespace ezxml
+{
+ XElementIterator::XElementIterator()
+ : myImpl( nullptr )
+ {
+
+ }
+
+
+ XElementIterator::XElementIterator( const XElementIterImpl& impl )
+ : myImpl( impl.clone() )
+ {
+
+ }
+
+
+ XElementIterator::XElementIterator( const XElementIterator& other )
+ : myImpl( nullptr )
+ {
+ if( other.myImpl )
+ {
+ myImpl = other.myImpl->clone();
+ }
+ }
+
+
+ XElementIterator&
+ XElementIterator::operator=( const XElementIterator& other )
+ {
+ if( other.myImpl )
+ {
+ myImpl = other.myImpl->clone();
+ }
+ return *this;
+ }
+
+
+ bool
+ XElementIterator::operator==( const XElementIterator& rhs ) const
+ {
+ if( !myImpl )
+ {
+ return false;
+ }
+ return myImpl->equals( rhs );
+ }
+
+
+ bool
+ XElementIterator::operator!=( const XElementIterator& rhs ) const
+ {
+ if( !myImpl )
+ {
+ return false;
+ }
+ return !myImpl->equals( rhs );
+ }
+
+
+ XElement&
+ XElementIterator::operator*() const
+ {
+ if( !myImpl )
+ {
+ throw std::runtime_error( "XElementIterator::operator*() - null dereference attempted" );
+ }
+ return myImpl->getRef();
+ }
+
+
+ XElement*
+ XElementIterator::operator->() const
+ {
+ if( !myImpl )
+ {
+ throw std::runtime_error( "XElementIterator::operator->() - null dereference attempted" );
+ }
+ return myImpl->getPtr();
+ }
+
+
+ const XElementIterator&
+ XElementIterator::operator++()
+ {
+ if( myImpl )
+ {
+ myImpl->increment();
+ }
+ return *this;
+ }
+
+
+ const XElementIterator
+ XElementIterator::operator++( int )
+ {
+ if( !myImpl )
+ {
+ return XElementIterator{};
+ }
+ XElementIterImplUP temp = myImpl->clone();
+ myImpl->increment();
+ return XElementIterator{ *temp };
+ }
+
+
+ const XElementIterator&
+ XElementIterator::operator--()
+ {
+ if( myImpl )
+ {
+ myImpl->decrement();
+ }
+ return *this;
+ }
+
+
+ const XElementIterator
+ XElementIterator::operator--( int )
+ {
+ if( !myImpl )
+ {
+ return XElementIterator{};
+ }
+ XElementIterImplUP temp = myImpl->clone();
+ myImpl->decrement();
+ return XElementIterator{ *temp };
+ }
+
+
+ bool
+ XElementIterator::getIsPayloadNull() const
+ {
+ if( !myImpl )
+ {
+ return true;
+ }
+
+ if( myImpl->getIsPayloadNull() )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool
+ XElementIterator::getSkipProcessingInstructions() const
+ {
+ return myImpl->getSkipProcessingInstructions();
+ }
+
+
+ void
+ XElementIterator::setSkipProcessingInstructions( bool inValue )
+ {
+ myImpl->setSkipProcessingInstructions( inValue );
+ }
+
+
+ const XElementIterImplUP&
+ XElementIterator::reveal() const
+ {
+ return myImpl;
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/XFactory.cpp b/Sourcecode/private/extern/ezxml/src/private/private/XFactory.cpp
new file mode 100755
index 000000000..3feafc91b
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/XFactory.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) Matthew James Briggs
+
+#include "ezxml/XFactory.h"
+#include "private/PugiDoc.h"
+#include "private/PugiElement.h"
+
+namespace ezxml
+{
+ XDocPtr
+ XFactory::makeXDoc()
+ {
+ return XDocPtr{ new PugiDoc{} };
+ }
+
+
+ XElementPtr
+ XFactory::makeXElement()
+ {
+ return XElementPtr{ new PugiElement{} };
+ }
+}
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/XThrow.h b/Sourcecode/private/extern/ezxml/src/private/private/XThrow.h
new file mode 100755
index 000000000..f517d65ce
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/XThrow.h
@@ -0,0 +1,6 @@
+// Copyright (c) Matthew James Briggs
+
+#pragma once
+#include "private/Throw.h"
+
+#define EZXML_THROW_XNULL EZXML_THROW( "The internal object is null. This probably means that XDoc has gone out of scope causing all of its children to be destroyed." );
diff --git a/Sourcecode/private/mx/pugixml/pugiconfig.hpp b/Sourcecode/private/extern/ezxml/src/private/private/pugiconfig.hpp
old mode 100755
new mode 100644
similarity index 92%
rename from Sourcecode/private/mx/pugixml/pugiconfig.hpp
rename to Sourcecode/private/extern/ezxml/src/private/private/pugiconfig.hpp
index e50b580bf..2fb691802
--- a/Sourcecode/private/mx/pugixml/pugiconfig.hpp
+++ b/Sourcecode/private/extern/ezxml/src/private/private/pugiconfig.hpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.7
+ * pugixml parser - version 1.10
* --------------------------------------------------------
- * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
+ * Copyright (C) 2006-2019, 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.
@@ -49,7 +49,7 @@
#endif
/**
- * Copyright (c) 2006-2015 Arseny Kapoulkine
+ * Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -62,7 +62,7 @@
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
diff --git a/Sourcecode/private/extern/ezxml/src/private/private/pugilicense.txt b/Sourcecode/private/extern/ezxml/src/private/private/pugilicense.txt
new file mode 100644
index 000000000..3af59b178
--- /dev/null
+++ b/Sourcecode/private/extern/ezxml/src/private/private/pugilicense.txt
@@ -0,0 +1,50 @@
+pugixml 1.10 - an XML processing library
+
+Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+Report bugs and download new versions at https://pugixml.org/
+
+This is the distribution of pugixml, which is a C++ XML processing library,
+which consists of a DOM-like interface with rich traversal/modification
+capabilities, an extremely fast XML parser which constructs the DOM tree from
+an XML file/buffer, and an XPath 1.0 implementation for complex data-driven
+tree queries. Full Unicode support is also available, with Unicode interface
+variants and conversions between different Unicode encodings (which happen
+automatically during parsing/saving).
+
+The distribution contains the following folders:
+
+ docs/ - documentation
+ docs/samples - pugixml usage examples
+ docs/quickstart.html - quick start guide
+ docs/manual.html - complete manual
+
+ scripts/ - project files for IDE/build systems
+
+ src/ - header and source files
+
+ readme.txt - this file.
+
+This library is distributed under the MIT License:
+
+Copyright (c) 2006-2019 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/Sourcecode/private/mx/pugixml/pugixml.cpp b/Sourcecode/private/extern/ezxml/src/private/private/pugixml.cpp
old mode 100755
new mode 100644
similarity index 87%
rename from Sourcecode/private/mx/pugixml/pugixml.cpp
rename to Sourcecode/private/extern/ezxml/src/private/private/pugixml.cpp
index aad62d05f..90c48b215
--- a/Sourcecode/private/mx/pugixml/pugixml.cpp
+++ b/Sourcecode/private/extern/ezxml/src/private/private/pugixml.cpp
@@ -1,8 +1,8 @@
/**
- * pugixml parser - version 1.7
+ * pugixml parser - version 1.10
* --------------------------------------------------------
- * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
+ * Copyright (C) 2006-2019, 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.
@@ -11,14 +11,10 @@
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wsign-conversion"
-
#ifndef SOURCE_PUGIXML_CPP
#define SOURCE_PUGIXML_CPP
-#include "mx/pugixml/pugixml.hpp"
+#include "pugixml.hpp"
#include
#include
@@ -33,9 +29,6 @@
#ifndef PUGIXML_NO_XPATH
# include
# include
-# ifdef PUGIXML_NO_EXCEPTIONS
-# include
-# endif
#endif
#ifndef PUGIXML_NO_STL
@@ -51,14 +44,17 @@
# 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
@@ -80,17 +76,21 @@
# 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)
#elif defined(__GNUC__)
# define PUGI__NO_INLINE __attribute__((noinline))
#else
-# define PUGI__NO_INLINE
+# define PUGI__NO_INLINE
#endif
// Branch weight controls
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(__c2__)
# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define PUGI__UNLIKELY(cond) (cond)
@@ -106,6 +106,17 @@
# define PUGI__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__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+# else
+# define PUGI__UNSIGNED_OVERFLOW
+# endif
+#else
+# define PUGI__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;
@@ -113,11 +124,28 @@ using std::memmove;
using std::memset;
#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
#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__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
+#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
+#else
+# define PUGI__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 } }
@@ -136,9 +164,7 @@ using std::memset;
#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
@@ -149,6 +175,8 @@ namespace pugi
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
}
+#else
+# include
#endif
// Memory allocation
@@ -210,7 +238,7 @@ PUGI__NS_BEGIN
for (size_t i = 0; i < count; ++i)
if (lhs[i] != rhs[i])
return false;
-
+
return lhs[count] == 0;
}
@@ -231,8 +259,10 @@ PUGI__NS_END
// auto_ptr-like object for exception recovery
PUGI__NS_BEGIN
- template struct auto_deleter
+ template struct auto_deleter
{
+ typedef void (*D)(T*);
+
T* data;
D deleter;
@@ -274,67 +304,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;
-
- for (size_t probe = 0; probe <= hashmod; ++probe)
- {
- item_t& probe_item = _items[bucket];
-
- if (probe_item.key == key)
- return &probe_item.value;
+ item_t* item = get_item(key);
+ assert(item);
+ assert(item->key == key || (item->key == 0 && item->value == 0));
- 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(_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;
}
@@ -351,9 +351,32 @@ PUGI__NS_BEGIN
size_t _count;
- bool rehash();
+ bool rehash(size_t count);
+
+ item_t* get_item(const void* 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;
- static unsigned int hash(const void* key)
+ // hash collision, quadratic probing
+ bucket = (bucket + probe + 1) & hashmod;
+ }
+
+ assert(false && "Hash table is full"); // unreachable
+ return 0;
+ }
+
+ static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
{
unsigned int h = static_cast(reinterpret_cast(key));
@@ -368,25 +391,29 @@ PUGI__NS_BEGIN
}
};
- PUGI__FN_NO_INLINE bool compact_hash_table::rehash()
+ PUGI__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);
@@ -398,43 +425,33 @@ PUGI__NS_END
#endif
PUGI__NS_BEGIN
- static const size_t xml_memory_page_size =
- #ifdef PUGIXML_MEMORY_PAGE_SIZE
- PUGIXML_MEMORY_PAGE_SIZE
- #else
- 32768
- #endif
- ;
-
#ifdef PUGIXML_COMPACT
static const uintptr_t xml_memory_block_alignment = 4;
-
- 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__GETHEADER_IMPL(object, page, flags) // unused
#define PUGI__GETPAGE_IMPL(header) (header).get_page()
#else
- #define PUGI__GETPAGE_IMPL(header) reinterpret_cast((header) & impl::xml_memory_page_pointer_mask)
+ #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags))
+ // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
+ #define PUGI__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8))))
#endif
#define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
- #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1)
+ #define PUGI__NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask)
struct xml_allocator;
@@ -474,6 +491,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
@@ -494,30 +519,21 @@ PUGI__NS_BEGIN
size_t size = sizeof(xml_memory_page) + data_size;
// allocate block with some alignment, leaving memory for worst-case padding
- void* memory = xml_memory::allocate(size + xml_memory_page_alignment);
+ void* memory = xml_memory::allocate(size);
if (!memory) return 0;
- // align to next page boundary (note: this guarantees at least 1 usable byte before the page)
- char* page_memory = reinterpret_cast((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1));
-
// prepare page structure
- xml_memory_page* page = xml_memory_page::construct(page_memory);
+ xml_memory_page* page = xml_memory_page::construct(memory);
assert(page);
page->allocator = _root->allocator;
- // record the offset for freeing the memory block
- assert(page_memory > memory && page_memory - static_cast(memory) <= 127);
- page_memory[-1] = static_cast(page_memory - static_cast(memory));
-
return page;
}
static void deallocate_page(xml_memory_page* page)
{
- char* page_memory = reinterpret_cast(page);
-
- xml_memory::deallocate(page_memory - page_memory[-1]);
+ xml_memory::deallocate(page);
}
void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
@@ -626,7 +642,7 @@ PUGI__NS_BEGIN
// 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);
@@ -784,12 +800,12 @@ PUGI__NS_BEGIN
template PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
{
- return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object));
+ return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object));
}
template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
{
- *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value;
+ compact_get_page(object, header_offset)->allocator->_hash->insert(object, value);
}
template class compact_pointer
@@ -836,7 +852,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);
@@ -914,7 +930,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);
@@ -1050,7 +1066,7 @@ 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);
}
@@ -1077,8 +1093,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__GETHEADER_IMPL(this, page, 0);
}
uintptr_t header;
@@ -1092,8 +1109,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__GETHEADER_IMPL(this, page, type);
}
uintptr_t header;
@@ -1124,9 +1142,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;
@@ -1846,18 +1861,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
@@ -1873,7 +1888,7 @@ 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))
#else
@@ -1896,12 +1911,71 @@ PUGI__NS_BEGIN
if (sizeof(wchar_t) == 2)
return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
- else
+ else
return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
}
- PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
+ PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)
{
+ #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }
+ #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; }
+
+ // check if we have a non-empty XML declaration
+ if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space)))
+ return false;
+
+ // scan XML declaration until the encoding field
+ for (size_t i = 6; i + 1 < size; ++i)
+ {
+ // declaration can not contain ? in quoted values
+ if (data[i] == '?')
+ return false;
+
+ if (data[i] == 'e' && data[i + 1] == 'n')
+ {
+ size_t offset = i;
+
+ // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed
+ PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o');
+ PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g');
+
+ // S? = S?
+ PUGI__SCANCHARTYPE(ct_space);
+ PUGI__SCANCHAR('=');
+ PUGI__SCANCHARTYPE(ct_space);
+
+ // the only two valid delimiters are ' and "
+ uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\'';
+
+ PUGI__SCANCHAR(delimiter);
+
+ size_t start = offset;
+
+ out_encoding = data + offset;
+
+ PUGI__SCANCHARTYPE(ct_symbol);
+
+ out_length = offset - start;
+
+ PUGI__SCANCHAR(delimiter);
+
+ return true;
+ }
+ }
+
+ return false;
+
+ #undef PUGI__SCANCHAR
+ #undef PUGI__SCANCHARTYPE
+ }
+
+ PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)
+ {
+ // skip encoding autodetection if input buffer is too small
+ if (size < 4) return encoding_utf8;
+
+ uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];
+
// look for BOM in first few bytes
if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;
if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;
@@ -1914,13 +1988,32 @@ PUGI__NS_BEGIN
if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;
if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;
if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;
- if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8;
// look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)
if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;
if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;
- // no known BOM detected, assume utf8
+ // no known BOM detected; parse declaration
+ const uint8_t* enc = 0;
+ size_t enc_length = 0;
+
+ if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length))
+ {
+ // iso-8859-1 (case-insensitive)
+ if (enc_length == 10
+ && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o'
+ && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9'
+ && enc[8] == '-' && enc[9] == '1')
+ return encoding_latin1;
+
+ // latin1 (case-insensitive)
+ if (enc_length == 6
+ && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't'
+ && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n'
+ && enc[5] == '1')
+ return encoding_latin1;
+ }
+
return encoding_utf8;
}
@@ -1938,15 +2031,10 @@ PUGI__NS_BEGIN
// only do autodetection if no explicit encoding is requested
if (encoding != encoding_auto) return encoding;
- // skip encoding autodetection if input buffer is too small
- if (size < 4) return encoding_utf8;
-
// try to guess encoding (based on XML specification, Appendix F.1)
const uint8_t* data = static_cast(contents);
- PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];
-
- return guess_buffer_encoding(d0, d1, d2, d3);
+ return guess_buffer_encoding(data, size);
}
PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
@@ -2079,7 +2167,7 @@ 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
@@ -2184,7 +2272,7 @@ 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
@@ -2200,12 +2288,12 @@ PUGI__NS_BEGIN
// 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)
{
@@ -2273,7 +2361,7 @@ PUGI__NS_BEGIN
xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
if (header & header_mask) alloc->deallocate_string(dest);
-
+
// mark the string as not allocated
dest = 0;
header &= ~header_mask;
@@ -2285,7 +2373,7 @@ 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
@@ -2304,7 +2392,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;
@@ -2317,11 +2405,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)
@@ -2332,14 +2420,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)
{
@@ -2354,7 +2442,7 @@ PUGI__NS_BEGIN
else return s;
}
};
-
+
PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
{
char_t* stre = s + 1;
@@ -2386,7 +2474,7 @@ PUGI__NS_BEGIN
ch = *++stre;
}
-
+
++stre;
}
else // ... (dec code)
@@ -2397,7 +2485,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;
@@ -2406,7 +2494,7 @@ PUGI__NS_BEGIN
ch = *++stre;
}
-
+
++stre;
}
@@ -2415,7 +2503,7 @@ PUGI__NS_BEGIN
#else
s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc));
#endif
-
+
g.push(s, stre - s);
return stre;
}
@@ -2430,7 +2518,7 @@ PUGI__NS_BEGIN
{
*s++ = '&';
++stre;
-
+
g.push(s, stre - s);
return stre;
}
@@ -2455,7 +2543,7 @@ PUGI__NS_BEGIN
{
*s++ = '>';
++stre;
-
+
g.push(s, stre - s);
return stre;
}
@@ -2468,7 +2556,7 @@ PUGI__NS_BEGIN
{
*s++ = '<';
++stre;
-
+
g.push(s, stre - s);
return stre;
}
@@ -2481,7 +2569,7 @@ PUGI__NS_BEGIN
{
*s++ = '"';
++stre;
-
+
g.push(s, stre - s);
return stre;
}
@@ -2491,7 +2579,7 @@ PUGI__NS_BEGIN
default:
break;
}
-
+
return stre;
}
@@ -2499,7 +2587,7 @@ PUGI__NS_BEGIN
#define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
#define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
#define PUGI__OPTSET(OPT) ( optmsk & (OPT) )
- #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
+ #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
#define PUGI__POPNODE() { cursor = cursor->parent; }
#define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }
#define PUGI__SCANWHILE(X) { while (X) ++s; }
@@ -2511,21 +2599,21 @@ PUGI__NS_BEGIN
PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
{
gap g;
-
+
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment));
-
+
if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
{
*s++ = '\n'; // replace first one with 0x0a
-
+
if (*s == '\n') g.push(s, 1);
}
else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
{
*g.flush(s) = 0;
-
+
return s + (s[2] == '>' ? 3 : 2);
}
else if (*s == 0)
@@ -2539,21 +2627,21 @@ PUGI__NS_BEGIN
PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
{
gap g;
-
+
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata));
-
+
if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
{
*s++ = '\n'; // replace first one with 0x0a
-
+
if (*s == '\n') g.push(s, 1);
}
else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
{
*g.flush(s) = 0;
-
+
return s + 1;
}
else if (*s == 0)
@@ -2563,9 +2651,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)
@@ -2587,13 +2675,13 @@ PUGI__NS_BEGIN
--end;
*end = 0;
-
+
return s + 1;
}
else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
{
*s++ = '\n'; // replace first one with 0x0a
-
+
if (*s == '\n') g.push(s, 1);
}
else if (opt_escape::value && *s == '&')
@@ -2616,12 +2704,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)
+ 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;
@@ -2631,12 +2719,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)
@@ -2647,35 +2735,35 @@ PUGI__NS_BEGIN
if (PUGI__IS_CHARTYPE(*s, ct_space))
{
char_t* str = s;
-
+
do ++str;
while (PUGI__IS_CHARTYPE(*str, ct_space));
-
+
g.push(s, str - s);
}
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
-
+
if (*s == end_quote)
{
char_t* str = g.flush(s);
-
+
do *str-- = 0;
while (PUGI__IS_CHARTYPE(*str, ct_space));
-
+
return s + 1;
}
else if (PUGI__IS_CHARTYPE(*s, ct_space))
{
*s++ = ' ';
-
+
if (PUGI__IS_CHARTYPE(*s, ct_space))
{
char_t* str = s + 1;
while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
-
+
g.push(s, str - s);
}
}
@@ -2698,11 +2786,11 @@ PUGI__NS_BEGIN
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws));
-
+
if (*s == end_quote)
{
*g.flush(s) = 0;
-
+
return s + 1;
}
else if (PUGI__IS_CHARTYPE(*s, ct_space))
@@ -2710,7 +2798,7 @@ PUGI__NS_BEGIN
if (*s == '\r')
{
*s++ = ' ';
-
+
if (*s == '\n') g.push(s, 1);
}
else *s++ = ' ';
@@ -2734,17 +2822,17 @@ PUGI__NS_BEGIN
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
-
+
if (*s == end_quote)
{
*g.flush(s) = 0;
-
+
return s + 1;
}
else if (*s == '\r')
{
*s++ = '\n';
-
+
if (*s == '\n') g.push(s, 1);
}
else if (opt_escape::value && *s == '&')
@@ -2766,11 +2854,11 @@ PUGI__NS_BEGIN
while (true)
{
PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
-
+
if (*s == end_quote)
{
*g.flush(s) = 0;
-
+
return s + 1;
}
else if (opt_escape::value && *s == '&')
@@ -2789,8 +2877,8 @@ PUGI__NS_BEGIN
PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
{
PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
-
- switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
+
+ 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;
@@ -2808,7 +2896,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
}
}
@@ -2823,18 +2911,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:
@@ -3161,7 +3243,7 @@ PUGI__NS_BEGIN
{
strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);
-
+
char_t ch = 0;
xml_node_struct* cursor = root;
char_t* mark = s;
@@ -3192,10 +3274,10 @@ PUGI__NS_BEGIN
while (true)
{
PUGI__SKIPWS(); // Eat any whitespace.
-
+
if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
{
- xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute.
+ xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.
if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
a->name = s; // Save the offset.
@@ -3210,7 +3292,7 @@ PUGI__NS_BEGIN
ch = *s;
++s;
}
-
+
if (ch == '=') // '<... #=...'
{
PUGI__SKIPWS(); // Eat any whitespace.
@@ -3222,7 +3304,7 @@ PUGI__NS_BEGIN
a->value = s; // Save the offset.
s = strconv_attribute(s, ch);
-
+
if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
// After this line the loop continues from the start;
@@ -3237,7 +3319,7 @@ PUGI__NS_BEGIN
else if (*s == '/')
{
++s;
-
+
if (*s == '>')
{
PUGI__POPNODE();
@@ -3278,7 +3360,7 @@ PUGI__NS_BEGIN
{
// we stepped over null terminator, backtrack & handle closing tag
--s;
-
+
if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
}
else PUGI__THROW_ERROR(status_bad_start_element, s);
@@ -3287,20 +3369,22 @@ PUGI__NS_BEGIN
{
++s;
+ mark = s;
+
char_t* name = cursor->name;
- if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);
-
+ if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+
while (PUGI__IS_CHARTYPE(*s, ct_symbol))
{
- if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
}
if (*name)
{
if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
- else PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ else PUGI__THROW_ERROR(status_end_element_mismatch, mark);
}
-
+
PUGI__POPNODE(); // Pop.
PUGI__SKIPWS();
@@ -3354,23 +3438,31 @@ PUGI__NS_BEGIN
if (!PUGI__OPTSET(parse_trim_pcdata))
s = mark;
-
+
if (cursor->parent || PUGI__OPTSET(parse_fragment))
{
- PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
- cursor->value = s; // Save the offset.
+ if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)
+ {
+ cursor->value = s; // Save the offset.
+ }
+ else
+ {
+ PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
+
+ cursor->value = s; // Save the offset.
+
+ PUGI__POPNODE(); // Pop since this is a standalone.
+ }
s = strconv_pcdata(s);
-
- PUGI__POPNODE(); // Pop since this is a standalone.
-
+
if (!*s) break;
}
else
{
PUGI__SCANFOR(*s == '<'); // '...<'
if (!*s) break;
-
+
++s;
}
@@ -3418,14 +3510,14 @@ PUGI__NS_BEGIN
// 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);
@@ -3515,7 +3607,7 @@ PUGI__NS_BEGIN
{
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;
}
@@ -3528,7 +3620,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());
@@ -3553,7 +3645,7 @@ 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
@@ -3592,7 +3684,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
@@ -3811,15 +3903,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__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));
-
+
writer.write_buffer(prev, static_cast(s - prev));
switch (*s)
@@ -3838,7 +3930,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
@@ -3846,7 +3948,8 @@ 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'), ';');
}
}
}
@@ -3857,7 +3960,7 @@ PUGI__NS_BEGIN
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)
@@ -3971,6 +4074,7 @@ 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)
{
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)
{
@@ -3986,12 +4090,12 @@ PUGI__NS_BEGIN
}
writer.write_string(a->name ? a->name + 0 : default_name);
- writer.write('=', '"');
+ writer.write('=', enquotation_char);
if (a->value)
text_output(writer, a->value, ctx_special_attr, flags);
- writer.write('"');
+ writer.write(enquotation_char);
}
}
@@ -4006,17 +4110,54 @@ 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;
+ }
}
}
@@ -4082,7 +4223,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(!"Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
}
}
@@ -4124,6 +4265,10 @@ PUGI__NS_BEGIN
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;
@@ -4290,6 +4435,7 @@ PUGI__NS_BEGIN
while (sit && sit != sn)
{
+ // 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));
@@ -4340,7 +4486,7 @@ PUGI__NS_BEGIN
}
// get value with conversion functions
- template U string_to_integer(const char_t* value, U minneg, U maxpos)
+ template PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
{
U result = 0;
const char_t* s = value;
@@ -4410,14 +4556,21 @@ PUGI__NS_BEGIN
}
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)
{
- return string_to_integer(value, 0 - static_cast(INT_MIN), INT_MAX);
+ return string_to_integer(value, static_cast(INT_MIN), INT_MAX);
}
PUGI__FN unsigned int get_value_uint(const char_t* value)
@@ -4455,7 +4608,7 @@ PUGI__NS_BEGIN
#ifdef PUGIXML_HAS_LONG_LONG
PUGI__FN long long get_value_llong(const char_t* value)
{
- return string_to_integer(value, 0 - static_cast(LLONG_MIN), LLONG_MAX);
+ return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX);
}
PUGI__FN unsigned long long get_value_ullong(const char_t* value)
@@ -4464,8 +4617,7 @@ PUGI__NS_BEGIN
}
#endif
- template
- PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
+ template PUGI__FN PUGI__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;
@@ -4502,31 +4654,21 @@ PUGI__NS_BEGIN
#endif
}
- template
- PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value)
+ template
+ PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
{
char_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)
+ PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float 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)
- {
- char buf[128];
- sprintf(buf, "%.9g", value);
+ char buf[128];
+ PUGI__SNPRINTF(buf, "%.9g", double(value));
return set_value_ascii(dest, header, header_mask, buf);
}
@@ -4535,38 +4677,16 @@ PUGI__NS_BEGIN
PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
{
char buf[128];
- sprintf(buf, "%.17g", value);
+ PUGI__SNPRINTF(buf, "%.17g", 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)
- {
- 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)
+ PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool 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);
+ return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
}
-#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)
{
@@ -4580,6 +4700,7 @@ PUGI__NS_BEGIN
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);
// delete original buffer if we performed a conversion
@@ -4628,7 +4749,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);
@@ -4641,7 +4762,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__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
@@ -4673,7 +4794,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
@@ -4694,6 +4815,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__FN void close_file(FILE* file)
+ {
+ fclose(file);
+ }
+
#ifndef PUGIXML_NO_STL
template struct xml_stream_chunk
{
@@ -4701,7 +4827,7 @@ PUGI__NS_BEGIN
{
void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
if (!memory) return 0;
-
+
return new (memory) xml_stream_chunk();
}
@@ -4811,7 +4937,7 @@ 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);
@@ -4839,7 +4965,7 @@ 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
@@ -4959,7 +5085,7 @@ namespace pugi
PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)
{
}
-
+
PUGI__FN xml_tree_walker::~xml_tree_walker()
{
}
@@ -5005,7 +5131,7 @@ namespace pugi
{
return (_attr == r._attr);
}
-
+
PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const
{
return (_attr != r._attr);
@@ -5015,17 +5141,17 @@ namespace pugi
{
return (_attr < r._attr);
}
-
+
PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const
{
return (_attr > r._attr);
}
-
+
PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const
{
return (_attr <= r._attr);
}
-
+
PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const
{
return (_attr >= r._attr);
@@ -5113,7 +5239,7 @@ namespace pugi
set_value(rhs);
return *this;
}
-
+
PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)
{
set_value(rhs);
@@ -5126,12 +5252,24 @@ namespace pugi
return *this;
}
+ PUGI__FN xml_attribute& xml_attribute::operator=(long rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)
{
set_value(rhs);
return *this;
}
-
+
PUGI__FN xml_attribute& xml_attribute::operator=(float rhs)
{
set_value(rhs);
@@ -5161,10 +5299,10 @@ namespace pugi
PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
{
if (!_attr) return false;
-
+
return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
}
-
+
PUGI__FN bool xml_attribute::set_value(const char_t* rhs)
{
if (!_attr) return false;
@@ -5176,14 +5314,28 @@ namespace pugi
{
if (!_attr) return false;
- return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
}
PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
{
if (!_attr) return false;
- return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(long rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(unsigned long rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
}
PUGI__FN bool xml_attribute::set_value(double rhs)
@@ -5192,7 +5344,7 @@ namespace pugi
return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
}
-
+
PUGI__FN bool xml_attribute::set_value(float rhs)
{
if (!_attr) return false;
@@ -5204,7 +5356,7 @@ namespace pugi
{
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_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
}
#ifdef PUGIXML_HAS_LONG_LONG
@@ -5212,14 +5364,14 @@ namespace pugi
{
if (!_attr) return false;
- return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
}
PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
{
if (!_attr) return false;
- return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
}
#endif
@@ -5242,7 +5394,7 @@ namespace pugi
PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)
{
}
-
+
PUGI__FN static void unspecified_bool_xml_node(xml_node***)
{
}
@@ -5266,7 +5418,7 @@ namespace pugi
{
return iterator(0, _root);
}
-
+
PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
{
return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
@@ -5276,7 +5428,7 @@ namespace pugi
{
return attribute_iterator(0, _root);
}
-
+
PUGI__FN xml_object_range xml_node::children() const
{
return xml_object_range(begin(), end());
@@ -5306,17 +5458,17 @@ namespace pugi
{
return (_root < r._root);
}
-
+
PUGI__FN bool xml_node::operator>(const xml_node& r) const
{
return (_root > r._root);
}
-
+
PUGI__FN bool xml_node::operator<=(const xml_node& r) const
{
return (_root <= r._root);
}
-
+
PUGI__FN bool xml_node::operator>=(const xml_node& r) const
{
return (_root >= r._root);
@@ -5326,7 +5478,7 @@ namespace pugi
{
return !_root;
}
-
+
PUGI__FN const char_t* xml_node::name() const
{
return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
@@ -5336,12 +5488,12 @@ namespace pugi
{
return _root ? PUGI__NODETYPE(_root) : node_null;
}
-
+
PUGI__FN const char_t* xml_node::value() const
{
return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
}
-
+
PUGI__FN xml_node xml_node::child(const char_t* name_) const
{
if (!_root) return xml_node();
@@ -5359,14 +5511,14 @@ namespace pugi
for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
if (i->name && impl::strequal(name_, i->name))
return xml_attribute(i);
-
+
return xml_attribute();
}
-
+
PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const
{
if (!_root) return xml_node();
-
+
for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
@@ -5381,7 +5533,7 @@ namespace pugi
PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const
{
if (!_root) return xml_node();
-
+
for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
@@ -5424,7 +5576,7 @@ namespace pugi
PUGI__FN xml_node xml_node::previous_sibling() const
{
if (!_root) return xml_node();
-
+
if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
else return xml_node();
}
@@ -5447,7 +5599,11 @@ namespace pugi
PUGI__FN const char_t* xml_node::child_value() const
{
if (!_root) return PUGIXML_TEXT("");
-
+
+ // element nodes can have value if parse_embed_pcdata was used
+ if (PUGI__NODETYPE(_root) == node_element && _root->value)
+ return _root->value;
+
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
if (impl::is_text_node(i) && i->value)
return i->value;
@@ -5489,7 +5645,7 @@ namespace pugi
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)
{
xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
@@ -5503,7 +5659,7 @@ namespace pugi
PUGI__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();
@@ -5513,14 +5669,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_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
-
+
impl::xml_allocator& alloc = impl::get_allocator(_root);
if (!alloc.reserve()) return xml_attribute();
@@ -5538,7 +5694,7 @@ namespace pugi
{
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();
@@ -5556,7 +5712,7 @@ namespace pugi
{
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();
@@ -5643,7 +5799,7 @@ namespace pugi
PUGI__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();
@@ -5663,12 +5819,12 @@ namespace pugi
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;
@@ -5681,7 +5837,7 @@ namespace pugi
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();
@@ -5699,7 +5855,7 @@ namespace pugi
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();
@@ -5931,14 +6087,20 @@ namespace pugi
// disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense
doc->header |= impl::xml_memory_page_contents_shared_mask;
-
+
// get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)
impl::xml_memory_page* page = 0;
- impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));
+ 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;
@@ -5953,7 +6115,7 @@ namespace pugi
PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
{
if (!_root) return xml_node();
-
+
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
if (i->name && impl::strequal(name_, i->name))
{
@@ -5968,7 +6130,7 @@ namespace pugi
PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
{
if (!_root) return xml_node();
-
+
for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
@@ -5998,7 +6160,7 @@ namespace pugi
if (j != _root)
result[--offset] = delimiter;
- if (j->name && *j->name)
+ if (j->name)
{
size_t length = impl::strlength(j->name);
@@ -6017,7 +6179,7 @@ namespace pugi
{
xml_node found = *this; // Current search context.
- if (!_root || !path_ || !path_[0]) return found;
+ if (!_root || !path_[0]) return found;
if (path_[0] == delimiter)
{
@@ -6063,48 +6225,47 @@ namespace pugi
PUGI__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);
}
@@ -6171,6 +6332,7 @@ 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;
}
}
@@ -6195,6 +6357,10 @@ namespace pugi
{
if (!_root || impl::is_text_node(_root)) return _root;
+ // element nodes can have value if parse_embed_pcdata was used
+ if (PUGI__NODETYPE(_root) == node_element && _root->value)
+ return _root;
+
for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)
if (impl::is_text_node(node))
return node;
@@ -6309,14 +6475,28 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
}
PUGI__FN bool xml_text::set(unsigned 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(long rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
+ }
+
+ PUGI__FN bool xml_text::set(unsigned long rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
}
PUGI__FN bool xml_text::set(float rhs)
@@ -6337,7 +6517,7 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
}
#ifdef PUGIXML_HAS_LONG_LONG
@@ -6345,14 +6525,14 @@ namespace pugi
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
}
PUGI__FN bool xml_text::set(unsigned long long rhs)
{
xml_node_struct* dn = _data_new();
- return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
}
#endif
@@ -6374,6 +6554,18 @@ namespace pugi
return *this;
}
+ PUGI__FN xml_text& xml_text::operator=(long rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(unsigned long rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
PUGI__FN xml_text& xml_text::operator=(double rhs)
{
set(rhs);
@@ -6439,7 +6631,7 @@ namespace pugi
{
return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
}
-
+
PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
{
return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
@@ -6454,7 +6646,7 @@ namespace pugi
PUGI__FN xml_node* xml_node_iterator::operator->() const
{
assert(_wrap._root);
- return const_cast(&_wrap); // BCC32 workaround
+ return const_cast(&_wrap); // BCC5 workaround
}
PUGI__FN const xml_node_iterator& xml_node_iterator::operator++()
@@ -6500,7 +6692,7 @@ namespace pugi
{
return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;
}
-
+
PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
{
return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;
@@ -6515,7 +6707,7 @@ namespace pugi
PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const
{
assert(_wrap._attr);
- return const_cast(&_wrap); // BCC32 workaround
+ return const_cast(&_wrap); // BCC5 workaround
}
PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++()
@@ -6576,7 +6768,7 @@ namespace pugi
PUGI__FN xml_node* xml_named_node_iterator::operator->() const
{
assert(_wrap._root);
- return const_cast(&_wrap); // BCC32 workaround
+ return const_cast(&_wrap); // BCC5 workaround
}
PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
@@ -6657,18 +6849,37 @@ namespace pugi
PUGI__FN xml_document::xml_document(): _buffer(0)
{
- create();
+ _create();
}
PUGI__FN xml_document::~xml_document()
{
- destroy();
+ _destroy();
+ }
+
+#ifdef PUGIXML_HAS_MOVE
+ PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)
+ {
+ _create();
+ _move(rhs);
+ }
+
+ PUGI__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__FN void xml_document::reset()
{
- destroy();
- create();
+ _destroy();
+ _create();
}
PUGI__FN void xml_document::reset(const xml_document& proto)
@@ -6679,24 +6890,22 @@ namespace pugi
append_copy(cur);
}
- PUGI__FN void xml_document::create()
+ PUGI__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__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;
@@ -6715,11 +6924,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__FN void xml_document::_destroy()
{
assert(_root);
@@ -6758,6 +6972,113 @@ namespace pugi
_root = 0;
}
+#ifdef PUGIXML_HAS_MOVE
+ PUGI__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
+ 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__GETPAGE(doc);
+ assert(doc_page && !doc_page->prev && !doc_page->next);
+
+ impl::xml_memory_page* other_page = PUGI__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__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)
{
@@ -6796,7 +7117,7 @@ namespace pugi
reset();
using impl::auto_deleter; // MSVC7 workaround
- auto_deleter file(fopen(path_, "rb"), fclose);
+ auto_deleter file(fopen(path_, "rb"), impl::close_file);
return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer);
}
@@ -6806,7 +7127,7 @@ namespace pugi
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);
}
@@ -6879,7 +7200,7 @@ namespace pugi
PUGI__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(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
return impl::save_file_impl(*this, file.data, indent, flags, encoding);
}
@@ -6887,7 +7208,7 @@ namespace pugi
PUGI__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);
}
@@ -6915,14 +7236,14 @@ namespace pugi
{
return impl::as_utf8_impl(str.c_str(), str.size());
}
-
+
PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str)
{
assert(str);
return impl::as_wide_impl(str, strlen(str));
}
-
+
PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str)
{
return impl::as_wide_impl(str.c_str(), str.size());
@@ -7023,14 +7344,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__FN I min_element(I begin, I end, const Pred& pred)
{
I result = begin;
@@ -7041,20 +7362,23 @@ PUGI__NS_BEGIN
return result;
}
- template void reverse(I begin, I end)
+ template PUGI__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__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)
@@ -7069,134 +7393,79 @@ PUGI__NS_BEGIN
return write + 1;
}
- template void copy_backwards(I begin, I end, I target)
+ template PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred)
{
- while (begin != end) *--target = *--end;
- }
-
- template void insertion_sort(I begin, I end, const Pred& pred, T*)
- {
- assert(begin != end);
+ if (begin == end)
+ return;
- 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--;
}
- else
- {
- I hole = it;
-
- // move hole backwards
- while (pred(val, *(hole - 1)))
- {
- *hole = *(hole - 1);
- hole--;
- }
- // fill hole with element
- *hole = val;
- }
+ // fill hole with element
+ *hole = val;
}
}
- // std variant for elements with ==
- template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend)
+ template inline I median3(I first, I middle, I last, const Pred& pred)
{
- 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;
- }
+ if (pred(*middle, *first))
+ swap(middle, first);
+ if (pred(*last, *middle))
+ swap(last, middle);
+ if (pred(*middle, *first))
+ swap(middle, first);
- // 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);
- }
+ return middle;
}
- template void median3(I first, I middle, I last, const Pred& pred)
+ template PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
{
- if (pred(*middle, *first)) swap(*middle, *first);
- if (pred(*last, *middle)) swap(*last, *middle);
- if (pred(*middle, *first)) swap(*middle, *first);
- }
+ // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
+ T* eq = begin;
+ T* lt = begin;
+ T* gt = end;
- template void median(I first, I middle, I last, const Pred& pred)
- {
- if (last - first <= 40)
+ while (lt < gt)
{
- // median of three for small chunks
- median3(first, middle, last, pred);
+ if (pred(*lt, pivot))
+ lt++;
+ else if (*lt == pivot)
+ swap(*eq++, *lt++);
+ else
+ swap(*lt, *--gt);
}
- 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);
- }
+ // 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;
}
- template void sort(I begin, I end, const Pred& pred)
+ template PUGI__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)
@@ -7212,52 +7481,80 @@ PUGI__NS_BEGIN
}
// insertion sort small chunk
- if (begin != end) insertion_sort(begin, end, pred, &*begin);
+ insertion_sort(begin, end, pred);
}
-PUGI__NS_END
-// Allocator used for AST and evaluation stacks
-PUGI__NS_BEGIN
- static const size_t xpath_memory_page_size =
- #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
- PUGIXML_MEMORY_XPATH_PAGE_SIZE
- #else
- 4096
- #endif
- ;
+ PUGI__FN bool hash_insert(const void** table, size_t size, const void* key)
+ {
+ assert(key);
- static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
+ unsigned int h = static_cast(reinterpret_cast(key));
- struct xpath_memory_block
- {
- xpath_memory_block* next;
- size_t capacity;
+ // MurmurHash3 32-bit finalizer
+ h ^= h >> 16;
+ h *= 0x85ebca6bu;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35u;
+ h ^= h >> 16;
- union
+ 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__NS_END
+
+// Allocator used for AST and evaluation stacks
+PUGI__NS_BEGIN
+ static const size_t xpath_memory_page_size =
+ #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
+ PUGIXML_MEMORY_XPATH_PAGE_SIZE
+ #else
+ 4096
+ #endif
+ ;
+
+ static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
+
+ struct xpath_memory_block
+ {
+ xpath_memory_block* next;
+ size_t capacity;
+
+ union
{
char data[xpath_memory_page_size];
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);
@@ -7278,33 +7575,20 @@ 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 block->data;
}
-
- return result;
}
void* reallocate(void* ptr, size_t old_size, size_t new_size)
@@ -7316,33 +7600,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);
-
- if (ptr) _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;
+ }
- // 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;
}
@@ -7414,22 +7700,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()
@@ -7451,7 +7730,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;
@@ -7480,9 +7759,13 @@ PUGI__NS_BEGIN
{
assert(begin <= end);
+ if (begin == end)
+ return xpath_string();
+
size_t length = static_cast(end - begin);
+ const char_t* data = duplicate_string(begin, length, alloc);
- return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length);
+ return data ? xpath_string(data, true, length) : xpath_string();
}
xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0)
@@ -7508,7 +7791,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));
@@ -7533,15 +7816,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_;
}
@@ -7623,14 +7909,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)
@@ -7648,16 +7938,16 @@ PUGI__NS_BEGIN
if (cur != n) cur = cur.next_sibling();
}
}
-
+
return result;
}
-
+
default:
return xpath_string();
}
}
}
-
+
PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
{
assert(ln->parent == rn->parent);
@@ -7681,7 +7971,7 @@ PUGI__NS_BEGIN
// if rn sibling chain ended ln must be before rn
return !rs;
}
-
+
PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)
{
// find common ancestor at the same depth, if any
@@ -7732,9 +8022,9 @@ PUGI__NS_BEGIN
return parent && node == parent;
}
- PUGI__FN const void* document_buffer_order(const xpath_node& XElement)
+ PUGI__FN const void* document_buffer_order(const xpath_node& xnode)
{
- xml_node_struct* node = XElement.node().internal_object();
+ xml_node_struct* node = xnode.node().internal_object();
if (node)
{
@@ -7747,7 +8037,7 @@ PUGI__NS_BEGIN
return 0;
}
- xml_attribute_struct* attr = XElement.attribute().internal_object();
+ xml_attribute_struct* attr = xnode.attribute().internal_object();
if (attr)
{
@@ -7762,7 +8052,7 @@ PUGI__NS_BEGIN
return 0;
}
-
+
struct document_order_comparator
{
bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
@@ -7786,10 +8076,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();
@@ -7798,47 +8088,40 @@ 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()
{
#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
- union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1];
- u[0].i = 0x7fc00000;
- return u[0].f;
+ PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
+ typedef uint32_t UI; // BCC5 workaround
+ union { float f; UI i; } u;
+ u.i = 0x7fc00000;
+ return double(u.f);
#else
// fallback
const volatile double zero = 0.0;
return zero / zero;
#endif
}
-
+
PUGI__FN bool is_nan(double value)
{
#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
@@ -7851,7 +8134,7 @@ PUGI__NS_BEGIN
return v != v;
#endif
}
-
+
PUGI__FN const char_t* convert_number_to_string_special(double value)
{
#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
@@ -7883,12 +8166,12 @@ PUGI__NS_BEGIN
return 0;
#endif
}
-
+
PUGI__FN bool convert_number_to_boolean(double value)
{
return (value != 0 && !is_nan(value));
}
-
+
PUGI__FN void truncate_zeros(char* begin, char* end)
{
while (begin != end && end[-1] == '0') end--;
@@ -7898,11 +8181,11 @@ 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)
+ PUGI__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));
@@ -7912,12 +8195,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__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__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
// get the exponent (possibly negative)
char* exponent_string = strchr(buffer, 'e');
@@ -7954,12 +8235,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;
@@ -7976,7 +8257,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--;
}
@@ -8009,7 +8290,7 @@ PUGI__NS_BEGIN
return xpath_string::from_heap_preallocated(result, s);
}
-
+
PUGI__FN bool check_string_to_number_format(const char_t* string)
{
// parse leading whitespace
@@ -8076,7 +8357,7 @@ PUGI__NS_BEGIN
return true;
}
-
+
PUGI__FN double round_nearest(double value)
{
return floor(value + 0.5);
@@ -8088,17 +8369,17 @@ PUGI__NS_BEGIN
// ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)
return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);
}
-
+
PUGI__FN const char_t* qualified_name(const xpath_node& node)
{
return node.attribute() ? node.attribute().name() : node.node().name();
}
-
+
PUGI__FN const char_t* local_name(const xpath_node& node)
{
const char_t* name = qualified_name(node);
const char_t* p = find_char(name, ':');
-
+
return p ? p + 1 : name;
}
@@ -8128,39 +8409,39 @@ PUGI__NS_BEGIN
PUGI__FN const char_t* namespace_uri(xml_node node)
{
namespace_uri_predicate pred = node.name();
-
+
xml_node p = node;
-
+
while (p)
{
xml_attribute a = p.find_attribute(pred);
-
+
if (a) return a.value();
-
+
p = p.parent();
}
-
+
return PUGIXML_TEXT("");
}
PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
{
namespace_uri_predicate pred = attr.name();
-
+
// Default namespace does not apply to attributes
if (!pred.prefix) return PUGIXML_TEXT("");
-
+
xml_node p = parent;
-
+
while (p)
{
xml_attribute a = p.find_attribute(pred);
-
+
if (a) return a.value();
-
+
p = p.parent();
}
-
+
return PUGIXML_TEXT("");
}
@@ -8243,12 +8524,10 @@ 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);
}
@@ -8335,7 +8614,7 @@ PUGI__NS_BEGIN
static const xpath_node_set dummy_node_set;
- PUGI__FN unsigned int hash_string(const char_t* str)
+ PUGI__FN PUGI__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;
@@ -8346,11 +8625,11 @@ PUGI__NS_BEGIN
result += result << 10;
result ^= result >> 6;
}
-
+
result += result << 3;
result ^= result >> 11;
result += result << 15;
-
+
return result;
}
@@ -8418,7 +8697,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(!"Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
}
}
@@ -8439,7 +8718,7 @@ PUGI__NS_BEGIN
return lhs->set(static_cast(rhs)->value);
default:
- assert(!"Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return false;
}
}
@@ -8504,9 +8783,9 @@ PUGI__NS_BEGIN
else
type = sorted;
}
-
+
if (type != order) reverse(begin, end);
-
+
return order;
}
@@ -8526,7 +8805,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();
}
}
@@ -8591,7 +8870,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;
@@ -8615,12 +8894,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
@@ -8643,7 +8952,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;
@@ -8727,12 +9036,12 @@ PUGI__NS_BEGIN
{
next();
}
-
+
const char_t* state() const
{
return _cur;
}
-
+
void next()
{
const char_t* cur = _cur;
@@ -8747,7 +9056,7 @@ PUGI__NS_BEGIN
case 0:
_cur_lexeme = lex_eof;
break;
-
+
case '>':
if (*(cur+1) == '=')
{
@@ -8791,7 +9100,7 @@ PUGI__NS_BEGIN
_cur_lexeme = lex_equal;
break;
-
+
case '+':
cur += 1;
_cur_lexeme = lex_plus;
@@ -8815,7 +9124,7 @@ PUGI__NS_BEGIN
_cur_lexeme = lex_union;
break;
-
+
case '$':
cur += 1;
@@ -8833,7 +9142,7 @@ PUGI__NS_BEGIN
}
_cur_lexeme_contents.end = cur;
-
+
_cur_lexeme = lex_var_ref;
}
else
@@ -8854,7 +9163,7 @@ PUGI__NS_BEGIN
_cur_lexeme = lex_close_brace;
break;
-
+
case '[':
cur += 1;
_cur_lexeme = lex_open_square_brace;
@@ -8885,7 +9194,7 @@ PUGI__NS_BEGIN
_cur_lexeme = lex_slash;
}
break;
-
+
case '.':
if (*(cur+1) == '.')
{
@@ -8901,7 +9210,7 @@ PUGI__NS_BEGIN
while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
_cur_lexeme_contents.end = cur;
-
+
_cur_lexeme = lex_number;
}
else
@@ -8927,7 +9236,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
@@ -8957,7 +9266,7 @@ PUGI__NS_BEGIN
_cur_lexeme_contents.begin = cur;
while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
-
+
if (*cur == '.')
{
cur++;
@@ -8977,11 +9286,11 @@ PUGI__NS_BEGIN
if (cur[0] == ':')
{
- if (cur[1] == '*') // namespace coreest ncname:*
+ if (cur[1] == '*') // namespace test ncname:*
{
cur += 2; // :*
}
- else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace coreest qname
+ else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
{
cur++; // :
@@ -8990,7 +9299,7 @@ PUGI__NS_BEGIN
}
_cur_lexeme_contents.end = cur;
-
+
_cur_lexeme = lex_string;
}
else
@@ -9101,7 +9410,7 @@ PUGI__NS_BEGIN
axis_preceding_sibling,
axis_self
};
-
+
enum nodetest_t
{
nodetest_none,
@@ -9136,7 +9445,7 @@ PUGI__NS_BEGIN
};
template const axis_t axis_to_type::axis = N;
-
+
class xpath_ast_node
{
private:
@@ -9256,7 +9565,7 @@ PUGI__NS_BEGIN
}
}
- assert(!"Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
@@ -9331,7 +9640,7 @@ PUGI__NS_BEGIN
}
else
{
- assert(!"Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
}
@@ -9455,7 +9764,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_type_node:
case nodetest_all:
if (is_xpath_attribute(name))
@@ -9464,7 +9773,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_all_in_namespace:
if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
{
@@ -9472,14 +9781,14 @@ 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);
@@ -9495,11 +9804,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)
{
@@ -9507,7 +9816,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_type_text:
if (type == node_pcdata || type == node_cdata)
{
@@ -9515,7 +9824,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_type_pi:
if (type == node_pi)
{
@@ -9523,7 +9832,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_pi:
if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
{
@@ -9531,7 +9840,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_all:
if (type == node_element)
{
@@ -9539,7 +9848,7 @@ PUGI__NS_BEGIN
return true;
}
break;
-
+
case nodetest_all_in_namespace:
if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
{
@@ -9549,7 +9858,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(!"Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
}
return false;
@@ -9566,33 +9875,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
@@ -9603,32 +9912,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;
@@ -9707,7 +10016,7 @@ PUGI__NS_BEGIN
break;
}
-
+
case axis_ancestor:
case axis_ancestor_or_self:
{
@@ -9716,15 +10025,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;
}
@@ -9742,12 +10051,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;
@@ -9762,15 +10071,15 @@ PUGI__NS_BEGIN
return;
xml_node_struct* cur = p;
-
+
while (cur)
{
if (step_push(ns, cur, alloc) & once)
return;
-
+
cur = cur->parent;
}
-
+
break;
}
@@ -9786,7 +10095,7 @@ PUGI__NS_BEGIN
case axis_following:
{
xml_node_struct* cur = p;
-
+
while (cur)
{
if (cur->first_child)
@@ -9823,9 +10132,9 @@ PUGI__NS_BEGIN
step_fill(ns, p, alloc, once, v);
break;
}
-
+
default:
- assert(!"Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -9849,6 +10158,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;
@@ -9867,7 +10177,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);
}
@@ -9881,11 +10191,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