diff --git a/.github/workflows/analysis-sonarcloud.yml b/.github/workflows/analysis-sonarcloud.yml
index cc987942..4b37386d 100644
--- a/.github/workflows/analysis-sonarcloud.yml
+++ b/.github/workflows/analysis-sonarcloud.yml
@@ -4,7 +4,21 @@ name: Analysis - SonarCloud
on:
pull_request_target:
types: [opened, synchronize, reopened]
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
push:
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
branches:
- main
@@ -20,6 +34,13 @@ jobs:
runs-on: ubuntu-22.04
steps:
+ - name: Cancel Previous Runs
+ if: github.ref != 'refs/heads/main'
+ uses: fkirc/skip-duplicate-actions@master
+ with:
+ concurrent_skipping: 'same_content'
+ cancel_others: true
+
- uses: actions/checkout@v3
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
with:
@@ -36,12 +57,6 @@ jobs:
run: >
sudo apt-get update && sudo apt-get install ccache linux-headers-$(uname -r)
- - name: Switch to gcc-11
- run: |
- sudo apt install gcc-11 g++-11
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11
- sudo update-alternatives --set gcc /usr/bin/gcc-11
-
- name: CCache
id: ccache
uses: actions/cache@main
@@ -64,23 +79,24 @@ jobs:
run: |
vcpkgCommitId=$(grep '.builtin-baseline' vcpkg.json | awk -F: '{print $2}' | tr -d '," ')
echo "vcpkg commit ID: $vcpkgCommitId"
- echo "::set-output name=vcpkgGitCommitId::$vcpkgCommitId"
+ echo "VCPKG_GIT_COMMIT_ID=$vcpkgCommitId" >> $GITHUB_ENV
+
- name: Get vcpkg commit id from vcpkg.json
uses: lukka/run-vcpkg@main
with:
vcpkgGitURL: "https://github.com/microsoft/vcpkg.git"
- vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
+ vcpkgGitCommitId: ${{ env.VCPKG_GIT_COMMIT_ID }}
- name: Install additional libraries
run: sudo apt-get install libasio-dev nlohmann-json3-dev libfmt-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev mesa-common-dev libxrandr-dev libxxf86vm-dev libglib2.0-dev at-spi2-core libwxgtk3.0-gtk3-dev libarchive-dev freeglut3-dev libxmu-dev libdbus-1-dev libxtst-dev
- name: Install sonar-scanner
- uses: SonarSource/sonarcloud-github-c-cpp@v1
+ uses: SonarSource/sonarcloud-github-c-cpp@v2
- name: Generate compilation database
run: |
mkdir -p build
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DOPTIONS_ENABLE_CCACHE=ON -S . -B build
+ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DOPTIONS_ENABLE_CCACHE=ON -DSPEED_UP_BUILD_UNITY=OFF -S . -B build
- name: Run PR sonar-scanner
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index 22edbd42..924114f8 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -5,7 +5,21 @@ on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
push:
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
branches:
- main
@@ -22,25 +36,26 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-22.04]
- buildtype: [Release, Debug]
+ os: [ ubuntu-22.04 ]
+ buildtype: [ linux-release ]
include:
- os: ubuntu-22.04
triplet: x64-linux
steps:
+ - name: Cancel Previous Runs
+ if: github.ref != 'refs/heads/main'
+ uses: fkirc/skip-duplicate-actions@master
+ with:
+ concurrent_skipping: 'same_content'
+ cancel_others: true
+
- name: Checkout repository
uses: actions/checkout@main
- name: Install Linux Dependencies
run: >
sudo apt-get update && sudo apt-get install ccache linux-headers-$(uname -r)
-
- - name: Switch to gcc-11
- run: |
- sudo apt install gcc-11 g++-11
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11
- sudo update-alternatives --set gcc /usr/bin/gcc-11
- name: CCache
uses: hendrikmuhs/ccache-action@main
@@ -55,12 +70,13 @@ jobs:
run: |
vcpkgCommitId=$(grep '.builtin-baseline' vcpkg.json | awk -F: '{print $2}' | tr -d '," ')
echo "vcpkg commit ID: $vcpkgCommitId"
- echo "::set-output name=vcpkgGitCommitId::$vcpkgCommitId"
+ echo "VCPKG_GIT_COMMIT_ID=$vcpkgCommitId" >> $GITHUB_ENV
+
- name: Get vcpkg commit id from vcpkg.json
uses: lukka/run-vcpkg@main
with:
vcpkgGitURL: "https://github.com/microsoft/vcpkg.git"
- vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
+ vcpkgGitCommitId: ${{ env.VCPKG_GIT_COMMIT_ID }}
- name: Get latest CMake and ninja
uses: lukka/get-cmake@main
@@ -69,15 +85,13 @@ jobs:
run: sudo apt-get install libasio-dev nlohmann-json3-dev libfmt-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev mesa-common-dev libxrandr-dev libxxf86vm-dev libglib2.0-dev at-spi2-core libwxgtk3.0-gtk3-dev libarchive-dev freeglut3-dev libxmu-dev libdbus-1-dev libxtst-dev
- name: Run CMake Cache and Build
- uses: lukka/run-cmake@v3
+ uses: lukka/run-cmake@main
with:
- cmakeListsTxtPath: ${{ github.workspace }}/CMakeLists.txt
- useVcpkgToolchainFile: true
- buildDirectory: ${{ github.workspace }}/build/
- cmakeBuildType: ${{ matrix.buildtype }}
+ configurePreset: ${{ matrix.buildtype }}
+ buildPreset: ${{ matrix.buildtype }}
- name: Upload artifacts
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@main
with:
name: ${{ matrix.os }}-${{ matrix.buildtype }}
path: |
diff --git a/.github/workflows/build-windows-cmake.yml b/.github/workflows/build-windows-cmake.yml
new file mode 100644
index 00000000..76441bff
--- /dev/null
+++ b/.github/workflows/build-windows-cmake.yml
@@ -0,0 +1,91 @@
+---
+name: Build - Windows - CMake
+
+on:
+ workflow_dispatch:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
+ push:
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
+ branches:
+ - main
+
+env:
+ CMAKE_BUILD_PARALLEL_LEVEL: 2
+ MAKEFLAGS: '-j 2'
+jobs:
+ job:
+ if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
+ name: ${{ matrix.os }}-${{ matrix.buildtype }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [windows-2022]
+ buildtype: [ windows-release ]
+ include:
+ - os: windows-2022
+ triplet: x64-windows-static
+ packages: >
+ sccache
+ steps:
+ - name: Cancel Previous Runs
+ if: github.ref != 'refs/heads/main'
+ uses: fkirc/skip-duplicate-actions@master
+ with:
+ concurrent_skipping: 'same_content'
+ cancel_others: true
+
+ - name: Checkout repository
+ uses: actions/checkout@main
+
+ - name: CCache
+ uses: hendrikmuhs/ccache-action@main
+ with:
+ max-size: "1G"
+ variant: "sccache"
+ key: ccache-${{ matrix.os }}-${{ matrix.buildtype }}
+ restore-keys: |
+ ccache-${{ matrix.os }}
+
+ - name: Restore artifacts and install vcpkg
+ id: vcpkg-step
+ run: |
+ $json=Get-Content vcpkg.json -Raw | ConvertFrom-Json
+ $vcpkgCommitId=$json.'builtin-baseline'
+ Write-Host "vcpkg commit ID: $vcpkgCommitId"
+ echo "VCPKG_GIT_COMMIT_ID=$vcpkgCommitId" | Out-File -FilePath $env:GITHUB_ENV -Append
+
+ - name: Get vcpkg commit id from vcpkg.json
+ uses: lukka/run-vcpkg@main
+ with:
+ vcpkgGitURL: "https://github.com/microsoft/vcpkg.git"
+ vcpkgGitCommitId: ${{ env.VCPKG_GIT_COMMIT_ID }}
+
+ - name: Get latest CMake and ninja
+ uses: lukka/get-cmake@main
+
+ - name: Run CMake
+ uses: lukka/run-cmake@main
+ with:
+ configurePreset: ${{ matrix.buildtype }}
+ buildPreset: ${{ matrix.buildtype }}
+
+ - name: Create and Upload Artifact
+ uses: actions/upload-artifact@main
+ with:
+ name: canary-${{ matrix.buildtype }}-${{ github.sha }}
+ path: |
+ ${{ github.workspace }}/*.exe
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows-solution.yml
similarity index 65%
rename from .github/workflows/build-windows.yml
rename to .github/workflows/build-windows-solution.yml
index 71aaed43..b5c716cf 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows-solution.yml
@@ -1,14 +1,29 @@
---
-name: Build - Windows
+name: Build - Windows - Solution
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
+ - 'vcproj/**'
push:
+ paths:
+ - 'src/**'
+ - 'source/**'
+ - 'cmake/**'
+ - 'CMakeLists.txt'
+ - 'CMakePresets.json'
+ - 'vcpkg.json'
+ - 'vcproj/**'
branches:
- main
- - master
jobs:
job:
@@ -25,11 +40,18 @@ jobs:
packages: >
sccache
steps:
+ - name: Cancel Previous Runs
+ if: github.ref != 'refs/heads/main'
+ uses: fkirc/skip-duplicate-actions@master
+ with:
+ concurrent_skipping: 'same_content'
+ cancel_others: true
+
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.1
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@main
- name: Install vcpkg
run: |
@@ -42,7 +64,7 @@ jobs:
run: msbuild.exe /p:VcpkgEnableManifest=true /p:Configuration=Release /p:Platform=x64 /p:VcpkgRoot=$env:GITHUB_WORKSPACE/vcpkg vcproj/RME.sln
- name: Upload artifacts
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@main
with:
name: ${{ matrix.os }}-${{ matrix.buildtype }}
path: |
diff --git a/.gitignore b/.gitignore
index 98ca7bbc..46f6ceff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,6 @@ cmake-build-debug/
*.exe
*.pdb
*.rar
+vcpkg_installed/
+*.ilk
+remeres.exe.manifest
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9dd9c90e..24a0d977 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.22)
project(cmake)
@@ -25,12 +25,22 @@ set(CMAKE_CXX_STANDARD 20)
set(GNUCXX_MINIMUM_VERSION 11)
set(MSVC_MINIMUM_VERSION "19.32")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
+
+# Make will print more details
+set(CMAKE_VERBOSE_MAKEFILE OFF)
+
+# Generate compile_commands.json
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# *****************************************************************************
# Options
# *****************************************************************************
option(OPTIONS_ENABLE_CCACHE "Enable ccache" OFF)
option(OPTIONS_ENABLE_SCCACHE "Use sccache to speed up compilation process" OFF)
+option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON)
# *****************************************************************************
# Set Sanity Check
@@ -90,6 +100,22 @@ if(OPTIONS_ENABLE_SCCACHE)
endif()
endif()
+# === IPO ===
+option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON)
+if(OPTIONS_ENABLE_IPO)
+ log_option_enabled("ipo")
+
+ include(CheckIPOSupported)
+ check_ipo_supported(RESULT result OUTPUT output)
+ if(result)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+ else()
+ log_war("IPO is not supported: ${output}")
+ endif()
+else()
+ log_option_disabled("ipo")
+endif()
+
# *****************************************************************************
# Add source project
# *****************************************************************************
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 00000000..d7894fef
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,102 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 22,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "windows-release",
+ "displayName": "Windows - Release",
+ "description": "Sets Ninja generator, compilers, build and install directory and set build type as release",
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": {
+ "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
+ "type": "FILEPATH"
+ },
+ "VCPKG_TARGET_TRIPLET": "x64-windows-static",
+ "CMAKE_BUILD_TYPE": "Release",
+ "OPTIONS_ENABLE_SCCACHE": "ON"
+ },
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ },
+ "vendor": {
+ "microsoft.com/VisualStudioSettings/CMake/1.0": {
+ "hostOS": [ "Windows" ]
+ }
+ },
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ },
+ {
+ "name": "linux-release",
+ "displayName": "Linux - Release",
+ "description": "Sets Ninja generator, compilers, build and install directory and set build type as release",
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": {
+ "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
+ "type": "FILEPATH"
+ },
+ "CMAKE_BUILD_TYPE": "Release",
+ "OPTIONS_ENABLE_CCACHE": "ON"
+ },
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ }
+ },
+ {
+ "name": "windows-debug",
+ "inherits": "windows-release",
+ "displayName": "Windows - Debug",
+ "description": "Build Debug Mode",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "DEBUG_LOG": "ON"
+ },
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ }
+ },
+ {
+ "name": "linux-debug",
+ "inherits": "linux-release",
+ "displayName": "Linux - Debug Build",
+ "description": "Build Debug Mode",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "DEBUG_LOG": "ON"
+ }
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "linux-release",
+ "configurePreset": "linux-release"
+ },
+ {
+ "name": "linux-debug",
+ "configurePreset": "linux-debug"
+ },
+ {
+ "name": "windows-release",
+ "configurePreset": "windows-release"
+ },
+ {
+ "name": "windows-Xdebug",
+ "configurePreset": "windows-debug"
+ }
+ ]
+}
diff --git a/CMakeSettings.json b/CMakeSettings.json
deleted file mode 100644
index c825dfc9..00000000
--- a/CMakeSettings.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "configurations": [
- {
- "name": "x64-Release",
- "generator": "Ninja",
- "configurationType": "Release",
- "buildRoot": "${projectDir}\\build\\${name}",
- "installRoot": "${projectDir}\\install\\${name}",
- "cmakeCommandArgs": "",
- "buildCommandArgs": "",
- "ctestCommandArgs": "",
- "inheritEnvironments": [ "msvc_x64_x64" ]
- },
- {
- "name": "x64-Debug",
- "generator": "Ninja",
- "configurationType": "Debug",
- "buildRoot": "${projectDir}\\build\\${name}",
- "installRoot": "${projectDir}\\install\\${name}",
- "cmakeCommandArgs": "",
- "buildCommandArgs": "",
- "ctestCommandArgs": "",
- "inheritEnvironments": [ "msvc_x64_x64" ],
- "variables": []
- }
- ]
-}
diff --git a/data/creatures/monsters.xml b/data/creatures/monsters.xml
index 097dfc3e..ae000a97 100644
--- a/data/creatures/monsters.xml
+++ b/data/creatures/monsters.xml
@@ -79,7 +79,11 @@
+
+
+
+
@@ -103,6 +107,7 @@
+
@@ -147,6 +152,7 @@
+
@@ -188,6 +194,7 @@
+
@@ -201,6 +208,7 @@
+
@@ -220,6 +228,7 @@
+
@@ -374,6 +383,7 @@
+
@@ -451,6 +461,7 @@
+
@@ -471,6 +482,7 @@
+
@@ -490,12 +502,15 @@
+
-
+
+
+
@@ -525,6 +540,8 @@
+
+
@@ -557,9 +574,12 @@
+
+
+
@@ -583,6 +603,7 @@
+
@@ -595,6 +616,11 @@
+
+
+
+
+
@@ -625,6 +651,8 @@
+
+
@@ -638,8 +666,8 @@
-
+
@@ -663,6 +691,7 @@
+
@@ -708,6 +737,7 @@
+
@@ -717,6 +747,7 @@
+
@@ -738,6 +769,7 @@
+
@@ -798,11 +830,14 @@
+
+
+
@@ -815,6 +850,7 @@
+
@@ -873,13 +909,13 @@
+
-
@@ -898,6 +934,7 @@
+
@@ -957,6 +994,7 @@
+
@@ -975,6 +1013,7 @@
+
@@ -986,6 +1025,7 @@
+
@@ -1025,6 +1065,7 @@
+
@@ -1086,6 +1127,7 @@
+
@@ -1102,7 +1144,9 @@
+
+
@@ -1226,8 +1270,8 @@
-
+
@@ -1236,6 +1280,7 @@
+
@@ -1251,6 +1296,7 @@
+
@@ -1265,6 +1311,7 @@
+
@@ -1361,4 +1408,4 @@
-
\ No newline at end of file
+
diff --git a/data/creatures/npcs.xml b/data/creatures/npcs.xml
index f6f10cdc..06393bef 100644
--- a/data/creatures/npcs.xml
+++ b/data/creatures/npcs.xml
@@ -21,8 +21,8 @@
-
+
@@ -68,6 +68,7 @@
+
@@ -81,8 +82,8 @@
-
+
@@ -95,6 +96,7 @@
+
@@ -115,6 +117,7 @@
+
@@ -152,11 +155,11 @@
-
+
@@ -171,20 +174,20 @@
-
+
-
+
-
+
@@ -358,15 +361,17 @@
+
+
-
+
@@ -432,8 +437,8 @@
-
+
@@ -476,11 +481,11 @@
-
-
+
+
@@ -515,8 +520,8 @@
-
+
@@ -577,9 +582,9 @@
-
+
@@ -612,9 +617,9 @@
-
+
@@ -640,6 +645,7 @@
+
@@ -727,8 +733,8 @@
-
+
@@ -775,12 +781,12 @@
-
+
-
+
@@ -824,6 +830,7 @@
+
@@ -845,8 +852,8 @@
-
+
@@ -863,6 +870,7 @@
+
diff --git a/data/materials/tilesets/blank.xml b/data/materials/tilesets/blank.xml
index e3f83d37..078afd18 100644
--- a/data/materials/tilesets/blank.xml
+++ b/data/materials/tilesets/blank.xml
@@ -35,7 +35,6 @@
-
@@ -145,7 +144,6 @@
-
@@ -170,18 +168,16 @@
-
-
-
-
+
+
@@ -445,13 +441,10 @@
-
-
-
@@ -573,7 +566,6 @@
-
@@ -622,6 +614,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/materials/tilesets/borders.xml b/data/materials/tilesets/borders.xml
index 53d5ce31..e32b2f0c 100644
--- a/data/materials/tilesets/borders.xml
+++ b/data/materials/tilesets/borders.xml
@@ -197,6 +197,7 @@
+
diff --git a/data/materials/tilesets/nature_grass.xml b/data/materials/tilesets/nature_grass.xml
index 07ffeef5..c68d9bec 100644
--- a/data/materials/tilesets/nature_grass.xml
+++ b/data/materials/tilesets/nature_grass.xml
@@ -24,6 +24,8 @@
+
+
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 0d7f4eb2..d2c37971 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -12,6 +12,13 @@ find_package(OpenGL REQUIRED)
find_package(Threads REQUIRED)
find_package(wxWidgets COMPONENTS html aui gl adv core net base CONFIG REQUIRED)
find_package(ZLIB REQUIRED)
+find_package(pugixml CONFIG REQUIRED)
+
+option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" OFF)
+option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON)
+option(DEBUG_LOG "Enable Debug Log" OFF)
+option(BUILD_STATIC_LIBRARY "Build using static libraries" ON)
+option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON)
# LibArchive disabled in compilation level by default, see "#define OTGZ_SUPPORT" in the "definitions.h" file
#if(APPLE)
@@ -21,14 +28,73 @@ find_package(ZLIB REQUIRED)
#find_package(LibArchive REQUIRED)
#${LibArchive_INCLUDE_DIRS} ${LibArchive_LIBRARIES}
+# Build static libs
+if(BUILD_STATIC_LIBRARY)
+ log_option_enabled("STATIC_LIBRARY")
+
+ if(MSVC)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
+ elseif(UNIX AND NOT APPLE)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+ elseif(APPLE)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".dylib")
+ endif()
+else()
+ log_option_disabled("STATIC_LIBRARY")
+endif()
+
+# === DEBUG LOG ===
+# cmake -DDEBUG_LOG=ON ..
+if(DEBUG_LOG)
+ add_definitions(-DDEBUG_LOG=ON)
+ log_option_enabled("DEBUG LOG")
+else()
+ log_option_disabled("DEBUG LOG")
+endif(DEBUG_LOG)
+
if (MSVC)
add_executable(${PROJECT_NAME} "" ../cmake/remeres.rc)
+
+ if(BUILD_STATIC_LIBRARY)
+ set(CMAKE_CXX_FLAGS_RELEASE "/MT")
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MT")
+ set(CMAKE_CXX_FLAGS_DEBUG "/MTd")
+ set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
+ endif()
+
+ target_compile_options(${PROJECT_NAME} PUBLIC /MP /FS /Zf /EHsc )
else()
add_executable(${PROJECT_NAME} "")
endif()
+# === OpenMP ===
+if(OPTIONS_ENABLE_OPENMP)
+ log_option_enabled("openmp")
+ find_package(OpenMP)
+ if(OpenMP_CXX_FOUND)
+ target_link_libraries(${PROJECT_NAME} PUBLIC OpenMP::OpenMP_CXX)
+ endif()
+else()
+ log_option_disabled("openmp")
+endif()
+
+# === IPO ===
+check_ipo_supported(RESULT result OUTPUT output)
+if(result)
+ set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
+else()
+ message(WARNING "IPO is not supported: ${output}")
+endif()
+
# === PRECOMPILED HEADER ===
-#target_precompile_headers(${PROJECT_NAME} PRIVATE main.h)
+target_precompile_headers(${PROJECT_NAME} PRIVATE main.h)
+
+# === UNITY BUILD (compile time reducer) ===
+if(SPEED_UP_BUILD_UNITY)
+ set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON)
+ log_option_enabled("Build unity for speed up compilation")
+endif()
+
target_sources(${PROJECT_NAME}
PRIVATE
@@ -144,16 +210,26 @@ target_include_directories(${PROJECT_NAME}
)
target_link_libraries(${PROJECT_NAME}
+ PRIVATE
${OPENGL_LIBRARIES}
${GLUT_LIBRARIES}
${ZLIB_LIBRARIES}
fmt::fmt
asio::asio
nlohmann_json::nlohmann_json
+ pugixml::pugixml
wx::base wx::core wx::net wx::gl wx::html wx::aui wx::adv
)
-set_target_properties(${PROJECT_NAME}
- PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}"
-)
+## Link compilation files to build/bin folder, else link to the main dir
+if (TOGGLE_BIN_FOLDER)
+ set_target_properties(${PROJECT_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+ )
+else()
+ set_target_properties(${PROJECT_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/"
+ )
+endif()
diff --git a/source/about_window.cpp b/source/about_window.cpp
index d098017e..443635f2 100644
--- a/source/about_window.cpp
+++ b/source/about_window.cpp
@@ -20,9 +20,6 @@
#include "gui.h"
#include "about_window.h"
-#include
-#include
-#include
class GamePanel : public wxPanel {
diff --git a/source/action.h b/source/action.h
index 0ea3eb47..456cdcf5 100644
--- a/source/action.h
+++ b/source/action.h
@@ -20,8 +20,6 @@
#include "position.h"
-#include
-
class Editor;
class Tile;
class House;
@@ -66,11 +64,11 @@ struct WaypointData {
Position position;
};
-class Change
-{
- Change();
+class Change {
+private:
+ ChangeType type;
+ void* data;
-public:
Change(Tile* tile);
~Change();
diff --git a/source/application.cpp b/source/application.cpp
index aa773482..f8f503a6 100644
--- a/source/application.cpp
+++ b/source/application.cpp
@@ -36,8 +36,6 @@
#include "monster.h"
#include "npc.h"
-#include
-
#if defined(__LINUX__) || defined(__WINDOWS__)
#include
#endif
diff --git a/source/carpet_brush.cpp b/source/carpet_brush.cpp
index 1db32693..19f15418 100644
--- a/source/carpet_brush.cpp
+++ b/source/carpet_brush.cpp
@@ -42,11 +42,11 @@ bool CarpetBrush::load(pugi::xml_node node, wxArrayString& warnings)
{
pugi::xml_attribute attribute;
if((attribute = node.attribute("lookid"))) {
- look_id = attribute.as_ushort();
+ look_id = attribute.as_uint();
}
if((attribute = node.attribute("server_lookid"))) {
- look_id = g_items.getItemType(attribute.as_ushort()).clientID;
+ look_id = g_items.getItemType(attribute.as_uint()).clientID;
}
for(pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
@@ -119,7 +119,7 @@ bool CarpetBrush::load(pugi::xml_node node, wxArrayString& warnings)
continue;
}
- uint16_t id = attribute.as_ushort();
+ uint16_t id = attribute.as_uint();
ItemType* type = g_items.getRawItemType(id);
if(!type) {
warnings.push_back("There is no itemtype with id " + std::to_string(id));
diff --git a/source/common.cpp b/source/common.cpp
index 5abf298f..024999b6 100644
--- a/source/common.cpp
+++ b/source/common.cpp
@@ -20,9 +20,6 @@
#include "common.h"
#include "math.h"
-#include
-#include
-
// random generator
std::mt19937& getRandomGenerator()
{
diff --git a/source/common.h b/source/common.h
index bcac09a9..c4e1d3c0 100644
--- a/source/common.h
+++ b/source/common.h
@@ -19,11 +19,6 @@
#define RME_COMMONS_H_
#include "main.h"
-#include
-#include
-#include
-#include
-#include
#include "mt_rand.h"
diff --git a/source/common_windows.cpp b/source/common_windows.cpp
index 98501b9f..333700e8 100644
--- a/source/common_windows.cpp
+++ b/source/common_windows.cpp
@@ -136,7 +136,7 @@ MapPropertiesWindow::MapPropertiesWindow(wxWindow* parent, MapTab* view, Editor&
spawn_filename_ctrl =
newd wxTextCtrl(this, wxID_ANY, wxstr(map.getSpawnFilename())), 1, wxEXPAND
);
-
+
grid_sizer->Add(
newd wxStaticText(this, wxID_ANY, "External npc file")
);
diff --git a/source/copybuffer.h b/source/copybuffer.h
index 8fb791a8..6e4233c1 100644
--- a/source/copybuffer.h
+++ b/source/copybuffer.h
@@ -18,8 +18,6 @@
#ifndef RME_COPYBUFFER_H_
#define RME_COPYBUFFER_H_
-#include
-
#include "position.h"
#include "basemap.h"
diff --git a/source/dcbutton.cpp b/source/dcbutton.cpp
index f7439608..b9ab2e67 100644
--- a/source/dcbutton.cpp
+++ b/source/dcbutton.cpp
@@ -108,12 +108,17 @@ void DCButton::OnPaint(wxPaintEvent& event)
static std::unique_ptr light_shadow_pen;
static std::unique_ptr shadow_pen;
- if(highlight_pen.get() == nullptr) highlight_pen.reset(newd wxPen(wxColor(0xFF,0xFF,0xFF), 1, wxSOLID));
- if(dark_highlight_pen.get() == nullptr) dark_highlight_pen.reset(newd wxPen(wxColor(0xD4,0xD0,0xC8), 1, wxSOLID));
- if(light_shadow_pen.get() == nullptr) light_shadow_pen.reset(newd wxPen(wxColor(0x80,0x80,0x80), 1, wxSOLID));
- if(shadow_pen.get() == nullptr) shadow_pen.reset(newd wxPen(wxColor(0x40,0x40,0x40), 1, wxSOLID));
+ if(highlight_pen.get() == nullptr)
+ highlight_pen.reset(new wxPen(wxColor(0xFF,0xFF,0xFF), 1, wxPENSTYLE_SOLID));
+ if(dark_highlight_pen.get() == nullptr)
+ dark_highlight_pen.reset(new wxPen(wxColor(0xD4,0xD0,0xC8), 1, wxPENSTYLE_SOLID));
+ if(light_shadow_pen.get() == nullptr)
+ light_shadow_pen.reset(new wxPen(wxColor(0x80,0x80,0x80), 1, wxPENSTYLE_SOLID));
+ if(shadow_pen.get() == nullptr)
+ shadow_pen.reset(new wxPen(wxColor(0x40,0x40,0x40), 1, wxPENSTYLE_SOLID));
- int size_x = 20, size_y = 20;
+
+ int size_x = 20, size_y = 20;
if(size == RENDER_SIZE_16x16) {
size_x = 20;
diff --git a/source/doodad_brush.cpp b/source/doodad_brush.cpp
index a1bf7de5..09d42e54 100644
--- a/source/doodad_brush.cpp
+++ b/source/doodad_brush.cpp
@@ -176,11 +176,11 @@ bool DoodadBrush::load(pugi::xml_node node, wxArrayString& warnings)
{
pugi::xml_attribute attribute;
if((attribute = node.attribute("lookid"))) {
- look_id = attribute.as_ushort();
+ look_id = attribute.as_uint();
}
if((attribute = node.attribute("server_lookid"))) {
- look_id = g_items.getItemType(attribute.as_ushort()).clientID;
+ look_id = g_items.getItemType(attribute.as_uint()).clientID;
}
if((attribute = node.attribute("on_blocking"))) {
diff --git a/source/editor.cpp b/source/editor.cpp
index ab632952..e54e809a 100644
--- a/source/editor.cpp
+++ b/source/editor.cpp
@@ -228,7 +228,7 @@ void Editor::redo(int indexes)
indexes--;
}
g_gui.UpdateActions();
- g_gui.RefreshView();
+ g_gui.RefreshView();
}
void Editor::updateActions()
@@ -444,7 +444,7 @@ void Editor::saveMap(FileName filename, bool showdialog)
std::string spawn_filename = map_path + nstr(converter.GetName());
std::rename(backup_spawn.c_str(), std::string(spawn_filename + "." + date.str() + ".xml").c_str());
}
-
+
if(!backup_spawn_npc.empty()) {
converter.SetFullName(wxstr(map.spawnnpcfile));
std::string spawnnpc_filename = map_path + nstr(converter.GetName());
diff --git a/source/ext/pugiconfig.hpp b/source/ext/pugiconfig.hpp
deleted file mode 100644
index c8846bb7..00000000
--- a/source/ext/pugiconfig.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * pugixml parser - version 1.2
- * --------------------------------------------------------
- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
- *
- * This library is distributed under the MIT License. See notice at the end
- * of this file.
- *
- * This work is based on the pugxml parser, which is:
- * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
- */
-
-#ifndef HEADER_PUGICONFIG_HPP
-#define HEADER_PUGICONFIG_HPP
-
-// Uncomment this to enable wchar_t mode
-// #define PUGIXML_WCHAR_MODE
-
-// Uncomment this to disable XPath
-#define PUGIXML_NO_XPATH
-
-// Uncomment this to disable STL
-// #define PUGIXML_NO_STL
-
-// Uncomment this to disable exceptions
-// #define PUGIXML_NO_EXCEPTIONS
-
-// Set this to control attributes for public classes/functions, i.e.:
-// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
-// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
-// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
-// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
-
-// Uncomment this to switch to header-only version
-#define PUGIXML_HEADER_ONLY
-#include "pugixml.cpp"
-
-// Tune these constants to adjust memory-related behavior
-// #define PUGIXML_MEMORY_PAGE_SIZE 32768
-// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
-// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
-
-#endif
-
-/**
- * Copyright (c) 2006-2012 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/source/ext/pugixml.cpp b/source/ext/pugixml.cpp
deleted file mode 100644
index 36cd40fd..00000000
--- a/source/ext/pugixml.cpp
+++ /dev/null
@@ -1,10266 +0,0 @@
-/**
- * pugixml parser - version 1.2
- * --------------------------------------------------------
- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
- *
- * This library is distributed under the MIT License. See notice at the end
- * of this file.
- *
- * This work is based on the pugxml parser, which is:
- * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
- */
-
-#ifndef SOURCE_PUGIXML_CPP
-#define SOURCE_PUGIXML_CPP
-
-#include "pugixml.hpp"
-
-#include
-#include
-#include
-#include
-#include
-
-#ifndef PUGIXML_NO_XPATH
-# include
-# include
-# ifdef PUGIXML_NO_EXCEPTIONS
-# include
-# endif
-#endif
-
-#ifndef PUGIXML_NO_STL
-# include
-# include
-# include
-#endif
-
-// For placement new
-#include
-
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable: 4127) // conditional expression is constant
-# pragma warning(disable: 4324) // structure was padded due to __declspec(align())
-# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
-# pragma warning(disable: 4702) // unreachable code
-# pragma warning(disable: 4996) // this function or variable may be unsafe
-# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
-#endif
-
-#ifdef __INTEL_COMPILER
-# pragma warning(disable: 177) // function was declared but never referenced
-# pragma warning(disable: 279) // controlling expression is constant
-# pragma warning(disable: 1478 1786) // function was declared "deprecated"
-# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type
-#endif
-
-#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
-# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away
-#endif
-
-#ifdef __BORLANDC__
-# pragma option push
-# pragma warn -8008 // condition is always false
-# pragma warn -8066 // unreachable code
-#endif
-
-#ifdef __SNC__
-// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug
-# pragma diag_suppress=178 // function was declared but never referenced
-# pragma diag_suppress=237 // controlling expression is constant
-#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
-#endif
-
-// Simple static assertion
-#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
-
-// Digital Mars C++ bug workaround for passing char loaded from memory via stack
-#ifdef __DMC__
-# define PUGI__DMC_VOLATILE volatile
-#else
-# define PUGI__DMC_VOLATILE
-#endif
-
-// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
-#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
-using std::memcpy;
-using std::memmove;
-#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
-
-#ifdef PUGIXML_HEADER_ONLY
-# define PUGI__NS_BEGIN namespace pugi { namespace impl {
-# define PUGI__NS_END } }
-# define PUGI__FN inline
-# define PUGI__FN_NO_INLINE inline
-#else
-# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
-# define PUGI__NS_BEGIN namespace pugi { namespace impl {
-# define PUGI__NS_END } }
-# else
-# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
-# define PUGI__NS_END } } }
-# endif
-# define PUGI__FN
-# define PUGI__FN_NO_INLINE PUGI__NO_INLINE
-#endif
-
-// uintptr_t
-#if !defined(_MSC_VER) || _MSC_VER >= 1600
-# include
-#else
-# ifndef _UINTPTR_T_DEFINED
-// No native uintptr_t in MSVC6 and in some WinCE versions
-typedef size_t uintptr_t;
-#define _UINTPTR_T_DEFINED
-# endif
-PUGI__NS_BEGIN
- typedef unsigned __int8 uint8_t;
- typedef unsigned __int16 uint16_t;
- typedef unsigned __int32 uint32_t;
-PUGI__NS_END
-#endif
-
-// Memory allocation
-PUGI__NS_BEGIN
- PUGI__FN void* default_allocate(size_t size)
- {
- return malloc(size);
- }
-
- PUGI__FN void default_deallocate(void* ptr)
- {
- free(ptr);
- }
-
- template
- struct xml_memory_management_function_storage
- {
- static allocation_function allocate;
- static deallocation_function deallocate;
- };
-
- template allocation_function xml_memory_management_function_storage::allocate = default_allocate;
- template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate;
-
- typedef xml_memory_management_function_storage xml_memory;
-PUGI__NS_END
-
-// String utilities
-PUGI__NS_BEGIN
- // Get string length
- PUGI__FN size_t strlength(const char_t* s)
- {
- assert(s);
-
- #ifdef PUGIXML_WCHAR_MODE
- return wcslen(s);
- #else
- return strlen(s);
- #endif
- }
-
- // Compare two strings
- PUGI__FN bool strequal(const char_t* src, const char_t* dst)
- {
- assert(src && dst);
-
- #ifdef PUGIXML_WCHAR_MODE
- return wcscmp(src, dst) == 0;
- #else
- return strcmp(src, dst) == 0;
- #endif
- }
-
- // Compare lhs with [rhs_begin, rhs_end)
- PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
- {
- for (size_t i = 0; i < count; ++i)
- if (lhs[i] != rhs[i])
- return false;
-
- return lhs[count] == 0;
- }
-
-#ifdef PUGIXML_WCHAR_MODE
- // Convert string to wide string, assuming all symbols are ASCII
- PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
- {
- for (const char* i = source; *i; ++i) *dest++ = *i;
- *dest = 0;
- }
-#endif
-PUGI__NS_END
-
-#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
-// auto_ptr-like buffer holder for exception recovery
-PUGI__NS_BEGIN
- struct buffer_holder
- {
- void* data;
- void (*deleter)(void*);
-
- buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
- {
- }
-
- ~buffer_holder()
- {
- if (data) deleter(data);
- }
-
- void* release()
- {
- void* result = data;
- data = 0;
- return result;
- }
- };
-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
- ;
-
- static const uintptr_t xml_memory_page_alignment = 32;
- static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
- 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;
-
- struct xml_allocator;
-
- struct xml_memory_page
- {
- static xml_memory_page* construct(void* memory)
- {
- if (!memory) return 0; //$ redundant, left for performance
-
- xml_memory_page* result = static_cast(memory);
-
- result->allocator = 0;
- result->memory = 0;
- result->prev = 0;
- result->next = 0;
- result->busy_size = 0;
- result->freed_size = 0;
-
- return result;
- }
-
- xml_allocator* allocator;
-
- void* memory;
-
- xml_memory_page* prev;
- xml_memory_page* next;
-
- size_t busy_size;
- size_t freed_size;
-
- char data[1];
- };
-
- struct xml_memory_string_header
- {
- uint16_t page_offset; // offset from page->data
- uint16_t full_size; // 0 if string occupies whole page
- };
-
- struct xml_allocator
- {
- xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
- {
- }
-
- xml_memory_page* allocate_page(size_t data_size)
- {
- size_t size = offsetof(xml_memory_page, data) + data_size;
-
- // allocate block with some alignment, leaving memory for worst-case padding
- void* memory = xml_memory::allocate(size + xml_memory_page_alignment);
- if (!memory) return 0;
-
- // align upwards to page boundary
- void* page_memory = reinterpret_cast((reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
-
- // prepare page structure
- xml_memory_page* page = xml_memory_page::construct(page_memory);
-
- page->memory = memory;
- page->allocator = _root->allocator;
-
- return page;
- }
-
- static void deallocate_page(xml_memory_page* page)
- {
- xml_memory::deallocate(page->memory);
- }
-
- void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
-
- void* allocate_memory(size_t size, xml_memory_page*& out_page)
- {
- if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
-
- void* buf = _root->data + _busy_size;
-
- _busy_size += size;
-
- out_page = _root;
-
- return buf;
- }
-
- void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
- {
- if (page == _root) page->busy_size = _busy_size;
-
- assert(ptr >= page->data && ptr < page->data + page->busy_size);
- (void)!ptr;
-
- page->freed_size += size;
- assert(page->freed_size <= page->busy_size);
-
- if (page->freed_size == page->busy_size)
- {
- if (page->next == 0)
- {
- assert(_root == page);
-
- // top page freed, just reset sizes
- page->busy_size = page->freed_size = 0;
- _busy_size = 0;
- }
- else
- {
- assert(_root != page);
- assert(page->prev);
-
- // remove from the list
- page->prev->next = page->next;
- page->next->prev = page->prev;
-
- // deallocate
- deallocate_page(page);
- }
- }
- }
-
- char_t* allocate_string(size_t length)
- {
- // allocate memory for string and header block
- size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
-
- // round size up to pointer alignment boundary
- size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
-
- xml_memory_page* page;
- xml_memory_string_header* header = static_cast(allocate_memory(full_size, page));
-
- if (!header) return 0;
-
- // setup header
- ptrdiff_t page_offset = reinterpret_cast(header) - page->data;
-
- assert(page_offset >= 0 && page_offset < (1 << 16));
- header->page_offset = static_cast(page_offset);
-
- // full_size == 0 for large strings that occupy the whole page
- assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
- header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0);
-
- // round-trip through void* to avoid 'cast increases required alignment of target type' warning
- // header is guaranteed a pointer-sized alignment, which should be enough for char_t
- return static_cast(static_cast(header + 1));
- }
-
- void deallocate_string(char_t* string)
- {
- // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
- // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string
-
- // get header
- xml_memory_string_header* header = static_cast(static_cast(string)) - 1;
-
- // deallocate
- size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;
- xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset));
-
- // if full_size == 0 then this string occupies the whole page
- size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
-
- deallocate_memory(header, full_size, page);
- }
-
- xml_memory_page* _root;
- size_t _busy_size;
- };
-
- PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
- {
- const size_t large_allocation_threshold = xml_memory_page_size / 4;
-
- xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
- out_page = page;
-
- if (!page) return 0;
-
- if (size <= large_allocation_threshold)
- {
- _root->busy_size = _busy_size;
-
- // insert page at the end of linked list
- page->prev = _root;
- _root->next = page;
- _root = page;
-
- _busy_size = size;
- }
- else
- {
- // insert page before the end of linked list, so that it is deleted as soon as possible
- // the last page is not deleted even if it's empty (see deallocate_memory)
- assert(_root->prev);
-
- page->prev = _root->prev;
- page->next = _root;
-
- _root->prev->next = page;
- _root->prev = page;
- }
-
- // allocate inside page
- page->busy_size = size;
-
- return page->data;
- }
-PUGI__NS_END
-
-namespace pugi
-{
- /// A 'name=value' XML attribute structure.
- struct xml_attribute_struct
- {
- /// Default ctor
- xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
- {
- }
-
- uintptr_t header;
-
- char_t* name; ///< Pointer to attribute name.
- char_t* value; ///< Pointer to attribute value.
-
- xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list)
- xml_attribute_struct* next_attribute; ///< Next attribute
- };
-
- /// An XML document tree node.
- struct xml_node_struct
- {
- /// Default ctor
- /// \param type - node type
- xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
- {
- }
-
- uintptr_t header;
-
- xml_node_struct* parent; ///< Pointer to parent
-
- char_t* name; ///< Pointer to element name.
- char_t* value; ///< Pointer to any associated string data.
-
- xml_node_struct* first_child; ///< First child
-
- xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list)
- xml_node_struct* next_sibling; ///< Right brother
-
- xml_attribute_struct* first_attribute; ///< First attribute
- };
-}
-
-PUGI__NS_BEGIN
- struct xml_document_struct: public xml_node_struct, public xml_allocator
- {
- xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0)
- {
- }
-
- const char_t* buffer;
- };
-
- inline xml_allocator& get_allocator(const xml_node_struct* node)
- {
- assert(node);
-
- return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator;
- }
-PUGI__NS_END
-
-// Low-level DOM operations
-PUGI__NS_BEGIN
- inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
- {
- xml_memory_page* page;
- void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
-
- return new (memory) xml_attribute_struct(page);
- }
-
- inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)
- {
- xml_memory_page* page;
- void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
-
- return new (memory) xml_node_struct(page, type);
- }
-
- inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
- {
- uintptr_t header = a->header;
-
- if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
- if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
-
- alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask));
- }
-
- inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
- {
- uintptr_t header = n->header;
-
- if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
- if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
-
- for (xml_attribute_struct* attr = n->first_attribute; attr; )
- {
- xml_attribute_struct* next = attr->next_attribute;
-
- destroy_attribute(attr, alloc);
-
- attr = next;
- }
-
- for (xml_node_struct* child = n->first_child; child; )
- {
- xml_node_struct* next = child->next_sibling;
-
- destroy_node(child, alloc);
-
- child = next;
- }
-
- alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(header & xml_memory_page_pointer_mask));
- }
-
- PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
- {
- xml_node_struct* child = allocate_node(alloc, type);
- if (!child) return 0;
-
- child->parent = node;
-
- xml_node_struct* first_child = node->first_child;
-
- if (first_child)
- {
- xml_node_struct* last_child = first_child->prev_sibling_c;
-
- last_child->next_sibling = child;
- child->prev_sibling_c = last_child;
- first_child->prev_sibling_c = child;
- }
- else
- {
- node->first_child = child;
- child->prev_sibling_c = child;
- }
-
- return child;
- }
-
- PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)
- {
- xml_attribute_struct* a = allocate_attribute(alloc);
- if (!a) return 0;
-
- xml_attribute_struct* first_attribute = node->first_attribute;
-
- if (first_attribute)
- {
- xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;
-
- last_attribute->next_attribute = a;
- a->prev_attribute_c = last_attribute;
- first_attribute->prev_attribute_c = a;
- }
- else
- {
- node->first_attribute = a;
- a->prev_attribute_c = a;
- }
-
- return a;
- }
-PUGI__NS_END
-
-// Helper classes for code generation
-PUGI__NS_BEGIN
- struct opt_false
- {
- enum { value = 0 };
- };
-
- struct opt_true
- {
- enum { value = 1 };
- };
-PUGI__NS_END
-
-// Unicode utilities
-PUGI__NS_BEGIN
- inline uint16_t endian_swap(uint16_t value)
- {
- return static_cast(((value & 0xff) << 8) | (value >> 8));
- }
-
- inline uint32_t endian_swap(uint32_t value)
- {
- return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);
- }
-
- struct utf8_counter
- {
- typedef size_t value_type;
-
- static value_type low(value_type result, uint32_t ch)
- {
- // U+0000..U+007F
- if (ch < 0x80) return result + 1;
- // U+0080..U+07FF
- else if (ch < 0x800) return result + 2;
- // U+0800..U+FFFF
- else return result + 3;
- }
-
- static value_type high(value_type result, uint32_t)
- {
- // U+10000..U+10FFFF
- return result + 4;
- }
- };
-
- struct utf8_writer
- {
- typedef uint8_t* value_type;
-
- static value_type low(value_type result, uint32_t ch)
- {
- // U+0000..U+007F
- if (ch < 0x80)
- {
- *result = static_cast(ch);
- return result + 1;
- }
- // U+0080..U+07FF
- else if (ch < 0x800)
- {
- result[0] = static_cast(0xC0 | (ch >> 6));
- result[1] = static_cast(0x80 | (ch & 0x3F));
- return result + 2;
- }
- // U+0800..U+FFFF
- else
- {
- result[0] = static_cast(0xE0 | (ch >> 12));
- result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F));
- result[2] = static_cast(0x80 | (ch & 0x3F));
- return result + 3;
- }
- }
-
- static value_type high(value_type result, uint32_t ch)
- {
- // U+10000..U+10FFFF
- result[0] = static_cast(0xF0 | (ch >> 18));
- result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F));
- result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F));
- result[3] = static_cast(0x80 | (ch & 0x3F));
- return result + 4;
- }
-
- static value_type any(value_type result, uint32_t ch)
- {
- return (ch < 0x10000) ? low(result, ch) : high(result, ch);
- }
- };
-
- struct utf16_counter
- {
- typedef size_t value_type;
-
- static value_type low(value_type result, uint32_t)
- {
- return result + 1;
- }
-
- static value_type high(value_type result, uint32_t)
- {
- return result + 2;
- }
- };
-
- struct utf16_writer
- {
- typedef uint16_t* value_type;
-
- static value_type low(value_type result, uint32_t ch)
- {
- *result = static_cast(ch);
-
- return result + 1;
- }
-
- static value_type high(value_type result, uint32_t ch)
- {
- uint32_t msh = static_cast(ch - 0x10000) >> 10;
- uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff;
-
- result[0] = static_cast(0xD800 + msh);
- result[1] = static_cast(0xDC00 + lsh);
-
- return result + 2;
- }
-
- static value_type any(value_type result, uint32_t ch)
- {
- return (ch < 0x10000) ? low(result, ch) : high(result, ch);
- }
- };
-
- struct utf32_counter
- {
- typedef size_t value_type;
-
- static value_type low(value_type result, uint32_t)
- {
- return result + 1;
- }
-
- static value_type high(value_type result, uint32_t)
- {
- return result + 1;
- }
- };
-
- struct utf32_writer
- {
- typedef uint32_t* value_type;
-
- static value_type low(value_type result, uint32_t ch)
- {
- *result = ch;
-
- return result + 1;
- }
-
- static value_type high(value_type result, uint32_t ch)
- {
- *result = ch;
-
- return result + 1;
- }
-
- static value_type any(value_type result, uint32_t ch)
- {
- *result = ch;
-
- return result + 1;
- }
- };
-
- struct latin1_writer
- {
- typedef uint8_t* value_type;
-
- static value_type low(value_type result, uint32_t ch)
- {
- *result = static_cast(ch > 255 ? '?' : ch);
-
- return result + 1;
- }
-
- static value_type high(value_type result, uint32_t ch)
- {
- (void)ch;
-
- *result = '?';
-
- return result + 1;
- }
- };
-
- template struct wchar_selector;
-
- template <> struct wchar_selector<2>
- {
- typedef uint16_t type;
- typedef utf16_counter counter;
- typedef utf16_writer writer;
- };
-
- template <> struct wchar_selector<4>
- {
- typedef uint32_t type;
- typedef utf32_counter counter;
- typedef utf32_writer writer;
- };
-
- typedef wchar_selector::counter wchar_counter;
- typedef wchar_selector::writer wchar_writer;
-
- template struct utf_decoder
- {
- static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)
- {
- const uint8_t utf8_byte_mask = 0x3f;
-
- while (size)
- {
- uint8_t lead = *data;
-
- // 0xxxxxxx -> U+0000..U+007F
- if (lead < 0x80)
- {
- result = Traits::low(result, lead);
- data += 1;
- size -= 1;
-
- // process aligned single-byte (ascii) blocks
- if ((reinterpret_cast(data) & 3) == 0)
- {
- // round-trip through void* to silence 'cast increases required alignment of target type' warnings
- while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0)
- {
- result = Traits::low(result, data[0]);
- result = Traits::low(result, data[1]);
- result = Traits::low(result, data[2]);
- result = Traits::low(result, data[3]);
- data += 4;
- size -= 4;
- }
- }
- }
- // 110xxxxx -> U+0080..U+07FF
- else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)
- {
- result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));
- data += 2;
- size -= 2;
- }
- // 1110xxxx -> U+0800-U+FFFF
- else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)
- {
- result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));
- data += 3;
- size -= 3;
- }
- // 11110xxx -> U+10000..U+10FFFF
- else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)
- {
- result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));
- data += 4;
- size -= 4;
- }
- // 10xxxxxx or 11111xxx -> invalid
- else
- {
- data += 1;
- size -= 1;
- }
- }
-
- return result;
- }
-
- static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)
- {
- const uint16_t* end = data + size;
-
- while (data < end)
- {
- uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;
-
- // U+0000..U+D7FF
- if (lead < 0xD800)
- {
- result = Traits::low(result, lead);
- data += 1;
- }
- // U+E000..U+FFFF
- else if (static_cast(lead - 0xE000) < 0x2000)
- {
- result = Traits::low(result, lead);
- data += 1;
- }
- // surrogate pair lead
- else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end)
- {
- uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
-
- if (static_cast(next - 0xDC00) < 0x400)
- {
- result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
- data += 2;
- }
- else
- {
- data += 1;
- }
- }
- else
- {
- data += 1;
- }
- }
-
- return result;
- }
-
- static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)
- {
- const uint32_t* end = data + size;
-
- while (data < end)
- {
- uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
-
- // U+0000..U+FFFF
- if (lead < 0x10000)
- {
- result = Traits::low(result, lead);
- data += 1;
- }
- // U+10000..U+10FFFF
- else
- {
- result = Traits::high(result, lead);
- data += 1;
- }
- }
-
- return result;
- }
-
- static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)
- {
- for (size_t i = 0; i < size; ++i)
- {
- result = Traits::low(result, data[i]);
- }
-
- return result;
- }
-
- static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)
- {
- return decode_utf16_block(data, size, result);
- }
-
- static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)
- {
- return decode_utf32_block(data, size, result);
- }
-
- static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)
- {
- return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result);
- }
- };
-
- template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)
- {
- for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);
- }
-
-#ifdef PUGIXML_WCHAR_MODE
- PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
- {
- for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i])));
- }
-#endif
-PUGI__NS_END
-
-PUGI__NS_BEGIN
- enum chartype_t
- {
- ct_parse_pcdata = 1, // \0, &, \r, <
- ct_parse_attr = 2, // \0, &, \r, ', "
- ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab
- ct_space = 8, // \r, \n, space, tab
- ct_parse_cdata = 16, // \0, ], >, \r
- ct_parse_comment = 32, // \0, -, >, \r
- ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
- ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, :
- };
-
- static const unsigned char chartype_table[256] =
- {
- 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
- 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63
- 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95
- 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127
-
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192
- };
-
- 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_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, 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, 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
- 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127
-
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 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
- #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct))
-#endif
-
- #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)
- #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)
-
- PUGI__FN bool is_little_endian()
- {
- unsigned int ui = 1;
-
- return *reinterpret_cast(&ui) == 1;
- }
-
- PUGI__FN xml_encoding get_wchar_encoding()
- {
- PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
-
- if (sizeof(wchar_t) == 2)
- return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
- else
- 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)
- {
- // 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;
- if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;
- if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;
- if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;
-
- // look for <, or (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);
- }
-
- PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
- {
- if (is_mutable)
- {
- out_buffer = static_cast(const_cast(contents));
- }
- else
- {
- void* buffer = xml_memory::allocate(size > 0 ? size : 1);
- if (!buffer) return false;
-
- memcpy(buffer, contents, size);
-
- out_buffer = static_cast(buffer);
- }
-
- out_length = size / sizeof(char_t);
-
- return true;
- }
-
-#ifdef PUGIXML_WCHAR_MODE
- PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
- {
- return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||
- (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);
- }
-
- PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
- {
- const char_t* data = static_cast(contents);
-
- if (is_mutable)
- {
- out_buffer = const_cast(data);
- }
- else
- {
- out_buffer = static_cast(xml_memory::allocate(size > 0 ? size : 1));
- if (!out_buffer) return false;
- }
-
- out_length = size / sizeof(char_t);
-
- convert_wchar_endian_swap(out_buffer, data, out_length);
-
- return true;
- }
-
- PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
- {
- const uint8_t* data = static_cast(contents);
-
- // first pass: get length in wchar_t units
- out_length = utf_decoder::decode_utf8_block(data, size, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert utf8 input to wchar_t
- wchar_writer::value_type out_begin = reinterpret_cast(out_buffer);
- wchar_writer::value_type out_end = utf_decoder::decode_utf8_block(data, size, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
- {
- const uint16_t* data = static_cast(contents);
- size_t length = size / sizeof(uint16_t);
-
- // first pass: get length in wchar_t units
- out_length = utf_decoder::decode_utf16_block(data, length, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert utf16 input to wchar_t
- wchar_writer::value_type out_begin = reinterpret_cast(out_buffer);
- wchar_writer::value_type out_end = utf_decoder::decode_utf16_block(data, length, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
- {
- const uint32_t* data = static_cast(contents);
- size_t length = size / sizeof(uint32_t);
-
- // first pass: get length in wchar_t units
- out_length = utf_decoder::decode_utf32_block(data, length, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert utf32 input to wchar_t
- wchar_writer::value_type out_begin = reinterpret_cast(out_buffer);
- wchar_writer::value_type out_end = utf_decoder::decode_utf32_block(data, length, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
- {
- const uint8_t* data = static_cast(contents);
-
- // get length in wchar_t units
- out_length = size;
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // convert latin1 input to wchar_t
- wchar_writer::value_type out_begin = reinterpret_cast(out_buffer);
- wchar_writer::value_type out_end = utf_decoder::decode_latin1_block(data, size, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
- {
- // get native encoding
- xml_encoding wchar_encoding = get_wchar_encoding();
-
- // fast path: no conversion required
- if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
-
- // only endian-swapping is required
- if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
-
- // source encoding is utf8
- if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size);
-
- // source encoding is utf16
- if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
- {
- xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
- return (native_encoding == encoding) ?
- convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
- convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
- }
-
- // source encoding is utf32
- if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
- {
- xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
- return (native_encoding == encoding) ?
- convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
- convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
- }
-
- // source encoding is latin1
- if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);
-
- assert(!"Invalid encoding");
- return false;
- }
-#else
- template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
- {
- const uint16_t* data = static_cast(contents);
- size_t length = size / sizeof(uint16_t);
-
- // first pass: get length in utf8 units
- out_length = utf_decoder::decode_utf16_block(data, length, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert utf16 input to utf8
- uint8_t* out_begin = reinterpret_cast(out_buffer);
- uint8_t* out_end = utf_decoder::decode_utf16_block(data, length, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
- {
- const uint32_t* data = static_cast(contents);
- size_t length = size / sizeof(uint32_t);
-
- // first pass: get length in utf8 units
- out_length = utf_decoder::decode_utf32_block(data, length, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert utf32 input to utf8
- uint8_t* out_begin = reinterpret_cast(out_buffer);
- uint8_t* out_end = utf_decoder::decode_utf32_block(data, length, out_begin);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
- {
- for (size_t i = 0; i < size; ++i)
- if (data[i] > 127)
- return i;
-
- return size;
- }
-
- PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
- {
- const uint8_t* data = static_cast(contents);
-
- // get size of prefix that does not need utf8 conversion
- size_t prefix_length = get_latin1_7bit_prefix_length(data, size);
- assert(prefix_length <= size);
-
- const uint8_t* postfix = data + prefix_length;
- size_t postfix_length = size - prefix_length;
-
- // if no conversion is needed, just return the original buffer
- if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
-
- // first pass: get length in utf8 units
- out_length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0);
-
- // allocate buffer of suitable length
- out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
- if (!out_buffer) return false;
-
- // second pass: convert latin1 input to utf8
- memcpy(out_buffer, data, prefix_length);
-
- uint8_t* out_begin = reinterpret_cast(out_buffer);
- uint8_t* out_end = utf_decoder::decode_latin1_block(postfix, postfix_length, out_begin + prefix_length);
-
- assert(out_end == out_begin + out_length);
- (void)!out_end;
-
- return true;
- }
-
- PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
- {
- // fast path: no conversion required
- if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
-
- // source encoding is utf16
- if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
- {
- xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
- return (native_encoding == encoding) ?
- convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
- convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
- }
-
- // source encoding is utf32
- if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
- {
- xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
- return (native_encoding == encoding) ?
- convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
- convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
- }
-
- // source encoding is latin1
- if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
-
- assert(!"Invalid encoding");
- return false;
- }
-#endif
-
- PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
- {
- // get length in utf8 characters
- return utf_decoder::decode_wchar_block(str, length, 0);
- }
-
- PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
- {
- // convert to utf8
- uint8_t* begin = reinterpret_cast(buffer);
- uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin);
-
- assert(begin + size == end);
- (void)!end;
-
- // zero-terminate
- buffer[size] = 0;
- }
-
-#ifndef PUGIXML_NO_STL
- PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
- {
- // first pass: get length in utf8 characters
- size_t size = as_utf8_begin(str, length);
-
- // allocate resulting string
- std::string result;
- result.resize(size);
-
- // second pass: convert to utf8
- if (size > 0) as_utf8_end(&result[0], size, str, length);
-
- return result;
- }
-
- PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size)
- {
- const uint8_t* data = reinterpret_cast(str);
-
- // first pass: get length in wchar_t units
- size_t length = utf_decoder::decode_utf8_block(data, size, 0);
-
- // allocate resulting string
- std::basic_string result;
- result.resize(length);
-
- // second pass: convert to wchar_t
- if (length > 0)
- {
- wchar_writer::value_type begin = reinterpret_cast(&result[0]);
- wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin);
-
- assert(begin + length == end);
- (void)!end;
- }
-
- return result;
- }
-#endif
-
- inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)
- {
- assert(target);
- size_t target_length = strlength(target);
-
- // always reuse document buffer memory if possible
- if (!allocated) return target_length >= length;
-
- // reuse heap memory if waste is not too great
- const size_t reuse_threshold = 32;
-
- return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
- }
-
- PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
- {
- size_t source_length = strlength(source);
-
- if (source_length == 0)
- {
- // empty string and null pointer are equivalent, so just deallocate old memory
- xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator;
-
- if (header & header_mask) alloc->deallocate_string(dest);
-
- // mark the string as not allocated
- dest = 0;
- header &= ~header_mask;
-
- return true;
- }
- else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))
- {
- // we can reuse old buffer, so just copy the new data (including zero terminator)
- memcpy(dest, source, (source_length + 1) * sizeof(char_t));
-
- return true;
- }
- else
- {
- xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator;
-
- // allocate new buffer
- char_t* buf = alloc->allocate_string(source_length + 1);
- if (!buf) return false;
-
- // copy the string (including zero terminator)
- memcpy(buf, source, (source_length + 1) * sizeof(char_t));
-
- // 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;
-
- return true;
- }
- }
-
- struct gap
- {
- 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)
- {
- if (end) // there was a gap already; collapse it
- {
- // Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
- 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)
- {
- if (end)
- {
- // Move [old_gap_end, current_pos) to [old_gap_start, ...)
- assert(s >= end);
- memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end));
-
- return s - size;
- }
- else return s;
- }
- };
-
- PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
- {
- char_t* stre = s + 1;
-
- switch (*stre)
- {
- case '#': // ...
- {
- unsigned int ucsc = 0;
-
- if (stre[1] == 'x') // ... (hex code)
- {
- stre += 2;
-
- char_t ch = *stre;
-
- if (ch == ';') return stre;
-
- for (;;)
- {
- if (static_cast(ch - '0') <= 9)
- ucsc = 16 * ucsc + (ch - '0');
- else if (static_cast((ch | ' ') - 'a') <= 5)
- ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);
- else if (ch == ';')
- break;
- else // cancel
- return stre;
-
- ch = *++stre;
- }
-
- ++stre;
- }
- else // ... (dec code)
- {
- char_t ch = *++stre;
-
- if (ch == ';') return stre;
-
- for (;;)
- {
- if (static_cast(ch - '0') <= 9)
- ucsc = 10 * ucsc + (ch - '0');
- else if (ch == ';')
- break;
- else // cancel
- return stre;
-
- ch = *++stre;
- }
-
- ++stre;
- }
-
- #ifdef PUGIXML_WCHAR_MODE
- s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc));
- #else
- s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc));
- #endif
-
- g.push(s, stre - s);
- return stre;
- }
-
- case 'a': // &a
- {
- ++stre;
-
- if (*stre == 'm') // &am
- {
- if (*++stre == 'p' && *++stre == ';') // &
- {
- *s++ = '&';
- ++stre;
-
- g.push(s, stre - s);
- return stre;
- }
- }
- else if (*stre == 'p') // &ap
- {
- if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // '
- {
- *s++ = '\'';
- ++stre;
-
- g.push(s, stre - s);
- return stre;
- }
- }
- break;
- }
-
- case 'g': // &g
- {
- if (*++stre == 't' && *++stre == ';') // >
- {
- *s++ = '>';
- ++stre;
-
- g.push(s, stre - s);
- return stre;
- }
- break;
- }
-
- case 'l': // &l
- {
- if (*++stre == 't' && *++stre == ';') // <
- {
- *s++ = '<';
- ++stre;
-
- g.push(s, stre - s);
- return stre;
- }
- break;
- }
-
- case 'q': // &q
- {
- if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // "
- {
- *s++ = '"';
- ++stre;
-
- g.push(s, stre - s);
- return stre;
- }
- break;
- }
-
- default:
- break;
- }
-
- return stre;
- }
-
- // Utility macro for last character handling
- #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
-
- PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;
-
- 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] == '-' && ENDSWITH(s[2], '>')) // comment ends here
- {
- *g.flush(s) = 0;
-
- return s + (s[2] == '>' ? 3 : 2);
- }
- else if (*s == 0)
- {
- return 0;
- }
- else ++s;
- }
- }
-
- PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;
-
- 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] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here
- {
- *g.flush(s) = 0;
-
- return s + 1;
- }
- else if (*s == 0)
- {
- return 0;
- }
- else ++s;
- }
- }
-
- typedef char_t* (*strconv_pcdata_t)(char_t*);
-
- template struct strconv_pcdata_impl
- {
- static char_t* parse(char_t* s)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;
-
- if (*s == '<') // PCDATA ends here
- {
- *g.flush(s) = 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 == '&')
- {
- s = strconv_escape(s, g);
- }
- else if (*s == 0)
- {
- return s;
- }
- else ++s;
- }
- }
- };
-
- PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
- {
- PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20);
-
- switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes)
- {
- case 0: return strconv_pcdata_impl::parse;
- case 1: return strconv_pcdata_impl::parse;
- case 2: return strconv_pcdata_impl::parse;
- case 3: return strconv_pcdata_impl::parse;
- default: return 0; // should not get here
- }
- }
-
- 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)
- {
- gap g;
-
- // trim leading whitespaces
- 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)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;
-
- 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);
- }
- }
- else if (opt_escape::value && *s == '&')
- {
- s = strconv_escape(s, g);
- }
- else if (!*s)
- {
- return 0;
- }
- else ++s;
- }
- }
-
- static char_t* parse_wconv(char_t* s, char_t end_quote)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;
-
- if (*s == end_quote)
- {
- *g.flush(s) = 0;
-
- return s + 1;
- }
- else if (PUGI__IS_CHARTYPE(*s, ct_space))
- {
- if (*s == '\r')
- {
- *s++ = ' ';
-
- if (*s == '\n') g.push(s, 1);
- }
- else *s++ = ' ';
- }
- else if (opt_escape::value && *s == '&')
- {
- s = strconv_escape(s, g);
- }
- else if (!*s)
- {
- return 0;
- }
- else ++s;
- }
- }
-
- static char_t* parse_eol(char_t* s, char_t end_quote)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
-
- 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 == '&')
- {
- s = strconv_escape(s, g);
- }
- else if (!*s)
- {
- return 0;
- }
- else ++s;
- }
- }
-
- static char_t* parse_simple(char_t* s, char_t end_quote)
- {
- gap g;
-
- while (true)
- {
- while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
-
- if (*s == end_quote)
- {
- *g.flush(s) = 0;
-
- return s + 1;
- }
- else if (opt_escape::value && *s == '&')
- {
- s = strconv_escape(s, g);
- }
- else if (!*s)
- {
- return 0;
- }
- else ++s;
- }
- }
- };
-
- PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
- {
- PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
-
- switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
- {
- case 0: return strconv_attribute_impl::parse_simple;
- case 1: return strconv_attribute_impl::parse_simple;
- case 2: return strconv_attribute_impl::parse_eol;
- case 3: return strconv_attribute_impl::parse_eol;
- case 4: return strconv_attribute_impl::parse_wconv;
- case 5: return strconv_attribute_impl::parse_wconv;
- case 6: return strconv_attribute_impl::parse_wconv;
- case 7: return strconv_attribute_impl::parse_wconv;
- case 8: return strconv_attribute_impl::parse_wnorm;
- case 9: return strconv_attribute_impl::parse_wnorm;
- case 10: return strconv_attribute_impl::parse_wnorm;
- case 11: return strconv_attribute_impl::parse_wnorm;
- case 12: return strconv_attribute_impl::parse_wnorm;
- 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: return 0; // should not get here
- }
- }
-
- inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)
- {
- xml_parse_result result;
- result.status = status;
- result.offset = offset;
-
- return result;
- }
-
- struct xml_parser
- {
- xml_allocator alloc;
- char_t* error_offset;
- xml_parse_status error_status;
-
- // Parser utilities.
- #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
- #define PUGI__OPTSET(OPT) ( optmsk & (OPT) )
- #define PUGI__PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
- #define PUGI__POPNODE() { cursor = cursor->parent; }
- #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }
- #define PUGI__SCANWHILE(X) { while ((X)) ++s; }
- #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; }
- #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0)
- #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); }
-
- xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
- {
- }
-
- // DOCTYPE consists of nested sections of the following possible types:
- // , ... ?>, "...", '...'
- //
- //
- // First group can not contain nested groups
- // Second group can contain nested groups of the same type
- // Third group can contain all other groups
- char_t* parse_doctype_primitive(char_t* s)
- {
- if (*s == '"' || *s == '\'')
- {
- // quoted string
- char_t ch = *s++;
- PUGI__SCANFOR(*s == ch);
- if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
- s++;
- }
- else if (s[0] == '<' && s[1] == '?')
- {
- // ... ?>
- s += 2;
- PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
- if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
- s += 2;
- }
- else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
- {
- s += 4;
- PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
- if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
- s += 4;
- }
- else PUGI__THROW_ERROR(status_bad_doctype, s);
-
- return s;
- }
-
- char_t* parse_doctype_ignore(char_t* s)
- {
- assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
- s++;
-
- while (*s)
- {
- if (s[0] == '<' && s[1] == '!' && s[2] == '[')
- {
- // nested ignore section
- s = parse_doctype_ignore(s);
- if (!s) return s;
- }
- else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
- {
- // ignore section end
- s += 3;
-
- return s;
- }
- else s++;
- }
-
- PUGI__THROW_ERROR(status_bad_doctype, s);
- }
-
- char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
- {
- assert(s[0] == '<' && s[1] == '!');
- s++;
-
- while (*s)
- {
- if (s[0] == '<' && s[1] == '!' && s[2] != '-')
- {
- if (s[2] == '[')
- {
- // ignore
- s = parse_doctype_ignore(s);
- if (!s) return s;
- }
- else
- {
- // some control group
- s = parse_doctype_group(s, endch, false);
- if (!s) return s;
- }
- }
- else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
- {
- // unknown tag (forbidden), or some primitive group
- s = parse_doctype_primitive(s);
- if (!s) return s;
- }
- else if (*s == '>')
- {
- s++;
-
- return s;
- }
- else s++;
- }
-
- if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
-
- return s;
- }
-
- char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)
- {
- // parse node contents, starting with exclamation mark
- ++s;
-
- if (*s == '-') // 'value = s; // Save the offset.
- }
-
- if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
- {
- s = strconv_comment(s, endch);
-
- if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
- }
- else
- {
- // Scan for terminating '-->'.
- PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));
- PUGI__CHECK_ERROR(status_bad_comment, s);
-
- if (PUGI__OPTSET(parse_comments))
- *s = 0; // Zero-terminate this segment at the first terminating '-'.
-
- s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
- }
- }
- else PUGI__THROW_ERROR(status_bad_comment, s);
- }
- else if (*s == '[')
- {
- // 'value = s; // Save the offset.
-
- if (PUGI__OPTSET(parse_eol))
- {
- s = strconv_cdata(s, endch);
-
- if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
- }
- else
- {
- // Scan for terminating ']]>'.
- PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
- PUGI__CHECK_ERROR(status_bad_cdata, s);
-
- *s++ = 0; // Zero-terminate this segment.
- }
- }
- else // Flagged for discard, but we still have to scan for the terminator.
- {
- // Scan for terminating ']]>'.
- PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
- PUGI__CHECK_ERROR(status_bad_cdata, s);
-
- ++s;
- }
-
- s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
- }
- else PUGI__THROW_ERROR(status_bad_cdata, s);
- }
- else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))
- {
- s -= 2;
-
- if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
-
- char_t* mark = s + 9;
-
- s = parse_doctype_group(s, endch, true);
- if (!s) return s;
-
- if (PUGI__OPTSET(parse_doctype))
- {
- while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;
-
- PUGI__PUSHNODE(node_doctype);
-
- cursor->value = mark;
-
- assert((s[0] == 0 && endch == '>') || s[-1] == '>');
- s[*s == 0 ? 0 : -1] = 0;
-
- PUGI__POPNODE();
- }
- }
- else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
- else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
- else PUGI__THROW_ERROR(status_unrecognized_tag, s);
-
- return s;
- }
-
- char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)
- {
- // load into registers
- xml_node_struct* cursor = ref_cursor;
- char_t ch = 0;
-
- // parse node contents, starting with question mark
- ++s;
-
- // read PI target
- char_t* target = s;
-
- if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
-
- PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));
- PUGI__CHECK_ERROR(status_bad_pi, s);
-
- // determine node type; stricmp / strcasecmp is not portable
- bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
-
- if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
- {
- if (declaration)
- {
- // disallow non top-level declarations
- if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
-
- PUGI__PUSHNODE(node_declaration);
- }
- else
- {
- PUGI__PUSHNODE(node_pi);
- }
-
- cursor->name = target;
-
- PUGI__ENDSEG();
-
- // parse value/attributes
- if (ch == '?')
- {
- // empty node
- if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
- s += (*s == '>');
-
- PUGI__POPNODE();
- }
- else if (PUGI__IS_CHARTYPE(ch, ct_space))
- {
- PUGI__SKIPWS();
-
- // scan for tag end
- char_t* value = s;
-
- PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
- PUGI__CHECK_ERROR(status_bad_pi, s);
-
- if (declaration)
- {
- // replace ending ? with / so that 'element' terminates properly
- *s = '/';
-
- // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES
- s = value;
- }
- else
- {
- // store value and step over >
- cursor->value = value;
- PUGI__POPNODE();
-
- PUGI__ENDSEG();
-
- s += (*s == '>');
- }
- }
- else PUGI__THROW_ERROR(status_bad_pi, s);
- }
- else
- {
- // scan for tag end
- PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
- PUGI__CHECK_ERROR(status_bad_pi, s);
-
- s += (s[1] == '>' ? 2 : 1);
- }
-
- // store from registers
- ref_cursor = cursor;
-
- return s;
- }
-
- char_t* parse(char_t* s, xml_node_struct* xmldoc, unsigned int optmsk, char_t endch)
- {
- 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 = xmldoc;
- char_t* mark = s;
-
- while (*s != 0)
- {
- if (*s == '<')
- {
- ++s;
-
- LOC_TAG:
- if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
- {
- PUGI__PUSHNODE(node_element); // Append a new node to the tree.
-
- cursor->name = s;
-
- PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
- PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
-
- if (ch == '>')
- {
- // end of tag
- }
- else if (PUGI__IS_CHARTYPE(ch, ct_space))
- {
- LOC_ATTRIBUTES:
- while (true)
- {
- PUGI__SKIPWS(); // Eat any whitespace.
-
- if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
- {
- xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute.
- if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
-
- a->name = s; // Save the offset.
-
- PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
- PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
- PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
- PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
- if (PUGI__IS_CHARTYPE(ch, ct_space))
- {
- PUGI__SKIPWS(); // Eat any whitespace.
- PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
- ch = *s;
- ++s;
- }
-
- if (ch == '=') // '<... #=...'
- {
- PUGI__SKIPWS(); // Eat any whitespace.
-
- if (*s == '"' || *s == '\'') // '<... #="...'
- {
- ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
- ++s; // Step over the quote.
- 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;
- // Whitespaces, / and > are ok, symbols and EOF are wrong,
- // everything else will be detected
- if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
- }
- else PUGI__THROW_ERROR(status_bad_attribute, s);
- }
- else PUGI__THROW_ERROR(status_bad_attribute, s);
- }
- else if (*s == '/')
- {
- ++s;
-
- if (*s == '>')
- {
- PUGI__POPNODE();
- s++;
- break;
- }
- else if (*s == 0 && endch == '>')
- {
- PUGI__POPNODE();
- break;
- }
- else PUGI__THROW_ERROR(status_bad_start_element, s);
- }
- else if (*s == '>')
- {
- ++s;
-
- break;
- }
- else if (*s == 0 && endch == '>')
- {
- break;
- }
- else PUGI__THROW_ERROR(status_bad_start_element, s);
- }
-
- // !!!
- }
- else if (ch == '/') // '<#.../'
- {
- if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
-
- PUGI__POPNODE(); // Pop.
-
- s += (*s == '>');
- }
- else if (ch == 0)
- {
- // 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);
- }
- else if (*s == '/')
- {
- ++s;
-
- char_t* name = cursor->name;
- if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);
-
- while (PUGI__IS_CHARTYPE(*s, ct_symbol))
- {
- if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);
- }
-
- 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);
- }
-
- PUGI__POPNODE(); // Pop.
-
- PUGI__SKIPWS();
-
- if (*s == 0)
- {
- if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
- }
- else
- {
- if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
- ++s;
- }
- }
- else if (*s == '?') // '...'
- {
- s = parse_question(s, cursor, optmsk, endch);
- if (!s) return s;
-
- assert(cursor);
- if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;
- }
- else if (*s == '!') // 'first_child) continue;
- }
- }
-
- s = mark;
-
- if (cursor->parent)
- {
- PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
- cursor->value = s; // Save the offset.
-
- s = strconv_pcdata(s);
-
- PUGI__POPNODE(); // Pop since this is a standalone.
-
- if (!*s) break;
- }
- else
- {
- PUGI__SCANFOR(*s == '<'); // '...<'
- if (!*s) break;
-
- ++s;
- }
-
- // We're after '<'
- goto LOC_TAG;
- }
- }
-
- // check that last tag is closed
- if (cursor != xmldoc) PUGI__THROW_ERROR(status_end_element_mismatch, s);
-
- return s;
- }
-
- static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk)
- {
- xml_document_struct* xmldoc = static_cast(root);
-
- // store buffer for offset_debug
- xmldoc->buffer = buffer;
-
- // early-out for empty documents
- if (length == 0) return make_parse_result(status_ok);
-
- // create parser on stack
- xml_parser parser(*xmldoc);
-
- // save last character and make buffer zero-terminated (speeds up parsing)
- char_t endch = buffer[length - 1];
- buffer[length - 1] = 0;
-
- // perform actual parsing
- parser.parse(buffer, xmldoc, optmsk, endch);
-
- xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
- assert(result.offset >= 0 && static_cast(result.offset) <= length);
-
- // update allocator state
- *static_cast(xmldoc) = parser.alloc;
-
- // since we removed last character, we have to handle the only possible false positive
- if (result && endch == '<')
- {
- // there's no possible well-formed document with < at the end
- return make_parse_result(status_unrecognized_tag, length);
- }
-
- return result;
- }
- };
-
- // Output facilities
- PUGI__FN xml_encoding get_write_native_encoding()
- {
- #ifdef PUGIXML_WCHAR_MODE
- return get_wchar_encoding();
- #else
- return encoding_utf8;
- #endif
- }
-
- PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
- {
- // replace wchar encoding with utf implementation
- if (encoding == encoding_wchar) return get_wchar_encoding();
-
- // replace utf16 encoding with utf16 with specific endianness
- if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
- // replace utf32 encoding with utf32 with specific endianness
- if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
- // only do autodetection if no explicit encoding is requested
- if (encoding != encoding_auto) return encoding;
-
- // assume utf8 encoding
- return encoding_utf8;
- }
-
-#ifdef PUGIXML_WCHAR_MODE
- PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
- {
- assert(length > 0);
-
- // discard last character if it's the lead of a surrogate pair
- return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;
- }
-
- PUGI__FN size_t convert_buffer(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
- {
- // only endian-swapping is required
- if (need_endian_swap_utf(encoding, get_wchar_encoding()))
- {
- convert_wchar_endian_swap(r_char, data, length);
-
- return length * sizeof(char_t);
- }
-
- // convert to utf8
- if (encoding == encoding_utf8)
- {
- uint8_t* dest = r_u8;
- uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest);
-
- return static_cast(end - dest);
- }
-
- // convert to utf16
- if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
- {
- uint16_t* dest = r_u16;
-
- // convert to native utf16
- uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest);
-
- // swap if necessary
- xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
- if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest));
-
- return static_cast(end - dest) * sizeof(uint16_t);
- }
-
- // convert to utf32
- if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
- {
- uint32_t* dest = r_u32;
-
- // convert to native utf32
- uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest);
-
- // swap if necessary
- xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
- if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest));
-
- return static_cast(end - dest) * sizeof(uint32_t);
- }
-
- // convert to latin1
- if (encoding == encoding_latin1)
- {
- uint8_t* dest = r_u8;
- uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest);
-
- return static_cast(end - dest);
- }
-
- assert(!"Invalid encoding");
- return 0;
- }
-#else
- PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
- {
- assert(length > 4);
-
- for (size_t i = 1; i <= 4; ++i)
- {
- uint8_t ch = static_cast(data[length - i]);
-
- // either a standalone character or a leading one
- if ((ch & 0xc0) != 0x80) return length - i;
- }
-
- // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk
- return length;
- }
-
- PUGI__FN size_t convert_buffer(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
- {
- if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
- {
- uint16_t* dest = r_u16;
-
- // convert to native utf16
- uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest);
-
- // swap if necessary
- xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
- if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest));
-
- return static_cast(end - dest) * sizeof(uint16_t);
- }
-
- if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
- {
- uint32_t* dest = r_u32;
-
- // convert to native utf32
- uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest);
-
- // swap if necessary
- xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
- if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest));
-
- return static_cast(end - dest) * sizeof(uint32_t);
- }
-
- if (encoding == encoding_latin1)
- {
- uint8_t* dest = r_u8;
- uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest);
-
- return static_cast(end - dest);
- }
-
- assert(!"Invalid encoding");
- return 0;
- }
-#endif
-
- class xml_buffered_writer
- {
- xml_buffered_writer(const xml_buffered_writer&);
- xml_buffered_writer& operator=(const xml_buffered_writer&);
-
- public:
- xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
- {
- PUGI__STATIC_ASSERT(bufcapacity >= 8);
- }
-
- ~xml_buffered_writer()
- {
- flush();
- }
-
- void flush()
- {
- flush(buffer, bufsize);
- bufsize = 0;
- }
-
- void flush(const char_t* data, size_t size)
- {
- if (size == 0) return;
-
- // fast path, just write data
- if (encoding == get_write_native_encoding())
- writer.write(data, size * sizeof(char_t));
- else
- {
- // convert chunk
- size_t result = convert_buffer(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
- assert(result <= sizeof(scratch));
-
- // write data
- writer.write(scratch.data_u8, result);
- }
- }
-
- void write(const char_t* data, size_t length)
- {
- if (bufsize + length > bufcapacity)
- {
- // flush the remaining buffer contents
- flush();
-
- // handle large chunks
- if (length > bufcapacity)
- {
- if (encoding == get_write_native_encoding())
- {
- // fast path, can just write data chunk
- writer.write(data, length * sizeof(char_t));
- return;
- }
-
- // need to convert in suitable chunks
- while (length > bufcapacity)
- {
- // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
- // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
- size_t chunk_size = get_valid_length(data, bufcapacity);
-
- // convert chunk and write
- flush(data, chunk_size);
-
- // iterate
- data += chunk_size;
- length -= chunk_size;
- }
-
- // small tail is copied below
- bufsize = 0;
- }
- }
-
- memcpy(buffer + bufsize, data, length * sizeof(char_t));
- bufsize += length;
- }
-
- void write(const char_t* data)
- {
- write(data, strlength(data));
- }
-
- void write(char_t d0)
- {
- if (bufsize + 1 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- bufsize += 1;
- }
-
- void write(char_t d0, char_t d1)
- {
- if (bufsize + 2 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- buffer[bufsize + 1] = d1;
- bufsize += 2;
- }
-
- void write(char_t d0, char_t d1, char_t d2)
- {
- if (bufsize + 3 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- buffer[bufsize + 1] = d1;
- buffer[bufsize + 2] = d2;
- bufsize += 3;
- }
-
- void write(char_t d0, char_t d1, char_t d2, char_t d3)
- {
- if (bufsize + 4 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- buffer[bufsize + 1] = d1;
- buffer[bufsize + 2] = d2;
- buffer[bufsize + 3] = d3;
- bufsize += 4;
- }
-
- void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
- {
- if (bufsize + 5 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- buffer[bufsize + 1] = d1;
- buffer[bufsize + 2] = d2;
- buffer[bufsize + 3] = d3;
- buffer[bufsize + 4] = d4;
- bufsize += 5;
- }
-
- void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
- {
- if (bufsize + 6 > bufcapacity) flush();
-
- buffer[bufsize + 0] = d0;
- buffer[bufsize + 1] = d1;
- buffer[bufsize + 2] = d2;
- buffer[bufsize + 3] = d3;
- buffer[bufsize + 4] = d4;
- buffer[bufsize + 5] = d5;
- bufsize += 6;
- }
-
- // utf8 maximum expansion: x4 (-> utf32)
- // utf16 maximum expansion: x2 (-> utf32)
- // utf32 maximum expansion: x1
- enum
- {
- bufcapacitybytes =
- #ifdef PUGIXML_MEMORY_OUTPUT_STACK
- PUGIXML_MEMORY_OUTPUT_STACK
- #else
- 10240
- #endif
- ,
- bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)
- };
-
- char_t buffer[bufcapacity];
-
- union
- {
- uint8_t data_u8[4 * bufcapacity];
- uint16_t data_u16[2 * bufcapacity];
- uint32_t data_u32[bufcapacity];
- char_t data_char[bufcapacity];
- } scratch;
-
- xml_writer& writer;
- size_t bufsize;
- xml_encoding encoding;
- };
-
- PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)
- {
- while (*s)
- {
- const char_t* prev = s;
-
- // While *s is a usual symbol
- while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;
-
- writer.write(prev, static_cast(s - prev));
-
- switch (*s)
- {
- case 0: break;
- case '&':
- writer.write('&', 'a', 'm', 'p', ';');
- ++s;
- break;
- case '<':
- writer.write('&', 'l', 't', ';');
- ++s;
- break;
- case '>':
- writer.write('&', 'g', 't', ';');
- ++s;
- break;
- case '"':
- writer.write('&', 'q', 'u', 'o', 't', ';');
- ++s;
- break;
- default: // s is not a usual symbol
- {
- unsigned int ch = static_cast(*s++);
- assert(ch < 32);
-
- writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';');
- }
- }
- }
- }
-
- PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
- {
- if (flags & format_no_escapes)
- writer.write(s);
- else
- text_output_escaped(writer, s, type);
- }
-
- PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
- {
- do
- {
- writer.write('<', '!', '[', 'C', 'D');
- writer.write('A', 'T', 'A', '[');
-
- const char_t* prev = s;
-
- // look for ]]> sequence - we can't output it as is since it terminates CDATA
- while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;
-
- // skip ]] if we stopped at ]]>, > will go to the next CDATA section
- if (*s) s += 2;
-
- writer.write(prev, static_cast(s - prev));
-
- writer.write(']', ']', '>');
- }
- while (*s);
- }
-
- PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
- {
- const char_t* default_name = PUGIXML_TEXT(":anonymous");
-
- for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
- {
- writer.write(' ');
- writer.write(a.name()[0] ? a.name() : default_name);
- writer.write('=', '"');
-
- text_output(writer, a.value(), ctx_special_attr, flags);
-
- writer.write('"');
- }
- }
-
- PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)
- {
- const char_t* default_name = PUGIXML_TEXT(":anonymous");
-
- if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
- for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-
- switch (node.type())
- {
- case node_document:
- {
- for (xml_node n = node.first_child(); n; n = n.next_sibling())
- node_output(writer, n, indent, flags, depth);
- break;
- }
-
- case node_element:
- {
- const char_t* name = node.name()[0] ? node.name() : default_name;
-
- writer.write('<');
- writer.write(name);
-
- node_output_attributes(writer, node, flags);
-
- if (flags & format_raw)
- {
- if (!node.first_child())
- writer.write(' ', '/', '>');
- else
- {
- writer.write('>');
-
- for (xml_node n = node.first_child(); n; n = n.next_sibling())
- node_output(writer, n, indent, flags, depth + 1);
-
- writer.write('<', '/');
- writer.write(name);
- writer.write('>');
- }
- }
- else if (!node.first_child())
- writer.write(' ', '/', '>', '\n');
- else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
- {
- writer.write('>');
-
- if (node.first_child().type() == node_pcdata)
- text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
- else
- text_output_cdata(writer, node.first_child().value());
-
- writer.write('<', '/');
- writer.write(name);
- writer.write('>', '\n');
- }
- else
- {
- writer.write('>', '\n');
-
- for (xml_node n = node.first_child(); n; n = n.next_sibling())
- node_output(writer, n, indent, flags, depth + 1);
-
- if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
- for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-
- writer.write('<', '/');
- writer.write(name);
- writer.write('>', '\n');
- }
-
- break;
- }
-
- case node_pcdata:
- text_output(writer, node.value(), ctx_special_pcdata, flags);
- if ((flags & format_raw) == 0) writer.write('\n');
- break;
-
- case node_cdata:
- text_output_cdata(writer, node.value());
- if ((flags & format_raw) == 0) writer.write('\n');
- break;
-
- case node_comment:
- writer.write('<', '!', '-', '-');
- writer.write(node.value());
- writer.write('-', '-', '>');
- if ((flags & format_raw) == 0) writer.write('\n');
- break;
-
- case node_pi:
- case node_declaration:
- writer.write('<', '?');
- writer.write(node.name()[0] ? node.name() : default_name);
-
- if (node.type() == node_declaration)
- {
- node_output_attributes(writer, node, flags);
- }
- else if (node.value()[0])
- {
- writer.write(' ');
- writer.write(node.value());
- }
-
- writer.write('?', '>');
- if ((flags & format_raw) == 0) writer.write('\n');
- break;
-
- case node_doctype:
- writer.write('<', '!', 'D', 'O', 'C');
- writer.write('T', 'Y', 'P', 'E');
-
- if (node.value()[0])
- {
- writer.write(' ');
- writer.write(node.value());
- }
-
- writer.write('>');
- if ((flags & format_raw) == 0) writer.write('\n');
- break;
-
- default:
- assert(!"Invalid node type");
- }
- }
-
- inline bool has_declaration(const xml_node& node)
- {
- for (xml_node child = node.first_child(); child; child = child.next_sibling())
- {
- xml_node_type type = child.type();
-
- if (type == node_declaration) return true;
- if (type == node_element) return false;
- }
-
- return false;
- }
-
- inline bool allow_insert_child(xml_node_type parent, xml_node_type child)
- {
- if (parent != node_document && parent != node_element) return false;
- if (child == node_document || child == node_null) return false;
- if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;
-
- return true;
- }
-
- PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)
- {
- assert(dest.type() == source.type());
-
- switch (source.type())
- {
- case node_element:
- {
- dest.set_name(source.name());
-
- for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
- dest.append_attribute(a.name()).set_value(a.value());
-
- for (xml_node c = source.first_child(); c; c = c.next_sibling())
- {
- if (c == skip) continue;
-
- xml_node cc = dest.append_child(c.type());
- assert(cc);
-
- recursive_copy_skip(cc, c, skip);
- }
-
- break;
- }
-
- case node_pcdata:
- case node_cdata:
- case node_comment:
- case node_doctype:
- dest.set_value(source.value());
- break;
-
- case node_pi:
- dest.set_name(source.name());
- dest.set_value(source.value());
- break;
-
- case node_declaration:
- {
- dest.set_name(source.name());
-
- for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
- dest.append_attribute(a.name()).set_value(a.value());
-
- break;
- }
-
- default:
- assert(!"Invalid node type");
- }
- }
-
- inline bool is_text_node(xml_node_struct* node)
- {
- xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1);
-
- return type == node_pcdata || type == node_cdata;
- }
-
- // get value with conversion functions
- PUGI__FN unsigned short get_value_ushort(const char_t* value, unsigned short def)
- {
- if (!value) return def;
-
- #ifdef PUGIXML_WCHAR_MODE
- return static_cast(wcstoul(value, 0, 10));
- #else
- return static_cast(strtoul(value, 0, 10));
- #endif
- }
-
- PUGI__FN int get_value_int(const char_t* value, int def)
- {
- if (!value) return def;
-
- #ifdef PUGIXML_WCHAR_MODE
- return static_cast(wcstol(value, 0, 10));
- #else
- return static_cast(strtol(value, 0, 10));
- #endif
- }
-
- PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)
- {
- if (!value) return def;
-
- #ifdef PUGIXML_WCHAR_MODE
- return static_cast(wcstoul(value, 0, 10));
- #else
- return static_cast(strtoul(value, 0, 10));
- #endif
- }
-
- PUGI__FN double get_value_double(const char_t* value, double def)
- {
- if (!value) return def;
-
- #ifdef PUGIXML_WCHAR_MODE
- return wcstod(value, 0);
- #else
- return strtod(value, 0);
- #endif
- }
-
- PUGI__FN float get_value_float(const char_t* value, float def)
- {
- if (!value) return def;
-
- #ifdef PUGIXML_WCHAR_MODE
- return static_cast(wcstod(value, 0));
- #else
- return static_cast(strtod(value, 0));
- #endif
- }
-
- PUGI__FN bool get_value_bool(const char_t* value, bool def)
- {
- if (!value) return def;
-
- // only look at first char
- char_t first = *value;
-
- // 1*, t* (true), T* (True), y* (yes), Y* (YES)
- return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
- }
-
- // set value with conversion functions
- PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
- {
- #ifdef PUGIXML_WCHAR_MODE
- char_t wbuf[128];
- impl::widen_ascii(wbuf, buf);
-
- return strcpy_insitu(dest, header, header_mask, wbuf);
- #else
- return strcpy_insitu(dest, header, header_mask, buf);
- #endif
- }
-
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
- {
- char buf[128];
- sprintf(buf, "%d", value);
-
- return set_value_buffer(dest, header, header_mask, buf);
- }
-
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
- {
- char buf[128];
- sprintf(buf, "%u", value);
-
- return set_value_buffer(dest, header, header_mask, buf);
- }
-
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
- {
- char buf[128];
- sprintf(buf, "%g", value);
-
- return set_value_buffer(dest, header, header_mask, buf);
- }
-
- PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
- {
- return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
- }
-
- // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
- PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
- {
- #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
- // there are 64-bit versions of fseek/ftell, let's use them
- typedef __int64 length_type;
-
- _fseeki64(file, 0, SEEK_END);
- length_type length = _ftelli64(file);
- _fseeki64(file, 0, SEEK_SET);
- #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)
- // there are 64-bit versions of fseek/ftell, let's use them
- typedef off64_t length_type;
-
- fseeko64(file, 0, SEEK_END);
- length_type length = ftello64(file);
- fseeko64(file, 0, SEEK_SET);
- #else
- // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.
- typedef long length_type;
-
- fseek(file, 0, SEEK_END);
- length_type length = ftell(file);
- fseek(file, 0, SEEK_SET);
- #endif
-
- // check for I/O errors
- if (length < 0) return status_io_error;
-
- // check for overflow
- size_t result = static_cast(length);
-
- if (static_cast(result) != length) return status_out_of_memory;
-
- // finalize
- out_result = result;
-
- return status_ok;
- }
-
- PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
- {
- if (!file) return make_parse_result(status_file_not_found);
-
- // get file size (can result in I/O errors)
- size_t size = 0;
- xml_parse_status size_status = get_file_size(file, size);
-
- if (size_status != status_ok)
- {
- fclose(file);
- return make_parse_result(size_status);
- }
-
- // allocate buffer for the whole file
- char* contents = static_cast(xml_memory::allocate(size > 0 ? size : 1));
-
- if (!contents)
- {
- fclose(file);
- return make_parse_result(status_out_of_memory);
- }
-
- // read file in memory
- size_t read_size = fread(contents, 1, size, file);
- fclose(file);
-
- if (read_size != size)
- {
- xml_memory::deallocate(contents);
- return make_parse_result(status_io_error);
- }
-
- return doc.load_buffer_inplace_own(contents, size, options, encoding);
- }
-
-#ifndef PUGIXML_NO_STL
- template struct xml_stream_chunk
- {
- static xml_stream_chunk* create()
- {
- void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
-
- return new (memory) xml_stream_chunk();
- }
-
- static void destroy(void* ptr)
- {
- xml_stream_chunk* chunk = static_cast(ptr);
-
- // free chunk chain
- while (chunk)
- {
- xml_stream_chunk* next = chunk->next;
- xml_memory::deallocate(chunk);
- chunk = next;
- }
- }
-
- xml_stream_chunk(): next(0), size(0)
- {
- }
-
- xml_stream_chunk* next;
- size_t size;
-
- T data[xml_memory_page_size / sizeof(T)];
- };
-
- template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size)
- {
- buffer_holder chunks(0, xml_stream_chunk::destroy);
-
- // read file to a chunk list
- size_t total = 0;
- xml_stream_chunk* last = 0;
-
- while (!stream.eof())
- {
- // allocate new chunk
- xml_stream_chunk* chunk = xml_stream_chunk::create();
- if (!chunk) return status_out_of_memory;
-
- // append chunk to list
- if (last) last = last->next = chunk;
- else chunks.data = last = chunk;
-
- // read data to chunk
- stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T)));
- chunk->size = static_cast(stream.gcount()) * sizeof(T);
-
- // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors
- if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
-
- // guard against huge files (chunk size is small enough to make this overflow check work)
- if (total + chunk->size < total) return status_out_of_memory;
- total += chunk->size;
- }
-
- // copy chunk list to a contiguous buffer
- char* buffer = static_cast(xml_memory::allocate(total));
- if (!buffer) return status_out_of_memory;
-
- char* write = buffer;
-
- for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next)
- {
- assert(write + chunk->size <= buffer + total);
- memcpy(write, chunk->data, chunk->size);
- write += chunk->size;
- }
-
- assert(write == buffer + total);
-
- // return buffer
- *out_buffer = buffer;
- *out_size = total;
-
- return status_ok;
- }
-
- template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size)
- {
- // get length of remaining data in stream
- typename std::basic_istream::pos_type pos = stream.tellg();
- stream.seekg(0, std::ios::end);
- std::streamoff length = stream.tellg() - pos;
- stream.seekg(pos);
-
- if (stream.fail() || pos < 0) return status_io_error;
-
- // guard against huge files
- size_t read_length = static_cast(length);
-
- if (static_cast(read_length) != length || length < 0) return status_out_of_memory;
-
- // read stream data into memory (guard against stream exceptions with buffer holder)
- buffer_holder buffer(xml_memory::allocate((read_length > 0 ? read_length : 1) * sizeof(T)), xml_memory::deallocate);
- if (!buffer.data) return status_out_of_memory;
-
- stream.read(static_cast(buffer.data), static_cast(read_length));
-
- // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors
- if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
-
- // return buffer
- size_t actual_length = static_cast(stream.gcount());
- assert(actual_length <= read_length);
-
- *out_buffer = buffer.release();
- *out_size = actual_length * sizeof(T);
-
- return status_ok;
- }
-
- template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding)
- {
- void* buffer = 0;
- size_t size = 0;
-
- // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
- xml_parse_status status = (stream.tellg() < 0) ? load_stream_data_noseek(stream, &buffer, &size) : load_stream_data_seek(stream, &buffer, &size);
- if (status != status_ok) return make_parse_result(status);
-
- return doc.load_buffer_inplace_own(buffer, size, options, encoding);
- }
-#endif
-
-#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
- PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
- {
- return _wfopen(path, mode);
- }
-#else
- PUGI__FN char* convert_path_heap(const wchar_t* str)
- {
- assert(str);
-
- // first pass: get length in utf8 characters
- size_t length = wcslen(str);
- size_t size = as_utf8_begin(str, length);
-
- // allocate resulting string
- char* result = static_cast(xml_memory::allocate(size + 1));
- if (!result) return 0;
-
- // second pass: convert to utf8
- as_utf8_end(result, size, str, length);
-
- return result;
- }
-
- PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
- {
- // there is no standard function to open wide paths, so our best bet is to try utf8 path
- char* path_utf8 = convert_path_heap(path);
- if (!path_utf8) return 0;
-
- // convert mode to ASCII (we mirror _wfopen interface)
- char mode_ascii[4] = {0};
- for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]);
-
- // try to open the utf8 path
- FILE* result = fopen(path_utf8, mode_ascii);
-
- // free dummy buffer
- xml_memory::deallocate(path_utf8);
-
- return result;
- }
-#endif
-
- PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
- {
- if (!file) return false;
-
- xml_writer_file writer(file);
- doc.save(writer, indent, flags, encoding);
-
- int result = ferror(file);
-
- fclose(file);
-
- return result == 0;
- }
-PUGI__NS_END
-
-namespace pugi
-{
- PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)
- {
- }
-
- PUGI__FN void xml_writer_file::write(const void* data, size_t size)
- {
- size_t result = fwrite(data, 1, size, static_cast(file));
- (void)!result; // unfortunately we can't do proper error handling here
- }
-
-#ifndef PUGIXML_NO_STL
- PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0)
- {
- }
-
- PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream)
- {
- }
-
- PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
- {
- if (narrow_stream)
- {
- assert(!wide_stream);
- narrow_stream->write(reinterpret_cast