5 |
6 | // tag::code[]
7 | bool load_preprocess(pugi::xml_document& doc, const char* path);
8 |
9 | bool preprocess(pugi::xml_node node)
10 | {
11 | for (pugi::xml_node child = node.first_child(); child; )
12 | {
13 | if (child.type() == pugi::node_pi && strcmp(child.name(), "include") == 0)
14 | {
15 | pugi::xml_node include = child;
16 |
17 | // load new preprocessed document (note: ideally this should handle relative paths)
18 | const char* path = include.value();
19 |
20 | pugi::xml_document doc;
21 | if (!load_preprocess(doc, path)) return false;
22 |
23 | // insert the comment marker above include directive
24 | node.insert_child_before(pugi::node_comment, include).set_value(path);
25 |
26 | // copy the document above the include directive (this retains the original order!)
27 | for (pugi::xml_node ic = doc.first_child(); ic; ic = ic.next_sibling())
28 | {
29 | node.insert_copy_before(ic, include);
30 | }
31 |
32 | // remove the include node and move to the next child
33 | child = child.next_sibling();
34 |
35 | node.remove_child(include);
36 | }
37 | else
38 | {
39 | if (!preprocess(child)) return false;
40 |
41 | child = child.next_sibling();
42 | }
43 | }
44 |
45 | return true;
46 | }
47 |
48 | bool load_preprocess(pugi::xml_document& doc, const char* path)
49 | {
50 | pugi::xml_parse_result result = doc.load_file(path, pugi::parse_default | pugi::parse_pi); // for
51 |
52 | return result ? preprocess(doc) : false;
53 | }
54 | // end::code[]
55 |
56 | int main()
57 | {
58 | pugi::xml_document doc;
59 | if (!load_preprocess(doc, "character.xml")) return -1;
60 |
61 | doc.print(std::cout);
62 | }
63 |
64 | // vim:et
65 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'master'
7 | pull_request:
8 |
9 | jobs:
10 | unix:
11 | strategy:
12 | matrix:
13 | os: [ubuntu, macos]
14 | compiler: [g++, clang++]
15 | defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
16 | exclude:
17 | - os: macos
18 | compiler: g++
19 | name: ${{matrix.os}} (${{matrix.compiler}}, ${{matrix.defines}})
20 | runs-on: ${{matrix.os}}-latest
21 | steps:
22 | - uses: actions/checkout@v1
23 | - name: make test
24 | run: |
25 | export CXX=${{matrix.compiler}}
26 | make test cxxstd=c++11 defines=${{matrix.defines}} config=release -j2
27 | make test cxxstd=c++98 defines=${{matrix.defines}} config=debug -j2
28 | make test defines=${{matrix.defines}} config=sanitize -j2
29 | - name: make coverage
30 | if: ${{!(matrix.os == 'ubuntu' && matrix.compiler == 'clang++')}} # linux/clang produces coverage info gcov can't parse
31 | run: |
32 | export CXX=${{matrix.compiler}}
33 | make test defines=${{matrix.defines}} config=coverage -j2
34 | bash <(curl -s https://codecov.io/bash) -f pugixml.cpp.gcov -X search -t ${{secrets.CODECOV_TOKEN}} -B ${{github.ref}}
35 |
36 | windows:
37 | runs-on: windows-latest
38 | strategy:
39 | matrix:
40 | arch: [Win32, x64]
41 | defines: [standard, PUGIXML_WCHAR_MODE, PUGIXML_COMPACT, PUGIXML_NO_EXCEPTIONS]
42 | steps:
43 | - uses: actions/checkout@v1
44 | - name: cmake configure
45 | run: cmake . -DPUGIXML_BUILD_TESTS=ON -D${{matrix.defines}}=ON -A ${{matrix.arch}}
46 | - name: cmake test
47 | shell: bash # necessary for fail-fast
48 | run: |
49 | cmake --build . -- -property:Configuration=Debug -verbosity:minimal
50 | Debug/pugixml-check.exe
51 | cmake --build . -- -property:Configuration=Release -verbosity:minimal
52 | Release/pugixml-check.exe
53 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | pugixml 1.12 - an XML processing library
2 |
3 | Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
4 | Report bugs and download new versions at https://pugixml.org/
5 |
6 | This is the distribution of pugixml, which is a C++ XML processing library,
7 | which consists of a DOM-like interface with rich traversal/modification
8 | capabilities, an extremely fast XML parser which constructs the DOM tree from
9 | an XML file/buffer, and an XPath 1.0 implementation for complex data-driven
10 | tree queries. Full Unicode support is also available, with Unicode interface
11 | variants and conversions between different Unicode encodings (which happen
12 | automatically during parsing/saving).
13 |
14 | The distribution contains the following folders:
15 |
16 | docs/ - documentation
17 | docs/samples - pugixml usage examples
18 | docs/quickstart.html - quick start guide
19 | docs/manual.html - complete manual
20 |
21 | scripts/ - project files for IDE/build systems
22 |
23 | src/ - header and source files
24 |
25 | readme.txt - this file.
26 |
27 | This library is distributed under the MIT License:
28 |
29 | Copyright (c) 2006-2022 Arseny Kapoulkine
30 |
31 | Permission is hereby granted, free of charge, to any person
32 | obtaining a copy of this software and associated documentation
33 | files (the "Software"), to deal in the Software without
34 | restriction, including without limitation the rights to use,
35 | copy, modify, merge, publish, distribute, sublicense, and/or sell
36 | copies of the Software, and to permit persons to whom the
37 | Software is furnished to do so, subject to the following
38 | conditions:
39 |
40 | The above copyright notice and this permission notice shall be
41 | included in all copies or substantial portions of the Software.
42 |
43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
44 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
45 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
46 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
47 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
48 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
49 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
50 | OTHER DEALINGS IN THE SOFTWARE.
51 |
--------------------------------------------------------------------------------
/tests/data/utftest_utf8_clean.xml:
--------------------------------------------------------------------------------
1 | <週報>
2 | The world has many languages
3 | Мир имеет много языков
4 | el mundo tiene muchos idiomas
5 | 世界有很多语言
6 | <Русский название="name" ценность="value"><имеет>Русский>
7 | <汉语 名字="name" 价值="value">世界有很多语言𤭢汉语>
8 | quot;Mëtæl!quot;
9 | <ä>Umlaut Elementä>
10 |
11 | <年月週>
12 | <年度>1997年度>
13 | <月度>1月度>
14 | <週>1週>
15 | 年月週>
16 |
17 | <氏名>
18 | <氏>山田氏>
19 | <名>太郎名>
20 | 氏名>
21 |
22 | <業務報告リスト>
23 | <業務報告>
24 | <業務名>XMLエディターの作成業務名>
25 | <業務コード>X3355-23業務コード>
26 | <工数管理>
27 | <見積もり工数>1600見積もり工数>
28 | <実績工数>320実績工数>
29 | <当月見積もり工数>160当月見積もり工数>
30 | <当月実績工数>24当月実績工数>
31 | 工数管理>
32 | <予定項目リスト>
33 | <予定項目>
34 | XMLエディターの基本仕様の作成
35 | 予定項目>
36 | 予定項目リスト>
37 | <実施事項リスト>
38 | <実施事項>
39 | XMLエディターの基本仕様の作成
40 | 実施事項>
41 | <実施事項>
42 | 競合他社製品の機能調査
43 | 実施事項>
44 | 実施事項リスト>
45 | <上長への要請事項リスト>
46 | <上長への要請事項>
47 | 特になし
48 | 上長への要請事項>
49 | 上長への要請事項リスト>
50 | <問題点対策>
51 | XMLとは何かわからない。
52 | 問題点対策>
53 | 業務報告>
54 |
55 | <業務報告>
56 | <業務名>検索エンジンの開発業務名>
57 | <業務コード>S8821-76業務コード>
58 | <工数管理>
59 | <見積もり工数>120見積もり工数>
60 | <実績工数>6実績工数>
61 | <当月見積もり工数>32当月見積もり工数>
62 | <当月実績工数>2当月実績工数>
63 | 工数管理>
64 | <予定項目リスト>
65 | <予定項目>
66 | gooの機能を調べてみる
67 | 予定項目>
68 | 予定項目リスト>
69 | <実施事項リスト>
70 | <実施事項>
71 | 更に、どういう検索エンジンがあるか調査する
72 | 実施事項>
73 | 実施事項リスト>
74 | <上長への要請事項リスト>
75 | <上長への要請事項>
76 | 開発をするのはめんどうなので、Yahoo!を買収して下さい。
77 | 上長への要請事項>
78 | 上長への要請事項リスト>
79 | <問題点対策>
80 | 検索エンジンで車を走らせることができない。(要調査)
81 | 問題点対策>
82 | 業務報告>
83 | 業務報告リスト>
84 | 週報>
--------------------------------------------------------------------------------
/tests/data/utftest_utf8_nodecl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | <週報>
4 | The world has many languages
5 | Мир имеет много языков
6 | el mundo tiene muchos idiomas
7 | 世界有很多语言
8 | <Русский название="name" ценность="value"><имеет>Русский>
9 | <汉语 名字="name" 价值="value">世界有很多语言𤭢汉语>
10 | "Mëtæl!"
11 | <ä>Umlaut Elementä>
12 |
13 | <年月週>
14 | <年度>1997年度>
15 | <月度>1月度>
16 | <週>1週>
17 | 年月週>
18 |
19 | <氏名>
20 | <氏>山田氏>
21 | <名>太郎名>
22 | 氏名>
23 |
24 | <業務報告リスト>
25 | <業務報告>
26 | <業務名>XMLエディターの作成業務名>
27 | <業務コード>X3355-23業務コード>
28 | <工数管理>
29 | <見積もり工数>1600見積もり工数>
30 | <実績工数>320実績工数>
31 | <当月見積もり工数>160当月見積もり工数>
32 | <当月実績工数>24当月実績工数>
33 | 工数管理>
34 | <予定項目リスト>
35 | <予定項目>
36 | XMLエディターの基本仕様の作成
37 | 予定項目>
38 | 予定項目リスト>
39 | <実施事項リスト>
40 | <実施事項>
41 | XMLエディターの基本仕様の作成
42 | 実施事項>
43 | <実施事項>
44 | 競合他社製品の機能調査
45 | 実施事項>
46 | 実施事項リスト>
47 | <上長への要請事項リスト>
48 | <上長への要請事項>
49 | 特になし
50 | 上長への要請事項>
51 | 上長への要請事項リスト>
52 | <問題点対策>
53 | XMLとは何かわからない。
54 | 問題点対策>
55 | 業務報告>
56 |
57 | <業務報告>
58 | <業務名>検索エンジンの開発業務名>
59 | <業務コード>S8821-76業務コード>
60 | <工数管理>
61 | <見積もり工数>120見積もり工数>
62 | <実績工数>6実績工数>
63 | <当月見積もり工数>32当月見積もり工数>
64 | <当月実績工数>2当月実績工数>
65 | 工数管理>
66 | <予定項目リスト>
67 | <予定項目>
68 | gooの機能を調べてみる
69 | 予定項目>
70 | 予定項目リスト>
71 | <実施事項リスト>
72 | <実施事項>
73 | 更に、どういう検索エンジンがあるか調査する
74 | 実施事項>
75 | 実施事項リスト>
76 | <上長への要請事項リスト>
77 | <上長への要請事項>
78 | 開発をするのはめんどうなので、Yahoo!を買収して下さい。
79 | 上長への要請事項>
80 | 上長への要請事項リスト>
81 | <問題点対策>
82 | 検索エンジンで車を走らせることができない。(要調査)
83 | 問題点対策>
84 | 業務報告>
85 | 業務報告リスト>
86 | 週報>
87 |
--------------------------------------------------------------------------------
/tests/data/utftest_utf8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <週報>
5 | The world has many languages
6 | Мир имеет много языков
7 | el mundo tiene muchos idiomas
8 | 世界有很多语言
9 | <Русский название="name" ценность="value"><имеет>Русский>
10 | <汉语 名字="name" 价值="value">世界有很多语言𤭢汉语>
11 | "Mëtæl!"
12 | <ä>Umlaut Elementä>
13 |
14 | <年月週>
15 | <年度>1997年度>
16 | <月度>1月度>
17 | <週>1週>
18 | 年月週>
19 |
20 | <氏名>
21 | <氏>山田氏>
22 | <名>太郎名>
23 | 氏名>
24 |
25 | <業務報告リスト>
26 | <業務報告>
27 | <業務名>XMLエディターの作成業務名>
28 | <業務コード>X3355-23業務コード>
29 | <工数管理>
30 | <見積もり工数>1600見積もり工数>
31 | <実績工数>320実績工数>
32 | <当月見積もり工数>160当月見積もり工数>
33 | <当月実績工数>24当月実績工数>
34 | 工数管理>
35 | <予定項目リスト>
36 | <予定項目>
37 | XMLエディターの基本仕様の作成
38 | 予定項目>
39 | 予定項目リスト>
40 | <実施事項リスト>
41 | <実施事項>
42 | XMLエディターの基本仕様の作成
43 | 実施事項>
44 | <実施事項>
45 | 競合他社製品の機能調査
46 | 実施事項>
47 | 実施事項リスト>
48 | <上長への要請事項リスト>
49 | <上長への要請事項>
50 | 特になし
51 | 上長への要請事項>
52 | 上長への要請事項リスト>
53 | <問題点対策>
54 | XMLとは何かわからない。
55 | 問題点対策>
56 | 業務報告>
57 |
58 | <業務報告>
59 | <業務名>検索エンジンの開発業務名>
60 | <業務コード>S8821-76業務コード>
61 | <工数管理>
62 | <見積もり工数>120見積もり工数>
63 | <実績工数>6実績工数>
64 | <当月見積もり工数>32当月見積もり工数>
65 | <当月実績工数>2当月実績工数>
66 | 工数管理>
67 | <予定項目リスト>
68 | <予定項目>
69 | gooの機能を調べてみる
70 | 予定項目>
71 | 予定項目リスト>
72 | <実施事項リスト>
73 | <実施事項>
74 | 更に、どういう検索エンジンがあるか調査する
75 | 実施事項>
76 | 実施事項リスト>
77 | <上長への要請事項リスト>
78 | <上長への要請事項>
79 | 開発をするのはめんどうなので、Yahoo!を買収して下さい。
80 | 上長への要請事項>
81 | 上長への要請事項リスト>
82 | <問題点対策>
83 | 検索エンジンで車を走らせることができない。(要調査)
84 | 問題点対策>
85 | 業務報告>
86 | 業務報告リスト>
87 | 週報>
88 |
--------------------------------------------------------------------------------
/tests/data/utftest_utf8_bom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <週報>
5 | The world has many languages
6 | Мир имеет много языков
7 | el mundo tiene muchos idiomas
8 | 世界有很多语言
9 | <Русский название="name" ценность="value"><имеет>Русский>
10 | <汉语 名字="name" 价值="value">世界有很多语言𤭢汉语>
11 | "Mëtæl!"
12 | <ä>Umlaut Elementä>
13 |
14 | <年月週>
15 | <年度>1997年度>
16 | <月度>1月度>
17 | <週>1週>
18 | 年月週>
19 |
20 | <氏名>
21 | <氏>山田氏>
22 | <名>太郎名>
23 | 氏名>
24 |
25 | <業務報告リスト>
26 | <業務報告>
27 | <業務名>XMLエディターの作成業務名>
28 | <業務コード>X3355-23業務コード>
29 | <工数管理>
30 | <見積もり工数>1600見積もり工数>
31 | <実績工数>320実績工数>
32 | <当月見積もり工数>160当月見積もり工数>
33 | <当月実績工数>24当月実績工数>
34 | 工数管理>
35 | <予定項目リスト>
36 | <予定項目>
37 | XMLエディターの基本仕様の作成
38 | 予定項目>
39 | 予定項目リスト>
40 | <実施事項リスト>
41 | <実施事項>
42 | XMLエディターの基本仕様の作成
43 | 実施事項>
44 | <実施事項>
45 | 競合他社製品の機能調査
46 | 実施事項>
47 | 実施事項リスト>
48 | <上長への要請事項リスト>
49 | <上長への要請事項>
50 | 特になし
51 | 上長への要請事項>
52 | 上長への要請事項リスト>
53 | <問題点対策>
54 | XMLとは何かわからない。
55 | 問題点対策>
56 | 業務報告>
57 |
58 | <業務報告>
59 | <業務名>検索エンジンの開発業務名>
60 | <業務コード>S8821-76業務コード>
61 | <工数管理>
62 | <見積もり工数>120見積もり工数>
63 | <実績工数>6実績工数>
64 | <当月見積もり工数>32当月見積もり工数>
65 | <当月実績工数>2当月実績工数>
66 | 工数管理>
67 | <予定項目リスト>
68 | <予定項目>
69 | gooの機能を調べてみる
70 | 予定項目>
71 | 予定項目リスト>
72 | <実施事項リスト>
73 | <実施事項>
74 | 更に、どういう検索エンジンがあるか調査する
75 | 実施事項>
76 | 実施事項リスト>
77 | <上長への要請事項リスト>
78 | <上長への要請事項>
79 | 開発をするのはめんどうなので、Yahoo!を買収して下さい。
80 | 上長への要請事項>
81 | 上長への要請事項リスト>
82 | <問題点対策>
83 | 検索エンジンで車を走らせることができない。(要調査)
84 | 問題点対策>
85 | 業務報告>
86 | 業務報告リスト>
87 | 週報>
88 |
--------------------------------------------------------------------------------
/scripts/nuget_build.ps1:
--------------------------------------------------------------------------------
1 | function Run-Command([string]$cmd)
2 | {
3 | Invoke-Expression $cmd
4 | if ($LastExitCode) { exit $LastExitCode }
5 | }
6 |
7 | function Force-Copy([string]$from, [string]$to)
8 | {
9 | Write-Host $from "->" $to
10 | New-Item -Force $to | Out-Null
11 | Copy-Item -Force $from $to
12 | if (! $?) { exit 1 }
13 | }
14 |
15 | function Build-Version([string]$vs, [string]$toolset, [string]$linkage)
16 | {
17 | $prjsuffix = if ($linkage -eq "static") { "_static" } else { "" }
18 | $cfgsuffix = if ($linkage -eq "static") { "Static" } else { "" }
19 |
20 | foreach ($configuration in "Debug","Release")
21 | {
22 | Run-Command "msbuild pugixml_$vs$prjsuffix.vcxproj /t:Rebuild /p:Configuration=$configuration /p:Platform=x86 /v:minimal /nologo"
23 | Run-Command "msbuild pugixml_$vs$prjsuffix.vcxproj /t:Rebuild /p:Configuration=$configuration /p:Platform=x64 /v:minimal /nologo"
24 |
25 | Force-Copy "$vs/Win32_$configuration$cfgsuffix/pugixml.lib" "nuget/build/native/lib/Win32/$toolset/$linkage/$configuration/pugixml.lib"
26 | Force-Copy "$vs/x64_$configuration$cfgsuffix/pugixml.lib" "nuget/build/native/lib/x64/$toolset/$linkage/$configuration/pugixml.lib"
27 | }
28 | }
29 |
30 | Push-Location
31 | $scriptdir = Split-Path $MyInvocation.MyCommand.Path
32 | cd $scriptdir
33 |
34 | Force-Copy "../src/pugiconfig.hpp" "nuget/build/native/include/pugiconfig.hpp"
35 | Force-Copy "../src/pugixml.hpp" "nuget/build/native/include/pugixml.hpp"
36 | Force-Copy "../src/pugixml.cpp" "nuget/build/native/include/pugixml.cpp"
37 |
38 | if ($args[0] -eq 2022){
39 | Build-Version "vs2022" "v143" "dynamic"
40 | Build-Version "vs2022" "v143" "static"
41 |
42 | } elseif ($args[0] -eq 2019){
43 | Build-Version "vs2019" "v142" "dynamic"
44 | Build-Version "vs2019" "v142" "static"
45 |
46 | } elseif ($args[0] -eq 2017){
47 | Build-Version "vs2017" "v141" "dynamic"
48 | Build-Version "vs2017" "v141" "static"
49 |
50 | Build-Version "vs2015" "v140" "dynamic"
51 | Build-Version "vs2015" "v140" "static"
52 |
53 | Build-Version "vs2013" "v120" "dynamic"
54 | Build-Version "vs2013" "v120" "static"
55 |
56 | } elseif($args[0] -eq 2015){
57 | Build-Version "vs2015" "v140" "dynamic"
58 | Build-Version "vs2015" "v140" "static"
59 |
60 | Build-Version "vs2013" "v120" "dynamic"
61 | Build-Version "vs2013" "v120" "static"
62 |
63 | } elseif($args[0] -eq 2013){
64 | Build-Version "vs2013" "v120" "dynamic"
65 | Build-Version "vs2013" "v120" "static"
66 | }
67 |
68 | Run-Command "nuget pack nuget"
69 |
70 | Pop-Location
71 |
--------------------------------------------------------------------------------
/scripts/pugixml_codelite.project:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | None
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | None
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/tests/helpers.hpp:
--------------------------------------------------------------------------------
1 | #ifndef HEADER_TEST_HELPERS_HPP
2 | #define HEADER_TEST_HELPERS_HPP
3 |
4 | #include "test.hpp"
5 |
6 | #include
7 |
8 | template static void generic_bool_ops_test(const T& obj)
9 | {
10 | T null;
11 |
12 | CHECK(!null);
13 | CHECK(obj);
14 | CHECK(!!obj);
15 |
16 | #ifdef _MSC_VER
17 | # pragma warning(push)
18 | # pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning) - we really want to just cast to bool instead of !!
19 | #endif
20 |
21 | bool b1 = null, b2 = obj;
22 |
23 | #ifdef _MSC_VER
24 | # pragma warning(pop)
25 | #endif
26 |
27 | CHECK(!b1);
28 | CHECK(b2);
29 |
30 | CHECK(obj && b2);
31 | CHECK(obj || b2);
32 | CHECK(obj && obj);
33 | CHECK(obj || obj);
34 | }
35 |
36 | template static void generic_eq_ops_test(const T& obj1, const T& obj2)
37 | {
38 | T null = T();
39 |
40 | // operator==
41 | CHECK(null == null);
42 | CHECK(obj1 == obj1);
43 | CHECK(!(null == obj1));
44 | CHECK(!(null == obj2));
45 | CHECK(T(null) == null);
46 | CHECK(T(obj1) == obj1);
47 |
48 | // operator!=
49 | CHECK(!(null != null));
50 | CHECK(!(obj1 != obj1));
51 | CHECK(null != obj1);
52 | CHECK(null != obj2);
53 | CHECK(!(T(null) != null));
54 | CHECK(!(T(obj1) != obj1));
55 | }
56 |
57 | template static void generic_rel_ops_test(T obj1, T obj2)
58 | {
59 | T null = T();
60 |
61 | // obj1 < obj2 (we use operator<, but there is no other choice
62 | if (obj1 > obj2)
63 | {
64 | T temp = obj1;
65 | obj1 = obj2;
66 | obj2 = temp;
67 | }
68 |
69 | // operator<
70 | CHECK(null < obj1);
71 | CHECK(null < obj2);
72 | CHECK(obj1 < obj2);
73 | CHECK(!(null < null));
74 | CHECK(!(obj1 < obj1));
75 | CHECK(!(obj1 < null));
76 | CHECK(!(obj2 < obj1));
77 |
78 | // operator<=
79 | CHECK(null <= obj1);
80 | CHECK(null <= obj2);
81 | CHECK(obj1 <= obj2);
82 | CHECK(null <= null);
83 | CHECK(obj1 <= obj1);
84 | CHECK(!(obj1 <= null));
85 | CHECK(!(obj2 <= obj1));
86 |
87 | // operator>
88 | CHECK(obj1 > null);
89 | CHECK(obj2 > null);
90 | CHECK(obj2 > obj1);
91 | CHECK(!(null > null));
92 | CHECK(!(obj1 > obj1));
93 | CHECK(!(null > obj1));
94 | CHECK(!(obj1 > obj2));
95 |
96 | // operator>=
97 | CHECK(obj1 >= null);
98 | CHECK(obj2 >= null);
99 | CHECK(obj2 >= obj1);
100 | CHECK(null >= null);
101 | CHECK(obj1 >= obj1);
102 | CHECK(!(null >= obj1));
103 | CHECK(!(obj1 >= obj2));
104 | }
105 |
106 | template static void generic_empty_test(const T& obj)
107 | {
108 | T null;
109 |
110 | CHECK(null.empty());
111 | CHECK(!obj.empty());
112 | }
113 |
114 | #endif
115 |
--------------------------------------------------------------------------------
/scripts/premake4.lua:
--------------------------------------------------------------------------------
1 | -- Reset RNG seed to get consistent results across runs (i.e. XCode)
2 | math.randomseed(12345)
3 |
4 | local static = _ARGS[1] == 'static'
5 | local action = premake.action.current()
6 |
7 | if string.startswith(_ACTION, "vs") then
8 | if action then
9 | -- Disable solution generation
10 | function action.onsolution(sln)
11 | sln.vstudio_configs = premake.vstudio_buildconfigs(sln)
12 | end
13 |
14 | -- Rename output file
15 | function action.onproject(prj)
16 | local name = "%%_" .. _ACTION .. (static and "_static" or "")
17 |
18 | if static then
19 | for k, v in pairs(prj.project.__configs) do
20 | v.objectsdir = v.objectsdir .. "Static"
21 | end
22 | end
23 |
24 | if _ACTION == "vs2010" then
25 | premake.generate(prj, name .. ".vcxproj", premake.vs2010_vcxproj)
26 | else
27 | premake.generate(prj, name .. ".vcproj", premake.vs200x_vcproj)
28 | end
29 | end
30 | end
31 | elseif _ACTION == "codeblocks" then
32 | action.onsolution = nil
33 |
34 | function action.onproject(prj)
35 | premake.generate(prj, "%%_" .. _ACTION .. ".cbp", premake.codeblocks_cbp)
36 | end
37 | elseif _ACTION == "codelite" then
38 | action.onsolution = nil
39 |
40 | function action.onproject(prj)
41 | premake.generate(prj, "%%_" .. _ACTION .. ".project", premake.codelite_project)
42 | end
43 | end
44 |
45 | solution "pugixml"
46 | objdir(_ACTION)
47 | targetdir(_ACTION)
48 |
49 | if string.startswith(_ACTION, "vs") then
50 | if _ACTION ~= "vs2002" and _ACTION ~= "vs2003" then
51 | platforms { "x32", "x64" }
52 |
53 | configuration "x32" targetdir(_ACTION .. "/x32")
54 | configuration "x64" targetdir(_ACTION .. "/x64")
55 | end
56 |
57 | configurations { "Debug", "Release" }
58 |
59 | if static then
60 | configuration "Debug" targetsuffix "sd"
61 | configuration "Release" targetsuffix "s"
62 | else
63 | configuration "Debug" targetsuffix "d"
64 | end
65 | else
66 | if _ACTION == "xcode3" then
67 | platforms "universal"
68 | end
69 |
70 | configurations { "Debug", "Release" }
71 |
72 | configuration "Debug" targetsuffix "d"
73 | end
74 |
75 | project "pugixml"
76 | kind "StaticLib"
77 | language "C++"
78 | files { "../src/pugixml.hpp", "../src/pugiconfig.hpp", "../src/pugixml.cpp" }
79 | flags { "NoPCH", "NoMinimalRebuild", "NoEditAndContinue", "Symbols" }
80 | uuid "89A1E353-E2DC-495C-B403-742BE206ACED"
81 |
82 | configuration "Debug"
83 | defines { "_DEBUG" }
84 |
85 | configuration "Release"
86 | defines { "NDEBUG" }
87 | flags { "Optimize" }
88 |
89 | if static then
90 | configuration "*"
91 | flags { "StaticRuntime" }
92 | end
93 |
--------------------------------------------------------------------------------
/docs/samples/load_memory.cpp:
--------------------------------------------------------------------------------
1 | #include "pugixml.hpp"
2 |
3 | #include
4 | #include
5 |
6 | int main()
7 | {
8 | // tag::decl[]
9 | const char source[] = "0 0 1 1";
10 | size_t size = sizeof(source);
11 | // end::decl[]
12 |
13 | pugi::xml_document doc;
14 |
15 | {
16 | // tag::load_buffer[]
17 | // You can use load_buffer to load document from immutable memory block:
18 | pugi::xml_parse_result result = doc.load_buffer(source, size);
19 | // end::load_buffer[]
20 |
21 | std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
22 | }
23 |
24 | {
25 | // tag::load_buffer_inplace_begin[]
26 | // You can use load_buffer_inplace to load document from mutable memory block; the block's lifetime must exceed that of document
27 | char* buffer = new char[size];
28 | memcpy(buffer, source, size);
29 |
30 | // The block can be allocated by any method; the block is modified during parsing
31 | pugi::xml_parse_result result = doc.load_buffer_inplace(buffer, size);
32 | // end::load_buffer_inplace_begin[]
33 |
34 | std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
35 |
36 | // tag::load_buffer_inplace_end[]
37 | // You have to destroy the block yourself after the document is no longer used
38 | delete[] buffer;
39 | // end::load_buffer_inplace_end[]
40 | }
41 |
42 | {
43 | // tag::load_buffer_inplace_own[]
44 | // You can use load_buffer_inplace_own to load document from mutable memory block and to pass the ownership of this block
45 | // The block has to be allocated via pugixml allocation function - using i.e. operator new here is incorrect
46 | char* buffer = static_cast(pugi::get_memory_allocation_function()(size));
47 | memcpy(buffer, source, size);
48 |
49 | // The block will be deleted by the document
50 | pugi::xml_parse_result result = doc.load_buffer_inplace_own(buffer, size);
51 | // end::load_buffer_inplace_own[]
52 |
53 | std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
54 | }
55 |
56 | {
57 | // tag::load_string[]
58 | // You can use load to load document from null-terminated strings, for example literals:
59 | pugi::xml_parse_result result = doc.load_string("0 0 1 1");
60 | // end::load_string[]
61 |
62 | std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
63 | }
64 | }
65 |
66 | // vim:et
67 |
--------------------------------------------------------------------------------
/tests/writer_string.cpp:
--------------------------------------------------------------------------------
1 | #include "writer_string.hpp"
2 |
3 | #include "test.hpp"
4 |
5 | static bool test_narrow(const std::string& result, const char* expected, size_t length)
6 | {
7 | // check result
8 | if (result != std::string(expected, expected + length)) return false;
9 |
10 | // check comparison operator (incorrect implementation can theoretically early-out on zero terminators...)
11 | if (length > 0 && result == std::string(expected, expected + length - 1) + "?") return false;
12 |
13 | return true;
14 | }
15 |
16 | void xml_writer_string::write(const void* data, size_t size)
17 | {
18 | contents.append(static_cast(data), size);
19 | }
20 |
21 | std::string xml_writer_string::as_narrow() const
22 | {
23 | return contents;
24 | }
25 |
26 | std::basic_string xml_writer_string::as_wide() const
27 | {
28 | CHECK(contents.size() % sizeof(wchar_t) == 0);
29 |
30 | // round-trip pointer through void* to avoid pointer alignment warnings; contents data should be heap allocated => safe to cast
31 | return std::basic_string(static_cast(static_cast(contents.data())), contents.size() / sizeof(wchar_t));
32 | }
33 |
34 | std::basic_string xml_writer_string::as_string() const
35 | {
36 | #ifdef PUGIXML_WCHAR_MODE // to avoid "condition is always true" warning in BCC
37 | CHECK(contents.size() % sizeof(pugi::char_t) == 0);
38 | #endif
39 |
40 | // round-trip pointer through void* to avoid pointer alignment warnings; contents data should be heap allocated => safe to cast
41 | return std::basic_string(static_cast(static_cast(contents.data())), contents.size() / sizeof(pugi::char_t));
42 | }
43 |
44 | std::string save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding)
45 | {
46 | xml_writer_string writer;
47 |
48 | doc.save(writer, STR("\t"), flags, encoding);
49 |
50 | return writer.as_narrow();
51 | }
52 |
53 | bool test_save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length)
54 | {
55 | return test_narrow(save_narrow(doc, flags, encoding), expected, length);
56 | }
57 |
58 | std::string write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding)
59 | {
60 | xml_writer_string writer;
61 |
62 | node.print(writer, STR("\t"), flags, encoding);
63 |
64 | return writer.as_narrow();
65 | }
66 |
67 | bool test_write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length)
68 | {
69 | return test_narrow(write_narrow(node, flags, encoding), expected, length);
70 | }
71 |
72 | std::basic_string write_wide(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding)
73 | {
74 | xml_writer_string writer;
75 |
76 | node.print(writer, STR("\t"), flags, encoding);
77 |
78 | return writer.as_wide();
79 | }
80 |
--------------------------------------------------------------------------------
/tests/autotest-appveyor.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-CmdScript($scriptName)
2 | {
3 | $cmdLine = """$scriptName"" $args & set"
4 | & $Env:SystemRoot\system32\cmd.exe /c $cmdLine |
5 | select-string '^([^=]*)=(.*)$' | foreach-object {
6 | $varName = $_.Matches[0].Groups[1].Value
7 | $varValue = $_.Matches[0].Groups[2].Value
8 | set-item Env:$varName $varValue
9 | }
10 | }
11 |
12 | $sources = @("src/pugixml.cpp") + (Get-ChildItem -Path "tests/*.cpp" -Exclude "fuzz_*.cpp")
13 | $failed = $FALSE
14 |
15 | foreach ($vs in $args)
16 | {
17 | foreach ($arch in "x86","x64")
18 | {
19 | Write-Output "# Setting up VS$vs $arch"
20 |
21 | if ($vs -eq 15) {
22 | $vsdevcmdarch = if ($arch -eq "x64") { "amd64" } else { "x86" }
23 | Invoke-CmdScript "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" "-arch=$vsdevcmdarch"
24 | }
25 | elseif ($vs -eq 19) {
26 | $vsdevcmdarch = if ($arch -eq "x64") { "amd64" } else { "x86" }
27 | Invoke-CmdScript "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" "-arch=$vsdevcmdarch"
28 | }
29 | elseif ($vs -eq 22) {
30 | $vsdevcmdarch = if ($arch -eq "x64") { "amd64" } else { "x86" }
31 | Invoke-CmdScript "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" "-arch=$vsdevcmdarch"
32 | }
33 | else
34 | {
35 | Invoke-CmdScript "C:\Program Files (x86)\Microsoft Visual Studio $vs.0\VC\vcvarsall.bat" $arch
36 | }
37 |
38 | if (! $?) { throw "Error setting up VS$vs $arch" }
39 |
40 | foreach ($defines in "standard", "PUGIXML_WCHAR_MODE", "PUGIXML_COMPACT")
41 | {
42 | $target = "tests_vs${vs}_${arch}_${defines}"
43 | $deflist = if ($defines -eq "standard") { "" } else { "/D$defines" }
44 |
45 | Add-AppveyorTest $target -Outcome Running
46 |
47 | Write-Output "# Building $target.exe"
48 | & cmd /c "cl.exe /Fe$target.exe /EHsc /W4 /WX $deflist $sources 2>&1" | Tee-Object -Variable buildOutput
49 |
50 | if ($?)
51 | {
52 | Write-Output "# Running $target.exe"
53 |
54 | $sw = [Diagnostics.Stopwatch]::StartNew()
55 |
56 | & .\$target | Tee-Object -Variable testOutput
57 |
58 | if ($?)
59 | {
60 | Write-Output "# Passed"
61 |
62 | Update-AppveyorTest $target -Outcome Passed -StdOut ($testOutput | out-string) -Duration $sw.ElapsedMilliseconds
63 | }
64 | else
65 | {
66 | Write-Output "# Failed"
67 |
68 | Update-AppveyorTest $target -Outcome Failed -StdOut ($testOutput | out-string) -ErrorMessage "Running failed"
69 |
70 | $failed = $TRUE
71 | }
72 | }
73 | else
74 | {
75 | Write-Output "# Failed to build"
76 |
77 | Update-AppveyorTest $target -Outcome Failed -StdOut ($buildOutput | out-string) -ErrorMessage "Compilation failed"
78 |
79 | $failed = $TRUE
80 | }
81 | }
82 | }
83 | }
84 |
85 | if ($failed) { throw "One or more build steps failed" }
86 |
87 | Write-Output "# End"
88 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .SUFFIXES:
2 | MAKEFLAGS+=-r
3 |
4 | config=debug
5 | defines=standard
6 | cxxstd=c++11
7 | # set cxxstd=any to disable use of -std=...
8 |
9 | BUILD=build/make-$(CXX)-$(config)-$(defines)-$(cxxstd)
10 |
11 | SOURCES=src/pugixml.cpp $(filter-out tests/fuzz_%,$(wildcard tests/*.cpp))
12 | EXECUTABLE=$(BUILD)/test
13 |
14 | VERSION=$(shell sed -n 's/.*version \(.*\).*/\1/p' src/pugiconfig.hpp)
15 | RELEASE=$(filter-out scripts/archive.py docs/%.adoc,$(shell git ls-files docs scripts src CMakeLists.txt LICENSE.md readme.txt))
16 |
17 | CXXFLAGS=-g -Wall -Wextra -Werror -pedantic -Wundef -Wshadow -Wcast-align -Wcast-qual -Wold-style-cast -Wdouble-promotion
18 | LDFLAGS=
19 |
20 | ifeq ($(config),release)
21 | CXXFLAGS+=-O3 -DNDEBUG
22 | endif
23 |
24 | ifeq ($(config),coverage)
25 | CXXFLAGS+=-coverage
26 | LDFLAGS+=-coverage
27 | endif
28 |
29 | ifeq ($(config),sanitize)
30 | CXXFLAGS+=-fsanitize=address,undefined -fno-sanitize=float-divide-by-zero,float-cast-overflow -fno-sanitize-recover=all
31 | LDFLAGS+=-fsanitize=address,undefined
32 | endif
33 |
34 | ifeq ($(config),analyze)
35 | CXXFLAGS+=--analyze
36 | endif
37 |
38 | ifneq ($(defines),standard)
39 | COMMA=,
40 | CXXFLAGS+=-D $(subst $(COMMA), -D ,$(defines))
41 | endif
42 |
43 | ifneq ($(findstring PUGIXML_NO_EXCEPTIONS,$(defines)),)
44 | CXXFLAGS+=-fno-exceptions
45 | endif
46 |
47 | ifneq ($(cxxstd),any)
48 | CXXFLAGS+=-std=$(cxxstd)
49 | endif
50 |
51 | OBJECTS=$(SOURCES:%=$(BUILD)/%.o)
52 |
53 | all: $(EXECUTABLE)
54 |
55 | ifeq ($(config),coverage)
56 | test: $(EXECUTABLE)
57 | -@find $(BUILD) -name '*.gcda' -exec rm {} +
58 | ./$(EXECUTABLE)
59 | @gcov -b -o $(BUILD)/src/ pugixml.cpp.gcda | sed -e '/./{H;$!d;}' -e 'x;/pugixml.cpp/!d;'
60 | @find . -name '*.gcov' -and -not -name 'pugixml.cpp.gcov' -exec rm {} +
61 | @sed -i -e "s/#####\(.*\)\(\/\/ unreachable.*\)/ 1\1\2/" pugixml.cpp.gcov
62 | else
63 | test: $(EXECUTABLE)
64 | ./$(EXECUTABLE)
65 | endif
66 |
67 | fuzz_%: $(BUILD)/fuzz_%
68 | @mkdir -p build/$@
69 | $< build/$@ tests/data_fuzz_$* -max_len=1024 -dict=tests/fuzz_$*.dict
70 |
71 | clean:
72 | rm -rf $(BUILD)
73 |
74 | release: build/pugixml-$(VERSION).tar.gz build/pugixml-$(VERSION).zip
75 |
76 | docs: docs/quickstart.html docs/manual.html
77 |
78 | build/pugixml-%: .FORCE | $(RELEASE)
79 | @mkdir -p $(BUILD)
80 | TIMESTAMP=`git show v$(VERSION) -s --format=%ct` && python3 scripts/archive.py $@ pugixml-$(VERSION) $$TIMESTAMP $|
81 |
82 | $(EXECUTABLE): $(OBJECTS)
83 | $(CXX) $(OBJECTS) $(LDFLAGS) -o $@
84 |
85 | $(BUILD)/fuzz_%: tests/fuzz_%.cpp src/pugixml.cpp
86 | @mkdir -p $(BUILD)
87 | $(CXX) $(CXXFLAGS) -fsanitize=address,fuzzer $^ -o $@
88 |
89 | $(BUILD)/%.o: %
90 | @mkdir -p $(dir $@)
91 | $(CXX) $< $(CXXFLAGS) -c -MMD -MP -o $@
92 |
93 | -include $(OBJECTS:.o=.d)
94 |
95 | .SECONDEXPANSION:
96 | docs/%.html: docs/%.adoc $$(shell sed -n 's/include\:\:\(.*\)\[.*/docs\/\1/p' docs/%.adoc)
97 | asciidoctor -b html5 -a version=$(VERSION) $< -o $@
98 |
99 | .PHONY: all test clean release .FORCE
100 |
--------------------------------------------------------------------------------
/src/pugiconfig.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * pugixml parser - version 1.12
3 | * --------------------------------------------------------
4 | * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
5 | * Report bugs and download new versions at https://pugixml.org/
6 | *
7 | * This library is distributed under the MIT License. See notice at the end
8 | * of this file.
9 | *
10 | * This work is based on the pugxml parser, which is:
11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
12 | */
13 |
14 | #ifndef HEADER_PUGICONFIG_HPP
15 | #define HEADER_PUGICONFIG_HPP
16 |
17 | // Uncomment this to enable wchar_t mode
18 | // #define PUGIXML_WCHAR_MODE
19 |
20 | // Uncomment this to enable compact mode
21 | // #define PUGIXML_COMPACT
22 |
23 | // Uncomment this to disable XPath
24 | // #define PUGIXML_NO_XPATH
25 |
26 | // Uncomment this to disable STL
27 | // #define PUGIXML_NO_STL
28 |
29 | // Uncomment this to disable exceptions
30 | // #define PUGIXML_NO_EXCEPTIONS
31 |
32 | // Set this to control attributes for public classes/functions, i.e.:
33 | // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
34 | // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
35 | // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
36 | // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
37 |
38 | // Tune these constants to adjust memory-related behavior
39 | // #define PUGIXML_MEMORY_PAGE_SIZE 32768
40 | // #define PUGIXML_MEMORY_OUTPUT_STACK 10240
41 | // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
42 |
43 | // Tune this constant to adjust max nesting for XPath queries
44 | // #define PUGIXML_XPATH_DEPTH_LIMIT 1024
45 |
46 | // Uncomment this to switch to header-only version
47 | // #define PUGIXML_HEADER_ONLY
48 |
49 | // Uncomment this to enable long long support
50 | // #define PUGIXML_HAS_LONG_LONG
51 |
52 | #endif
53 |
54 | /**
55 | * Copyright (c) 2006-2022 Arseny Kapoulkine
56 | *
57 | * Permission is hereby granted, free of charge, to any person
58 | * obtaining a copy of this software and associated documentation
59 | * files (the "Software"), to deal in the Software without
60 | * restriction, including without limitation the rights to use,
61 | * copy, modify, merge, publish, distribute, sublicense, and/or sell
62 | * copies of the Software, and to permit persons to whom the
63 | * Software is furnished to do so, subject to the following
64 | * conditions:
65 | *
66 | * The above copyright notice and this permission notice shall be
67 | * included in all copies or substantial portions of the Software.
68 | *
69 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
70 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
71 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
72 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
73 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
74 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
75 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
76 | * OTHER DEALINGS IN THE SOFTWARE.
77 | */
78 |
--------------------------------------------------------------------------------
/docs/samples/save_custom_writer.cpp:
--------------------------------------------------------------------------------
1 | #include "pugixml.hpp"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | // tag::code[]
8 | struct xml_string_writer: pugi::xml_writer
9 | {
10 | std::string result;
11 |
12 | virtual void write(const void* data, size_t size)
13 | {
14 | result.append(static_cast(data), size);
15 | }
16 | };
17 | // end::code[]
18 |
19 | struct xml_memory_writer: pugi::xml_writer
20 | {
21 | char* buffer;
22 | size_t capacity;
23 |
24 | size_t result;
25 |
26 | xml_memory_writer(): buffer(0), capacity(0), result(0)
27 | {
28 | }
29 |
30 | xml_memory_writer(char* buffer, size_t capacity): buffer(buffer), capacity(capacity), result(0)
31 | {
32 | }
33 |
34 | size_t written_size() const
35 | {
36 | return result < capacity ? result : capacity;
37 | }
38 |
39 | virtual void write(const void* data, size_t size)
40 | {
41 | if (result < capacity)
42 | {
43 | size_t chunk = (capacity - result < size) ? capacity - result : size;
44 |
45 | memcpy(buffer + result, data, chunk);
46 | }
47 |
48 | result += size;
49 | }
50 | };
51 |
52 | std::string node_to_string(pugi::xml_node node)
53 | {
54 | xml_string_writer writer;
55 | node.print(writer);
56 |
57 | return writer.result;
58 | }
59 |
60 | char* node_to_buffer(pugi::xml_node node, char* buffer, size_t size)
61 | {
62 | if (size == 0) return buffer;
63 |
64 | // leave one character for null terminator
65 | xml_memory_writer writer(buffer, size - 1);
66 | node.print(writer);
67 |
68 | // null terminate
69 | buffer[writer.written_size()] = 0;
70 |
71 | return buffer;
72 | }
73 |
74 | char* node_to_buffer_heap(pugi::xml_node node)
75 | {
76 | // first pass: get required memory size
77 | xml_memory_writer counter;
78 | node.print(counter);
79 |
80 | // allocate necessary size (+1 for null termination)
81 | char* buffer = new char[counter.result + 1];
82 |
83 | // second pass: actual printing
84 | xml_memory_writer writer(buffer, counter.result);
85 | node.print(writer);
86 |
87 | // null terminate
88 | buffer[writer.written_size()] = 0;
89 |
90 | return buffer;
91 | }
92 |
93 | int main()
94 | {
95 | // get a test document
96 | pugi::xml_document doc;
97 | doc.load_string("hey");
98 |
99 | // get contents as std::string (single pass)
100 | std::cout << "contents: [" << node_to_string(doc) << "]\n";
101 |
102 | // get contents into fixed-size buffer (single pass)
103 | char large_buf[128];
104 | std::cout << "contents: [" << node_to_buffer(doc, large_buf, sizeof(large_buf)) << "]\n";
105 |
106 | // get contents into fixed-size buffer (single pass, shows truncating behavior)
107 | char small_buf[22];
108 | std::cout << "contents: [" << node_to_buffer(doc, small_buf, sizeof(small_buf)) << "]\n";
109 |
110 | // get contents into heap-allocated buffer (two passes)
111 | char* heap_buf = node_to_buffer_heap(doc);
112 | std::cout << "contents: [" << heap_buf << "]\n";
113 | delete[] heap_buf;
114 | }
115 |
116 | // vim:et
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | pugixml [](https://github.com/zeux/pugixml/actions) [](https://ci.appveyor.com/project/zeux/pugixml) [](https://codecov.io/github/zeux/pugixml?branch=master) 
2 | =======
3 |
4 | pugixml is a C++ XML processing library, which consists of a DOM-like interface with rich traversal/modification
5 | capabilities, an extremely fast XML parser which constructs the DOM tree from an XML file/buffer, and an XPath 1.0
6 | implementation for complex data-driven tree queries. Full Unicode support is also available, with Unicode interface
7 | variants and conversions between different Unicode encodings (which happen automatically during parsing/saving).
8 |
9 | pugixml is used by a lot of projects, both open-source and proprietary, for performance and easy-to-use interface.
10 |
11 | ## Documentation
12 |
13 | Documentation for the current release of pugixml is available on-line as two separate documents:
14 |
15 | * [Quick-start guide](https://pugixml.org/docs/quickstart.html), that aims to provide enough information to start using the library;
16 | * [Complete reference manual](https://pugixml.org/docs/manual.html), that describes all features of the library in detail.
17 |
18 | You’re advised to start with the quick-start guide; however, many important library features are either not described in it at all or only mentioned briefly; if you require more information you should read the complete manual.
19 |
20 | ## Example
21 |
22 | Here's an example of how code using pugixml looks; it opens an XML file, goes over all Tool nodes and prints tools that have a Timeout attribute greater than 0:
23 |
24 | ```c++
25 | #include "pugixml.hpp"
26 | #include
27 |
28 | int main()
29 | {
30 | pugi::xml_document doc;
31 | pugi::xml_parse_result result = doc.load_file("xgconsole.xml");
32 | if (!result)
33 | return -1;
34 |
35 | for (pugi::xml_node tool: doc.child("Profile").child("Tools").children("Tool"))
36 | {
37 | int timeout = tool.attribute("Timeout").as_int();
38 |
39 | if (timeout > 0)
40 | std::cout << "Tool " << tool.attribute("Filename").value() << " has timeout " << timeout << "\n";
41 | }
42 | }
43 | ```
44 |
45 | And the same example using XPath:
46 |
47 | ```c++
48 | #include "pugixml.hpp"
49 | #include
50 |
51 | int main()
52 | {
53 | pugi::xml_document doc;
54 | pugi::xml_parse_result result = doc.load_file("xgconsole.xml");
55 | if (!result)
56 | return -1;
57 |
58 | pugi::xpath_node_set tools_with_timeout = doc.select_nodes("/Profile/Tools/Tool[@Timeout > 0]");
59 |
60 | for (pugi::xpath_node node: tools_with_timeout)
61 | {
62 | pugi::xml_node tool = node.node();
63 | std::cout << "Tool " << tool.attribute("Filename").value() <<
64 | " has timeout " << tool.attribute("Timeout").as_int() << "\n";
65 | }
66 | }
67 | ```
68 |
69 |
70 | ## License
71 |
72 | This library is available to anybody free of charge, under the terms of MIT License (see LICENSE.md).
73 |
--------------------------------------------------------------------------------
/docs/samples/load_stream.cpp:
--------------------------------------------------------------------------------
1 | #include "pugixml.hpp"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | void print_doc(const char* message, const pugi::xml_document& doc, const pugi::xml_parse_result& result)
8 | {
9 | std::cout
10 | << message
11 | << "\t: load result '" << result.description() << "'"
12 | << ", first character of root name: U+" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << pugi::as_wide(doc.first_child().name())[0]
13 | << ", year: " << doc.first_child().first_child().first_child().child_value()
14 | << std::endl;
15 | }
16 |
17 | bool try_imbue(std::wistream& stream, const char* name)
18 | {
19 | try
20 | {
21 | stream.imbue(std::locale(name));
22 |
23 | return true;
24 | }
25 | catch (const std::exception&)
26 | {
27 | return false;
28 | }
29 | }
30 |
31 | int main()
32 | {
33 | pugi::xml_document doc;
34 |
35 | {
36 | // tag::code[]
37 | std::ifstream stream("weekly-utf-8.xml");
38 | pugi::xml_parse_result result = doc.load(stream);
39 | // end::code[]
40 |
41 | // first character of root name: U+9031, year: 1997
42 | print_doc("UTF8 file from narrow stream", doc, result);
43 | }
44 |
45 | {
46 | std::ifstream stream("weekly-utf-16.xml");
47 | pugi::xml_parse_result result = doc.load(stream);
48 |
49 | // first character of root name: U+9031, year: 1997
50 | print_doc("UTF16 file from narrow stream", doc, result);
51 | }
52 |
53 | {
54 | // Since wide streams are treated as UTF-16/32 ones, you can't load the UTF-8 file from a wide stream
55 | // directly if you have localized characters; you'll have to provide a UTF8 locale (there is no
56 | // standard one; you can use utf8_codecvt_facet from Boost or codecvt_utf8 from C++0x)
57 | std::wifstream stream("weekly-utf-8.xml");
58 |
59 | if (try_imbue(stream, "en_US.UTF-8")) // try Linux encoding
60 | {
61 | pugi::xml_parse_result result = doc.load(stream);
62 |
63 | // first character of root name: U+00E9, year: 1997
64 | print_doc("UTF8 file from wide stream", doc, result);
65 | }
66 | else
67 | {
68 | std::cout << "UTF-8 locale is not available\n";
69 | }
70 | }
71 |
72 | {
73 | // Since wide streams are treated as UTF-16/32 ones, you can't load the UTF-16 file from a wide stream without
74 | // using custom codecvt; you can use codecvt_utf16 from C++0x
75 | }
76 |
77 | {
78 | // Since encoding names are non-standard, you can't load the Shift-JIS (or any other non-ASCII) file
79 | // from a wide stream portably
80 | std::wifstream stream("weekly-shift_jis.xml");
81 |
82 | if (try_imbue(stream, ".932") || // try Microsoft encoding
83 | try_imbue(stream, "ja_JP.SJIS")) // try Linux encoding; run "localedef -i ja_JP -c -f SHIFT_JIS /usr/lib/locale/ja_JP.SJIS" to get it
84 | {
85 | pugi::xml_parse_result result = doc.load(stream);
86 |
87 | // first character of root name: U+9031, year: 1997
88 | print_doc("Shift-JIS file from wide stream", doc, result);
89 | }
90 | else
91 | {
92 | std::cout << "Shift-JIS locale is not available\n";
93 | }
94 | }
95 | }
96 |
97 | // vim:et
98 |
--------------------------------------------------------------------------------
/scripts/natvis/pugixml.natvis:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {_root}
5 | none
6 |
7 | _root
8 |
9 |
10 |
11 |
12 | {(pugi::xml_node_type)(header & 0xf),en} name={name,na} value={value,na}
13 | {(pugi::xml_node_type)(header & 0xf),en} name={name,na}
14 | {(pugi::xml_node_type)(header & 0xf),en} value={value,na}
15 | {(pugi::xml_node_type)(header & 0xf),en}
16 |
17 | - value,na
18 |
19 |
20 |
21 |
22 |
23 |
24 | - curr,view(child)na
25 | curr = curr->next_attribute
26 |
27 |
28 |
29 |
30 |
31 | first_child
32 | next_sibling
33 | this,na
34 |
35 |
36 |
37 |
38 |
39 | {_attr}
40 | none
41 |
42 | _attr
43 |
44 |
45 |
46 |
47 | {name,na} = {value,na}
48 | {value,na}
49 |
50 | - name,na
51 | - value,na
52 |
53 |
54 |
55 |
56 | {_node,na} "{_attribute._attr->name,na}"="{_attribute._attr->value,na}"
57 | {_node,na}
58 | {_attribute}
59 | empty
60 |
61 | _node
62 | _attribute
63 | - _node,na
64 | - _attribute,na
65 |
66 |
67 |
68 |
69 |
70 | - _type
71 |
72 | _end - _begin
73 | _begin
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/tests/test_unicode.cpp:
--------------------------------------------------------------------------------
1 | #ifndef PUGIXML_NO_STL
2 |
3 | #include "test.hpp"
4 |
5 | #include
6 |
7 | using namespace pugi;
8 |
9 | // letters taken from http://www.utf8-chartable.de/
10 |
11 | TEST(as_wide_empty)
12 | {
13 | CHECK(as_wide("") == L"");
14 | }
15 |
16 | TEST(as_wide_valid_basic)
17 | {
18 | // valid 1-byte, 2-byte and 3-byte inputs
19 | #ifdef U_LITERALS
20 | CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\u0400\u203D");
21 | #else
22 | CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\x0400\x203D");
23 | #endif
24 | }
25 |
26 | TEST(as_wide_valid_astral)
27 | {
28 | // valid 4-byte input
29 | std::basic_string b4 = as_wide("\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
30 |
31 | size_t wcharsize = sizeof(wchar_t);
32 |
33 | if (wcharsize == 4)
34 | {
35 | CHECK(b4.size() == 3 && b4[0] == wchar_cast(0x97624) && b4[1] == L' ' && b4[2] == wchar_cast(0x1003ff));
36 | }
37 | else
38 | {
39 | CHECK(b4.size() == 5 && b4[0] == wchar_cast(0xda1d) && b4[1] == wchar_cast(0xde24) && b4[2] == L' ' && b4[3] == wchar_cast(0xdbc0) && b4[4] == wchar_cast(0xdfff));
40 | }
41 | }
42 |
43 | TEST(as_wide_invalid)
44 | {
45 | // invalid 1-byte input
46 | CHECK(as_wide("a\xb0") == L"a");
47 | CHECK(as_wide("a\xb0_") == L"a_");
48 |
49 | // invalid 2-byte input
50 | CHECK(as_wide("a\xc0") == L"a");
51 | CHECK(as_wide("a\xd0") == L"a");
52 | CHECK(as_wide("a\xc0_") == L"a_");
53 | CHECK(as_wide("a\xd0_") == L"a_");
54 |
55 | // invalid 3-byte input
56 | CHECK(as_wide("a\xe2\x80") == L"a");
57 | CHECK(as_wide("a\xe2") == L"a");
58 | CHECK(as_wide("a\xe2\x80_") == L"a_");
59 | CHECK(as_wide("a\xe2_") == L"a_");
60 |
61 | // invalid 4-byte input
62 | CHECK(as_wide("a\xf2\x97\x98") == L"a");
63 | CHECK(as_wide("a\xf2\x97") == L"a");
64 | CHECK(as_wide("a\xf2") == L"a");
65 | CHECK(as_wide("a\xf2\x97\x98_") == L"a_");
66 | CHECK(as_wide("a\xf2\x97_") == L"a_");
67 | CHECK(as_wide("a\xf2_") == L"a_");
68 |
69 | // invalid 5-byte input
70 | std::basic_string b5 = as_wide("\xf8\nbcd");
71 | CHECK(b5 == L"\nbcd");
72 | }
73 |
74 | TEST(as_wide_string)
75 | {
76 | std::string s = "abcd";
77 |
78 | CHECK(as_wide(s) == L"abcd");
79 | }
80 |
81 | TEST(as_utf8_empty)
82 | {
83 | CHECK(as_utf8(L"") == "");
84 | }
85 |
86 | TEST(as_utf8_valid_basic)
87 | {
88 | // valid 1-byte, 2-byte and 3-byte outputs
89 | #ifdef U_LITERALS
90 | CHECK(as_utf8(L"?\u0400\u203D") == "?\xd0\x80\xe2\x80\xbd");
91 | #else
92 | CHECK(as_utf8(L"?\x0400\x203D") == "?\xd0\x80\xe2\x80\xbd");
93 | #endif
94 | }
95 |
96 | TEST(as_utf8_valid_astral)
97 | {
98 | // valid 4-byte output
99 | size_t wcharsize = sizeof(wchar_t);
100 |
101 | if (wcharsize == 4)
102 | {
103 | std::basic_string s;
104 | s.resize(3);
105 | s[0] = wchar_cast(0x97624);
106 | s[1] = ' ';
107 | s[2] = wchar_cast(0x1003ff);
108 |
109 | CHECK(as_utf8(s.c_str()) == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
110 | }
111 | else
112 | {
113 | #ifdef U_LITERALS
114 | CHECK(as_utf8(L"\uda1d\ude24 \udbc0\udfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
115 | #else
116 | CHECK(as_utf8(L"\xda1d\xde24 \xdbc0\xdfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
117 | #endif
118 | }
119 | }
120 |
121 | TEST(as_utf8_invalid)
122 | {
123 | size_t wcharsize = sizeof(wchar_t);
124 |
125 | if (wcharsize == 2)
126 | {
127 | // check non-terminated degenerate handling
128 | #ifdef U_LITERALS
129 | CHECK(as_utf8(L"a\uda1d") == "a");
130 | CHECK(as_utf8(L"a\uda1d_") == "a_");
131 | #else
132 | CHECK(as_utf8(L"a\xda1d") == "a");
133 | CHECK(as_utf8(L"a\xda1d_") == "a_");
134 | #endif
135 |
136 | // check incorrect leading code
137 | #ifdef U_LITERALS
138 | CHECK(as_utf8(L"a\ude24") == "a");
139 | CHECK(as_utf8(L"a\ude24_") == "a_");
140 | #else
141 | CHECK(as_utf8(L"a\xde24") == "a");
142 | CHECK(as_utf8(L"a\xde24_") == "a_");
143 | #endif
144 | }
145 | }
146 |
147 | TEST(as_utf8_string)
148 | {
149 | std::basic_string s = L"abcd";
150 |
151 | CHECK(as_utf8(s) == "abcd");
152 | }
153 | #endif
154 |
--------------------------------------------------------------------------------
/tests/allocator.cpp:
--------------------------------------------------------------------------------
1 | #include "allocator.hpp"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | // Address sanitizer
8 | #if defined(__has_feature)
9 | # define ADDRESS_SANITIZER __has_feature(address_sanitizer)
10 | #else
11 | # if defined(__SANITIZE_ADDRESS__)
12 | # define ADDRESS_SANITIZER 1
13 | # else
14 | # define ADDRESS_SANITIZER 0
15 | # endif
16 | #endif
17 |
18 | // Low-level allocation functions
19 | #if defined(_WIN32) || defined(_WIN64)
20 | # ifdef __MWERKS__
21 | # pragma ANSI_strict off // disable ANSI strictness to include windows.h
22 | # pragma cpp_extensions on // enable some extensions to include windows.h
23 | # endif
24 |
25 | # if defined(_MSC_VER)
26 | # pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
27 | # endif
28 |
29 | # ifdef _XBOX_VER
30 | # define NOD3D
31 | # include
32 | # else
33 | # include
34 | # endif
35 |
36 | namespace
37 | {
38 | const size_t page_size = 4096;
39 |
40 | size_t align_to_page(size_t value)
41 | {
42 | return (value + page_size - 1) & ~(page_size - 1);
43 | }
44 |
45 | void* allocate_page_aligned(size_t size)
46 | {
47 | // We can't use VirtualAlloc because it has 64Kb granularity so we run out of address space quickly
48 | // We can't use malloc because of occasional problems with CW on CRT termination
49 | static HANDLE heap = HeapCreate(0, 0, 0);
50 |
51 | void* result = HeapAlloc(heap, 0, size + page_size);
52 |
53 | return reinterpret_cast(align_to_page(reinterpret_cast(result)));
54 | }
55 |
56 | void* allocate(size_t size)
57 | {
58 | size_t aligned_size = align_to_page(size);
59 |
60 | void* ptr = allocate_page_aligned(aligned_size + page_size);
61 | if (!ptr) return 0;
62 |
63 | char* end = static_cast(ptr) + aligned_size;
64 |
65 | DWORD old_flags;
66 | VirtualProtect(end, page_size, PAGE_NOACCESS, &old_flags);
67 |
68 | return end - size;
69 | }
70 |
71 | void deallocate(void* ptr, size_t size)
72 | {
73 | size_t aligned_size = align_to_page(size);
74 |
75 | void* rptr = static_cast(ptr) + size - aligned_size;
76 |
77 | DWORD old_flags;
78 | VirtualProtect(rptr, aligned_size + page_size, PAGE_NOACCESS, &old_flags);
79 | }
80 | }
81 | #elif (defined(__APPLE__) || defined(__linux__)) && (defined(__i386) || defined(__x86_64)) && !ADDRESS_SANITIZER
82 | # include
83 |
84 | namespace
85 | {
86 | const size_t page_size = 4096;
87 |
88 | size_t align_to_page(size_t value)
89 | {
90 | return (value + page_size - 1) & ~(page_size - 1);
91 | }
92 |
93 | void* allocate_page_aligned(size_t size)
94 | {
95 | void* result = malloc(size + page_size);
96 |
97 | return reinterpret_cast(align_to_page(reinterpret_cast(result)));
98 | }
99 |
100 | void* allocate(size_t size)
101 | {
102 | size_t aligned_size = align_to_page(size);
103 |
104 | void* ptr = allocate_page_aligned(aligned_size + page_size);
105 | if (!ptr) return 0;
106 |
107 | char* end = static_cast(ptr) + aligned_size;
108 |
109 | int res = mprotect(end, page_size, PROT_NONE);
110 | assert(res == 0);
111 | (void)!res;
112 |
113 | return end - size;
114 | }
115 |
116 | void deallocate(void* ptr, size_t size)
117 | {
118 | size_t aligned_size = align_to_page(size);
119 |
120 | void* rptr = static_cast(ptr) + size - aligned_size;
121 |
122 | int res = mprotect(rptr, aligned_size + page_size, PROT_NONE);
123 | assert(res == 0);
124 | (void)!res;
125 | }
126 | }
127 | #else
128 | namespace
129 | {
130 | void* allocate(size_t size)
131 | {
132 | return malloc(size);
133 | }
134 |
135 | void deallocate(void* ptr, size_t size)
136 | {
137 | (void)size;
138 |
139 | free(ptr);
140 | }
141 | }
142 | #endif
143 |
144 | // High-level allocation functions
145 | const size_t memory_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
146 |
147 | void* memory_allocate(size_t size)
148 | {
149 | void* result = allocate(size + memory_alignment);
150 | if (!result) return 0;
151 |
152 | memcpy(result, &size, sizeof(size_t));
153 |
154 | return static_cast(result) + memory_alignment;
155 | }
156 |
157 | size_t memory_size(void* ptr)
158 | {
159 | assert(ptr);
160 |
161 | size_t result;
162 | memcpy(&result, static_cast(ptr) - memory_alignment, sizeof(size_t));
163 |
164 | return result;
165 | }
166 |
167 | void memory_deallocate(void* ptr)
168 | {
169 | if (!ptr) return;
170 |
171 | size_t size = memory_size(ptr);
172 |
173 | deallocate(static_cast(ptr) - memory_alignment, size + memory_alignment);
174 | }
175 |
176 |
--------------------------------------------------------------------------------
/tests/test_compact.cpp:
--------------------------------------------------------------------------------
1 | #ifdef PUGIXML_COMPACT
2 | #include "test.hpp"
3 |
4 | using namespace pugi;
5 |
6 | static void overflow_hash_table(xml_document& doc)
7 | {
8 | xml_node n = doc.child(STR("n"));
9 |
10 | // compact encoding assumes next_sibling is a forward-only pointer so we can allocate hash entries by reordering nodes
11 | // we allocate enough hash entries to be exactly on the edge of rehash threshold
12 | for (int i = 0; i < 8; ++i)
13 | CHECK(n.prepend_child(node_element));
14 | }
15 |
16 | TEST_XML_FLAGS(compact_out_of_memory_string, "", parse_pi)
17 | {
18 | test_runner::_memory_fail_threshold = 1;
19 |
20 | overflow_hash_table(doc);
21 |
22 | xml_attribute a = doc.child(STR("n")).attribute(STR("a"));
23 | xml_node pi = doc.last_child();
24 |
25 | CHECK_ALLOC_FAIL(CHECK(!pi.set_name(STR("name"))));
26 | CHECK_ALLOC_FAIL(CHECK(!pi.set_value(STR("value"))));
27 | CHECK_ALLOC_FAIL(CHECK(!a.set_name(STR("name"))));
28 | CHECK_ALLOC_FAIL(CHECK(!a.set_value(STR("value"))));
29 | }
30 |
31 | TEST_XML(compact_out_of_memory_attribute, "")
32 | {
33 | test_runner::_memory_fail_threshold = 1;
34 |
35 | overflow_hash_table(doc);
36 |
37 | xml_node n = doc.child(STR("n"));
38 | xml_attribute a = n.attribute(STR("a"));
39 |
40 | CHECK_ALLOC_FAIL(CHECK(!n.append_attribute(STR(""))));
41 | CHECK_ALLOC_FAIL(CHECK(!n.prepend_attribute(STR(""))));
42 | CHECK_ALLOC_FAIL(CHECK(!n.insert_attribute_after(STR(""), a)));
43 | CHECK_ALLOC_FAIL(CHECK(!n.insert_attribute_before(STR(""), a)));
44 | }
45 |
46 | TEST_XML(compact_out_of_memory_attribute_copy, "")
47 | {
48 | test_runner::_memory_fail_threshold = 1;
49 |
50 | overflow_hash_table(doc);
51 |
52 | xml_node n = doc.child(STR("n"));
53 | xml_attribute a = n.attribute(STR("a"));
54 |
55 | CHECK_ALLOC_FAIL(CHECK(!n.append_copy(a)));
56 | CHECK_ALLOC_FAIL(CHECK(!n.prepend_copy(a)));
57 | CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_after(a, a)));
58 | CHECK_ALLOC_FAIL(CHECK(!n.insert_copy_before(a, a)));
59 | }
60 |
61 | TEST_XML(compact_out_of_memory_node, "")
62 | {
63 | test_runner::_memory_fail_threshold = 1;
64 |
65 | overflow_hash_table(doc);
66 |
67 | xml_node n = doc.child(STR("n"));
68 |
69 | CHECK_ALLOC_FAIL(CHECK(!doc.append_child(node_element)));
70 | CHECK_ALLOC_FAIL(CHECK(!doc.prepend_child(node_element)));
71 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_child_after(node_element, n)));
72 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_child_before(node_element, n)));
73 | }
74 |
75 | TEST_XML(compact_out_of_memory_node_copy, "")
76 | {
77 | test_runner::_memory_fail_threshold = 1;
78 |
79 | overflow_hash_table(doc);
80 |
81 | xml_node n = doc.child(STR("n"));
82 |
83 | CHECK_ALLOC_FAIL(CHECK(!doc.append_copy(n)));
84 | CHECK_ALLOC_FAIL(CHECK(!doc.prepend_copy(n)));
85 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_copy_after(n, n)));
86 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_copy_before(n, n)));
87 | }
88 |
89 | TEST_XML(compact_out_of_memory_node_move, "")
90 | {
91 | test_runner::_memory_fail_threshold = 1;
92 |
93 | overflow_hash_table(doc);
94 |
95 | xml_node n = doc.child(STR("n"));
96 | xml_node ne = doc.child(STR("ne"));
97 |
98 | CHECK_ALLOC_FAIL(CHECK(!doc.append_move(n)));
99 | CHECK_ALLOC_FAIL(CHECK(!doc.prepend_move(n)));
100 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_move_after(n, ne)));
101 | CHECK_ALLOC_FAIL(CHECK(!doc.insert_move_before(n, ne)));
102 | }
103 |
104 | TEST_XML(compact_out_of_memory_remove, "")
105 | {
106 | test_runner::_memory_fail_threshold = 1;
107 |
108 | overflow_hash_table(doc);
109 |
110 | xml_node n = doc.child(STR("n"));
111 | xml_attribute a = n.attribute(STR("a"));
112 |
113 | CHECK_ALLOC_FAIL(CHECK(!n.remove_attribute(a)));
114 | CHECK_ALLOC_FAIL(CHECK(!doc.remove_child(n)));
115 | }
116 |
117 | TEST_XML(compact_pointer_attribute_list, "")
118 | {
119 | xml_node n = doc.child(STR("n"));
120 | xml_attribute a = n.attribute(STR("a"));
121 |
122 | // make sure we fill the page with node x
123 | for (int i = 0; i < 1000; ++i)
124 | doc.append_child(STR("x"));
125 |
126 | // this requires extended encoding for prev_attribute_c/next_attribute
127 | n.append_attribute(STR("b"));
128 |
129 | // this requires extended encoding for first_attribute
130 | n.remove_attribute(a);
131 |
132 | CHECK(!n.attribute(STR("a")));
133 | CHECK(n.attribute(STR("b")));
134 | }
135 |
136 | TEST_XML(compact_pointer_node_list, "")
137 | {
138 | xml_node n = doc.child(STR("n"));
139 |
140 | // make sure we fill the page with node x
141 | // this requires extended encoding for prev_sibling_c/next_sibling
142 | for (int i = 0; i < 1000; ++i)
143 | doc.append_child(STR("x"));
144 |
145 | // this requires extended encoding for first_child
146 | n.append_child(STR("child"));
147 |
148 | CHECK(n.child(STR("child")));
149 | }
150 | #endif
151 |
--------------------------------------------------------------------------------
/tests/test.cpp:
--------------------------------------------------------------------------------
1 | #define _SCL_SECURE_NO_WARNINGS
2 | #define _SCL_SECURE_NO_DEPRECATE
3 |
4 | #include "test.hpp"
5 |
6 | #include "writer_string.hpp"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 |
16 | #ifndef PUGIXML_NO_XPATH
17 | static void build_document_order(std::vector& result, pugi::xml_node root)
18 | {
19 | result.push_back(pugi::xpath_node());
20 |
21 | pugi::xml_node cur = root;
22 |
23 | for (;;)
24 | {
25 | result.push_back(cur);
26 |
27 | for (pugi::xml_attribute a = cur.first_attribute(); a; a = a.next_attribute())
28 | result.push_back(pugi::xpath_node(a, cur));
29 |
30 | if (cur.first_child())
31 | cur = cur.first_child();
32 | else if (cur.next_sibling())
33 | cur = cur.next_sibling();
34 | else
35 | {
36 | while (cur && !cur.next_sibling()) cur = cur.parent();
37 | cur = cur.next_sibling();
38 |
39 | if (!cur) break;
40 | }
41 | }
42 | }
43 | #endif
44 |
45 | bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs)
46 | {
47 | return (!lhs || !rhs) ? lhs == rhs :
48 | #ifdef PUGIXML_WCHAR_MODE
49 | wcscmp(lhs, rhs) == 0;
50 | #else
51 | strcmp(lhs, rhs) == 0;
52 | #endif
53 | }
54 |
55 | bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags)
56 | {
57 | xml_writer_string writer;
58 |
59 | node.print(writer, indent, flags, get_native_encoding());
60 |
61 | return writer.as_string() == contents;
62 | }
63 |
64 | bool test_double_nan(double value)
65 | {
66 | #if defined(_MSC_VER) || defined(__BORLANDC__)
67 | return _isnan(value) != 0;
68 | #else
69 | return value != value;
70 | #endif
71 | }
72 |
73 | #ifndef PUGIXML_NO_XPATH
74 | static size_t strlength(const pugi::char_t* s)
75 | {
76 | #ifdef PUGIXML_WCHAR_MODE
77 | return wcslen(s);
78 | #else
79 | return strlen(s);
80 | #endif
81 | }
82 |
83 | bool test_xpath_string(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, const pugi::char_t* expected)
84 | {
85 | pugi::xpath_query q(query, variables);
86 | if (!q) return false;
87 |
88 | const size_t capacity = 64;
89 | pugi::char_t result[capacity];
90 |
91 | size_t size = q.evaluate_string(result, capacity, node);
92 |
93 | if (size != strlength(expected) + 1)
94 | return false;
95 |
96 | if (size <= capacity)
97 | return test_string_equal(result, expected);
98 |
99 | std::basic_string buffer(size, ' ');
100 |
101 | return q.evaluate_string(&buffer[0], size, node) == size && test_string_equal(buffer.c_str(), expected);
102 | }
103 |
104 | bool test_xpath_boolean(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, bool expected)
105 | {
106 | pugi::xpath_query q(query, variables);
107 | if (!q) return false;
108 |
109 | return q.evaluate_boolean(node) == expected;
110 | }
111 |
112 | bool test_xpath_number(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, double expected)
113 | {
114 | pugi::xpath_query q(query, variables);
115 | if (!q) return false;
116 |
117 | double value = q.evaluate_number(node);
118 | double absolute_error = fabs(value - expected);
119 |
120 | const double tolerance = 1e-15;
121 | return absolute_error < tolerance || absolute_error < fabs(expected) * tolerance;
122 | }
123 |
124 | bool test_xpath_number_nan(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables)
125 | {
126 | pugi::xpath_query q(query, variables);
127 | if (!q) return false;
128 |
129 | return test_double_nan(q.evaluate_number(node));
130 | }
131 |
132 | bool test_xpath_fail_compile(const pugi::char_t* query, pugi::xpath_variable_set* variables)
133 | {
134 | #ifdef PUGIXML_NO_EXCEPTIONS
135 | return !pugi::xpath_query(query, variables);
136 | #else
137 | try
138 | {
139 | pugi::xpath_query q(query, variables);
140 | return false;
141 | }
142 | catch (const pugi::xpath_exception&)
143 | {
144 | return true;
145 | }
146 | #endif
147 | }
148 |
149 | void xpath_node_set_tester::check(bool condition)
150 | {
151 | if (!condition)
152 | {
153 | test_runner::_failure_message = message;
154 | longjmp(test_runner::_failure_buffer, 1);
155 | }
156 | }
157 |
158 | xpath_node_set_tester::xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message_): last(0), message(message_)
159 | {
160 | result = set;
161 |
162 | // only sort unsorted sets so that we're able to verify reverse order for some axes
163 | if (result.type() == pugi::xpath_node_set::type_unsorted) result.sort();
164 |
165 | if (result.empty())
166 | {
167 | document_order = 0;
168 | document_size = 0;
169 | }
170 | else
171 | {
172 | std::vector order;
173 | build_document_order(order, (result[0].attribute() ? result[0].parent() : result[0].node()).root());
174 |
175 | document_order = new pugi::xpath_node[order.size()];
176 | std::copy(order.begin(), order.end(), document_order);
177 |
178 | document_size = order.size();
179 | }
180 | }
181 |
182 | xpath_node_set_tester::~xpath_node_set_tester()
183 | {
184 | // check that we processed everything
185 | check(last == result.size());
186 |
187 | delete[] document_order;
188 | }
189 |
190 | xpath_node_set_tester& xpath_node_set_tester::operator%(unsigned int expected)
191 | {
192 | // check element count
193 | check(last < result.size());
194 |
195 | // check document order
196 | check(expected < document_size);
197 | check(result.begin()[last] == document_order[expected]);
198 |
199 | // continue to the next element
200 | last++;
201 |
202 | return *this;
203 | }
204 |
205 | #endif
206 |
207 | bool is_little_endian()
208 | {
209 | unsigned int ui = 1;
210 | return *reinterpret_cast(&ui) == 1;
211 | }
212 |
213 | pugi::xml_encoding get_native_encoding()
214 | {
215 | #ifdef PUGIXML_WCHAR_MODE
216 | return pugi::encoding_wchar;
217 | #else
218 | return pugi::encoding_utf8;
219 | #endif
220 | }
221 |
--------------------------------------------------------------------------------
/tests/main.cpp:
--------------------------------------------------------------------------------
1 | #include "test.hpp"
2 | #include "allocator.hpp"
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | #ifndef PUGIXML_NO_EXCEPTIONS
12 | # include
13 | #endif
14 |
15 | #ifdef _WIN32_WCE
16 | # undef DebugBreak
17 | # pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
18 | # include
19 | #endif
20 |
21 | test_runner* test_runner::_tests = 0;
22 | size_t test_runner::_memory_fail_threshold = 0;
23 | bool test_runner::_memory_fail_triggered = false;
24 | jmp_buf test_runner::_failure_buffer;
25 | const char* test_runner::_failure_message;
26 | const char* test_runner::_temp_path;
27 |
28 | static size_t g_memory_total_size = 0;
29 | static size_t g_memory_total_count = 0;
30 | static size_t g_memory_fail_triggered = false;
31 |
32 | static void* custom_allocate(size_t size)
33 | {
34 | if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size)
35 | {
36 | g_memory_fail_triggered = true;
37 | test_runner::_memory_fail_triggered = true;
38 |
39 | return 0;
40 | }
41 | else
42 | {
43 | void* ptr = memory_allocate(size);
44 | if (!ptr) return 0;
45 |
46 | g_memory_total_size += memory_size(ptr);
47 | g_memory_total_count++;
48 |
49 | return ptr;
50 | }
51 | }
52 |
53 | #ifndef PUGIXML_NO_EXCEPTIONS
54 | static void* custom_allocate_throw(size_t size)
55 | {
56 | void* result = custom_allocate(size);
57 |
58 | if (!result)
59 | throw std::bad_alloc();
60 |
61 | return result;
62 | }
63 | #endif
64 |
65 | static void custom_deallocate(void* ptr)
66 | {
67 | assert(ptr);
68 |
69 | g_memory_total_size -= memory_size(ptr);
70 | g_memory_total_count--;
71 |
72 | memory_deallocate(ptr);
73 | }
74 |
75 | static void replace_memory_management()
76 | {
77 | // create some document to touch original functions
78 | {
79 | pugi::xml_document doc;
80 | doc.append_child().set_name(STR("node"));
81 | }
82 |
83 | // replace functions
84 | pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
85 | }
86 |
87 | #if defined(_MSC_VER) && _MSC_VER > 1200 && _MSC_VER < 1400 && !defined(__INTEL_COMPILER) && !defined(__DMC__)
88 | #include
89 |
90 | namespace std
91 | {
92 | _CRTIMP2 _Prhand _Raise_handler;
93 | _CRTIMP2 void __cdecl _Throw(const exception&) {}
94 | }
95 | #endif
96 |
97 | static bool run_test(test_runner* test, const char* test_name, pugi::allocation_function allocate)
98 | {
99 | #ifndef PUGIXML_NO_EXCEPTIONS
100 | try
101 | {
102 | #endif
103 | g_memory_total_size = 0;
104 | g_memory_total_count = 0;
105 | g_memory_fail_triggered = false;
106 | test_runner::_memory_fail_threshold = 0;
107 | test_runner::_memory_fail_triggered = false;
108 |
109 | pugi::set_memory_management_functions(allocate, custom_deallocate);
110 |
111 | #ifdef _MSC_VER
112 | # pragma warning(push)
113 | # pragma warning(disable: 4611) // interaction between _setjmp and C++ object destruction is non-portable
114 | # pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
115 | #endif
116 |
117 | volatile int result = setjmp(test_runner::_failure_buffer);
118 |
119 | #ifdef _MSC_VER
120 | # pragma warning(pop)
121 | #endif
122 |
123 | if (result)
124 | {
125 | printf("Test %s failed: %s\n", test_name, test_runner::_failure_message);
126 | return false;
127 | }
128 |
129 | test->run();
130 |
131 | if (test_runner::_memory_fail_triggered)
132 | {
133 | printf("Test %s failed: unguarded memory fail triggered\n", test_name);
134 | return false;
135 | }
136 |
137 | if (g_memory_total_size != 0 || g_memory_total_count != 0)
138 | {
139 | printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test_name, static_cast(g_memory_total_size), static_cast(g_memory_total_count));
140 | return false;
141 | }
142 |
143 | return true;
144 | #ifndef PUGIXML_NO_EXCEPTIONS
145 | }
146 | catch (const std::exception& e)
147 | {
148 | printf("Test %s failed: exception %s\n", test_name, e.what());
149 | return false;
150 | }
151 | catch (...)
152 | {
153 | printf("Test %s failed for unknown reason\n", test_name);
154 | return false;
155 | }
156 | #endif
157 | }
158 |
159 | #if defined(__CELLOS_LV2__) && defined(PUGIXML_NO_EXCEPTIONS) && !defined(__SNC__)
160 | #include
161 |
162 | void std::exception::_Raise() const
163 | {
164 | abort();
165 | }
166 | #endif
167 |
168 | int main(int, char** argv)
169 | {
170 | #ifdef __BORLANDC__
171 | _control87(MCW_EM | PC_53, MCW_EM | MCW_PC);
172 | #endif
173 |
174 | // setup temp path as the executable folder
175 | std::string temp = argv[0];
176 | std::string::size_type slash = temp.find_last_of("\\/");
177 | temp.erase((slash != std::string::npos) ? slash + 1 : 0);
178 |
179 | test_runner::_temp_path = temp.c_str();
180 |
181 | replace_memory_management();
182 |
183 | unsigned int total = 0;
184 | unsigned int passed = 0;
185 |
186 | test_runner* test = 0; // gcc3 "variable might be used uninitialized in this function" bug workaround
187 |
188 | for (test = test_runner::_tests; test; test = test->_next)
189 | {
190 | total++;
191 | passed += run_test(test, test->_name, custom_allocate);
192 |
193 | if (g_memory_fail_triggered)
194 | {
195 | // run tests that trigger memory failures twice - with an allocator that returns NULL and with an allocator that throws
196 | #ifndef PUGIXML_NO_EXCEPTIONS
197 | total++;
198 | passed += run_test(test, (test->_name + std::string(" (throw)")).c_str(), custom_allocate_throw);
199 | #endif
200 | }
201 | }
202 |
203 | unsigned int failed = total - passed;
204 |
205 | if (failed != 0)
206 | printf("FAILURE: %u out of %u tests failed.\n", failed, total);
207 | else
208 | printf("Success: %u tests passed.\n", total);
209 |
210 | return failed;
211 | }
212 |
213 | #ifdef _WIN32_WCE
214 | int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
215 | {
216 | return main(0, NULL);
217 | }
218 | #endif
219 |
--------------------------------------------------------------------------------
/scripts/pugixml.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 45;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0424128F67AB5C730232235E /* pugixml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47481C4F0E03673E0E780637 /* pugixml.cpp */; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXFileReference section */
14 | 0B66463C5F896E6449051D38 /* pugiconfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugiconfig.hpp"; path = "pugiconfig.hpp"; sourceTree = ""; };
15 | 47481C4F0E03673E0E780637 /* pugixml.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "pugixml.cpp"; path = "pugixml.cpp"; sourceTree = ""; };
16 | 6C911F0460FC44CD3B1B5624 /* pugixml.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugixml.hpp"; path = "pugixml.hpp"; sourceTree = ""; };
17 | 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libpugixmld.a"; path = "libpugixmld.a"; sourceTree = BUILT_PRODUCTS_DIR; };
18 | /* End PBXFileReference section */
19 |
20 | /* Begin PBXFrameworksBuildPhase section */
21 | 2BA00212518037166623673F /* Frameworks */ = {
22 | isa = PBXFrameworksBuildPhase;
23 | buildActionMask = 2147483647;
24 | files = (
25 | );
26 | runOnlyForDeploymentPostprocessing = 0;
27 | };
28 | /* End PBXFrameworksBuildPhase section */
29 |
30 | /* Begin PBXGroup section */
31 | 19E0517F3CF26ED63AE23641 /* pugixml */ = {
32 | isa = PBXGroup;
33 | children = (
34 | 578963B4309E714F05E01D71 /* src */,
35 | 219F66186DDF392149043810 /* Products */,
36 | );
37 | name = "pugixml";
38 | sourceTree = "";
39 | };
40 | 578963B4309E714F05E01D71 /* src */ = {
41 | isa = PBXGroup;
42 | children = (
43 | 0B66463C5F896E6449051D38 /* pugiconfig.hpp */,
44 | 47481C4F0E03673E0E780637 /* pugixml.cpp */,
45 | 6C911F0460FC44CD3B1B5624 /* pugixml.hpp */,
46 | );
47 | name = "src";
48 | path = ../src;
49 | sourceTree = "";
50 | };
51 | 219F66186DDF392149043810 /* Products */ = {
52 | isa = PBXGroup;
53 | children = (
54 | 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */,
55 | );
56 | name = "Products";
57 | sourceTree = "";
58 | };
59 | /* End PBXGroup section */
60 |
61 | /* Begin PBXNativeTarget section */
62 | 6B55152571905B6C3A6F39D0 /* pugixml */ = {
63 | isa = PBXNativeTarget;
64 | buildConfigurationList = 73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget "pugixml" */;
65 | buildPhases = (
66 | 6CA66B9B6252229A36E8733C /* Resources */,
67 | 287808486FBF545206A47CC1 /* Sources */,
68 | 2BA00212518037166623673F /* Frameworks */,
69 | );
70 | buildRules = (
71 | );
72 | dependencies = (
73 | );
74 | name = "pugixml";
75 | productName = "pugixml";
76 | productReference = 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */;
77 | productType = "com.apple.product-type.library.static";
78 | };
79 | /* End PBXNativeTarget section */
80 |
81 | /* Begin PBXProject section */
82 | 08FB7793FE84155DC02AAC07 /* Project object */ = {
83 | isa = PBXProject;
84 | buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */;
85 | compatibilityVersion = "Xcode 3.1";
86 | hasScannedForEncodings = 1;
87 | mainGroup = 19E0517F3CF26ED63AE23641 /* pugixml */;
88 | projectDirPath = "";
89 | projectRoot = "";
90 | targets = (
91 | 6B55152571905B6C3A6F39D0 /* libpugixmld.a */,
92 | );
93 | };
94 | /* End PBXProject section */
95 |
96 | /* Begin PBXResourcesBuildPhase section */
97 | 6CA66B9B6252229A36E8733C /* Resources */ = {
98 | isa = PBXResourcesBuildPhase;
99 | buildActionMask = 2147483647;
100 | files = (
101 | );
102 | runOnlyForDeploymentPostprocessing = 0;
103 | };
104 | /* End PBXResourcesBuildPhase section */
105 |
106 | /* Begin PBXSourcesBuildPhase section */
107 | 287808486FBF545206A47CC1 /* Sources */ = {
108 | isa = PBXSourcesBuildPhase;
109 | buildActionMask = 2147483647;
110 | files = (
111 | 0424128F67AB5C730232235E /* pugixml.cpp in Sources */,
112 | );
113 | runOnlyForDeploymentPostprocessing = 0;
114 | };
115 | /* End PBXSourcesBuildPhase section */
116 |
117 | /* Begin PBXVariantGroup section */
118 | /* End PBXVariantGroup section */
119 |
120 | /* Begin XCBuildConfiguration section */
121 | 4FDB54E4253E36FC55CE27E8 /* Debug */ = {
122 | isa = XCBuildConfiguration;
123 | buildSettings = {
124 | ALWAYS_SEARCH_USER_PATHS = NO;
125 | CONFIGURATION_BUILD_DIR = xcode3;
126 | GCC_DYNAMIC_NO_PIC = NO;
127 | GCC_MODEL_TUNING = G5;
128 | INSTALL_PATH = /usr/local/lib;
129 | PRODUCT_NAME = "pugixmld";
130 | };
131 | name = "Debug";
132 | };
133 | 0A4C28F553990E0405306C15 /* Release */ = {
134 | isa = XCBuildConfiguration;
135 | buildSettings = {
136 | ALWAYS_SEARCH_USER_PATHS = NO;
137 | CONFIGURATION_BUILD_DIR = xcode3;
138 | GCC_DYNAMIC_NO_PIC = NO;
139 | GCC_MODEL_TUNING = G5;
140 | INSTALL_PATH = /usr/local/lib;
141 | PRODUCT_NAME = "pugixml";
142 | };
143 | name = "Release";
144 | };
145 | 65DB0F6D27EA20852B6E3BB4 /* Debug */ = {
146 | isa = XCBuildConfiguration;
147 | buildSettings = {
148 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
149 | CONFIGURATION_BUILD_DIR = "$(SYMROOT)";
150 | CONFIGURATION_TEMP_DIR = "$(OBJROOT)";
151 | COPY_PHASE_STRIP = NO;
152 | GCC_C_LANGUAGE_STANDARD = gnu99;
153 | GCC_OPTIMIZATION_LEVEL = 0;
154 | GCC_PREPROCESSOR_DEFINITIONS = (
155 | "_DEBUG",
156 | );
157 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
158 | GCC_WARN_UNUSED_VARIABLE = YES;
159 | OBJROOT = "xcode3/Universal/Debug";
160 | ONLY_ACTIVE_ARCH = NO;
161 | PREBINDING = NO;
162 | SYMROOT = "xcode3";
163 | };
164 | name = "Debug";
165 | };
166 | 5314084032B57C1A11945858 /* Release */ = {
167 | isa = XCBuildConfiguration;
168 | buildSettings = {
169 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
170 | CONFIGURATION_BUILD_DIR = "$(SYMROOT)";
171 | CONFIGURATION_TEMP_DIR = "$(OBJROOT)";
172 | COPY_PHASE_STRIP = NO;
173 | GCC_C_LANGUAGE_STANDARD = gnu99;
174 | GCC_OPTIMIZATION_LEVEL = s;
175 | GCC_PREPROCESSOR_DEFINITIONS = (
176 | "NDEBUG",
177 | );
178 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
179 | GCC_WARN_UNUSED_VARIABLE = YES;
180 | OBJROOT = "xcode3/Universal/Release";
181 | ONLY_ACTIVE_ARCH = NO;
182 | PREBINDING = NO;
183 | SYMROOT = "xcode3";
184 | };
185 | name = "Release";
186 | };
187 | /* End XCBuildConfiguration section */
188 |
189 | /* Begin XCConfigurationList section */
190 | 73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget "libpugixmld.a" */ = {
191 | isa = XCConfigurationList;
192 | buildConfigurations = (
193 | 4FDB54E4253E36FC55CE27E8 /* Debug */,
194 | 0A4C28F553990E0405306C15 /* Release */,
195 | );
196 | defaultConfigurationIsVisible = 0;
197 | defaultConfigurationName = "Debug";
198 | };
199 | 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */ = {
200 | isa = XCConfigurationList;
201 | buildConfigurations = (
202 | 65DB0F6D27EA20852B6E3BB4 /* Debug */,
203 | 5314084032B57C1A11945858 /* Release */,
204 | );
205 | defaultConfigurationIsVisible = 0;
206 | defaultConfigurationName = "Debug";
207 | };
208 | /* End XCConfigurationList section */
209 |
210 | };
211 | rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
212 | }
213 |
--------------------------------------------------------------------------------
/scripts/pugixml_vs2008.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
28 |
31 |
34 |
37 |
40 |
43 |
55 |
58 |
62 |
65 |
69 |
72 |
75 |
78 |
81 |
84 |
87 |
90 |
93 |
94 |
101 |
104 |
107 |
110 |
113 |
117 |
129 |
132 |
136 |
139 |
143 |
146 |
149 |
152 |
155 |
158 |
161 |
164 |
167 |
168 |
175 |
178 |
181 |
184 |
187 |
190 |
202 |
205 |
209 |
212 |
216 |
219 |
222 |
225 |
228 |
231 |
234 |
237 |
240 |
241 |
248 |
251 |
254 |
257 |
260 |
264 |
276 |
279 |
283 |
286 |
290 |
293 |
296 |
299 |
302 |
305 |
308 |
311 |
314 |
315 |
316 |
317 |
318 |
319 |
323 |
326 |
327 |
330 |
331 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
--------------------------------------------------------------------------------
/scripts/pugixml_vs2008_static.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
28 |
31 |
34 |
37 |
40 |
43 |
55 |
58 |
62 |
65 |
69 |
72 |
75 |
78 |
81 |
84 |
87 |
90 |
93 |
94 |
101 |
104 |
107 |
110 |
113 |
117 |
129 |
132 |
136 |
139 |
143 |
146 |
149 |
152 |
155 |
158 |
161 |
164 |
167 |
168 |
175 |
178 |
181 |
184 |
187 |
190 |
202 |
205 |
209 |
212 |
216 |
219 |
222 |
225 |
228 |
231 |
234 |
237 |
240 |
241 |
248 |
251 |
254 |
257 |
260 |
264 |
276 |
279 |
283 |
286 |
290 |
293 |
296 |
299 |
302 |
305 |
308 |
311 |
314 |
315 |
316 |
317 |
318 |
319 |
323 |
326 |
327 |
330 |
331 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
--------------------------------------------------------------------------------
/tests/test_memory.cpp:
--------------------------------------------------------------------------------
1 | #include "test.hpp"
2 |
3 | #include "writer_string.hpp"
4 | #include "allocator.hpp"
5 |
6 | #include
7 | #include
8 |
9 | using namespace pugi;
10 |
11 | namespace
12 | {
13 | int page_allocs = 0;
14 | int page_deallocs = 0;
15 |
16 | bool is_page(size_t size)
17 | {
18 | return size >= 16384;
19 | }
20 |
21 | void* allocate(size_t size)
22 | {
23 | void* ptr = memory_allocate(size);
24 | page_allocs += is_page(memory_size(ptr));
25 | return ptr;
26 | }
27 |
28 | void deallocate(void* ptr)
29 | {
30 | page_deallocs += is_page(memory_size(ptr));
31 | memory_deallocate(ptr);
32 | }
33 | }
34 |
35 | TEST(memory_custom_memory_management)
36 | {
37 | page_allocs = page_deallocs = 0;
38 |
39 | // remember old functions
40 | allocation_function old_allocate = get_memory_allocation_function();
41 | deallocation_function old_deallocate = get_memory_deallocation_function();
42 |
43 | // replace functions
44 | set_memory_management_functions(allocate, deallocate);
45 |
46 | {
47 | // parse document
48 | xml_document doc;
49 |
50 | CHECK(page_allocs == 0 && page_deallocs == 0);
51 |
52 | CHECK(doc.load_string(STR("")));
53 |
54 | CHECK(page_allocs == 1 && page_deallocs == 0);
55 |
56 | // modify document (no new page)
57 | CHECK(doc.first_child().set_name(STR("foobars")));
58 | CHECK(page_allocs == 1 && page_deallocs == 0);
59 |
60 | // modify document (new page)
61 | std::basic_string s(65536, 'x');
62 |
63 | CHECK(doc.first_child().set_name(s.c_str()));
64 | CHECK(page_allocs == 2 && page_deallocs == 0);
65 |
66 | // modify document (new page, old one should die)
67 | s += s;
68 |
69 | CHECK(doc.first_child().set_name(s.c_str()));
70 | CHECK(page_allocs == 3 && page_deallocs == 1);
71 | }
72 |
73 | CHECK(page_allocs == 3 && page_deallocs == 3);
74 |
75 | // restore old functions
76 | set_memory_management_functions(old_allocate, old_deallocate);
77 | }
78 |
79 | TEST(memory_large_allocations)
80 | {
81 | page_allocs = page_deallocs = 0;
82 |
83 | // remember old functions
84 | allocation_function old_allocate = get_memory_allocation_function();
85 | deallocation_function old_deallocate = get_memory_deallocation_function();
86 |
87 | // replace functions
88 | set_memory_management_functions(allocate, deallocate);
89 |
90 | {
91 | xml_document doc;
92 |
93 | CHECK(page_allocs == 0 && page_deallocs == 0);
94 |
95 | // initial fill
96 | for (size_t i = 0; i < 128; ++i)
97 | {
98 | std::basic_string s(i * 128, 'x');
99 |
100 | CHECK(doc.append_child(node_pcdata).set_value(s.c_str()));
101 | }
102 |
103 | CHECK(page_allocs > 0 && page_deallocs == 0);
104 |
105 | // grow-prune loop
106 | while (doc.first_child())
107 | {
108 | xml_node node;
109 |
110 | // grow
111 | for (node = doc.first_child(); node; node = node.next_sibling())
112 | {
113 | std::basic_string s = node.value();
114 |
115 | CHECK(node.set_value((s + s).c_str()));
116 | }
117 |
118 | // prune
119 | for (node = doc.first_child(); node; )
120 | {
121 | xml_node next = node.next_sibling().next_sibling();
122 |
123 | node.parent().remove_child(node);
124 |
125 | node = next;
126 | }
127 | }
128 |
129 | CHECK(page_allocs == page_deallocs + 1); // only one live page left (it waits for new allocations)
130 |
131 | char buffer;
132 | CHECK(doc.load_buffer_inplace(&buffer, 0, parse_fragment, get_native_encoding()));
133 |
134 | CHECK(page_allocs == page_deallocs); // no live pages left
135 | }
136 |
137 | CHECK(page_allocs == page_deallocs); // everything is freed
138 |
139 | // restore old functions
140 | set_memory_management_functions(old_allocate, old_deallocate);
141 | }
142 |
143 | TEST(memory_page_management)
144 | {
145 | page_allocs = page_deallocs = 0;
146 |
147 | // remember old functions
148 | allocation_function old_allocate = get_memory_allocation_function();
149 | deallocation_function old_deallocate = get_memory_deallocation_function();
150 |
151 | // replace functions
152 | set_memory_management_functions(allocate, deallocate);
153 |
154 | {
155 | xml_document doc;
156 |
157 | CHECK(page_allocs == 0 && page_deallocs == 0);
158 |
159 | // initial fill
160 | std::vector nodes;
161 |
162 | for (size_t i = 0; i < 4000; ++i)
163 | {
164 | xml_node node = doc.append_child(STR("n"));
165 | CHECK(node);
166 |
167 | nodes.push_back(node);
168 | }
169 |
170 | CHECK(page_allocs > 0 && page_deallocs == 0);
171 |
172 | // grow-prune loop
173 | size_t offset = 0;
174 | size_t prime = 15485863;
175 |
176 | while (nodes.size() > 0)
177 | {
178 | offset = (offset + prime) % nodes.size();
179 |
180 | doc.remove_child(nodes[offset]);
181 |
182 | nodes[offset] = nodes.back();
183 | nodes.pop_back();
184 | }
185 |
186 | CHECK(page_allocs == page_deallocs + 1); // only one live page left (it waits for new allocations)
187 |
188 | char buffer;
189 | CHECK(doc.load_buffer_inplace(&buffer, 0, parse_fragment, get_native_encoding()));
190 |
191 | CHECK(page_allocs == page_deallocs); // no live pages left
192 | }
193 |
194 | CHECK(page_allocs == page_deallocs); // everything is freed
195 |
196 | // restore old functions
197 | set_memory_management_functions(old_allocate, old_deallocate);
198 | }
199 |
200 | TEST(memory_string_allocate_increasing)
201 | {
202 | xml_document doc;
203 |
204 | doc.append_child(node_pcdata).set_value(STR("x"));
205 |
206 | std::basic_string s = STR("ab");
207 |
208 | for (int i = 0; i < 17; ++i)
209 | {
210 | doc.append_child(node_pcdata).set_value(s.c_str());
211 |
212 | s += s;
213 | }
214 |
215 | std::string result = save_narrow(doc, format_no_declaration | format_raw, encoding_utf8);
216 |
217 | CHECK(result.size() == 262143);
218 | CHECK(result[0] == 'x');
219 |
220 | for (size_t j = 1; j < result.size(); ++j)
221 | {
222 | CHECK(result[j] == (j % 2 ? 'a' : 'b'));
223 | }
224 | }
225 |
226 | TEST(memory_string_allocate_decreasing)
227 | {
228 | xml_document doc;
229 |
230 | std::basic_string s = STR("ab");
231 |
232 | for (int i = 0; i < 17; ++i) s += s;
233 |
234 | for (int j = 0; j < 17; ++j)
235 | {
236 | s.resize(s.size() / 2);
237 |
238 | doc.append_child(node_pcdata).set_value(s.c_str());
239 | }
240 |
241 | doc.append_child(node_pcdata).set_value(STR("x"));
242 |
243 | std::string result = save_narrow(doc, format_no_declaration | format_raw, encoding_utf8);
244 |
245 | CHECK(result.size() == 262143);
246 | CHECK(result[result.size() - 1] == 'x');
247 |
248 | for (size_t k = 0; k + 1 < result.size(); ++k)
249 | {
250 | CHECK(result[k] == (k % 2 ? 'b' : 'a'));
251 | }
252 | }
253 |
254 | TEST(memory_string_allocate_increasing_inplace)
255 | {
256 | xml_document doc;
257 |
258 | xml_node node = doc.append_child(node_pcdata);
259 |
260 | node.set_value(STR("x"));
261 |
262 | std::basic_string s = STR("ab");
263 |
264 | for (int i = 0; i < 17; ++i)
265 | {
266 | node.set_value(s.c_str());
267 |
268 | s += s;
269 | }
270 |
271 | std::string result = save_narrow(doc, format_no_declaration | format_raw, encoding_utf8);
272 |
273 | CHECK(result.size() == 131072);
274 |
275 | for (size_t j = 0; j < result.size(); ++j)
276 | {
277 | CHECK(result[j] == (j % 2 ? 'b' : 'a'));
278 | }
279 | }
280 |
281 | TEST(memory_string_allocate_decreasing_inplace)
282 | {
283 | xml_document doc;
284 |
285 | xml_node node = doc.append_child(node_pcdata);
286 |
287 | std::basic_string s = STR("ab");
288 |
289 | for (int i = 0; i < 17; ++i) s += s;
290 |
291 | for (int j = 0; j < 17; ++j)
292 | {
293 | s.resize(s.size() / 2);
294 |
295 | node.set_value(s.c_str());
296 | }
297 |
298 | node.set_value(STR("x"));
299 |
300 | std::string result = save_narrow(doc, format_no_declaration | format_raw, encoding_utf8);
301 |
302 | CHECK(result == "x");
303 | }
304 |
--------------------------------------------------------------------------------
/scripts/pugixml_vs2005.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
28 |
31 |
34 |
37 |
40 |
43 |
56 |
59 |
63 |
66 |
70 |
73 |
76 |
79 |
82 |
85 |
88 |
91 |
94 |
95 |
102 |
105 |
108 |
111 |
114 |
118 |
131 |
134 |
138 |
141 |
145 |
148 |
151 |
154 |
157 |
160 |
163 |
166 |
169 |
170 |
177 |
180 |
183 |
186 |
189 |
192 |
205 |
208 |
212 |
215 |
219 |
222 |
225 |
228 |
231 |
234 |
237 |
240 |
243 |
244 |
251 |
254 |
257 |
260 |
263 |
267 |
280 |
283 |
287 |
290 |
294 |
297 |
300 |
303 |
306 |
309 |
312 |
315 |
318 |
319 |
320 |
321 |
322 |
323 |
327 |
330 |
331 |
334 |
335 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
--------------------------------------------------------------------------------
/scripts/pugixml_vs2005_static.vcproj:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
28 |
31 |
34 |
37 |
40 |
43 |
56 |
59 |
63 |
66 |
70 |
73 |
76 |
79 |
82 |
85 |
88 |
91 |
94 |
95 |
102 |
105 |
108 |
111 |
114 |
118 |
131 |
134 |
138 |
141 |
145 |
148 |
151 |
154 |
157 |
160 |
163 |
166 |
169 |
170 |
177 |
180 |
183 |
186 |
189 |
192 |
205 |
208 |
212 |
215 |
219 |
222 |
225 |
228 |
231 |
234 |
237 |
240 |
243 |
244 |
251 |
254 |
257 |
260 |
263 |
267 |
280 |
283 |
287 |
290 |
294 |
297 |
300 |
303 |
306 |
309 |
312 |
315 |
318 |
319 |
320 |
321 |
322 |
323 |
327 |
330 |
331 |
334 |
335 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
--------------------------------------------------------------------------------
/tests/test_xpath_paths_abbrev_w3c.cpp:
--------------------------------------------------------------------------------
1 | #ifndef PUGIXML_NO_XPATH
2 |
3 | #include "test.hpp"
4 |
5 | using namespace pugi;
6 |
7 | TEST_XML(xpath_paths_abbrev_w3c_1, "")
8 | {
9 | xml_node c;
10 | xml_node n = doc.child(STR("node"));
11 |
12 | CHECK_XPATH_NODESET(c, STR("para"));
13 | CHECK_XPATH_NODESET(n, STR("para")) % 3 % 5;
14 | }
15 |
16 | TEST_XML(xpath_paths_abbrev_w3c_2, "")
17 | {
18 | xml_node c;
19 | xml_node n = doc.child(STR("node"));
20 |
21 | CHECK_XPATH_NODESET(c, STR("*"));
22 | CHECK_XPATH_NODESET(n, STR("*")) % 3 % 4 % 5;
23 | }
24 |
25 | TEST_XML(xpath_paths_abbrev_w3c_3, "pcdata")
26 | {
27 | xml_node c;
28 | xml_node n = doc.child(STR("node"));
29 |
30 | CHECK_XPATH_NODESET(c, STR("text()"));
31 | CHECK_XPATH_NODESET(n, STR("text()")) % 3 % 5;
32 | }
33 |
34 | TEST_XML(xpath_paths_abbrev_w3c_4, "")
35 | {
36 | xml_node c;
37 | xml_node n = doc.child(STR("node"));
38 |
39 | CHECK_XPATH_NODESET(c, STR("@name"));
40 | CHECK_XPATH_NODESET(n, STR("@name")) % 3;
41 | }
42 |
43 | TEST_XML(xpath_paths_abbrev_w3c_5, "")
44 | {
45 | xml_node c;
46 | xml_node n = doc.child(STR("node"));
47 |
48 | CHECK_XPATH_NODESET(c, STR("@*"));
49 | CHECK_XPATH_NODESET(n, STR("@*")) % 3 % 4;
50 | }
51 |
52 | TEST_XML(xpath_paths_abbrev_w3c_6, "")
53 | {
54 | xml_node c;
55 | xml_node n = doc.child(STR("node"));
56 |
57 | CHECK_XPATH_NODESET(c, STR("para[1]"));
58 | CHECK_XPATH_NODESET(n, STR("para[1]")) % 3;
59 | }
60 |
61 | TEST_XML(xpath_paths_abbrev_w3c_7, "")
62 | {
63 | xml_node c;
64 | xml_node n = doc.child(STR("node"));
65 |
66 | CHECK_XPATH_NODESET(c, STR("para[last()]"));
67 | CHECK_XPATH_NODESET(n, STR("para[last()]")) % 6;
68 | }
69 |
70 | TEST_XML(xpath_paths_abbrev_w3c_8, "")
71 | {
72 | xml_node c;
73 |
74 | CHECK_XPATH_NODESET(c, STR("*/para"));
75 | CHECK_XPATH_NODESET(doc, STR("*/para")) % 3 % 9;
76 | }
77 |
78 | TEST_XML(xpath_paths_abbrev_w3c_9, "")
79 | {
80 | xml_node c;
81 | xml_node n = doc.child(STR("doc")).child(STR("chapter"));
82 |
83 | CHECK_XPATH_NODESET(c, STR("/doc/chapter[5]/section[2]"));
84 | CHECK_XPATH_NODESET(n, STR("/doc/chapter[5]/section[2]")) % 9;
85 | CHECK_XPATH_NODESET(doc, STR("/doc/chapter[5]/section[2]")) % 9;
86 | }
87 |
88 | TEST_XML(xpath_paths_abbrev_w3c_10, "")
89 | {
90 | xml_node c;
91 |
92 | CHECK_XPATH_NODESET(c, STR("chapter//para"));
93 | CHECK_XPATH_NODESET(doc, STR("chapter//para")) % 3 % 4 % 5 % 7 % 9;
94 | }
95 |
96 | TEST_XML(xpath_paths_abbrev_w3c_11, "")
97 | {
98 | xml_node c;
99 | xml_node n = doc.child(STR("node"));
100 |
101 | CHECK_XPATH_NODESET(c, STR("//para"));
102 | CHECK_XPATH_NODESET(n, STR("//para")) % 3 % 4 % 5 % 7 % 9;
103 | CHECK_XPATH_NODESET(n.child(STR("para")), STR("//para")) % 3 % 4 % 5 % 7 % 9;
104 | }
105 |
106 | TEST_XML(xpath_paths_abbrev_w3c_12, " ")
107 | {
108 | xml_node c;
109 | xml_node n = doc.child(STR("node"));
110 |
111 | CHECK_XPATH_NODESET(c, STR("//olist/item"));
112 | CHECK_XPATH_NODESET(n, STR("//olist/item")) % 4 % 8 % 9;
113 | CHECK_XPATH_NODESET(n.child(STR("olist")), STR("//olist/item")) % 4 % 8 % 9;
114 | }
115 |
116 | TEST_XML(xpath_paths_abbrev_w3c_13, "")
117 | {
118 | xml_node c;
119 | xml_node n = doc.child(STR("node"));
120 |
121 | CHECK_XPATH_NODESET(c, STR("."));
122 | CHECK_XPATH_NODESET(n, STR(".")) % 2;
123 | CHECK_XPATH_NODESET(n.child(STR("child")), STR(".")) % 3;
124 | }
125 |
126 | TEST_XML(xpath_paths_abbrev_w3c_14, "")
127 | {
128 | xml_node c;
129 | xml_node n = doc.child(STR("node"));
130 |
131 | CHECK_XPATH_NODESET(c, STR(".//para"));
132 | CHECK_XPATH_NODESET(n, STR(".//para")) % 3 % 4 % 5 % 7 % 9;
133 | CHECK_XPATH_NODESET(n.child(STR("para")), STR(".//para")) % 4 % 5 % 7;
134 | }
135 |
136 | TEST_XML(xpath_paths_abbrev_w3c_15, "")
137 | {
138 | xml_node c;
139 | xml_node n = doc.child(STR("node"));
140 |
141 | CHECK_XPATH_NODESET(c, STR(".."));
142 | CHECK_XPATH_NODESET(n, STR("..")) % 1;
143 | CHECK_XPATH_NODESET(n.child(STR("child")), STR("..")) % 2;
144 | }
145 |
146 | TEST_XML(xpath_paths_abbrev_w3c_16, "")
147 | {
148 | xml_node c;
149 | xml_node n = doc.child(STR("node"));
150 |
151 | CHECK_XPATH_NODESET(c, STR("../@lang"));
152 | CHECK_XPATH_NODESET(n, STR("../@lang"));
153 | CHECK_XPATH_NODESET(n.child(STR("child")), STR("../@lang")) % 3;
154 | }
155 |
156 | TEST_XML(xpath_paths_abbrev_w3c_17, "")
157 | {
158 | xml_node c;
159 | xml_node n = doc.child(STR("node"));
160 |
161 | CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"]"));
162 | CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"]")) % 4 % 6 % 11 % 13 % 15;
163 | }
164 |
165 | TEST_XML(xpath_paths_abbrev_w3c_18, "")
166 | {
167 | xml_node c;
168 | xml_node n = doc.child(STR("node"));
169 |
170 | CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"][5]"));
171 | CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"][5]")) % 15;
172 | }
173 |
174 | TEST_XML(xpath_paths_abbrev_w3c_19a, "")
175 | {
176 | xml_node c;
177 | xml_node n = doc.child(STR("node"));
178 |
179 | CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]"));
180 | CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]"));
181 | }
182 |
183 | TEST_XML(xpath_paths_abbrev_w3c_19b, "")
184 | {
185 | xml_node c;
186 | xml_node n = doc.child(STR("node"));
187 |
188 | CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]"));
189 | CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]")) % 9;
190 | }
191 |
192 | TEST_XML(xpath_paths_abbrev_w3c_20, "fooIntroductionintroductionIntroductionfoo")
193 | {
194 | xml_node c;
195 | xml_node n = doc.child(STR("node"));
196 |
197 | CHECK_XPATH_NODESET(c, STR("chapter[title=\"Introduction\"]"));
198 | CHECK_XPATH_NODESET(n, STR("chapter[title=\"Introduction\"]")) % 6 % 13;
199 | }
200 |
201 | TEST_XML(xpath_paths_abbrev_w3c_21, "fooIntroductionintroductionIntroductionfoo")
202 | {
203 | xml_node c;
204 | xml_node n = doc.child(STR("node"));
205 |
206 | CHECK_XPATH_NODESET(c, STR("chapter[title]"));
207 | CHECK_XPATH_NODESET(n, STR("chapter[title]")) % 3 % 6 % 9 % 13;
208 | }
209 |
210 | TEST_XML(xpath_paths_abbrev_w3c_22, "")
211 | {
212 | xml_node c;
213 | xml_node n = doc.child(STR("node"));
214 |
215 | CHECK_XPATH_NODESET(c, STR("employee[@secretary and @assistant]"));
216 | CHECK_XPATH_NODESET(n, STR("employee[@secretary and @assistant]")) % 8 % 11;
217 | }
218 |
219 | #endif
220 |
--------------------------------------------------------------------------------
/tests/test.hpp:
--------------------------------------------------------------------------------
1 | #ifndef HEADER_TEST_TEST_HPP
2 | #define HEADER_TEST_TEST_HPP
3 |
4 | #include "../src/pugixml.hpp"
5 |
6 | #include
7 |
8 | #ifndef PUGIXML_NO_EXCEPTIONS
9 | #include
10 | #endif
11 |
12 | struct test_runner
13 | {
14 | test_runner(const char* name)
15 | {
16 | _name = name;
17 | _next = _tests;
18 | _tests = this;
19 | }
20 |
21 | virtual ~test_runner() {}
22 |
23 | virtual void run() = 0;
24 |
25 | const char* _name;
26 | test_runner* _next;
27 |
28 | static test_runner* _tests;
29 | static size_t _memory_fail_threshold;
30 | static bool _memory_fail_triggered;
31 | static jmp_buf _failure_buffer;
32 | static const char* _failure_message;
33 |
34 | static const char* _temp_path;
35 | };
36 |
37 | bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs);
38 |
39 | template inline bool test_node_name_value(const Node& node, const pugi::char_t* name, const pugi::char_t* value)
40 | {
41 | return test_string_equal(node.name(), name) && test_string_equal(node.value(), value);
42 | }
43 |
44 | bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags);
45 | bool test_double_nan(double value);
46 |
47 | #ifndef PUGIXML_NO_XPATH
48 | bool test_xpath_string(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, const pugi::char_t* expected);
49 | bool test_xpath_boolean(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, bool expected);
50 | bool test_xpath_number(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables, double expected);
51 | bool test_xpath_number_nan(const pugi::xpath_node& node, const pugi::char_t* query, pugi::xpath_variable_set* variables);
52 |
53 | bool test_xpath_fail_compile(const pugi::char_t* query, pugi::xpath_variable_set* variables);
54 |
55 | struct xpath_node_set_tester
56 | {
57 | pugi::xpath_node* document_order;
58 | size_t document_size;
59 |
60 | pugi::xpath_node_set result;
61 | unsigned int last;
62 | const char* message;
63 |
64 | void check(bool condition);
65 |
66 | xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message);
67 | ~xpath_node_set_tester();
68 |
69 | xpath_node_set_tester& operator%(unsigned int expected);
70 | };
71 |
72 | #endif
73 |
74 | struct dummy_fixture {};
75 |
76 | #define TEST_FIXTURE(name, fixture) \
77 | struct test_runner_helper_##name: fixture \
78 | { \
79 | void run(); \
80 | }; \
81 | static struct test_runner_##name: test_runner \
82 | { \
83 | test_runner_##name(): test_runner(#name) {} \
84 | \
85 | virtual void run() PUGIXML_OVERRIDE \
86 | { \
87 | test_runner_helper_##name helper; \
88 | helper.run(); \
89 | } \
90 | } test_runner_instance_##name; \
91 | void test_runner_helper_##name::run()
92 |
93 | #define TEST(name) TEST_FIXTURE(name, dummy_fixture)
94 |
95 | #define TEST_XML_FLAGS(name, xml, flags) \
96 | struct test_fixture_##name \
97 | { \
98 | pugi::xml_document doc; \
99 | \
100 | test_fixture_##name() \
101 | { \
102 | CHECK(doc.load_string(PUGIXML_TEXT(xml), flags)); \
103 | } \
104 | \
105 | private: \
106 | test_fixture_##name(const test_fixture_##name&); \
107 | test_fixture_##name& operator=(const test_fixture_##name&); \
108 | }; \
109 | \
110 | TEST_FIXTURE(name, test_fixture_##name)
111 |
112 | #define TEST_XML(name, xml) TEST_XML_FLAGS(name, xml, pugi::parse_default)
113 |
114 | #define CHECK_JOIN(text, file, line) text " at " file ":" #line
115 | #define CHECK_JOIN2(text, file, line) CHECK_JOIN(text, file, line)
116 | #define CHECK_TEXT(condition, text) if (condition) ; else test_runner::_failure_message = CHECK_JOIN2(text, __FILE__, __LINE__), longjmp(test_runner::_failure_buffer, 1)
117 | #define CHECK_FORCE_FAIL(text) test_runner::_failure_message = CHECK_JOIN2(text, __FILE__, __LINE__), longjmp(test_runner::_failure_buffer, 1)
118 |
119 | #if (defined(_MSC_VER) && _MSC_VER == 1200) || defined(__MWERKS__) || (defined(__BORLANDC__) && __BORLANDC__ <= 0x540)
120 | # define STRINGIZE(value) "??" // Some compilers have issues with stringizing expressions that contain strings w/escaping inside
121 | #else
122 | # define STRINGIZE(value) #value
123 | #endif
124 |
125 | #define CHECK(condition) CHECK_TEXT(condition, STRINGIZE(condition) " is false")
126 | #define CHECK_STRING(value, expected) CHECK_TEXT(test_string_equal(value, expected), STRINGIZE(value) " is not equal to " STRINGIZE(expected))
127 | #define CHECK_DOUBLE(value, expected) CHECK_TEXT((value > expected ? value - expected : expected - value) < 1e-6, STRINGIZE(value) " is not equal to " STRINGIZE(expected))
128 | #define CHECK_DOUBLE_NAN(value) CHECK_TEXT(test_double_nan(value), STRINGIZE(value) " is not equal to NaN")
129 | #define CHECK_NAME_VALUE(node, name, value) CHECK_TEXT(test_node_name_value(node, name, value), STRINGIZE(node) " name/value do not match " STRINGIZE(name) " and " STRINGIZE(value))
130 | #define CHECK_NODE_EX(node, expected, indent, flags) CHECK_TEXT(test_node(node, expected, indent, flags), STRINGIZE(node) " contents does not match " STRINGIZE(expected))
131 | #define CHECK_NODE(node, expected) CHECK_NODE_EX(node, expected, PUGIXML_TEXT(""), pugi::format_raw)
132 |
133 | #ifndef PUGIXML_NO_XPATH
134 | #define CHECK_XPATH_STRING_VAR(node, query, variables, expected) CHECK_TEXT(test_xpath_string(node, query, variables, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
135 | #define CHECK_XPATH_BOOLEAN_VAR(node, query, variables, expected) CHECK_TEXT(test_xpath_boolean(node, query, variables, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
136 | #define CHECK_XPATH_NUMBER_VAR(node, query, variables, expected) CHECK_TEXT(test_xpath_number(node, query, variables, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
137 | #define CHECK_XPATH_NUMBER_NAN_VAR(node, query, variables) CHECK_TEXT(test_xpath_number_nan(node, query, variables), STRINGIZE(query) " does not evaluate to NaN in context " STRINGIZE(node))
138 | #define CHECK_XPATH_NODESET_VAR(node, query, variables) xpath_node_set_tester(pugi::xpath_query(query, variables).evaluate_node_set(node), CHECK_JOIN2(STRINGIZE(query) " does not evaluate to expected set in context " STRINGIZE(node), __FILE__, __LINE__))
139 | #define CHECK_XPATH_FAIL_VAR(query, variables) CHECK_TEXT(test_xpath_fail_compile(query, variables), STRINGIZE(query) " should not compile")
140 |
141 | #define CHECK_XPATH_STRING(node, query, expected) CHECK_XPATH_STRING_VAR(node, query, 0, expected)
142 | #define CHECK_XPATH_BOOLEAN(node, query, expected) CHECK_XPATH_BOOLEAN_VAR(node, query, 0, expected)
143 | #define CHECK_XPATH_NUMBER(node, query, expected) CHECK_XPATH_NUMBER_VAR(node, query, 0, expected)
144 | #define CHECK_XPATH_NUMBER_NAN(node, query) CHECK_XPATH_NUMBER_NAN_VAR(node, query, 0)
145 | #define CHECK_XPATH_NODESET(node, query) CHECK_XPATH_NODESET_VAR(node, query, 0)
146 | #define CHECK_XPATH_FAIL(query) CHECK_XPATH_FAIL_VAR(query, 0)
147 | #endif
148 |
149 | #ifdef PUGIXML_NO_EXCEPTIONS
150 | #define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); code; CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered)
151 | #else
152 | #define CHECK_ALLOC_FAIL(code) do { CHECK(!test_runner::_memory_fail_triggered); try { code; } catch (std::bad_alloc&) {} CHECK(test_runner::_memory_fail_triggered); test_runner::_memory_fail_triggered = false; } while (test_runner::_memory_fail_triggered)
153 | #endif
154 |
155 | #define STR(text) PUGIXML_TEXT(text)
156 |
157 | #if defined(__DMC__) || defined(__BORLANDC__)
158 | #define U_LITERALS // DMC does not understand \x01234 (it parses first three digits), but understands \u01234
159 | #endif
160 |
161 | #if (defined(_MSC_VER) && _MSC_VER == 1200) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER == 800) || defined(__BORLANDC__)
162 | // NaN comparison on MSVC6 is incorrect, see http://www.nabble.com/assertDoubleEquals,-NaN---Microsoft-Visual-Studio-6-td9137859.html
163 | // IC8 and BCC are also affected by the same bug
164 | # define MSVC6_NAN_BUG
165 | #endif
166 |
167 | inline wchar_t wchar_cast(unsigned int value)
168 | {
169 | return static_cast(value); // to avoid C4310 on MSVC
170 | }
171 |
172 | bool is_little_endian();
173 | pugi::xml_encoding get_native_encoding();
174 |
175 | #endif
176 |
--------------------------------------------------------------------------------
/scripts/pugixml_vs2015.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {07CF01C0-B887-499D-AD9C-799CB6A9FE64}
23 | Win32Proj
24 | pugixml
25 | 8.1
26 |
27 |
28 |
29 | StaticLibrary
30 | true
31 | v140
32 | Unicode
33 |
34 |
35 | StaticLibrary
36 | false
37 | v140
38 | true
39 | Unicode
40 |
41 |
42 | StaticLibrary
43 | true
44 | v140
45 | Unicode
46 |
47 |
48 | StaticLibrary
49 | false
50 | v140
51 | true
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | vs2015\$(Platform)_$(Configuration)\
74 | vs2015\$(Platform)_$(Configuration)\
75 | pugixml
76 |
77 |
78 | vs2015\$(Platform)_$(Configuration)\
79 | vs2015\$(Platform)_$(Configuration)\
80 | pugixml
81 |
82 |
83 | vs2015\$(Platform)_$(Configuration)\
84 | vs2015\$(Platform)_$(Configuration)\
85 | pugixml
86 |
87 |
88 | vs2015\$(Platform)_$(Configuration)\
89 | vs2015\$(Platform)_$(Configuration)\
90 | pugixml
91 |
92 |
93 |
94 |
95 |
96 | Level3
97 | Disabled
98 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
99 | $(IntDir)$(TargetName).pdb
100 | OldStyle
101 | false
102 |
103 |
104 | Windows
105 | true
106 |
107 |
108 |
109 |
110 |
111 |
112 | Level3
113 | Disabled
114 | _DEBUG;_LIB;%(PreprocessorDefinitions)
115 | $(IntDir)$(TargetName).pdb
116 | OldStyle
117 | false
118 |
119 |
120 | Windows
121 | true
122 |
123 |
124 |
125 |
126 | Level3
127 |
128 |
129 | MaxSpeed
130 | true
131 | true
132 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
133 | $(IntDir)$(TargetName).pdb
134 | OldStyle
135 |
136 |
137 | Windows
138 | true
139 | true
140 | true
141 |
142 |
143 |
144 |
145 | Level3
146 |
147 |
148 | MaxSpeed
149 | true
150 | true
151 | NDEBUG;_LIB;%(PreprocessorDefinitions)
152 | $(IntDir)$(TargetName).pdb
153 | OldStyle
154 |
155 |
156 | Windows
157 | true
158 | true
159 | true
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------