diff --git a/.gitmodules b/.gitmodules index 0d1305756..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "src/xml/fox"] - path = src/xml/fox - url = https://github.com/mit-crpg/fox.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ecdc1e9b..f0a2af18b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.8 FATAL_ERROR) -project(openmc Fortran C) +project(openmc Fortran C CXX) # Setup output directories set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) @@ -103,21 +103,17 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) # Make sure version is sufficient execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if(GCC_VERSION VERSION_LESS 4.6) - message(FATAL_ERROR "gfortran version must be 4.6 or higher") + if(GCC_VERSION VERSION_LESS 4.8) + message(FATAL_ERROR "gfortran version must be 4.8 or higher") endif() # GCC compiler options list(APPEND f90flags -cpp -std=f2008 -fbacktrace) list(APPEND cflags -cpp -std=c99) if(debug) - if(NOT (GCC_VERSION VERSION_LESS 4.7)) - list(APPEND f90flags -Wall) - list(APPEND cflags -Wall) - endif() - list(APPEND f90flags -g -pedantic -fbounds-check + list(APPEND f90flags -g -Wall -pedantic -fbounds-check -ffpe-trap=invalid,overflow,underflow) - list(APPEND cflags -g -pedantic -fbounds-check) + list(APPEND cflags -g -Wall -pedantic -fbounds-check) list(APPEND ldflags -g) endif() if(profile) @@ -225,27 +221,14 @@ if(GIT_SHA1_SUCCESS EQUAL 0) endif() #=============================================================================== -# FoX Fortran XML Library +# pugixml library #=============================================================================== -# Only initialize git submodules if it is not there. User is responsible -# for future updates of fox xml submodule. -if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/xml/fox/.git) - if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) - message("-- Cloning FoX XML git repository...") - execute_process(COMMAND git clone https://github.com/mit-crpg/fox.git src/xml/fox - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - execute_process(COMMAND git checkout bdc852f4f43d969fb1b179cba79295c1e095a455 - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/xml/fox) - else() - message("-- Initializing/Updating FoX XML submodule...") - execute_process(COMMAND git submodule init - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - execute_process(COMMAND git submodule update - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - endif() -endif() -add_subdirectory(src/xml/fox) +add_library(pugixml src/pugixml/pugixml_c.cpp src/pugixml/pugixml.cpp) +set_property(TARGET pugixml PROPERTY CXX_STANDARD 11) + +add_library(pugixml_fortran src/pugixml/pugixml_f.F90) +target_link_libraries(pugixml_fortran pugixml) #=============================================================================== # RPATH information @@ -259,7 +242,7 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # Build faddeeva library #=============================================================================== -add_library(faddeeva STATIC src/Faddeeva.c) +add_library(faddeeva STATIC src/faddeeva/Faddeeva.c) #=============================================================================== # Build OpenMC executable @@ -352,11 +335,12 @@ set(LIBOPENMC_FORTRAN_SRC src/vector_header.F90 src/volume_calc.F90 src/volume_header.F90 - src/xml_interface.F90 - src/xml/openmc_fox.F90) + src/xml_interface.F90) add_library(libopenmc STATIC ${LIBOPENMC_FORTRAN_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) add_executable(${program} src/main.F90) +set_property(TARGET ${program} libopenmc pugixml_fortran + PROPERTY LINKER_LANGUAGE Fortran) # target_include_directories was added in CMake 2.8.11 and is the recommended # way to set include directories. For lesser versions, we revert to set_property @@ -388,7 +372,7 @@ endforeach() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. -target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} fox_dom faddeeva) +target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml_fortran faddeeva) target_link_libraries(${program} ${ldflags} libopenmc) #=============================================================================== diff --git a/docs/source/developers.rst b/docs/source/developers.rst index 0bdae2416..825fbd1c6 100644 --- a/docs/source/developers.rst +++ b/docs/source/developers.rst @@ -4,7 +4,8 @@ Development Team ================ -Active development of the OpenMC Monte Carlo code is currently led by: +The following people have contributed to development of the OpenMC Monte Carlo +code: * `Paul Romano `_ * `Bryan Herman `_ @@ -14,6 +15,9 @@ Active development of the OpenMC Monte Carlo code is currently led by: * `Sterling Harper `_ * `Will Boyd `_ * `Samuel Shaner `_ +* `Jingang Liang `_ +* `Colin Josey `_ +* `Amanda Lund `_ * `Benoit Forget `_ * `Kord Smith `_ * `Andrew Siegel `_ diff --git a/docs/source/devguide/index.rst b/docs/source/devguide/index.rst index 37b17bc0f..4dc1b2c74 100644 --- a/docs/source/devguide/index.rst +++ b/docs/source/devguide/index.rst @@ -15,5 +15,5 @@ as debugging. structures styleguide workflow - xml-parsing + user-input docbuild diff --git a/docs/source/devguide/user-input.rst b/docs/source/devguide/user-input.rst new file mode 100644 index 000000000..3e1ea1c38 --- /dev/null +++ b/docs/source/devguide/user-input.rst @@ -0,0 +1,73 @@ +.. _devguide_user_input: + +========================= +Making User Input Changes +========================= + +Users are encouraged to use OpenMC's :ref:`pythonapi` to build XML files that +the OpenMC solver then reads during the initialization phase. Thus, to modify, +add, or remove user input options, changes must be made both within the Python +API and the Fortran source that reads XML files produced by the Python API. The +following steps should be followed to make changes to user input: + +1. Determine the Python class you need to change. For example, if you are adding + a new setting, you probably want to change the :class:`openmc.Settings` + class. If you are adding a new surface type, you would need to create a + subclass of :class:`openmc.Surface`. + +2. To add a new option, the class will need a `property attribute`_. For + example, if you wanted to add a "fast_mode" setting, you would need two + methods that look like: + + .. code-block:: python + + @property + def fast_mode(self): + ... + + @fast_mode.setter + def fast_mode(self, fast_mode): + ... + +3. Make sure that when an instance of the class is exported to XML (usually + through a ``export_to_xml()`` or ``to_xml_element()`` method), a new element + is written to the appropriate file. OpenMC uses the + :mod:`xml.etree.ElementTree` API, so refer to the documentation of that + module for guidance on creating elements/attributes. + +4. Make sure that your input can be categorized as one of the datatypes from + `XML Schema Part 2`_ and that parsing of the data appropriately reflects + this. For example, for a boolean_ value, true can be represented either by + "true" or by "1". + +5. Now that you're done with the Python side, you need to make modifications to + the Fortran codebase. Make appropriate changes in the `input_xml module`_ to + read your new user input. You should use procedures and types defined by the + `xml_interface module`_. + +6. If you've made changes in the geometry or materials, make sure they are + written out to the statepoint or summary files and that the + :class:`openmc.StatePoint` and :class:`openmc.Summary` classes read them in. + +7. Finally, a set of `RELAX NG`_ schemas exists that enables validation of input + files. You should modify the RELAX NG schema for the file you changed. The + easiest way to do this is to change the `compact syntax`_ file + (e.g. ``src/relaxng/geometry.rnc``) and then convert it to regular XML syntax + using trang_:: + + trang geometry.rnc geometry.rng + +For most user input additions and changes, it is simple enough to follow a +"monkey see, monkey do" approach. When in doubt, contact your nearest OpenMC +developer or send a message to the `developers mailing list`_. + + +.. _property attribute: https://docs.python.org/3.6/library/functions.html#property +.. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/ +.. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean +.. _xml_interface module: https://github.com/mit-crpg/openmc/blob/develop/src/xml_interface.F90 +.. _input_xml module: https://github.com/mit-crpg/openmc/blob/develop/src/input_xml.F90 +.. _RELAX NG: http://relaxng.org/ +.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html +.. _trang: http://www.thaiopensource.com/relaxng/trang.html +.. _developers mailing list: https://groups.google.com/forum/?fromgroups=#!forum/openmc-dev diff --git a/docs/source/devguide/xml-parsing.rst b/docs/source/devguide/xml-parsing.rst deleted file mode 100644 index cf6f238bd..000000000 --- a/docs/source/devguide/xml-parsing.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. _devguide_xml-parsing: - -================= -XML Input Parsing -================= - -OpenMC relies on the FoX_ Fortran XML library for reading and intrepreting the -XML input files for geometry, materials, settings, tallies, etc. The use of an -XML format makes writing input files considerably more flexible than would -otherwise be possible. - -With the FoX library, extending the user input files to include new tags is -fairly straightforward. The steps for modifying/adding input are as follows: - -1. Add appropriate calls to procedures from the `xml_interface module`_, such as - ``check_for_node``, ``get_node_value``, and ``get_node_array``. All input - reading is performed in the `input_xml module`_. - -2. Make sure that your input can be categorized as one of the datatypes from - `XML Schema Part 2`_ and that parsing of the data appropriately reflects - this. For example, for a boolean_ value, true can be represented either by - "true" or by "1". - -3. Add code to check the variable for any possible errors. - -A set of `RELAX NG`_ schemata exists that enables real-time validation of input -files when using the GNU Emacs text editor. You should also modify the RELAX NG -schema for the file you changed (e.g. src/relaxng/geometry.rnc) so that -those who use Emacs can confirm whether their input is valid before they -run. You will need to be familiar with RELAX NG `compact syntax`_. - -Working with the FoX Submodule -============================== - -The FoX_ library is included as a submodule_ in OpenMC. This means that for a -given commit in OpenMC, there is an associated commit id that links to FoX. -The actual FoX source code is maintained at mit-crpg/fox, branch openmc. When -cloning the OpenMC repo for the first time, you will notice that the directory -*src/xml/fox* is empty. To fetch the submodule source code, you can manually -enter the following from the root directory of OpenMC: - -.. code-block:: sh - - git submodule init - git submodule update - -It should be noted that if the submodule is not initialized and updated, *cmake* -will automatically perform these commands if it cannot file the FoX source code. - -If you navigate into the FoX source code in OpenMC, src/xml/fox, and check git -information, you will notice that you are in a completely different repo. Actually, -you are in a clone of mit-crpg/fox. If you have write access to this repo, you can -make changes to the FoX source code, commit and push just like any other repo. -Just because you make changes to the FoX source code in OpenMC or in a standalone -repo, this does not mean that OpenMC will automatically fetch these changes. The -way submodules work is that they are just stored as a commit id. To save FoX xml -source changes to your OpenMC branch, do the following: - -1. Go into src/xml/fox and check out the appropriate source code state - -2. Navigate back out of fox subdirectory and type: - -.. code-block:: sh - - git status - -3. Make sure you see that git recognized that the state of FoX changed: - -:: - - # On branch fox_submodule - # Changes not staged for commit: - # (use "git add ..." to update what will be committed) - # (use "git checkout -- ..." to discard changes in working directory) - # - # modified: fox (new commits) - -4. Commit and push this change - -Editing FoX on Personal Fork -============================ - -If you don't have write access to mit-crpg/fox and thus can't make a branch off of the openmc -branch there, you will need to fork mit-crpg/fox to your personal account. You need to then -link your branch in your OpenMC repo, to the *openmc* branch on your own personal FoX fork. -To do this, edit the *.gitmodules* file in the root folder of the repo. It contains the -following information: - -:: - - [submodule "src/xml/fox"] - path = src/xml/fox - url = git@github.com:mit-crpg/fox - -Change the url remote to your own fork. The commit id should stay constant until you start -making modification to FoX yourself. Once you have made changes to your FoX fork and linked -the new commit id to your OpenMC branch, you can pull request your changes in by peforming -the following steps: - -1. Create a pull request from your fork of FoX to mit-crpg/fox and wait until it - is merged into the openmc branch. - -2. In your OpenMC repo, change your *.gitmodules* file back to point at mit-crpg/fox. - -3. Submit a pull request to mit-crpg/openmc - -.. warning:: If you make changes to your FoX submodule inside of an OpenMC repo and do not - commit, do **not** run *git submodule update*. This may throw away any changes that - were not committed. - -.. _FoX: https://github.com/mit-crpg/fox -.. _xml_interface module: https://github.com/mit-crpg/openmc/blob/develop/src/xml_interface.F90 -.. _input_xml module: https://github.com/mit-crpg/openmc/blob/develop/src/input_xml.F90 -.. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/ -.. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean -.. _RELAX NG: http://relaxng.org/ -.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html -.. _submodule: http://git-scm.com/book/en/Git-Tools-Submodules diff --git a/docs/source/pythonapi/examples/mgxs-part-iv.ipynb b/docs/source/pythonapi/examples/mgxs-part-iv.ipynb index f0fce27c2..319a27c16 100644 --- a/docs/source/pythonapi/examples/mgxs-part-iv.ipynb +++ b/docs/source/pythonapi/examples/mgxs-part-iv.ipynb @@ -8,7 +8,7 @@ "\n", " - Calculation of multi-group cross sections for a fuel assembly\n", " - Automated creation and storage of MGXS with openmc.mgxs.Library\n", - " - Steady-state pin-by-pin fission rates comparison between continuous-energy and multi-group OpenMC.\n" + " - Steady-state pin-by-pin fission rates comparison between continuous-energy and multi-group OpenMC." ] }, { @@ -26,14 +26,13 @@ }, "outputs": [], "source": [ + "%matplotlib inline\n", "from IPython.display import Image\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import os\n", "\n", - "import openmc\n", - "\n", - "%matplotlib inline" + "import openmc" ] }, { @@ -348,6 +347,7 @@ "settings_file.inactive = inactive\n", "settings_file.particles = particles\n", "settings_file.output = {'tallies': False}\n", + "settings_file.verbosity = 4 # only show results\n", "\n", "# Create an initial uniform spatial source distribution over fissionable zones\n", "bounds = [-10.71, -10.71, -10, 10.71, 10.71, 10.]\n", @@ -425,7 +425,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "" ] @@ -571,7 +571,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/mgxs/library.py:412: RuntimeWarning: The P0 correction will be ignored since the scattering order 0 is greater than zero\n", + "/home/romano/openmc/openmc/mgxs/library.py:412: RuntimeWarning: The P0 correction will be ignored since the scattering order 0 is greater than zero\n", " warn(msg, RuntimeWarning)\n" ] } @@ -698,22 +698,42 @@ "name": "stdout", "output_type": "stream", "text": [ - " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 3.2303E-01 seconds\n", - " Reading cross sections = 2.4414E-01 seconds\n", - " Total time in simulation = 1.4351E+02 seconds\n", - " Time in transport only = 1.4018E+02 seconds\n", - " Time in inactive batches = 7.9702E-01 seconds\n", - " Time in active batches = 1.4272E+02 seconds\n", - " Time synchronizing fission bank = 8.1742E-02 seconds\n", - " Sampling source sites = 5.6844E-02 seconds\n", - " SEND/RECV source sites = 2.4302E-02 seconds\n", - " Time accumulating tallies = 1.9605E-03 seconds\n", - " Total time for finalization = 6.3080E-06 seconds\n", - " Total time elapsed = 1.4385E+02 seconds\n", - " Calculation Rate (inactive) = 62733.9 neutrons/second\n", - " Calculation Rate (active) = 17166.9 neutrons/second\n", + " %%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%%%%%%\n", + " ##################### %%%%%%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%\n", + " ################# %%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%\n", + " ############ %%%%%%%%%%%%%%%\n", + " ######## %%%%%%%%%%%%%%\n", + " %%%%%%%%%%%\n", + "\n", + " | The OpenMC Monte Carlo Code\n", + " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " License | http://openmc.readthedocs.io/en/latest/license.html\n", + " Version | 0.8.0\n", + " Git SHA1 | 5e9b06a861d4f596314eff490ad63c051f833f3a\n", + " Date/Time | 2017-03-02 11:27:28\n", + " OpenMP Threads | 4\n", + "\n", + "\n", + " ====================> K EIGENVALUE SIMULATION <====================\n", + "\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -737,7 +757,7 @@ } ], "source": [ - "# Run OpenMC, showing only the summary information at the end.\n", + "# Run OpenMC\n", "openmc.run(output='summary')" ] }, @@ -856,11 +876,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/romano/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1836: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/romano/openmc/openmc/tallies.py:1836: RuntimeWarning: invalid value encountered in true_divide\n", " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1837: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/romano/openmc/openmc/tallies.py:1837: RuntimeWarning: invalid value encountered in true_divide\n", " new_tally._mean = data['self']['mean'] / data['other']['mean']\n" ] } @@ -969,9 +989,9 @@ "outputs": [ { "data": { - "image/png": "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\nSUVORK5CYII=\n", + "image/png": "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\nH7Zthvm1Mq06AAAAAElFTkSuQmCC\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -979,9 +999,9 @@ }, { "data": { - "image/png": "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\ntPx/RfHQqmw1MBwAAAAASUVORK5CYII=\n", + "image/png": "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\n4nDOOZeU/w9zzMXxVRzsNwAAAABJRU5ErkJggg==\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -989,9 +1009,9 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\ncVHiEBGRuPw/HPy/MBdqV5sAAAAASUVORK5CYII=\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1043,29 +1063,49 @@ "name": "stdout", "output_type": "stream", "text": [ - " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 4.3662E-02 seconds\n", - " Reading cross sections = 5.8801E-03 seconds\n", - " Total time in simulation = 1.7518E+02 seconds\n", - " Time in transport only = 1.7154E+02 seconds\n", - " Time in inactive batches = 8.0088E-01 seconds\n", - " Time in active batches = 1.7438E+02 seconds\n", - " Time synchronizing fission bank = 9.8651E-02 seconds\n", - " Sampling source sites = 6.7951E-02 seconds\n", - " SEND/RECV source sites = 2.9902E-02 seconds\n", - " Time accumulating tallies = 2.2328E-03 seconds\n", - " Total time for finalization = 5.9420E-06 seconds\n", - " Total time elapsed = 1.7525E+02 seconds\n", - " Calculation Rate (inactive) = 62431.1 neutrons/second\n", - " Calculation Rate (active) = 14049.6 neutrons/second\n", + " %%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%%%%%%\n", + " ##################### %%%%%%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%\n", + " ################# %%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%\n", + " ############ %%%%%%%%%%%%%%%\n", + " ######## %%%%%%%%%%%%%%\n", + " %%%%%%%%%%%\n", + "\n", + " | The OpenMC Monte Carlo Code\n", + " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " License | http://openmc.readthedocs.io/en/latest/license.html\n", + " Version | 0.8.0\n", + " Git SHA1 | 5e9b06a861d4f596314eff490ad63c051f833f3a\n", + " Date/Time | 2017-03-02 11:31:01\n", + " OpenMP Threads | 4\n", + "\n", + "\n", + " ====================> K EIGENVALUE SIMULATION <====================\n", + "\n", "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.02429 +/- 0.00068\n", - " k-effective (Track-length) = 1.02408 +/- 0.00075\n", - " k-effective (Absorption) = 1.02514 +/- 0.00050\n", - " Combined k-effective = 1.02488 +/- 0.00045\n", + " k-effective (Collision) = 1.02629 +/- 0.00071\n", + " k-effective (Track-length) = 1.02613 +/- 0.00081\n", + " k-effective (Absorption) = 1.02540 +/- 0.00048\n", + " Combined k-effective = 1.02552 +/- 0.00045\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] @@ -1082,8 +1122,8 @@ } ], "source": [ - "# Run OpenMC, showing only the summary information at the end.\n", - "openmc.run(output='summary')" + "# Run OpenMC\n", + "openmc.run()" ] }, { @@ -1147,8 +1187,8 @@ "output_type": "stream", "text": [ "Continuous-Energy keff = 1.025799\n", - "Multi-Group keff = 1.024883\n", - "bias [pcm]: 91.6\n" + "Multi-Group keff = 1.025523\n", + "bias [pcm]: 27.6\n" ] } ], @@ -1245,22 +1285,12 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\nAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ @@ -1293,7 +1323,7 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python [default]", "language": "python", "name": "python3" }, @@ -1307,7 +1337,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.5.2" } }, "nbformat": 4, diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index a64454e92..284f4f41a 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -839,17 +839,27 @@ problem. It has the following attributes/sub-elements: *Default*: None +.. _verbosity: + ```` Element ----------------------- The ```` element tells the code how much information to display to the standard output. A higher verbosity corresponds to more information being -displayed. This element takes the following attributes: +displayed. The text of this element should be an integer between between 1 +and 10. The verbosity levels are defined as follows: - :value: - The specified verbosity between 1 and 10. + :1: don't display any output + :2: only show OpenMC logo + :3: all of the above + headers + :4: all of the above + results + :5: all of the above + file I/O + :6: all of the above + timing statistics and initialization messages + :7: all of the above + :math:`k` by generation + :9: all of the above + indicate when each particle starts + :10: all of the above + event information - *Default*: 5 + *Default*: 7 ```` Element ------------------------------------- diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 91c977ca4..e98040708 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -58,7 +58,7 @@ Now OpenMC should be recognized within the repository and can be installed: .. code-block:: sh - sudo apt-get install openmc + sudo apt install openmc Binary packages from this PPA may exist for earlier versions of Ubuntu, but they are no longer supported. @@ -88,7 +88,16 @@ Prerequisites If you are using Debian or a Debian derivative such as Ubuntu, you can install the gfortran compiler using the following command:: - sudo apt-get install gfortran + sudo apt install gfortran + + * A C/C++ compiler such as gcc_ + + OpenMC includes two libraries written in C and C++, respectively. These + libraries have been tested to work with a wide variety of compilers. If + you are using a Debian-based distribution, you can install the g++ + compiler using the following command:: + + sudo apt install g++ * CMake_ cross-platform build system @@ -97,7 +106,7 @@ Prerequisites derivative such as Ubuntu, you can install CMake using the following command:: - sudo apt-get install cmake + sudo apt install cmake * HDF5_ Library for portable binary output format @@ -125,7 +134,7 @@ Prerequisites .. code-block:: sh - sudo apt-get install libhdf5-8 libhdf5-dev hdf5-helpers + sudo apt install libhdf5-dev hdf5-helpers Note that the exact package names may vary depending on your particular distribution and version. @@ -140,12 +149,13 @@ Prerequisites with the latest versions of both OpenMPI_ and MPICH_. OpenMPI and/or MPICH can be installed on Debian derivatives with:: - sudo apt-get install mpich libmpich-dev - sudo apt-get install openmpi-bin libopenmpi1.6 libopenmpi-dev + sudo apt install mpich libmpich-dev + sudo apt install openmpi-bin libopenmpi-dev * git_ version control software for obtaining source code .. _gfortran: http://gcc.gnu.org/wiki/GFortran +.. _gcc: https://gcc.gnu.org/ .. _CMake: http://www.cmake.org .. _OpenMPI: http://www.open-mpi.org .. _MPICH: http://www.mpich.org diff --git a/openmc/__init__.py b/openmc/__init__.py index 99baf4f29..04dd7f0aa 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -1,3 +1,5 @@ +import warnings + from openmc.arithmetic import * from openmc.cell import * from openmc.mesh import * @@ -28,6 +30,9 @@ from openmc.mixin import * from openmc.plotter import * try: - from openmc.openmoc_compatible import * + # Ignore matplotlib warning caused by OpenMOC calling matplotlib.use() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + from openmc.openmoc_compatible import * except ImportError: pass diff --git a/openmc/executor.py b/openmc/executor.py index 9bd97ca1d..1fc00b998 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -6,16 +6,12 @@ from six import string_types from openmc import VolumeCalculation -_summary_indicator = "TIMING STATISTICS" - def _run(args, output, cwd): # Launch a subprocess p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - storage_flag = False - # Capture and re-print OpenMC output in real-time while True: # If OpenMC is finished, break loop @@ -23,17 +19,9 @@ def _run(args, output, cwd): if not line and p.poll() is not None: break - if output == 'full': + if output: # If user requested output, print to screen print(line, end='') - elif output == 'summary' and _summary_indicator in line: - # If they requested a summary, look for the start of the summary - storage_flag = True - - if storage_flag: - # If a summary is requested, and we have reached the summary, - # then print it - print(line, end='') # Return the returncode (integer, zero if no problems encountered) return p.returncode @@ -52,8 +40,6 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): Path to working directory to run in. Defaults to the current working directory. """ - if output: - output = 'full' return _run([openmc_exec, '-p'], output, cwd) @@ -102,13 +88,11 @@ def calculate_volumes(threads=None, output=True, cwd='.', if mpi_args is not None: args = mpi_args + args - if output: - output = 'full' return _run(args, output, cwd) def run(particles=None, threads=None, geometry_debug=False, - restart_file=None, tracks=False, output='full', cwd='.', + restart_file=None, tracks=False, output=True, cwd='.', openmc_exec='openmc', mpi_args=None): """Run an OpenMC simulation. @@ -127,10 +111,8 @@ def run(particles=None, threads=None, geometry_debug=False, Path to restart file to use tracks : bool, optional Write tracks for all particles. Defaults to False. - output : {"full", "summary", "none", False}, optional - Degree of OpenMC output captured from standard out. "full" prints all - output; "summary" prints only the results summary, and "none" or False - does not show the output. Defaults to "full". + output : bool + Capture OpenMC output from standard out cwd : str, optional Path to working directory to run in. Defaults to the current working directory. diff --git a/openmc/filter.py b/openmc/filter.py index 71b1c2313..6f64f1c78 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -1,3 +1,4 @@ +from __future__ import division from abc import ABCMeta from collections import Iterable, OrderedDict import copy @@ -425,7 +426,7 @@ class Filter(object): df = pd.DataFrame() filter_bins = np.repeat(self.bins, self.stride) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) df = pd.concat([df, pd.DataFrame( {self.short_name.lower(): filter_bins})]) @@ -613,7 +614,7 @@ class SurfaceFilter(IntegralFilter): df = pd.DataFrame() filter_bins = np.repeat(self.bins, self.stride) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) filter_bins = [_CURRENT_NAMES[x] for x in filter_bins] df = pd.concat([df, pd.DataFrame( @@ -781,26 +782,26 @@ class MeshFilter(Filter): nz = 1 # Generate multi-index sub-column for x-axis - filter_bins = np.arange(1, nx+1) + filter_bins = np.arange(1, nx + 1) repeat_factor = ny * nz * self.stride filter_bins = np.repeat(filter_bins, repeat_factor) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) filter_dict[(mesh_key, 'x')] = filter_bins # Generate multi-index sub-column for y-axis - filter_bins = np.arange(1, ny+1) + filter_bins = np.arange(1, ny + 1) repeat_factor = nz * self.stride filter_bins = np.repeat(filter_bins, repeat_factor) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) filter_dict[(mesh_key, 'y')] = filter_bins # Generate multi-index sub-column for z-axis - filter_bins = np.arange(1, nz+1) + filter_bins = np.arange(1, nz + 1) repeat_factor = self.stride filter_bins = np.repeat(filter_bins, repeat_factor) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) filter_dict[(mesh_key, 'z')] = filter_bins @@ -1008,7 +1009,7 @@ class EnergyFilter(RealFilter): # them as necessary to account for other filters. lo_bins = np.repeat(self.bins[:-1], self.stride) hi_bins = np.repeat(self.bins[1:], self.stride) - tile_factor = int(data_size / len(lo_bins)) + tile_factor = data_size // len(lo_bins) lo_bins = np.tile(lo_bins, tile_factor) hi_bins = np.tile(hi_bins, tile_factor) @@ -1281,7 +1282,7 @@ class DistribcellFilter(Filter): # Tile the Multi-index columns for level_key, level_bins in level_dict.items(): level_bins = np.repeat(level_bins, self.stride) - tile_factor = data_size / len(level_bins) + tile_factor = data_size // len(level_bins) level_bins = np.tile(level_bins, tile_factor) level_dict[level_key] = level_bins @@ -1297,7 +1298,7 @@ class DistribcellFilter(Filter): # requests Summary geometric information filter_bins = np.arange(self.num_bins) filter_bins = np.repeat(filter_bins, self.stride) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) df = pd.DataFrame({self.short_name.lower() : filter_bins}) @@ -1402,7 +1403,7 @@ class MuFilter(RealFilter): # them as necessary to account for other filters. lo_bins = np.repeat(self.bins[:-1], self.stride) hi_bins = np.repeat(self.bins[1:], self.stride) - tile_factor = data_size / len(lo_bins) + tile_factor = data_size // len(lo_bins) lo_bins = np.tile(lo_bins, tile_factor) hi_bins = np.tile(hi_bins, tile_factor) @@ -1505,7 +1506,7 @@ class PolarFilter(RealFilter): # them as necessary to account for other filters. lo_bins = np.repeat(self.bins[:-1], self.stride) hi_bins = np.repeat(self.bins[1:], self.stride) - tile_factor = data_size / len(lo_bins) + tile_factor = data_size // len(lo_bins) lo_bins = np.tile(lo_bins, tile_factor) hi_bins = np.tile(hi_bins, tile_factor) @@ -1608,7 +1609,7 @@ class AzimuthalFilter(RealFilter): # them as necessary to account for other filters. lo_bins = np.repeat(self.bins[:-1], self.stride) hi_bins = np.repeat(self.bins[1:], self.stride) - tile_factor = data_size / len(lo_bins) + tile_factor = data_size // len(lo_bins) lo_bins = np.tile(lo_bins, tile_factor) hi_bins = np.tile(hi_bins, tile_factor) @@ -1871,7 +1872,7 @@ class EnergyFunctionFilter(Filter): out = out[:14] filter_bins = np.repeat(out, self.stride) - tile_factor = data_size / len(filter_bins) + tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) df = pd.concat([df, pd.DataFrame( {self.short_name.lower(): filter_bins})]) diff --git a/openmc/geometry.py b/openmc/geometry.py index 53a7ffa28..079647b79 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -230,8 +230,8 @@ class Geometry(object): """ material_universes = OrderedDict() - for universe in self.get_all_universes(): - for cell in universe.cells: + for universe in self.get_all_universes().values(): + for cell in universe.cells.values(): if cell.fill_type in ('material', 'distribmat', 'void'): if universe not in material_universes: material_universes[universe.id] = universe @@ -249,7 +249,7 @@ class Geometry(object): """ lattices = OrderedDict() - for cell in self.get_all_cells(): + for cell in self.get_all_cells().values(): if cell.fill_type == 'lattice': if cell.fill not in lattices: lattices[cell.fill.id] = cell.fill diff --git a/openmc/lattice.py b/openmc/lattice.py index 361c2a6be..23838881e 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -129,7 +129,7 @@ class Lattice(object): """ lattice_id = int(group.name.split('/')[-1].lstrip('lattice ')) - name = group['name'].value.decode() + name = group['name'].value.decode() if 'name' in group else '' lattice_type = group['type'].value.decode() if 'offsets' in group: diff --git a/openmc/material.py b/openmc/material.py index ab97915e7..e6f974b5d 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -292,7 +292,7 @@ class Material(object): """ mat_id = int(group.name.split('/')[-1].lstrip('material ')) - name = group['name'].value.decode() + name = group['name'].value.decode() if 'name' in group else '' density = group['atom_density'].value nuc_densities = group['nuclide_densities'][...] nuclides = group['nuclides'].value diff --git a/openmc/mesh.py b/openmc/mesh.py index 6e3f2a266..1ad01e2ca 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -331,7 +331,10 @@ class Mesh(EqualityMixin): lattice = openmc.RectLattice() lattice.lower_left = self.lower_left - lattice.universes = np.reshape(universes, self.dimension) + + # Assign the universe and rotate to match the indexing expected for + # the lattice + lattice.universes = np.rot90(np.reshape(universes, self.dimension)) if self.width is not None: lattice.pitch = self.width diff --git a/openmc/settings.py b/openmc/settings.py index a4dde8da3..9a627a5cf 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -142,7 +142,8 @@ class Settings(object): Mesh to be used for redistributing source sites via the uniform fision site (UFS) method. verbosity : int - Verbosity during simulation between 1 and 10 + Verbosity during simulation between 1 and 10. Verbosity levels are + described in :ref:`verbosity`. volume_calculations : VolumeCalculation or iterable of VolumeCalculation Stochastic volume calculation specifications diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 8b3be9241..b508c044d 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -341,7 +341,7 @@ class StatePoint(object): # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = self._f.filename - tally.name = group['name'].value.decode() + tally.name = group['name'].value.decode() if 'name' in group else '' tally.estimator = group['estimator'].value.decode() tally.num_realizations = n_realizations diff --git a/openmc/summary.py b/openmc/summary.py index 8347105b1..3dda39946 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -105,7 +105,7 @@ class Summary(object): for key, group in self._f['geometry/cells'].items(): cell_id = int(key.lstrip('cell ')) - name = group['name'].value.decode() + name = group['name'].value.decode() if 'name' in group else '' fill_type = group['fill_type'].value.decode() if fill_type == 'material': @@ -115,10 +115,7 @@ class Summary(object): else: fill = group['lattice'].value - if 'region' in group.keys(): - region = group['region'].value.decode() - else: - region = [] + region = group['region'].value.decode() if 'region' in group else '' # Create this Cell cell = openmc.Cell(cell_id=cell_id, name=name) diff --git a/openmc/surface.py b/openmc/surface.py index 5eb242003..0d78c0e1b 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -210,7 +210,7 @@ class Surface(object): """ surface_id = int(group.name.split('/')[-1].lstrip('surface ')) - name = group['name'].value.decode() + name = group['name'].value.decode() if 'name' in group else '' surf_type = group['type'].value.decode() bc = group['boundary_type'].value.decode() coeffs = group['coefficients'][...] diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index e5ed5df70..29e164440 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -782,7 +782,7 @@ contains ! write that dhats are zero if (dhat_reset) then - call write_message('Dhats reset to zero.', 1) + call write_message('Dhats reset to zero.', 8) end if end subroutine compute_dhat diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index b1f0041d3..0d4a9f4d3 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -363,7 +363,7 @@ contains integer :: i ! loop counter ! Print message - call write_message("CMFD tallies reset", 7) + call write_message("CMFD tallies reset", 6) ! Reset CMFD tallies do i = 1, size(cmfd_tallies) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 95e1b1811..1518d710f 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -61,10 +61,10 @@ contains logical :: file_exists ! does cmfd.xml exist? logical :: found character(MAX_LINE_LEN) :: filename - character(MAX_LINE_LEN) :: temp_str real(8) :: gs_tol(2) - type(Node), pointer :: doc => null() - type(Node), pointer :: node_mesh => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_mesh ! Read cmfd input file filename = trim(path_input) // "cmfd.xml" @@ -84,13 +84,14 @@ contains end if ! Parse cmfd.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! Get pointer to mesh XML node - call get_node_ptr(doc, "mesh", node_mesh, found = found) + node_mesh = root % child("mesh") ! Check if mesh is there - if (.not.found) then + if (.not. node_mesh % associated()) then call fatal_error("No CMFD mesh specified in CMFD XML file.") end if @@ -99,8 +100,8 @@ contains ! Get number of energy groups if (check_for_node(node_mesh, "energy")) then - ng = get_arraysize_double(node_mesh, "energy") - if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) + ng = node_word_count(node_mesh, "energy") + if(.not. allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) call get_node_array(node_mesh, "energy", cmfd%egrid) cmfd % indices(4) = ng - 1 ! sets energy group dimension ! If using MG mode, check to see if these egrid points at least match @@ -137,11 +138,11 @@ contains if (check_for_node(node_mesh, "map")) then allocate(cmfd % coremap(cmfd % indices(1), cmfd % indices(2), & cmfd % indices(3))) - if (get_arraysize_integer(node_mesh, "map") /= & + if (node_word_count(node_mesh, "map") /= & product(cmfd % indices(1:3))) then call fatal_error('CMFD coremap not to correct dimensions') end if - allocate(iarray(get_arraysize_integer(node_mesh, "map"))) + allocate(iarray(node_word_count(node_mesh, "map"))) call get_node_array(node_mesh, "map", iarray) cmfd % coremap = reshape(iarray,(cmfd % indices(1:3))) cmfd_coremap = .true. @@ -149,71 +150,53 @@ contains end if ! Check for normalization constant - if (check_for_node(doc, "norm")) then - call get_node_value(doc, "norm", cmfd % norm) + if (check_for_node(root, "norm")) then + call get_node_value(root, "norm", cmfd % norm) end if ! Set feedback logical - if (check_for_node(doc, "feedback")) then - call get_node_value(doc, "feedback", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_feedback = .true. + if (check_for_node(root, "feedback")) then + call get_node_value(root, "feedback", cmfd_feedback) end if ! Set downscatter logical - if (check_for_node(doc, "downscatter")) then - call get_node_value(doc, "downscatter", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_downscatter = .true. + if (check_for_node(root, "downscatter")) then + call get_node_value(root, "downscatter", cmfd_downscatter) end if ! Reset dhat parameters - if (check_for_node(doc, "dhat_reset")) then - call get_node_value(doc, "dhat_reset", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - dhat_reset = .true. + if (check_for_node(root, "dhat_reset")) then + call get_node_value(root, "dhat_reset", dhat_reset) end if ! Set monitoring - if (check_for_node(doc, "power_monitor")) then - call get_node_value(doc, "power_monitor", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_power_monitor = .true. + if (check_for_node(root, "power_monitor")) then + call get_node_value(root, "power_monitor", cmfd_power_monitor) end if ! Output logicals - if (check_for_node(doc, "write_matrices")) then - call get_node_value(doc, "write_matrices", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_write_matrices = .true. + if (check_for_node(root, "write_matrices")) then + call get_node_value(root, "write_matrices", cmfd_write_matrices) end if ! Run an adjoint calc - if (check_for_node(doc, "run_adjoint")) then - call get_node_value(doc, "run_adjoint", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - cmfd_run_adjoint = .true. + if (check_for_node(root, "run_adjoint")) then + call get_node_value(root, "run_adjoint", cmfd_run_adjoint) end if ! Batch to begin cmfd - if (check_for_node(doc, "begin")) & - call get_node_value(doc, "begin", cmfd_begin) + if (check_for_node(root, "begin")) & + call get_node_value(root, "begin", cmfd_begin) ! Check for cmfd tally resets - if (check_for_node(doc, "tally_reset")) then - n_cmfd_resets = get_arraysize_integer(doc, "tally_reset") + if (check_for_node(root, "tally_reset")) then + n_cmfd_resets = node_word_count(root, "tally_reset") else n_cmfd_resets = 0 end if if (n_cmfd_resets > 0) then allocate(int_array(n_cmfd_resets)) - call get_node_array(doc, "tally_reset", int_array) + call get_node_array(root, "tally_reset", int_array) do i = 1, n_cmfd_resets call cmfd_reset % add(int_array(i)) end do @@ -221,34 +204,34 @@ contains end if ! Get display - if (check_for_node(doc, "display")) & - call get_node_value(doc, "display", cmfd_display) + if (check_for_node(root, "display")) & + call get_node_value(root, "display", cmfd_display) ! Read in spectral radius estimate and tolerances - if (check_for_node(doc, "spectral")) & - call get_node_value(doc, "spectral", cmfd_spectral) - if (check_for_node(doc, "shift")) & - call get_node_value(doc, "shift", cmfd_shift) - if (check_for_node(doc, "ktol")) & - call get_node_value(doc, "ktol", cmfd_ktol) - if (check_for_node(doc, "stol")) & - call get_node_value(doc, "stol", cmfd_stol) - if (check_for_node(doc, "gauss_seidel_tolerance")) then - n_params = get_arraysize_double(doc, "gauss_seidel_tolerance") + if (check_for_node(root, "spectral")) & + call get_node_value(root, "spectral", cmfd_spectral) + if (check_for_node(root, "shift")) & + call get_node_value(root, "shift", cmfd_shift) + if (check_for_node(root, "ktol")) & + call get_node_value(root, "ktol", cmfd_ktol) + if (check_for_node(root, "stol")) & + call get_node_value(root, "stol", cmfd_stol) + if (check_for_node(root, "gauss_seidel_tolerance")) then + n_params = node_word_count(root, "gauss_seidel_tolerance") if (n_params /= 2) then call fatal_error('Gauss Seidel tolerance is not 2 parameters & &(absolute, relative).') end if - call get_node_array(doc, "gauss_seidel_tolerance", gs_tol) + call get_node_array(root, "gauss_seidel_tolerance", gs_tol) cmfd_atoli = gs_tol(1) cmfd_rtoli = gs_tol(2) end if ! Create tally objects - call create_cmfd_tally(doc) + call create_cmfd_tally(root) ! Close CMFD XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_cmfd_xml @@ -261,7 +244,7 @@ contains ! 3: Surface current !=============================================================================== - subroutine create_cmfd_tally(doc) + subroutine create_cmfd_tally(root) use constants, only: MAX_LINE_LEN use error, only: fatal_error, warning @@ -274,9 +257,8 @@ contains use tally_initialize, only: add_tallies use xml_interface - type(Node), pointer :: doc ! pointer to XML doc info + type(XMLNode), intent(in) :: root ! XML root element - character(MAX_LINE_LEN) :: temp_str ! temp string integer :: i, j ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) @@ -287,7 +269,7 @@ contains type(TallyObject), pointer :: t type(RegularMesh), pointer :: m type(TallyFilterContainer) :: filters(N_FILTER_TYPES) ! temporary filters - type(Node), pointer :: node_mesh + type(XMLNode) :: node_mesh ! Set global variables if they are 0 (this can happen if there is no tally ! file) @@ -304,10 +286,10 @@ contains m % type = LATTICE_RECT ! Get pointer to mesh XML node - call get_node_ptr(doc, "mesh", node_mesh) + node_mesh = root % child("mesh") ! Determine number of dimensions for mesh - n = get_arraysize_integer(node_mesh, "dimension") + n = node_word_count(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then call fatal_error("Mesh must be two or three dimensions.") end if @@ -330,7 +312,7 @@ contains m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then + if (m % n_dimension /= node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the same as & &the number of entries on .") end if @@ -352,8 +334,8 @@ contains if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions - if (get_arraysize_double(node_mesh, "width") /= & - get_arraysize_double(node_mesh, "lower_left")) then + if (node_word_count(node_mesh, "width") /= & + node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the same as the & &number of entries on .") end if @@ -370,8 +352,8 @@ contains elseif (check_for_node(node_mesh, "upper_right")) then ! Check to ensure width has same dimensions - if (get_arraysize_double(node_mesh, "upper_right") /= & - get_arraysize_double(node_mesh, "lower_left")) then + if (node_word_count(node_mesh, "upper_right") /= & + node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the same & &as the number of entries on .") end if @@ -404,11 +386,8 @@ contains t => cmfd_tallies(i) ! Set reset property - if (check_for_node(doc, "reset")) then - call get_node_value(doc, "reset", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - t % reset = .true. + if (check_for_node(root, "reset")) then + call get_node_value(root, "reset", t % reset) end if ! Set up mesh filter @@ -427,7 +406,7 @@ contains allocate(EnergyFilter :: filters(n_filters) % obj) select type (filt => filters(n_filters) % obj) type is (EnergyFilter) - ng = get_arraysize_double(node_mesh, "energy") + ng = node_word_count(node_mesh, "energy") filt % n_bins = ng - 1 allocate(filt % bins(ng)) call get_node_array(node_mesh, "energy", filt % bins) @@ -492,7 +471,7 @@ contains allocate(EnergyoutFilter :: filters(n_filters) % obj) select type (filt => filters(n_filters) % obj) type is (EnergyoutFilter) - ng = get_arraysize_double(node_mesh, "energy") + ng = node_word_count(node_mesh, "energy") filt % n_bins = ng - 1 allocate(filt % bins(ng)) call get_node_array(node_mesh, "energy", filt % bins) diff --git a/src/constants.F90 b/src/constants.F90 index a2590a12a..682ae79e8 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -57,7 +57,6 @@ module constants integer, parameter :: MAX_LINE_LEN = 250 integer, parameter :: MAX_WORD_LEN = 150 integer, parameter :: MAX_FILE_LEN = 255 - integer, parameter :: REGION_SPEC_LEN = 1000 ! Maximum number of external source spatial resamples to encounter before an ! error is thrown. diff --git a/src/distribution_univariate.F90 b/src/distribution_univariate.F90 index 6c053e594..3842db95c 100644 --- a/src/distribution_univariate.F90 +++ b/src/distribution_univariate.F90 @@ -5,6 +5,7 @@ module distribution_univariate use error, only: fatal_error use random_lcg, only: prn use math, only: maxwell_spectrum, watt_spectrum + use pugixml use string, only: to_lower use xml_interface @@ -265,7 +266,7 @@ contains subroutine distribution_from_xml(dist, node_dist) class(Distribution), allocatable, intent(inout) :: dist - type(Node), pointer :: node_dist + type(XMLNode), intent(in) :: node_dist character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: temp_str @@ -279,7 +280,7 @@ contains ! Determine number of parameters specified if (check_for_node(node_dist, "parameters")) then - n = get_arraysize_double(node_dist, "parameters") + n = node_word_count(node_dist, "parameters") else n = 0 end if diff --git a/src/Faddeeva.c b/src/faddeeva/Faddeeva.c similarity index 100% rename from src/Faddeeva.c rename to src/faddeeva/Faddeeva.c diff --git a/src/Faddeeva.cc b/src/faddeeva/Faddeeva.cc similarity index 100% rename from src/Faddeeva.cc rename to src/faddeeva/Faddeeva.cc diff --git a/src/Faddeeva.h b/src/faddeeva/Faddeeva.h similarity index 100% rename from src/Faddeeva.h rename to src/faddeeva/Faddeeva.h diff --git a/src/geometry.F90 b/src/geometry.F90 index ecb76d050..a7fa24fbf 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -1011,7 +1011,7 @@ contains type(VectorInt), allocatable :: neighbor_neg(:) call write_message("Building neighboring cells lists for each surface...", & - 4) + 6) allocate(neighbor_pos(n_surfaces)) allocate(neighbor_neg(n_surfaces)) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 3a2582511..e8c39b923 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1758,8 +1758,8 @@ contains integer(HID_T) :: dset ! data set handle integer(HID_T) :: dspace ! data or file space handle integer(HID_T) :: filetype - integer(HID_T) :: memtype - integer(SIZE_T) :: n + integer(SIZE_T) :: i, n + character(kind=C_CHAR), allocatable, target :: temp_buffer(:) type(c_ptr) :: f_ptr ! Set up collective vs. independent I/O @@ -1770,35 +1770,38 @@ contains ! Create datatype for HDF5 file based on C char n = len_trim(buffer) - call h5tcopy_f(H5T_C_S1, filetype, hdf5_err) - call h5tset_size_f(filetype, n + 1, hdf5_err) + if (n > 0) then + call h5tcopy_f(H5T_C_S1, filetype, hdf5_err) + call h5tset_size_f(filetype, n, hdf5_err) - ! Create datatype in memory based on Fortran character - call h5tcopy_f(H5T_FORTRAN_S1, memtype, hdf5_err) - if (n > 0) call h5tset_size_f(memtype, n, hdf5_err) + ! Create dataspace/dataset + call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) + call h5dcreate_f(group_id, trim(name), filetype, dspace, dset, hdf5_err) - ! Create dataspace/dataset - call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), filetype, dspace, dset, hdf5_err) + ! Copy string to temporary buffer + allocate(temp_buffer(n)) + do i = 1, n + temp_buffer(i) = buffer(i:i) + end do - ! Get pointer to start of string - f_ptr = c_loc(buffer(1:1)) + ! Get pointer to start of string + f_ptr = c_loc(temp_buffer(1)) - if (using_mpio_device(group_id)) then + if (using_mpio_device(group_id)) then #ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - if (n > 0) call h5dwrite_f(dset, memtype, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) + call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) + call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) + call h5dwrite_f(dset, filetype, f_ptr, hdf5_err, xfer_prp=plist) + call h5pclose_f(plist, hdf5_err) #endif - else - if (n > 0) call h5dwrite_f(dset, memtype, f_ptr, hdf5_err) - end if + else + call h5dwrite_f(dset, filetype, f_ptr, hdf5_err) + end if - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - call h5tclose_f(memtype, hdf5_err) - call h5tclose_f(filetype, hdf5_err) + call h5dclose_f(dset, hdf5_err) + call h5sclose_f(dspace, hdf5_err) + call h5tclose_f(filetype, hdf5_err) + end if end subroutine write_string !=============================================================================== @@ -1817,11 +1820,10 @@ contains integer(HID_T) :: plist ! property list #endif integer(HID_T) :: dset_id - integer(HID_T) :: space_id integer(HID_T) :: filetype integer(HID_T) :: memtype - integer(SIZE_T) :: size - integer(SIZE_T) :: n + integer(SIZE_T) :: i, n + character(kind=C_CHAR), allocatable, target :: temp_buffer(:) type(c_ptr) :: f_ptr if (present(name)) then @@ -1836,40 +1838,41 @@ contains if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F end if - ! Get dataspace - call h5dget_space_f(dset_id, space_id, hdf5_err) - ! Make sure buffer is large enough call h5dget_type_f(dset_id, filetype, hdf5_err) - call h5tget_size_f(filetype, size, hdf5_err) - if (size > len(buffer) + 1) then + call h5tget_size_f(filetype, n, hdf5_err) + if (n > len(buffer)) then call fatal_error("Character buffer is not long enough to & &read HDF5 string.") end if ! Get datatype in memory based on Fortran character - n = len(buffer) - call h5tcopy_f(H5T_FORTRAN_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, n, hdf5_err) + call h5tcopy_f(H5T_C_S1, memtype, hdf5_err) + call h5tset_size_f(memtype, n + 1, hdf5_err) ! Get pointer to start of string - f_ptr = c_loc(buffer(1:1)) + allocate(temp_buffer(n)) + f_ptr = c_loc(temp_buffer(1)) if (using_mpio_device(dset_id)) then #ifdef PHDF5 call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, & - mem_space_id=space_id, xfer_prp=plist) + call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, xfer_prp=plist) call h5pclose_f(plist, hdf5_err) #endif else - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, mem_space_id=space_id) + call h5dread_f(dset_id, memtype, f_ptr, hdf5_err) end if + buffer = '' + do i = 1, n + if (temp_buffer(i) == C_NULL_CHAR) cycle + buffer(i:i) = temp_buffer(i) + end do + if (present(name)) call h5dclose_f(dset_id, hdf5_err) - call h5sclose_f(space_id, hdf5_err) call h5tclose_f(filetype, hdf5_err) call h5tclose_f(memtype, hdf5_err) end subroutine read_string @@ -2372,9 +2375,9 @@ contains end subroutine read_attribute_integer_2D_explicit subroutine read_attribute_string(buffer, obj_id, name) - character(*), intent(inout), target :: buffer ! read data to here - integer(HID_T), intent(in) :: obj_id - character(*), intent(in) :: name ! name for data + character(*), intent(inout) :: buffer ! read data to here + integer(HID_T), intent(in) :: obj_id + character(*), intent(in) :: name ! name for data integer :: hdf5_err integer(HID_T) :: attr_id ! data set handle @@ -2407,9 +2410,9 @@ contains call h5aread_f(attr_id, memtype, f_ptr, hdf5_err) buffer = '' do i = 1, size + if (temp_buffer(i) == C_NULL_CHAR) cycle buffer(i:i) = temp_buffer(i) end do - deallocate(temp_buffer) call h5aclose_f(attr_id, hdf5_err) call h5tclose_f(filetype, hdf5_err) diff --git a/src/initialize.F90 b/src/initialize.F90 index 8455ac1c2..a5db7d153 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -24,8 +24,8 @@ module initialize use material_header, only: Material use message_passing use mgxs_data, only: read_mgxs, create_macro_xs - use output, only: title, header, print_version, write_message, & - print_usage, print_plot + use output, only: print_version, write_message, print_usage, & + print_plot use random_lcg, only: initialize_prng use state_point, only: load_state_point use string, only: to_str, starts_with, ends_with, str_to_int @@ -68,12 +68,6 @@ contains ! Read command line arguments call read_command_line() - if (master) then - ! Display title and initialization header - call title() - call header("INITIALIZATION", level=1) - end if - ! Read XML input files call read_input_xml() @@ -137,7 +131,7 @@ contains if (master) then if (run_mode == MODE_PLOTTING) then ! Display plotting information - call print_plot() + if (verbosity >= 5) call print_plot() else ! Write summary information if (output_summary) call write_summary() @@ -148,9 +142,8 @@ contains if (particle_restart_run) run_mode = MODE_PARTICLE ! Warn if overlap checking is on - if (master .and. check_overlaps) then - call write_message("") - call warning("Cell overlap checking is ON") + if (master .and. check_overlaps .and. run_mode /= MODE_PLOTTING) then + call warning("Cell overlap checking is ON.") end if ! Stop initialization timer diff --git a/src/input_xml.F90 b/src/input_xml.F90 index fc4d5a581..19af7eaf1 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -18,7 +18,7 @@ module input_xml use message_passing use mgxs_data, only: create_macro_xs, read_mgxs use multipole, only: multipole_read - use output, only: write_message + use output, only: write_message, title, header use plot_header use random_lcg, only: prn, seed use surface_header @@ -84,26 +84,26 @@ contains logical :: file_exists character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename - type(Node), pointer :: doc => null() - type(Node), pointer :: node_mode => null() - type(Node), pointer :: node_source => null() - type(Node), pointer :: node_space => null() - type(Node), pointer :: node_angle => null() - type(Node), pointer :: node_dist => null() - type(Node), pointer :: node_cutoff => null() - type(Node), pointer :: node_entropy => null() - type(Node), pointer :: node_ufs => null() - type(Node), pointer :: node_sp => null() - type(Node), pointer :: node_output => null() - type(Node), pointer :: node_verb => null() - type(Node), pointer :: node_res_scat => null() - type(Node), pointer :: node_scatterer => null() - type(Node), pointer :: node_trigger => null() - type(Node), pointer :: node_vol => null() - type(Node), pointer :: node_tab_leg => null() - type(NodeList), pointer :: node_scat_list => null() - type(NodeList), pointer :: node_source_list => null() - type(NodeList), pointer :: node_vol_list => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_mode + type(XMLNode) :: node_source + type(XMLNode) :: node_space + type(XMLNode) :: node_angle + type(XMLNode) :: node_dist + type(XMLNode) :: node_cutoff + type(XMLNode) :: node_entropy + type(XMLNode) :: node_ufs + type(XMLNode) :: node_sp + type(XMLNode) :: node_output + type(XMLNode) :: node_res_scat + type(XMLNode) :: node_scatterer + type(XMLNode) :: node_trigger + type(XMLNode) :: node_vol + type(XMLNode) :: node_tab_leg + type(XMLNode), allocatable :: node_scat_list(:) + type(XMLNode), allocatable :: node_source_list(:) + type(XMLNode), allocatable :: node_vol_list(:) ! Check if settings.xml exists filename = trim(path_input) // "settings.xml" @@ -119,16 +119,27 @@ contains ! The settings.xml file is optional if we just want to make a plot. return end if - else - call write_message("Reading settings XML file...", 5) end if ! Parse settings.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() + + ! Verbosity + if (check_for_node(root, "verbosity")) then + call get_node_value(root, "verbosity", verbosity) + end if + + ! To this point, we haven't displayed any output since we didn't know what + ! the verbosity is. Now that we checked for it, show the title if necessary + if (master) then + if (verbosity >= 2) call title() + end if + call write_message("Reading settings XML file...", 5) ! Find if a multi-group or continuous-energy simulation is desired - if (check_for_node(doc, "energy_mode")) then - call get_node_value(doc, "energy_mode", temp_str) + if (check_for_node(root, "energy_mode")) then + call get_node_value(root, "energy_mode", temp_str) temp_str = trim(to_lower(temp_str)) if (temp_str == "mg" .or. temp_str == "multi-group") then run_CE = .false. @@ -138,24 +149,24 @@ contains end if ! Look for deprecated cross_sections.xml file in settings.xml - if (check_for_node(doc, "cross_sections")) then + if (check_for_node(root, "cross_sections")) then call warning("Setting cross_sections in settings.xml has been deprecated.& & The cross_sections are now set in materials.xml and the & &cross_sections input to materials.xml and the OPENMC_CROSS_SECTIONS& & environment variable will take precendent over setting & &cross_sections in settings.xml.") - call get_node_value(doc, "cross_sections", path_cross_sections) + call get_node_value(root, "cross_sections", path_cross_sections) end if ! Look for deprecated windowed_multipole file in settings.xml if (run_mode /= MODE_PLOTTING) then - if (check_for_node(doc, "multipole_library")) then + if (check_for_node(root, "multipole_library")) then call warning("Setting multipole_library in settings.xml has been & &deprecated. The multipole_library is now set in materials.xml and& & the multipole_library input to materials.xml and the & &OPENMC_MULTIPOLE_LIBRARY environment variable will take & &precendent over setting multipole_library in settings.xml.") - call get_node_value(doc, "multipole_library", path_multipole) + call get_node_value(root, "multipole_library", path_multipole) end if if (.not. ends_with(path_multipole, "/")) & path_multipole = trim(path_multipole) // "/" @@ -163,8 +174,8 @@ contains if (.not. run_CE) then ! Scattering Treatments - if (check_for_node(doc, "max_order")) then - call get_node_value(doc, "max_order", max_order) + if (check_for_node(root, "max_order")) then + call get_node_value(root, "max_order", max_order) else ! Set to default of largest int - 1, which means to use whatever is ! contained in library. @@ -179,30 +190,20 @@ contains ! Set output directory if a path has been specified on the ! element - if (check_for_node(doc, "output_path")) then - call get_node_value(doc, "output_path", path_output) + if (check_for_node(root, "output_path")) then + call get_node_value(root, "output_path", path_output) if (.not. ends_with(path_output, "/")) & path_output = trim(path_output) // "/" end if ! Check for a trigger node and get trigger information - if (check_for_node(doc, "trigger")) then - call get_node_ptr(doc, "trigger", node_trigger) + if (check_for_node(root, "trigger")) then + node_trigger = root % child("trigger") ! Check if trigger(s) are to be turned on - call get_node_value(node_trigger, "active", temp_str) - temp_str = trim(to_lower(temp_str)) - - if (temp_str == 'true' .or. temp_str == '1') then - trigger_on = .true. - elseif (temp_str == 'false' .or. temp_str == '0') then - trigger_on = .false. - else - call fatal_error("Unrecognized trigger active: " // temp_str) - end if + call get_node_value(node_trigger, "active", trigger_on) if (trigger_on) then - if (check_for_node(node_trigger, "max_batches") )then call get_node_value(node_trigger, "max_batches", n_max_batches) else @@ -224,8 +225,8 @@ contains ! Check run mode if it hasn't been set from the command line if (run_mode == NONE) then - if (check_for_node(doc, "run_mode")) then - call get_node_value(doc, "run_mode", temp_str) + if (check_for_node(root, "run_mode")) then + call get_node_value(root, "run_mode", temp_str) select case (to_lower(temp_str)) case ("eigenvalue") run_mode = MODE_EIGENVALUE @@ -237,31 +238,27 @@ contains run_mode = MODE_PARTICLE case ("volume") run_mode = MODE_VOLUME + case default + call fatal_error("Unrecognized run mode: " // & + trim(temp_str) // ".") end select ! Assume XML specifics , , etc. directly - node_mode => doc + node_mode = root else call warning(" should be specified.") ! Make sure that either eigenvalue or fixed source was specified - if (.not. check_for_node(doc, "eigenvalue") .and. & - .not. check_for_node(doc, "fixed_source")) then - call fatal_error(" or not specified.") - end if - - if (check_for_node(doc, "eigenvalue")) then - ! Set run mode + node_mode = root % child("eigenvalue") + if (node_mode % associated()) then if (run_mode == NONE) run_mode = MODE_EIGENVALUE - - ! Get pointer to eigenvalue XML block - call get_node_ptr(doc, "eigenvalue", node_mode) - elseif (check_for_node(doc, "fixed_source")) then - ! Set run mode - if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE - - ! Get pointer to fixed_source XML block - call get_node_ptr(doc, "fixed_source", node_mode) + else + node_mode = root % child("fixed_source") + if (node_mode % associated()) then + if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE + else + call fatal_error(" or not specified.") + end if end if end if end if @@ -281,11 +278,11 @@ contains end if ! Copy random number seed if specified - if (check_for_node(doc, "seed")) call get_node_value(doc, "seed", seed) + if (check_for_node(root, "seed")) call get_node_value(root, "seed", seed) ! Number of bins for logarithmic grid - if (check_for_node(doc, "log_grid_bins")) then - call get_node_value(doc, "log_grid_bins", n_log_bins) + if (check_for_node(root, "log_grid_bins")) then + call get_node_value(root, "log_grid_bins", n_log_bins) if (n_log_bins < 1) then call fatal_error("Number of bins for logarithmic grid must be & &greater than zero.") @@ -294,17 +291,11 @@ contains n_log_bins = 8000 end if - ! Verbosity - if (check_for_node(doc, "verbosity")) then - call get_node_ptr(doc, "verbosity", node_verb) - call get_node_value(node_verb, "value", verbosity) - end if - ! Number of OpenMP threads - if (check_for_node(doc, "threads")) then + if (check_for_node(root, "threads")) then #ifdef _OPENMP if (n_threads == NONE) then - call get_node_value(doc, "threads", n_threads) + call get_node_value(root, "threads", n_threads) if (n_threads < 1) then call fatal_error("Invalid number of threads: " // to_str(n_threads)) end if @@ -319,8 +310,8 @@ contains ! EXTERNAL SOURCE ! Get point to list of elements and make sure there is at least one - call get_node_list(doc, "source", node_source_list) - n = get_list_size(node_source_list) + call get_node_list(root, "source", node_source_list) + n = size(node_source_list) if (run_mode == MODE_EIGENVALUE .or. run_mode == MODE_FIXEDSOURCE) then if (n == 0) call fatal_error("No source specified in settings XML file.") @@ -332,14 +323,11 @@ contains ! Read each source do i = 1, n ! Get pointer to source - call get_list_item(node_source_list, i, node_source) + node_source = node_source_list(i) ! Check if we want to write out source if (check_for_node(node_source, "write_initial")) then - call get_node_value(node_source, "write_initial", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - write_initial_source = .true. + call get_node_value(node_source, "write_initial", write_initial_source) end if ! Check for source strength @@ -367,7 +355,7 @@ contains if (check_for_node(node_source, "space")) then ! Get pointer to spatial distribution - call get_node_ptr(node_source, "space", node_space) + node_space = node_source % child("space") ! Check for type of spatial distribution type = '' @@ -399,7 +387,7 @@ contains type is (CartesianIndependent) ! Read distribution for x coordinate if (check_for_node(node_space, "x")) then - call get_node_ptr(node_space, "x", node_dist) + node_dist = node_space % child("x") call distribution_from_xml(space%x, node_dist) else allocate(Discrete :: space%x) @@ -413,7 +401,7 @@ contains ! Read distribution for y coordinate if (check_for_node(node_space, "y")) then - call get_node_ptr(node_space, "y", node_dist) + node_dist = node_space % child("y") call distribution_from_xml(space%y, node_dist) else allocate(Discrete :: space%y) @@ -426,7 +414,7 @@ contains end if if (check_for_node(node_space, "z")) then - call get_node_ptr(node_space, "z", node_dist) + node_dist = node_space % child("z") call distribution_from_xml(space%z, node_dist) else allocate(Discrete :: space%z) @@ -440,7 +428,7 @@ contains type is (SpatialBox) ! Make sure correct number of parameters are given - if (get_arraysize_double(node_space, "parameters") /= 6) then + if (node_word_count(node_space, "parameters") /= 6) then call fatal_error('Box/fission spatial source must have & &six parameters specified.') end if @@ -454,7 +442,7 @@ contains type is (SpatialPoint) ! Make sure correct number of parameters are given - if (get_arraysize_double(node_space, "parameters") /= 3) then + if (node_word_count(node_space, "parameters") /= 3) then call fatal_error('Point spatial source must have & &three parameters specified.') end if @@ -476,7 +464,7 @@ contains if (check_for_node(node_source, "angle")) then ! Get pointer to angular distribution - call get_node_ptr(node_source, "angle", node_angle) + node_angle = node_source % child("angle") ! Check for type of angular distribution type = '' @@ -499,7 +487,7 @@ contains ! Read reference directional unit vector if (check_for_node(node_angle, "reference_uvw")) then - n = get_arraysize_double(node_angle, "reference_uvw") + n = node_word_count(node_angle, "reference_uvw") if (n /= 3) then call fatal_error('Angular distribution reference direction must have & &three parameters specified.') @@ -519,7 +507,7 @@ contains type is (PolarAzimuthal) if (check_for_node(node_angle, "mu")) then - call get_node_ptr(node_angle, "mu", node_dist) + node_dist = node_angle % child("mu") call distribution_from_xml(angle%mu, node_dist) else allocate(Uniform :: angle%mu) @@ -531,7 +519,7 @@ contains end if if (check_for_node(node_angle, "phi")) then - call get_node_ptr(node_angle, "phi", node_dist) + node_dist = node_angle % child("phi") call distribution_from_xml(angle%phi, node_dist) else allocate(Uniform :: angle%phi) @@ -551,7 +539,7 @@ contains ! Determine external source energy distribution if (check_for_node(node_source, "energy")) then - call get_node_ptr(node_source, "energy", node_dist) + node_dist = node_source % child("energy") call distribution_from_xml(external_source(i)%energy, node_dist) else ! Default to a Watt spectrum with parameters 0.988 MeV and 2.249 MeV^-1 @@ -566,24 +554,18 @@ contains end do ! Survival biasing - if (check_for_node(doc, "survival_biasing")) then - call get_node_value(doc, "survival_biasing", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - survival_biasing = .true. + if (check_for_node(root, "survival_biasing")) then + call get_node_value(root, "survival_biasing", survival_biasing) end if ! Probability tables - if (check_for_node(doc, "ptables")) then - call get_node_value(doc, "ptables", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & - urr_ptables_on = .false. + if (check_for_node(root, "ptables")) then + call get_node_value(root, "ptables", urr_ptables_on) end if ! Cutoffs - if (check_for_node(doc, "cutoff")) then - call get_node_ptr(doc, "cutoff", node_cutoff) + if (check_for_node(root, "cutoff")) then + node_cutoff = root % child("cutoff") if (check_for_node(node_cutoff, "weight")) then call get_node_value(node_cutoff, "weight", weight_cutoff) end if @@ -596,17 +578,17 @@ contains end if ! Particle trace - if (check_for_node(doc, "trace")) then - call get_node_array(doc, "trace", temp_int_array3) + if (check_for_node(root, "trace")) then + call get_node_array(root, "trace", temp_int_array3) trace_batch = temp_int_array3(1) trace_gen = temp_int_array3(2) trace_particle = int(temp_int_array3(3), 8) end if ! Particle tracks - if (check_for_node(doc, "track")) then + if (check_for_node(root, "track")) then ! Make sure that there are three values per particle - n_tracks = get_arraysize_integer(doc, "track") + n_tracks = node_word_count(root, "track") if (mod(n_tracks, 3) /= 0) then call fatal_error("Number of integers specified in 'track' is not & &divisible by 3. Please provide 3 integers per particle to be & @@ -615,7 +597,7 @@ contains ! Allocate space and get list of tracks allocate(temp_int_array(n_tracks)) - call get_node_array(doc, "track", temp_int_array) + call get_node_array(root, "track", temp_int_array) ! Reshape into track_identifiers allocate(track_identifiers(3, n_tracks/3)) @@ -623,16 +605,16 @@ contains end if ! Shannon Entropy mesh - if (check_for_node(doc, "entropy")) then + if (check_for_node(root, "entropy")) then ! Get pointer to entropy node - call get_node_ptr(doc, "entropy", node_entropy) + node_entropy = root % child("entropy") ! Check to make sure enough values were supplied - if (get_arraysize_double(node_entropy, "lower_left") /= 3) then + if (node_word_count(node_entropy, "lower_left") /= 3) then call fatal_error("Need to specify (x,y,z) coordinates of lower-left & &corner of Shannon entropy mesh.") - elseif (get_arraysize_double(node_entropy, "upper_right") /= 3) then + elseif (node_word_count(node_entropy, "upper_right") /= 3) then call fatal_error("Need to specify (x,y,z) coordinates of upper-right & &corner of Shannon entropy mesh.") end if @@ -661,7 +643,7 @@ contains if (check_for_node(node_entropy, "dimension")) then ! If so, make sure proper number of values were given - if (get_arraysize_integer(node_entropy, "dimension") /= 3) then + if (node_word_count(node_entropy, "dimension") /= 3) then call fatal_error("Dimension of entropy mesh must be given as three & &integers.") end if @@ -684,19 +666,19 @@ contains end if ! Uniform fission source weighting mesh - if (check_for_node(doc, "uniform_fs")) then + if (check_for_node(root, "uniform_fs")) then ! Get pointer to ufs node - call get_node_ptr(doc, "uniform_fs", node_ufs) + node_ufs = root % child("uniform_fs") ! Check to make sure enough values were supplied - if (get_arraysize_double(node_ufs, "lower_left") /= 3) then + if (node_word_count(node_ufs, "lower_left") /= 3) then call fatal_error("Need to specify (x,y,z) coordinates of lower-left & &corner of UFS mesh.") - elseif (get_arraysize_double(node_ufs, "upper_right") /= 3) then + elseif (node_word_count(node_ufs, "upper_right") /= 3) then call fatal_error("Need to specify (x,y,z) coordinates of upper-right & &corner of UFS mesh.") - elseif (get_arraysize_integer(node_ufs, "dimension") /= 3) then + elseif (node_word_count(node_ufs, "dimension") /= 3) then call fatal_error("Dimension of UFS mesh must be given as three & &integers.") end if @@ -740,14 +722,14 @@ contains end if ! Check if the user has specified to write state points - if (check_for_node(doc, "state_point")) then + if (check_for_node(root, "state_point")) then ! Get pointer to state_point node - call get_node_ptr(doc, "state_point", node_sp) + node_sp = root % child("state_point") ! Determine number of batches at which to store state points if (check_for_node(node_sp, "batches")) then - n_state_points = get_arraysize_integer(node_sp, "batches") + n_state_points = node_word_count(node_sp, "batches") else n_state_points = 0 end if @@ -773,14 +755,14 @@ contains end if ! Check if the user has specified to write source points - if (check_for_node(doc, "source_point")) then + if (check_for_node(root, "source_point")) then ! Get pointer to source_point node - call get_node_ptr(doc, "source_point", node_sp) + node_sp = root % child("source_point") ! Determine number of batches at which to store source points if (check_for_node(node_sp, "batches")) then - n_source_points = get_arraysize_integer(node_sp, "batches") + n_source_points = node_word_count(node_sp, "batches") else n_source_points = 0 end if @@ -803,25 +785,14 @@ contains ! Check if the user has specified to write binary source file if (check_for_node(node_sp, "separate")) then - call get_node_value(node_sp, "separate", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. & - trim(temp_str) == '1') source_separate = .true. + call get_node_value(node_sp, "separate", source_separate) end if if (check_for_node(node_sp, "write")) then - call get_node_value(node_sp, "write", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'false' .or. & - trim(temp_str) == '0') source_write = .false. + call get_node_value(node_sp, "write", source_write) end if if (check_for_node(node_sp, "overwrite_latest")) then - call get_node_value(node_sp, "overwrite_latest", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. & - trim(temp_str) == '1') then - source_latest = .true. - source_separate = .true. - end if + call get_node_value(node_sp, "overwrite_latest", source_latest) + source_separate = source_latest end if else ! If no tag was present, by default we keep source bank in @@ -848,68 +819,52 @@ contains ! Check if the user has specified to not reduce tallies at the end of every ! batch - if (check_for_node(doc, "no_reduce")) then - call get_node_value(doc, "no_reduce", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - reduce_tallies = .false. + if (check_for_node(root, "no_reduce")) then + call get_node_value(root, "no_reduce", reduce_tallies) end if ! Check if the user has specified to use confidence intervals for ! uncertainties rather than standard deviations - if (check_for_node(doc, "confidence_intervals")) then - call get_node_value(doc, "confidence_intervals", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. & - trim(temp_str) == '1') confidence_intervals = .true. + if (check_for_node(root, "confidence_intervals")) then + call get_node_value(root, "confidence_intervals", confidence_intervals) end if ! Check for output options - if (check_for_node(doc, "output")) then + if (check_for_node(root, "output")) then ! Get pointer to output node - call get_node_ptr(doc, "output", node_output) + node_output = root % child("output") ! Check for summary option if (check_for_node(node_output, "summary")) then - call get_node_value(node_output, "summary", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'false' .or. & - trim(temp_str) == '0') output_summary = .false. + call get_node_value(node_output, "summary", output_summary) end if ! Check for ASCII tallies output option if (check_for_node(node_output, "tallies")) then - call get_node_value(node_output, "tallies", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'false' .or. & - trim(temp_str) == '0') output_tallies = .false. + call get_node_value(node_output, "tallies", output_tallies) end if end if ! Check for cmfd run - if (check_for_node(doc, "run_cmfd")) then - call get_node_value(doc, "run_cmfd", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then - cmfd_run = .true. - end if + if (check_for_node(root, "run_cmfd")) then + call get_node_value(root, "run_cmfd", cmfd_run) end if ! Resonance scattering parameters - if (check_for_node(doc, "resonance_scattering")) then - call get_node_ptr(doc, "resonance_scattering", node_res_scat) + if (check_for_node(root, "resonance_scattering")) then + node_res_scat = root % child("resonance_scattering") call get_node_list(node_res_scat, "scatterer", node_scat_list) ! check that a nuclide is specified - if (get_list_size(node_scat_list) >= 1) then + if (size(node_scat_list) >= 1) then treat_res_scat = .true. - n_res_scatterers_total = get_list_size(node_scat_list) + n_res_scatterers_total = size(node_scat_list) ! store 0K info for resonant scatterers allocate(nuclides_0K(n_res_scatterers_total)) do i = 1, n_res_scatterers_total - call get_list_item(node_scat_list, i, node_scatterer) + node_scatterer = node_scat_list(i) ! check to make sure a nuclide is specified if (.not. check_for_node(node_scatterer, "nuclide")) then @@ -955,20 +910,20 @@ contains end if end if - call get_node_list(doc, "volume_calc", node_vol_list) - n = get_list_size(node_vol_list) + call get_node_list(root, "volume_calc", node_vol_list) + n = size(node_vol_list) allocate(volume_calcs(n)) do i = 1, n - call get_list_item(node_vol_list, i, node_vol) + node_vol = node_vol_list(i) call volume_calcs(i) % from_xml(node_vol) end do ! Get temperature settings - if (check_for_node(doc, "temperature_default")) then - call get_node_value(doc, "temperature_default", temperature_default) + if (check_for_node(root, "temperature_default")) then + call get_node_value(root, "temperature_default", temperature_default) end if - if (check_for_node(doc, "temperature_method")) then - call get_node_value(doc, "temperature_method", temp_str) + if (check_for_node(root, "temperature_method")) then + call get_node_value(root, "temperature_method", temp_str) select case (to_lower(temp_str)) case ('nearest') temperature_method = TEMPERATURE_NEAREST @@ -978,34 +933,22 @@ contains call fatal_error("Unknown temperature method: " // trim(temp_str)) end select end if - if (check_for_node(doc, "temperature_tolerance")) then - call get_node_value(doc, "temperature_tolerance", temperature_tolerance) + if (check_for_node(root, "temperature_tolerance")) then + call get_node_value(root, "temperature_tolerance", temperature_tolerance) end if - if (check_for_node(doc, "temperature_multipole")) then - call get_node_value(doc, "temperature_multipole", temp_str) - select case (to_lower(temp_str)) - case ('true', '1') - temperature_multipole = .true. - case ('false', '0') - temperature_multipole = .false. - case default - call fatal_error("Unrecognized value for in & - &settings.xml") - end select + if (check_for_node(root, "temperature_multipole")) then + call get_node_value(root, "temperature_multipole", temperature_multipole) end if ! Check for tabular_legendre options - if (check_for_node(doc, "tabular_legendre")) then + if (check_for_node(root, "tabular_legendre")) then ! Get pointer to tabular_legendre node - call get_node_ptr(doc, "tabular_legendre", node_tab_leg) + node_tab_leg = root % child("tabular_legendre") ! Check for enable option if (check_for_node(node_tab_leg, "enable")) then - call get_node_value(node_tab_leg, "enable", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'false' .or. & - trim(temp_str) == '0') legendre_to_tabular = .false. + call get_node_value(node_tab_leg, "enable", legendre_to_tabular) end if ! Check for the number of points @@ -1021,19 +964,14 @@ contains ! Check whether create fission sites if (run_mode == MODE_FIXEDSOURCE) then - if (check_for_node(doc, "create_fission_neutrons")) then - call get_node_value(doc, "create_fission_neutrons", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then - create_fission_neutrons = .true. - else if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') then - create_fission_neutrons = .false. - end if + if (check_for_node(root, "create_fission_neutrons")) then + call get_node_value(root, "create_fission_neutrons", & + create_fission_neutrons) end if end if ! Close settings XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_settings_xml @@ -1042,23 +980,20 @@ contains !=============================================================================== subroutine get_run_parameters(node_base) - type(Node), pointer :: node_base + type(XMLNode), intent(in) :: node_base - integer(8) :: temp_long character(MAX_LINE_LEN) :: temp_str - type(Node), pointer :: node_keff_trigger => null() + type(XMLNode) :: node_keff_trigger ! Check number of particles if (.not. check_for_node(node_base, "particles")) then call fatal_error("Need to specify number of particles.") end if - ! Get number of particles - call get_node_value(node_base, "particles", temp_long) - - ! If the number of particles was specified as a command-line argument, we - ! don't set it here - if (n_particles == 0) n_particles = temp_long + ! Get number of particles if it wasn't specified as a command-line argument + if (n_particles == 0) then + call get_node_value(node_base, "particles", n_particles) + end if ! Get number of basic batches call get_node_value(node_base, "batches", n_batches) @@ -1082,7 +1017,7 @@ contains ! Get the trigger information for keff if (check_for_node(node_base, "keff_trigger")) then - call get_node_ptr(node_base, "keff_trigger", node_keff_trigger) + node_keff_trigger = node_base % child("keff_trigger") if (check_for_node(node_keff_trigger, "type")) then call get_node_value(node_keff_trigger, "type", temp_str) @@ -1139,18 +1074,19 @@ contains character(MAX_LINE_LEN) :: filename character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN), allocatable :: sarray(:) - character(REGION_SPEC_LEN) :: region_spec + character(:), allocatable :: region_spec type(Cell), pointer :: c class(Surface), pointer :: s class(Lattice), pointer :: lat - type(Node), pointer :: doc => null() - type(Node), pointer :: node_cell => null() - type(Node), pointer :: node_surf => null() - type(Node), pointer :: node_lat => null() - type(NodeList), pointer :: node_cell_list => null() - type(NodeList), pointer :: node_surf_list => null() - type(NodeList), pointer :: node_rlat_list => null() - type(NodeList), pointer :: node_hlat_list => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_cell + type(XMLNode) :: node_surf + type(XMLNode) :: node_lat + type(XMLNode), allocatable :: node_cell_list(:) + type(XMLNode), allocatable :: node_surf_list(:) + type(XMLNode), allocatable :: node_rlat_list(:) + type(XMLNode), allocatable :: node_hlat_list(:) type(VectorInt) :: tokens type(VectorInt) :: rpn @@ -1169,13 +1105,14 @@ contains end if ! Parse geometry.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! Get pointer to list of XML - call get_node_list(doc, "cell", node_cell_list) + call get_node_list(root, "cell", node_cell_list) ! Get number of tags - n_cells = get_list_size(node_cell_list) + n_cells = size(node_cell_list) ! Check for no cells if (n_cells == 0) then @@ -1199,7 +1136,7 @@ contains c % distribcell_index = NONE ! Get pointer to i-th cell node - call get_list_item(node_cell_list, i, node_cell) + node_cell = node_cell_list(i) ! Copy data into cells if (check_for_node(node_cell, "id")) then @@ -1232,7 +1169,7 @@ contains ! Read material if (check_for_node(node_cell, "material")) then - n_mats = get_arraysize_string(node_cell, "material") + n_mats = node_word_count(node_cell, "material") if (n_mats > 0) then allocate(sarray(n_mats)) @@ -1279,15 +1216,17 @@ contains end if ! Check for region specification (also under deprecated name surfaces) - region_spec = '' if (check_for_node(node_cell, "surfaces")) then call warning("The use of 'surfaces' is deprecated and will be & &disallowed in a future release. Use 'region' instead. The & &openmc-update-inputs utility can be used to automatically & &update geometry.xml files.") + region_spec = node_value_string(node_cell, "surfaces") call get_node_value(node_cell, "surfaces", region_spec) elseif (check_for_node(node_cell, "region")) then - call get_node_value(node_cell, "region", region_spec) + region_spec = node_value_string(node_cell, "region") + else + region_spec = '' end if if (len_trim(region_spec) > 0) then @@ -1326,7 +1265,7 @@ contains end if ! Read number of rotation parameters - n = get_arraysize_double(node_cell, "rotation") + n = node_word_count(node_cell, "rotation") if (n /= 3) then call fatal_error("Incorrect number of rotation parameters on cell " & // to_str(c % id)) @@ -1362,7 +1301,7 @@ contains end if ! Read number of translation parameters - n = get_arraysize_double(node_cell, "translation") + n = node_word_count(node_cell, "translation") if (n /= 3) then call fatal_error("Incorrect number of translation parameters on & &cell " // to_str(c % id)) @@ -1377,7 +1316,7 @@ contains ! ERROR_REAL for now. During initialization we'll replace ERROR_REAL with ! the temperature from the material data. if (check_for_node(node_cell, "temperature")) then - n = get_arraysize_double(node_cell, "temperature") + n = node_word_count(node_cell, "temperature") if (n > 0) then ! Make sure this is a "normal" cell. if (c % material(1) == NONE) call fatal_error("Cell " & @@ -1433,10 +1372,10 @@ contains boundary_exists = .false. ! get pointer to list of xml - call get_node_list(doc, "surface", node_surf_list) + call get_node_list(root, "surface", node_surf_list) ! Get number of tags - n_surfaces = get_list_size(node_surf_list) + n_surfaces = size(node_surf_list) ! Check for no surfaces if (n_surfaces == 0) then @@ -1455,7 +1394,7 @@ contains do i = 1, n_surfaces ! Get pointer to i-th surface node - call get_list_item(node_surf_list, i, node_surf) + node_surf = node_surf_list(i) ! Copy and interpret surface type word = '' @@ -1526,7 +1465,7 @@ contains ! have been specified for the given type of surface. Then copy ! surface coordinates. - n = get_arraysize_double(node_surf, "coeffs") + n = node_word_count(node_surf, "coeffs") if (n < coeffs_reqd) then call fatal_error("Not enough coefficients specified for surface: " & // trim(to_str(s%id))) @@ -1719,12 +1658,12 @@ contains ! READ LATTICES FROM GEOMETRY.XML ! Get pointer to list of XML - call get_node_list(doc, "lattice", node_rlat_list) - call get_node_list(doc, "hex_lattice", node_hlat_list) + call get_node_list(root, "lattice", node_rlat_list) + call get_node_list(root, "hex_lattice", node_hlat_list) ! Allocate lattices array - n_rlats = get_list_size(node_rlat_list) - n_hlats = get_list_size(node_hlat_list) + n_rlats = size(node_rlat_list) + n_hlats = size(node_hlat_list) n_lattices = n_rlats + n_hlats allocate(lattices(n_lattices)) @@ -1735,7 +1674,7 @@ contains type is (RectLattice) ! Get pointer to i-th lattice - call get_list_item(node_rlat_list, i, node_lat) + node_lat = node_rlat_list(i) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1756,7 +1695,7 @@ contains end if ! Read number of lattice cells in each dimension - n = get_arraysize_integer(node_lat, "dimension") + n = node_word_count(node_lat, "dimension") if (n == 2) then call get_node_array(node_lat, "dimension", lat % n_cells(1:2)) lat % n_cells(3) = 1 @@ -1769,7 +1708,7 @@ contains end if ! Read lattice lower-left location - if (get_arraysize_double(node_lat, "lower_left") /= n) then + if (node_word_count(node_lat, "lower_left") /= n) then call fatal_error("Number of entries on must be the same & &as the number of entries on .") end if @@ -1784,12 +1723,12 @@ contains &in a future release. Use 'pitch' instead. The utility openmc/& &src/utils/update_inputs.py can be used to automatically update & &geometry.xml files.") - if (get_arraysize_double(node_lat, "width") /= n) then + if (node_word_count(node_lat, "width") /= n) then call fatal_error("Number of entries on must be the same as & &the number of entries on .") end if - else if (get_arraysize_double(node_lat, "pitch") /= n) then + else if (node_word_count(node_lat, "pitch") /= n) then call fatal_error("Number of entries on must be the same as & &the number of entries on .") end if @@ -1816,7 +1755,7 @@ contains allocate(lat % universes(n_x, n_y, n_z)) ! Check that number of universes matches size - n = get_arraysize_integer(node_lat, "universes") + n = node_word_count(node_lat, "universes") if (n /= n_x*n_y*n_z) then call fatal_error("Number of universes on does not match & &size of lattice " // trim(to_str(lat % id)) // ".") @@ -1863,7 +1802,7 @@ contains type is (HexLattice) ! Get pointer to i-th lattice - call get_list_item(node_hlat_list, i, node_lat) + node_lat = node_hlat_list(i) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1894,7 +1833,7 @@ contains end if ! Read lattice lower-left location - n = get_arraysize_double(node_lat, "center") + n = node_word_count(node_lat, "center") if (lat % is_3d .and. n /= 3) then call fatal_error("A hexagonal lattice with must have & &
specified by 3 numbers.") @@ -1907,7 +1846,7 @@ contains call get_node_array(node_lat, "center", lat % center) ! Read lattice pitches - n = get_arraysize_double(node_lat, "pitch") + n = node_word_count(node_lat, "pitch") if (lat % is_3d .and. n /= 2) then call fatal_error("A hexagonal lattice with must have & &specified by 2 numbers.") @@ -1925,7 +1864,7 @@ contains allocate(lat % universes(2*n_rings - 1, 2*n_rings - 1, n_z)) ! Check that number of universes matches size - n = get_arraysize_integer(node_lat, "universes") + n = node_word_count(node_lat, "universes") if (n /= (3*n_rings**2 - 3*n_rings + 1)*n_z) then call fatal_error("Number of universes on does not match & &size of lattice " // trim(to_str(lat % id)) // ".") @@ -2036,7 +1975,7 @@ contains end do HEX_LATTICES ! Close geometry XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_geometry_xml @@ -2055,7 +1994,8 @@ contains logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_LINE_LEN) :: filename - type(Node), pointer :: doc => null() + type(XMLDocument) :: doc + type(XMLNode) :: root ! Display output message call write_message("Reading materials XML file...", 5) @@ -2069,12 +2009,13 @@ contains end if ! Parse materials.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! Find cross_sections.xml file -- the first place to look is the ! materials.xml file. If no file is found there, then we check the ! OPENMC_CROSS_SECTIONS environment variable - if (.not. check_for_node(doc, "cross_sections")) then + if (.not. check_for_node(root, "cross_sections")) then ! No cross_sections.xml file specified in settings.xml, check ! environment variable if (run_CE) then @@ -2113,25 +2054,25 @@ contains end if end if else - call get_node_value(doc, "cross_sections", path_cross_sections) + call get_node_value(root, "cross_sections", path_cross_sections) end if ! Find the windowed multipole library if (run_mode /= MODE_PLOTTING) then - if (.not. check_for_node(doc, "multipole_library")) then + if (.not. check_for_node(root, "multipole_library")) then ! No library location specified in materials.xml, check ! environment variable call get_environment_variable("OPENMC_MULTIPOLE_LIBRARY", env_variable) path_multipole = trim(env_variable) else - call get_node_value(doc, "multipole_library", path_multipole) + call get_node_value(root, "multipole_library", path_multipole) end if if (.not. ends_with(path_multipole, "/")) & path_multipole = trim(path_multipole) // "/" end if ! Close materials XML file - call close_xmldoc(doc) + call doc % clear() ! Now that the cross_sections.xml or mgxs.h5 has been located, read it in if (run_CE) then @@ -2204,16 +2145,17 @@ contains type(VectorInt) :: list_iso_lab ! temporary list of isotropic lab scatterers type(VectorReal) :: densities ! temporary list of nuclide densities type(Material), pointer :: mat => null() - type(Node), pointer :: doc => null() - type(Node), pointer :: node_mat => null() - type(Node), pointer :: node_dens => null() - type(Node), pointer :: node_nuc => null() - type(Node), pointer :: node_sab => null() - type(NodeList), pointer :: node_mat_list => null() - type(NodeList), pointer :: node_nuc_list => null() - type(NodeList), pointer :: node_ele_list => null() - type(NodeList), pointer :: node_macro_list => null() - type(NodeList), pointer :: node_sab_list => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_mat + type(XMLNode) :: node_dens + type(XMLNode) :: node_nuc + type(XMLNode) :: node_sab + type(XMLNode), allocatable :: node_mat_list(:) + type(XMLNode), allocatable :: node_nuc_list(:) + type(XMLNode), allocatable :: node_ele_list(:) + type(XMLNode), allocatable :: node_macro_list(:) + type(XMLNode), allocatable :: node_sab_list(:) ! Check is materials.xml exists filename = trim(path_input) // "materials.xml" @@ -2224,13 +2166,14 @@ contains end if ! Parse materials.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! Get pointer to list of XML - call get_node_list(doc, "material", node_mat_list) + call get_node_list(root, "material", node_mat_list) ! Allocate cells array - n_materials = get_list_size(node_mat_list) + n_materials = size(node_mat_list) allocate(materials(n_materials)) allocate(material_temps(n_materials)) @@ -2242,7 +2185,7 @@ contains mat => materials(i) ! Get pointer to i-th material node - call get_list_item(node_mat_list, i, node_mat) + node_mat = node_mat_list(i) ! Copy material id if (check_for_node(node_mat, "id")) then @@ -2278,7 +2221,7 @@ contains ! Get pointer to density element if (check_for_node(node_mat, "density")) then - call get_node_ptr(node_mat, "density", node_dens) + node_dens = node_mat % child("density") else call fatal_error("Must specify density element in material " & // trim(to_str(mat % id))) @@ -2333,7 +2276,7 @@ contains ! Issue error if elements are provided call get_node_list(node_mat, "element", node_ele_list) - if (get_list_size(node_ele_list) > 0) then + if (size(node_ele_list) > 0) then call fatal_error("Unable to add an element to material " & // trim(to_str(mat % id)) // " since the element option has & &been removed from the xml input. Elements can only be added via & @@ -2357,15 +2300,15 @@ contains ! as nuclides internally. ! Get pointer list of XML call get_node_list(node_mat, "macroscopic", node_macro_list) - if (run_CE .and. (get_list_size(node_macro_list) > 0)) then + if (run_CE .and. (size(node_macro_list) > 0)) then call fatal_error("Macroscopic can not be used in continuous-energy& & mode!") - else if (get_list_size(node_macro_list) > 1) then + else if (size(node_macro_list) > 1) then call fatal_error("Only one macroscopic object permitted per material, " & // trim(to_str(mat % id))) - else if (get_list_size(node_macro_list) == 1) then + else if (size(node_macro_list) == 1) then - call get_list_item(node_macro_list, 1, node_nuc) + node_nuc = node_macro_list(1) ! Check for empty name on nuclide if (.not. check_for_node(node_nuc, "name")) then @@ -2393,9 +2336,9 @@ contains call get_node_list(node_mat, "nuclide", node_nuc_list) ! Create list of nuclides based on those specified - INDIVIDUAL_NUCLIDES: do j = 1, get_list_size(node_nuc_list) + INDIVIDUAL_NUCLIDES: do j = 1, size(node_nuc_list) ! Combine nuclide identifier and cross section and copy into names - call get_list_item(node_nuc_list, j, node_nuc) + node_nuc = node_nuc_list(j) ! Check for empty name on nuclide if (.not. check_for_node(node_nuc, "name")) then @@ -2530,7 +2473,7 @@ contains ! Get pointer list to XML call get_node_list(node_mat, "sab", node_sab_list) - n_sab = get_list_size(node_sab_list) + n_sab = size(node_sab_list) if (n_sab > 0) then ! Set number of S(a,b) tables mat % n_sab = n_sab @@ -2544,7 +2487,7 @@ contains do j = 1, n_sab ! Get pointer to S(a,b) table - call get_list_item(node_sab_list, j, node_sab) + node_sab = node_sab_list(j) ! Determine name of S(a,b) table if (.not. check_for_node(node_sab, "name")) then @@ -2594,7 +2537,7 @@ contains n_sab_tables = index_sab ! Close materials XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_materials_xml @@ -2642,17 +2585,18 @@ contains type(TallyObject), pointer :: t type(RegularMesh), pointer :: m type(TallyFilterContainer), allocatable :: filters(:) ! temporary filters - type(Node), pointer :: doc => null() - type(Node), pointer :: node_mesh => null() - type(Node), pointer :: node_tal => null() - type(Node), pointer :: node_filt => null() - type(Node), pointer :: node_trigger => null() - type(Node), pointer :: node_deriv => null() - type(NodeList), pointer :: node_mesh_list => null() - type(NodeList), pointer :: node_tal_list => null() - type(NodeList), pointer :: node_filt_list => null() - type(NodeList), pointer :: node_trigger_list => null() - type(NodeList), pointer :: node_deriv_list => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_mesh + type(XMLNode) :: node_tal + type(XMLNode) :: node_filt + type(XMLNode) :: node_trigger + type(XMLNode) :: node_deriv + type(XMLNode), allocatable :: node_mesh_list(:) + type(XMLNode), allocatable :: node_tal_list(:) + type(XMLNode), allocatable :: node_filt_list(:) + type(XMLNode), allocatable :: node_trigger_list(:) + type(XMLNode), allocatable :: node_deriv_list(:) type(ElemKeyValueCI), pointer :: scores type(ElemKeyValueCI), pointer :: next @@ -2674,19 +2618,20 @@ contains call write_message("Reading tallies XML file...", 5) ! Parse tallies.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE ! Get pointer list to XML - call get_node_list(doc, "mesh", node_mesh_list) + call get_node_list(root, "mesh", node_mesh_list) ! Get pointer list to XML - call get_node_list(doc, "tally", node_tal_list) + call get_node_list(root, "tally", node_tal_list) ! Check for user meshes - n_user_meshes = get_list_size(node_mesh_list) + n_user_meshes = size(node_mesh_list) if (cmfd_run) then n_meshes = n_user_meshes + n_cmfd_meshes else @@ -2697,7 +2642,7 @@ contains if (n_meshes > 0) allocate(meshes(n_meshes)) ! Check for user tallies - n_user_tallies = get_list_size(node_tal_list) + n_user_tallies = size(node_tal_list) if (n_user_tallies == 0) then if (master) call warning("No tallies present in tallies.xml file!") end if @@ -2708,11 +2653,8 @@ contains end if ! Check for setting - if (check_for_node(doc, "assume_separate")) then - call get_node_value(doc, "assume_separate", temp_str) - temp_str = to_lower(temp_str) - if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - assume_separate = .true. + if (check_for_node(root, "assume_separate")) then + call get_node_value(root, "assume_separate", assume_separate) end if ! ========================================================================== @@ -2722,7 +2664,7 @@ contains m => meshes(i) ! Get pointer to mesh node - call get_list_item(node_mesh_list, i, node_mesh) + node_mesh = node_mesh_list(i) ! Copy mesh id if (check_for_node(node_mesh, "id")) then @@ -2753,7 +2695,7 @@ contains end select ! Determine number of dimensions for mesh - n = get_arraysize_integer(node_mesh, "dimension") + n = node_word_count(node_mesh, "dimension") if (n /= 1 .and. n /= 2 .and. n /= 3) then call fatal_error("Mesh must be one, two, or three dimensions.") end if @@ -2776,7 +2718,7 @@ contains m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then + if (m % n_dimension /= node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the same & &as the number of entries on .") end if @@ -2798,8 +2740,8 @@ contains if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions - if (get_arraysize_double(node_mesh, "width") /= & - get_arraysize_double(node_mesh, "lower_left")) then + if (node_word_count(node_mesh, "width") /= & + node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the same as & &the number of entries on .") end if @@ -2816,8 +2758,8 @@ contains elseif (check_for_node(node_mesh, "upper_right")) then ! Check to ensure width has same dimensions - if (get_arraysize_double(node_mesh, "upper_right") /= & - get_arraysize_double(node_mesh, "lower_left")) then + if (node_word_count(node_mesh, "upper_right") /= & + node_word_count(node_mesh, "lower_left")) then call fatal_error("Number of entries on must be the & &same as the number of entries on .") end if @@ -2849,21 +2791,21 @@ contains ! Get pointer list to XML nodes and allocate global array. ! The array is threadprivate so it must be allocated in parallel. - call get_node_list(doc, "derivative", node_deriv_list) + call get_node_list(root, "derivative", node_deriv_list) !$omp parallel - allocate(tally_derivs(get_list_size(node_deriv_list))) + allocate(tally_derivs(size(node_deriv_list))) !$omp end parallel ! Make sure this is not an MG run. - if (.not. run_CE .and. get_list_size(node_deriv_list) > 0) then + if (.not. run_CE .and. size(node_deriv_list) > 0) then call fatal_error("Differential tallies not supported in multi-group mode") end if ! Read derivative attributes. - do i = 1, get_list_size(node_deriv_list) + do i = 1, size(node_deriv_list) associate(deriv => tally_derivs(i)) ! Get pointer to derivative node. - call get_list_item(node_deriv_list, i, node_deriv) + node_deriv = node_deriv_list(i) ! Copy the derivative id. if (check_for_node(node_deriv, "id")) then @@ -2940,7 +2882,7 @@ contains t => tallies(i) ! Get pointer to tally xml node - call get_list_item(node_tal_list, i, node_tal) + node_tal = node_tal_list(i) ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -2975,14 +2917,14 @@ contains ! Get pointer list to XML and get number of filters call get_node_list(node_tal, "filter", node_filt_list) - n_filters = get_list_size(node_filt_list) + n_filters = size(node_filt_list) ! Allocate filters array allocate(t % filters(n_filters)) READ_FILTERS: do j = 1, n_filters ! Get pointer to filter xml node - call get_list_item(node_filt_list, j, node_filt) + node_filt = node_filt_list(j) ! Convert filter type to lower case temp_str = '' @@ -2997,14 +2939,14 @@ contains call fatal_error("Bins not set in filter on tally " & // trim(to_str(t % id))) end if - n_words = get_arraysize_double(node_filt, "bins") + n_words = node_word_count(node_filt, "bins") case ("mesh", "universe", "material", "cell", "distribcell", & "cellborn", "surface", "delayedgroup") if (.not. check_for_node(node_filt, "bins")) then call fatal_error("Bins not set in filter on tally " & // trim(to_str(t % id))) end if - n_words = get_arraysize_integer(node_filt, "bins") + n_words = node_word_count(node_filt, "bins") end select ! Determine type of filter @@ -3320,7 +3262,7 @@ contains call fatal_error("Energy grid not specified for EnergyFunction & &filter on tally " // trim(to_str(t % id))) end if - n_words = get_arraysize_double(node_filt, "energy") + n_words = node_word_count(node_filt, "energy") allocate(filt % energy(n_words)) call get_node_array(node_filt, "energy", filt % energy) @@ -3329,7 +3271,7 @@ contains call fatal_error("y values not specified for EnergyFunction & &filter on tally " // trim(to_str(t % id))) end if - n_words = get_arraysize_double(node_filt, "y") + n_words = node_word_count(node_filt, "y") allocate(filt % y(n_words)) call get_node_array(node_filt, "y", filt % y) end select @@ -3359,7 +3301,7 @@ contains if (check_for_node(node_tal, "nuclides")) then ! Allocate a temporary string array for nuclides and copy values over - allocate(sarray(get_arraysize_string(node_tal, "nuclides"))) + allocate(sarray(node_word_count(node_tal, "nuclides"))) call get_node_array(node_tal, "nuclides", sarray) if (trim(sarray(1)) == 'all') then @@ -3378,7 +3320,7 @@ contains t % all_nuclides = .true. else ! Any other case, e.g. U-235 Pu-239 - n_words = get_arraysize_string(node_tal, "nuclides") + n_words = node_word_count(node_tal, "nuclides") allocate(t % nuclide_bins(n_words)) do j = 1, n_words @@ -3434,7 +3376,7 @@ contains ! READ DATA FOR SCORES if (check_for_node(node_tal, "scores")) then - n_words = get_arraysize_string(node_tal, "scores") + n_words = node_word_count(node_tal, "scores") allocate(sarray(n_words)) call get_node_array(node_tal, "scores", sarray) @@ -3959,18 +3901,18 @@ contains call get_node_list(node_tal, "trigger", node_trigger_list) ! Initialize the number of triggers - n_user_trig = get_list_size(node_trigger_list) + n_user_trig = size(node_trigger_list) ! Count the number of triggers needed for all scores including "all" t % n_triggers = 0 COUNT_TRIGGERS: do user_trig_ind = 1, n_user_trig ! Get pointer to trigger node - call get_list_item(node_trigger_list, user_trig_ind, node_trigger) + node_trigger = node_trigger_list(user_trig_ind) ! Get scores for this trigger if (check_for_node(node_trigger, "scores")) then - n_words = get_arraysize_string(node_trigger, "scores") + n_words = node_word_count(node_trigger, "scores") allocate(sarray(n_words)) call get_node_array(node_trigger, "scores", sarray) else @@ -4015,7 +3957,7 @@ contains TRIGGER_LOOP: do user_trig_ind = 1, n_user_trig ! Get pointer to trigger node - call get_list_item(node_trigger_list, user_trig_ind, node_trigger) + node_trigger = node_trigger_list(user_trig_ind) ! Get the trigger type - "variance", "std_dev" or "rel_err" if (check_for_node(node_trigger, "type")) then @@ -4036,7 +3978,7 @@ contains ! Get list scores for this trigger if (check_for_node(node_trigger, "scores")) then - n_words = get_arraysize_string(node_trigger, "scores") + n_words = node_word_count(node_trigger, "scores") allocate(sarray(n_words)) call get_node_array(node_trigger, "scores", sarray) else @@ -4175,7 +4117,7 @@ contains end do READ_TALLIES ! Close XML document - call close_xmldoc(doc) + call doc % clear() end subroutine read_tallies_xml @@ -4195,15 +4137,16 @@ contains character(MAX_LINE_LEN) :: temp_str character(MAX_WORD_LEN) :: meshtype type(ObjectPlot), pointer :: pl => null() - type(Node), pointer :: doc => null() - type(Node), pointer :: node_plot => null() - type(Node), pointer :: node_col => null() - type(Node), pointer :: node_mask => null() - type(Node), pointer :: node_meshlines => null() - type(NodeList), pointer :: node_plot_list => null() - type(NodeList), pointer :: node_col_list => null() - type(NodeList), pointer :: node_mask_list => null() - type(NodeList), pointer :: node_meshline_list => null() + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_plot + type(XMLNode) :: node_col + type(XMLNode) :: node_mask + type(XMLNode) :: node_meshlines + type(XMLNode), allocatable :: node_plot_list(:) + type(XMLNode), allocatable :: node_col_list(:) + type(XMLNode), allocatable :: node_mask_list(:) + type(XMLNode), allocatable :: node_meshline_list(:) ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -4217,20 +4160,21 @@ contains call write_message("Reading plot XML file...", 5) ! Parse plots.xml file - call open_xmldoc(doc, filename) + call doc % load_file(filename) + root = doc % document_element() ! Get list pointer to XML - call get_node_list(doc, "plot", node_plot_list) + call get_node_list(root, "plot", node_plot_list) ! Allocate plots array - n_plots = get_list_size(node_plot_list) + n_plots = size(node_plot_list) allocate(plots(n_plots)) READ_PLOTS: do i = 1, n_plots pl => plots(i) ! Get pointer to plot XML node - call get_list_item(node_plot_list, i, node_plot) + node_plot = node_plot_list(i) ! Copy data into plots if (check_for_node(node_plot, "id")) then @@ -4273,14 +4217,14 @@ contains ! Copy plot pixel size if (pl % type == PLOT_TYPE_SLICE) then - if (get_arraysize_integer(node_plot, "pixels") == 2) then + if (node_word_count(node_plot, "pixels") == 2) then call get_node_array(node_plot, "pixels", pl % pixels(1:2)) else call fatal_error(" must be length 2 in slice plot " & // trim(to_str(pl % id))) end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (get_arraysize_integer(node_plot, "pixels") == 3) then + if (node_word_count(node_plot, "pixels") == 3) then call get_node_array(node_plot, "pixels", pl % pixels(1:3)) else call fatal_error(" must be length 3 in voxel plot " & @@ -4294,7 +4238,7 @@ contains if (master) call warning("Background color ignored in voxel plot " & // trim(to_str(pl % id))) end if - if (get_arraysize_integer(node_plot, "background") == 3) then + if (node_word_count(node_plot, "background") == 3) then call get_node_array(node_plot, "background", pl % not_found % rgb) else call fatal_error("Bad background RGB in plot " & @@ -4324,7 +4268,7 @@ contains end if ! Copy plotting origin - if (get_arraysize_double(node_plot, "origin") == 3) then + if (node_word_count(node_plot, "origin") == 3) then call get_node_array(node_plot, "origin", pl % origin) else call fatal_error("Origin must be length 3 in plot " & @@ -4333,14 +4277,14 @@ contains ! Copy plotting width if (pl % type == PLOT_TYPE_SLICE) then - if (get_arraysize_double(node_plot, "width") == 2) then + if (node_word_count(node_plot, "width") == 2) then call get_node_array(node_plot, "width", pl % width(1:2)) else call fatal_error(" must be length 2 in slice plot " & // trim(to_str(pl % id))) end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (get_arraysize_double(node_plot, "width") == 3) then + if (node_word_count(node_plot, "width") == 3) then call get_node_array(node_plot, "width", pl % width(1:3)) else call fatal_error(" must be length 3 in voxel plot " & @@ -4393,7 +4337,7 @@ contains ! Get the number of nodes and get a list of them call get_node_list(node_plot, "col_spec", node_col_list) - n_cols = get_list_size(node_col_list) + n_cols = size(node_col_list) ! Copy user specified colors if (n_cols /= 0) then @@ -4406,10 +4350,10 @@ contains do j = 1, n_cols ! Get pointer to color spec XML node - call get_list_item(node_col_list, j, node_col) + node_col = node_col_list(j) ! Check and make sure 3 values are specified for RGB - if (get_arraysize_double(node_col, "rgb") /= 3) then + if (node_word_count(node_col, "rgb") /= 3) then call fatal_error("Bad RGB in plot " & // trim(to_str(pl % id))) end if @@ -4450,7 +4394,7 @@ contains ! Deal with meshlines call get_node_list(node_plot, "meshlines", node_meshline_list) - n_meshlines = get_list_size(node_meshline_list) + n_meshlines = size(node_meshline_list) if (n_meshlines /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then @@ -4464,7 +4408,7 @@ contains case (1) ! Get pointer to meshlines - call get_list_item(node_meshline_list, 1, node_meshlines) + node_meshlines = node_meshline_list(1) ! Check mesh type if (check_for_node(node_meshlines, "meshtype")) then @@ -4487,7 +4431,7 @@ contains if (check_for_node(node_meshlines, "color")) then ! Check and make sure 3 values are specified for RGB - if (get_arraysize_double(node_meshlines, "color") /= 3) then + if (node_word_count(node_meshlines, "color") /= 3) then call fatal_error("Bad RGB for meshlines color in plot " & // trim(to_str(pl % id))) end if @@ -4576,7 +4520,7 @@ contains ! Deal with masks call get_node_list(node_plot, "mask", node_mask_list) - n_masks = get_list_size(node_mask_list) + n_masks = size(node_mask_list) if (n_masks /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then @@ -4591,11 +4535,11 @@ contains case (1) ! Get pointer to mask - call get_list_item(node_mask_list, 1, node_mask) + node_mask = node_mask_list(1) ! Determine how many components there are and allocate n_comp = 0 - n_comp = get_arraysize_integer(node_mask, "components") + n_comp = node_word_count(node_mask, "components") if (n_comp == 0) then call fatal_error("Missing in mask of plot " & // trim(to_str(pl % id))) @@ -4655,7 +4599,7 @@ contains end do READ_PLOTS ! Close plots XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_plots_xml @@ -4674,9 +4618,10 @@ contains character(MAX_WORD_LEN) :: directory ! directory with cross sections character(MAX_WORD_LEN) :: words(MAX_WORDS) character(10000) :: temp_str - type(Node), pointer :: doc - type(Node), pointer :: node_library - type(NodeList), pointer :: node_library_list + type(XMLDocument) :: doc + type(XMLNode) :: root + type(XMLNode) :: node_library + type(XMLNode), allocatable :: node_library_list(:) ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -4689,11 +4634,12 @@ contains call write_message("Reading cross sections XML file...", 5) ! Parse cross_sections.xml file - call open_xmldoc(doc, path_cross_sections) + call doc % load_file(path_cross_sections) + root = doc % document_element() - if (check_for_node(doc, "directory")) then + if (check_for_node(root, "directory")) then ! Copy directory information if present - call get_node_value(doc, "directory", directory) + call get_node_value(root, "directory", directory) else ! If no directory is listed in cross_sections.xml, by default select the ! directory in which the cross_sections.xml file resides @@ -4702,8 +4648,8 @@ contains end if ! Get node list of all - call get_node_list(doc, "library", node_library_list) - n_libraries = get_list_size(node_library_list) + call get_node_list(root, "library", node_library_list) + n_libraries = size(node_library_list) ! Allocate xs_listings array if (n_libraries == 0) then @@ -4715,7 +4661,7 @@ contains do i = 1, n_libraries ! Get pointer to ace table XML node - call get_list_item(node_library_list, i, node_library) + node_library = node_library_list(i) ! Get list of materials if (check_for_node(node_library, "materials")) then @@ -4763,7 +4709,7 @@ contains end do ! Close cross sections XML file - call close_xmldoc(doc) + call doc % clear() end subroutine read_ce_cross_sections_xml @@ -5214,7 +5160,7 @@ contains == energy_max_neutron) then call write_message("Maximum neutron transport energy: " // & trim(to_str(energy_max_neutron)) // " eV for " // & - trim(adjustl(nuclides(i) % name)), 6) + trim(adjustl(nuclides(i) % name)), 7) exit end if end do diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index 3a0d7c4b4..f687b6e5b 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -43,10 +43,10 @@ contains ! Could not find MGXS Library file call fatal_error("Cross sections HDF5 file '" & - &// trim(path_cross_sections) // "' does not exist!") + // trim(path_cross_sections) // "' does not exist!") end if - call write_message("Loading Cross Section Data...", 5) + call write_message("Loading cross section data...", 5) ! Get temperatures call get_temperatures(cells, materials, material_dict, nuclide_dict, & @@ -75,7 +75,7 @@ contains i_xsdata = xsdata_dict % get_key(to_lower(name)) i_nuclide = mat % nuclide(j) - call write_message("Loading " // trim(name) // " Data...", 5) + call write_message("Loading " // trim(name) // " data...", 6) ! Check to make sure cross section set exists in the library if (object_exists(file_id, trim(name))) then diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index bf6a9b489..571dd9023 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -23,7 +23,6 @@ module nuclide_header use stl_vector, only: VectorInt, VectorReal use string use urr_header, only: UrrData - use xml_interface implicit none diff --git a/src/output.F90 b/src/output.F90 index 4c8be0f29..600d9a0f8 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -91,6 +91,7 @@ contains write(UNIT=OUTPUT_UNIT, FMT='(4X,"OpenMP Threads | ",A)') & trim(to_str(omp_get_max_threads())) #endif + write(UNIT=OUTPUT_UNIT, FMT=*) end subroutine title @@ -112,27 +113,19 @@ contains !=============================================================================== ! HEADER displays a header block according to a specified level. If no level is -! specified, it is assumed to be a minor header block (H3). +! specified, it is assumed to be a minor header block. !=============================================================================== - subroutine header(msg, unit, level) + subroutine header(msg, level, unit) character(*), intent(in) :: msg ! header message + integer, intent(in) :: level integer, intent(in), optional :: unit ! unit to write to - integer, intent(in), optional :: level ! specified header level integer :: n ! number of = signs on left integer :: m ! number of = signs on right integer :: unit_ ! unit to write to - integer :: header_level ! actual header level character(MAX_LINE_LEN) :: line - ! set default level - if (present(level)) then - header_level = level - else - header_level = 3 - end if - ! set default unit if (present(unit)) then unit_ = unit @@ -149,17 +142,10 @@ contains line = to_upper(msg) ! print header based on level - select case (header_level) - case (1) - write(UNIT=unit_, FMT='(/3(1X,A/))') repeat('=', 75), & - repeat('=', n) // '> ' // trim(line) // ' <' // & - repeat('=', m), repeat('=', 75) - case (2) - write(UNIT=unit_, FMT='(/2(1X,A/))') trim(line), repeat('-', 75) - case (3) + if (verbosity >= level) then write(UNIT=unit_, FMT='(/1X,A/)') repeat('=', n) // '> ' // & trim(line) // ' <' // repeat('=', m) - end select + end if end subroutine header @@ -460,7 +446,7 @@ contains type(ObjectPlot), pointer :: pl ! Display header for plotting - call header("PLOTTING SUMMARY") + call header("PLOTTING SUMMARY", 5) do i = 1, n_plots pl => plots(i) @@ -535,7 +521,7 @@ contains character(15) :: string ! display header block - call header("Timing Statistics") + call header("Timing Statistics", 6) ! display time elapsed for various sections write(ou,100) "Total time for initialization", time_initialize % elapsed @@ -608,7 +594,7 @@ contains real(8) :: t_value ! t-value for confidence intervals ! display header block for results - call header("Results") + call header("Results", 4) if (confidence_intervals) then ! Calculate t-value for confidence intervals @@ -666,7 +652,7 @@ contains integer :: num_sparse = 0 ! display header block for geometry debugging section - call header("Cell Overlap Check Summary") + call header("Cell Overlap Check Summary", 1) write(ou,100) 'Cell ID','No. Overlap Checks' @@ -775,11 +761,10 @@ contains ! Write header block if (t % name == "") then - call header("TALLY " // trim(to_str(t % id)), unit=unit_tally, & - level=3) + call header("TALLY " // trim(to_str(t % id)), 1, unit=unit_tally) else call header("TALLY " // trim(to_str(t % id)) // ": " & - // trim(t % name), unit=unit_tally, level=3) + // trim(t % name), 1, unit=unit_tally) endif ! Write derivative information. diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 430ee4d44..b4ce841fe 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -74,7 +74,7 @@ contains ! Write meessage call write_message("Loading particle restart file " & - &// trim(path_particle_restart) // "...", 1) + // trim(path_particle_restart) // "...", 5) ! Open file file_id = file_open(path_particle_restart, 'r') diff --git a/src/pugixml/pugiconfig.hpp b/src/pugixml/pugiconfig.hpp new file mode 100644 index 000000000..2d1e6aa8b --- /dev/null +++ b/src/pugixml/pugiconfig.hpp @@ -0,0 +1,74 @@ +/** + * pugixml parser - version 1.8 + * -------------------------------------------------------- + * Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef HEADER_PUGICONFIG_HPP +#define HEADER_PUGICONFIG_HPP + +// Uncomment this to enable wchar_t mode +// #define PUGIXML_WCHAR_MODE + +// Uncomment this to enable compact mode +// #define PUGIXML_COMPACT + +// Uncomment this to disable XPath +// #define PUGIXML_NO_XPATH + +// Uncomment this to disable STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable exceptions +// #define PUGIXML_NO_EXCEPTIONS + +// Set this to control attributes for public classes/functions, i.e.: +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL +// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall +// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead + +// Tune these constants to adjust memory-related behavior +// #define PUGIXML_MEMORY_PAGE_SIZE 32768 +// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 +// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 + +// Uncomment this to switch to header-only version +// #define PUGIXML_HEADER_ONLY + +// Uncomment this to enable long long support +// #define PUGIXML_HAS_LONG_LONG + +#endif + +/** + * Copyright (c) 2006-2016 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/pugixml/pugixml.cpp b/src/pugixml/pugixml.cpp new file mode 100644 index 000000000..cac51a534 --- /dev/null +++ b/src/pugixml/pugixml.cpp @@ -0,0 +1,12622 @@ +/** + * pugixml parser - version 1.8 + * -------------------------------------------------------- + * Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef SOURCE_PUGIXML_CPP +#define SOURCE_PUGIXML_CPP + +#include "pugixml.hpp" + +#include +#include +#include +#include +#include + +#ifdef PUGIXML_WCHAR_MODE +# include +#endif + +#ifndef PUGIXML_NO_XPATH +# include +# include +# ifdef PUGIXML_NO_EXCEPTIONS +# include +# endif +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4996) // this function or variable may be unsafe +# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged +#endif + +#ifdef __INTEL_COMPILER +# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 279) // controlling expression is constant +# pragma warning(disable: 1478 1786) // function was declared "deprecated" +# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type +#endif + +#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) +# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away +#endif + +#ifdef __BORLANDC__ +# pragma option push +# pragma warn -8008 // condition is always false +# pragma warn -8066 // unreachable code +#endif + +#ifdef __SNC__ +// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug +# pragma diag_suppress=178 // function was declared but never referenced +# pragma diag_suppress=237 // controlling expression is constant +#endif + +// Inlining controls +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGI__NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# define PUGI__NO_INLINE __attribute__((noinline)) +#else +# define PUGI__NO_INLINE +#endif + +// Branch weight controls +#if defined(__GNUC__) +# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) +#else +# define PUGI__UNLIKELY(cond) (cond) +#endif + +// Simple static assertion +#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } + +// Digital Mars C++ bug workaround for passing char loaded from memory via stack +#ifdef __DMC__ +# define PUGI__DMC_VOLATILE volatile +#else +# define PUGI__DMC_VOLATILE +#endif + +// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) +#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) +using std::memcpy; +using std::memmove; +using std::memset; +#endif + +// Some MinGW versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions in strict ANSI mode +#if defined(PUGIXML_HAS_LONG_LONG) && defined(__MINGW32__) && defined(__STRICT_ANSI__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) +# define LLONG_MAX 9223372036854775807LL +# define LLONG_MIN (-LLONG_MAX-1) +# define ULLONG_MAX (2ULL*LLONG_MAX+1) +#endif + +// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features +#if defined(_MSC_VER) && !defined(__S3E__) +# define PUGI__MSVC_CRT_VERSION _MSC_VER +#endif + +#ifdef PUGIXML_HEADER_ONLY +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# define PUGI__FN inline +# define PUGI__FN_NO_INLINE inline +#else +# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# else +# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { +# define PUGI__NS_END } } } +# endif +# define PUGI__FN +# define PUGI__FN_NO_INLINE PUGI__NO_INLINE +#endif + +// uintptr_t +#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) +namespace pugi +{ +# ifndef _UINTPTR_T_DEFINED + typedef size_t uintptr_t; +# endif + + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +} +#else +# include +#endif + +// Memory allocation +PUGI__NS_BEGIN + PUGI__FN void* default_allocate(size_t size) + { + return malloc(size); + } + + PUGI__FN void default_deallocate(void* ptr) + { + free(ptr); + } + + template + struct xml_memory_management_function_storage + { + static allocation_function allocate; + static deallocation_function deallocate; + }; + + // Global allocation functions are stored in class statics so that in header mode linker deduplicates them + // Without a template<> we'll get multiple definitions of the same static + template allocation_function xml_memory_management_function_storage::allocate = default_allocate; + template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; + + typedef xml_memory_management_function_storage xml_memory; +PUGI__NS_END + +// String utilities +PUGI__NS_BEGIN + // Get string length + PUGI__FN size_t strlength(const char_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + return strlen(s); + #endif + } + + // Compare two strings + PUGI__FN bool strequal(const char_t* src, const char_t* dst) + { + assert(src && dst); + + #ifdef PUGIXML_WCHAR_MODE + return wcscmp(src, dst) == 0; + #else + return strcmp(src, dst) == 0; + #endif + } + + // Compare lhs with [rhs_begin, rhs_end) + PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) + { + for (size_t i = 0; i < count; ++i) + if (lhs[i] != rhs[i]) + return false; + + return lhs[count] == 0; + } + + // Get length of wide string, even if CRT lacks wide character support + PUGI__FN size_t strlength_wide(const wchar_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + const wchar_t* end = s; + while (*end) end++; + return static_cast(end - s); + #endif + } +PUGI__NS_END + +// auto_ptr-like object for exception recovery +PUGI__NS_BEGIN + template struct auto_deleter + { + typedef void (*D)(T*); + + T* data; + D deleter; + + auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) + { + } + + ~auto_deleter() + { + if (data) deleter(data); + } + + T* release() + { + T* result = data; + data = 0; + return result; + } + }; +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + class compact_hash_table + { + public: + compact_hash_table(): _items(0), _capacity(0), _count(0) + { + } + + void clear() + { + if (_items) + { + xml_memory::deallocate(_items); + _items = 0; + _capacity = 0; + _count = 0; + } + } + + void** find(const void* key) + { + assert(key); + + if (_capacity == 0) return 0; + + size_t hashmod = _capacity - 1; + size_t bucket = hash(key) & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + item_t& probe_item = _items[bucket]; + + if (probe_item.key == key) + return &probe_item.value; + + if (probe_item.key == 0) + return 0; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); + return 0; + } + + void** insert(const void* key) + { + assert(key); + assert(_capacity != 0 && _count < _capacity - _capacity / 4); + + size_t hashmod = _capacity - 1; + size_t bucket = hash(key) & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + item_t& probe_item = _items[bucket]; + + if (probe_item.key == 0) + { + probe_item.key = key; + _count++; + return &probe_item.value; + } + + if (probe_item.key == key) + return &probe_item.value; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); + return 0; + } + + bool reserve() + { + if (_count + 16 >= _capacity - _capacity / 4) + return rehash(); + + return true; + } + + private: + struct item_t + { + const void* key; + void* value; + }; + + item_t* _items; + size_t _capacity; + + size_t _count; + + bool rehash(); + + static unsigned int hash(const void* key) + { + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + return h; + } + }; + + PUGI__FN_NO_INLINE bool compact_hash_table::rehash() + { + compact_hash_table rt; + rt._capacity = (_capacity == 0) ? 32 : _capacity * 2; + rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * rt._capacity)); + + if (!rt._items) + return false; + + memset(rt._items, 0, sizeof(item_t) * rt._capacity); + + for (size_t i = 0; i < _capacity; ++i) + if (_items[i].key) + *rt.insert(_items[i].key) = _items[i].value; + + if (_items) + xml_memory::deallocate(_items); + + _capacity = rt._capacity; + _items = rt._items; + + assert(_count == rt._count); + + return true; + } + +PUGI__NS_END +#endif + +PUGI__NS_BEGIN +#ifdef PUGIXML_COMPACT + static const uintptr_t xml_memory_block_alignment = 4; +#else + static const uintptr_t xml_memory_block_alignment = sizeof(void*); +#endif + + // extra metadata bits + static const uintptr_t xml_memory_page_contents_shared_mask = 64; + static const uintptr_t xml_memory_page_name_allocated_mask = 32; + static const uintptr_t xml_memory_page_value_allocated_mask = 16; + static const uintptr_t xml_memory_page_type_mask = 15; + + // combined masks for string uniqueness + static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; + static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; + +#ifdef PUGIXML_COMPACT + #define PUGI__GETHEADER_IMPL(object, page, flags) // unused + #define PUGI__GETPAGE_IMPL(header) (header).get_page() +#else + #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) + // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + #define PUGI__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) +#endif + + #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) + #define PUGI__NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) + + struct xml_allocator; + + struct xml_memory_page + { + static xml_memory_page* construct(void* memory) + { + xml_memory_page* result = static_cast(memory); + + result->allocator = 0; + result->prev = 0; + result->next = 0; + result->busy_size = 0; + result->freed_size = 0; + + #ifdef PUGIXML_COMPACT + result->compact_string_base = 0; + result->compact_shared_parent = 0; + result->compact_page_marker = 0; + #endif + + return result; + } + + xml_allocator* allocator; + + xml_memory_page* prev; + xml_memory_page* next; + + size_t busy_size; + size_t freed_size; + + #ifdef PUGIXML_COMPACT + char_t* compact_string_base; + void* compact_shared_parent; + uint32_t* compact_page_marker; + #endif + }; + + static const size_t xml_memory_page_size = + #ifdef PUGIXML_MEMORY_PAGE_SIZE + (PUGIXML_MEMORY_PAGE_SIZE) + #else + 32768 + #endif + - sizeof(xml_memory_page); + + struct xml_memory_string_header + { + uint16_t page_offset; // offset from page->data + uint16_t full_size; // 0 if string occupies whole page + }; + + struct xml_allocator + { + xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) + { + #ifdef PUGIXML_COMPACT + _hash = 0; + #endif + } + + xml_memory_page* allocate_page(size_t data_size) + { + size_t size = sizeof(xml_memory_page) + data_size; + + // allocate block with some alignment, leaving memory for worst-case padding + void* memory = xml_memory::allocate(size); + if (!memory) return 0; + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(memory); + assert(page); + + page->allocator = _root->allocator; + + return page; + } + + static void deallocate_page(xml_memory_page* page) + { + xml_memory::deallocate(page); + } + + void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); + + void* allocate_memory(size_t size, xml_memory_page*& out_page) + { + if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size)) + return allocate_memory_oob(size, out_page); + + void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; + + _busy_size += size; + + out_page = _root; + + return buf; + } + + #ifdef PUGIXML_COMPACT + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + void* result = allocate_memory(size + sizeof(uint32_t), out_page); + if (!result) return 0; + + // adjust for marker + ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); + + if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) + { + // insert new marker + uint32_t* marker = static_cast(result); + + *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(out_page)); + out_page->compact_page_marker = marker; + + // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block + // this will make sure deallocate_memory correctly tracks the size + out_page->freed_size += sizeof(uint32_t); + + return marker + 1; + } + else + { + // roll back uint32_t part + _busy_size -= sizeof(uint32_t); + + return result; + } + } + #else + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + return allocate_memory(size, out_page); + } + #endif + + void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) + { + if (page == _root) page->busy_size = _busy_size; + + assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); + (void)!ptr; + + page->freed_size += size; + assert(page->freed_size <= page->busy_size); + + if (page->freed_size == page->busy_size) + { + if (page->next == 0) + { + assert(_root == page); + + // top page freed, just reset sizes + page->busy_size = 0; + page->freed_size = 0; + + #ifdef PUGIXML_COMPACT + // reset compact state to maximize efficiency + page->compact_string_base = 0; + page->compact_shared_parent = 0; + page->compact_page_marker = 0; + #endif + + _busy_size = 0; + } + else + { + assert(_root != page); + assert(page->prev); + + // remove from the list + page->prev->next = page->next; + page->next->prev = page->prev; + + // deallocate + deallocate_page(page); + } + } + } + + char_t* allocate_string(size_t length) + { + static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; + + PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); + + // allocate memory for string and header block + size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); + + // round size up to block alignment boundary + size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); + + xml_memory_page* page; + xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); + + if (!header) return 0; + + // setup header + ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); + + assert(page_offset % xml_memory_block_alignment == 0); + assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); + header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); + + // full_size == 0 for large strings that occupy the whole page + assert(full_size % xml_memory_block_alignment == 0); + assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); + header->full_size = static_cast(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); + + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + // header is guaranteed a pointer-sized alignment, which should be enough for char_t + return static_cast(static_cast(header + 1)); + } + + void deallocate_string(char_t* string) + { + // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string + + // get header + xml_memory_string_header* header = static_cast(static_cast(string)) - 1; + assert(header); + + // deallocate + size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; + xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); + + // if full_size == 0 then this string occupies the whole page + size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; + + deallocate_memory(header, full_size, page); + } + + bool reserve() + { + #ifdef PUGIXML_COMPACT + return _hash->reserve(); + #else + return true; + #endif + } + + xml_memory_page* _root; + size_t _busy_size; + + #ifdef PUGIXML_COMPACT + compact_hash_table* _hash; + #endif + }; + + PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) + { + const size_t large_allocation_threshold = xml_memory_page_size / 4; + + xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); + out_page = page; + + if (!page) return 0; + + if (size <= large_allocation_threshold) + { + _root->busy_size = _busy_size; + + // insert page at the end of linked list + page->prev = _root; + _root->next = page; + _root = page; + + _busy_size = size; + } + else + { + // insert page before the end of linked list, so that it is deleted as soon as possible + // the last page is not deleted even if it's empty (see deallocate_memory) + assert(_root->prev); + + page->prev = _root->prev; + page->next = _root; + + _root->prev->next = page; + _root->prev = page; + + page->busy_size = size; + } + + return reinterpret_cast(page) + sizeof(xml_memory_page); + } +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + static const uintptr_t compact_alignment_log2 = 2; + static const uintptr_t compact_alignment = 1 << compact_alignment_log2; + + class compact_header + { + public: + compact_header(xml_memory_page* page, unsigned int flags) + { + PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); + + ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); + assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); + + _page = static_cast(offset >> compact_alignment_log2); + _flags = static_cast(flags); + } + + void operator&=(uintptr_t mod) + { + _flags &= static_cast(mod); + } + + void operator|=(uintptr_t mod) + { + _flags |= static_cast(mod); + } + + uintptr_t operator&(uintptr_t mod) const + { + return _flags & mod; + } + + xml_memory_page* get_page() const + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); + const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); + + return const_cast(reinterpret_cast(static_cast(page))); + } + + private: + unsigned char _page; + unsigned char _flags; + }; + + PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset) + { + const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); + + return header->get_page(); + } + + template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) + { + return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object)); + } + + template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value) + { + *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value; + } + + template class compact_pointer + { + public: + compact_pointer(): _data(0) + { + } + + void operator=(const compact_pointer& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift rounding for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; + + if (static_cast(offset) <= 253) + _data = static_cast(offset + 1); + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + _data = 0; + } + + operator T*() const + { + if (_data) + { + if (_data < 255) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + ((_data - 1 + start) << compact_alignment_log2)); + } + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + unsigned char _data; + }; + + template class compact_pointer_parent + { + public: + compact_pointer_parent(): _data(0) + { + } + + void operator=(const compact_pointer_parent& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift behavior for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; + + if (static_cast(offset) <= 65533) + { + _data = static_cast(offset + 1); + } + else + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_shared_parent == 0)) + page->compact_shared_parent = value; + + if (page->compact_shared_parent == value) + { + _data = 65534; + } + else + { + compact_set_value(this, value); + + _data = 65535; + } + } + } + else + { + _data = 0; + } + } + + operator T*() const + { + if (_data) + { + if (_data < 65534) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + ((_data - 1 - 65533) << compact_alignment_log2)); + } + else if (_data == 65534) + return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + uint16_t _data; + }; + + template class compact_string + { + public: + compact_string(): _data(0) + { + } + + void operator=(const compact_string& rhs) + { + *this = rhs + 0; + } + + void operator=(char_t* value) + { + if (value) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_string_base == 0)) + page->compact_string_base = value; + + ptrdiff_t offset = value - page->compact_string_base; + + if (static_cast(offset) < (65535 << 7)) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + + if (*base == 0) + { + *base = static_cast((offset >> 7) + 1); + _data = static_cast((offset & 127) + 1); + } + else + { + ptrdiff_t remainder = offset - ((*base - 1) << 7); + + if (static_cast(remainder) <= 253) + { + _data = static_cast(remainder + 1); + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + { + _data = 0; + } + } + + operator char_t*() const + { + if (_data) + { + if (_data < 255) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + assert(*base); + + ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); + + return page->compact_string_base + offset; + } + else + { + return compact_get_value(this); + } + } + else + return 0; + } + + private: + unsigned char _data; + }; +PUGI__NS_END +#endif + +#ifdef PUGIXML_COMPACT +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer prev_attribute_c; + impl::compact_pointer next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer_parent parent; + + impl::compact_pointer first_child; + + impl::compact_pointer prev_sibling_c; + impl::compact_pointer next_sibling; + + impl::compact_pointer first_attribute; + }; +} +#else +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, 0); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_attribute_struct* prev_attribute_c; + xml_attribute_struct* next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, type); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_node_struct* parent; + + xml_node_struct* first_child; + + xml_node_struct* prev_sibling_c; + xml_node_struct* next_sibling; + + xml_attribute_struct* first_attribute; + }; +} +#endif + +PUGI__NS_BEGIN + struct xml_extra_buffer + { + char_t* buffer; + xml_extra_buffer* next; + }; + + struct xml_document_struct: public xml_node_struct, public xml_allocator + { + xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) + { + } + + const char_t* buffer; + + xml_extra_buffer* extra_buffers; + + #ifdef PUGIXML_COMPACT + compact_hash_table hash; + #endif + }; + + template inline xml_allocator& get_allocator(const Object* object) + { + assert(object); + + return *PUGI__GETPAGE(object)->allocator; + } + + template inline xml_document_struct& get_document(const Object* object) + { + assert(object); + + return *static_cast(PUGI__GETPAGE(object)->allocator); + } +PUGI__NS_END + +// Low-level DOM operations +PUGI__NS_BEGIN + inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); + if (!memory) return 0; + + return new (memory) xml_attribute_struct(page); + } + + inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); + if (!memory) return 0; + + return new (memory) xml_node_struct(page, type); + } + + inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) + { + if (a->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(a->name); + + if (a->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(a->value); + + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); + } + + inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) + { + if (n->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(n->name); + + if (n->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(n->value); + + for (xml_attribute_struct* attr = n->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + destroy_attribute(attr, alloc); + + attr = next; + } + + for (xml_node_struct* child = n->first_child; child; ) + { + xml_node_struct* next = child->next_sibling; + + destroy_node(child, alloc); + + child = next; + } + + alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n)); + } + + inline void append_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + xml_node_struct* tail = head->prev_sibling_c; + + tail->next_sibling = child; + child->prev_sibling_c = tail; + head->prev_sibling_c = child; + } + else + { + node->first_child = child; + child->prev_sibling_c = child; + } + } + + inline void prepend_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + child->prev_sibling_c = head->prev_sibling_c; + head->prev_sibling_c = child; + } + else + child->prev_sibling_c = child; + + child->next_sibling = head; + node->first_child = child; + } + + inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = child; + else + parent->first_child->prev_sibling_c = child; + + child->next_sibling = node->next_sibling; + child->prev_sibling_c = node; + + node->next_sibling = child; + } + + inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = child; + else + parent->first_child = child; + + child->prev_sibling_c = node->prev_sibling_c; + child->next_sibling = node; + + node->prev_sibling_c = child; + } + + inline void remove_node(xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = node->prev_sibling_c; + else + parent->first_child->prev_sibling_c = node->prev_sibling_c; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = node->next_sibling; + else + parent->first_child = node->next_sibling; + + node->parent = 0; + node->prev_sibling_c = 0; + node->next_sibling = 0; + } + + inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + xml_attribute_struct* tail = head->prev_attribute_c; + + tail->next_attribute = attr; + attr->prev_attribute_c = tail; + head->prev_attribute_c = attr; + } + else + { + node->first_attribute = attr; + attr->prev_attribute_c = attr; + } + } + + inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + attr->prev_attribute_c = head->prev_attribute_c; + head->prev_attribute_c = attr; + } + else + attr->prev_attribute_c = attr; + + attr->next_attribute = head; + node->first_attribute = attr; + } + + inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->next_attribute) + place->next_attribute->prev_attribute_c = attr; + else + node->first_attribute->prev_attribute_c = attr; + + attr->next_attribute = place->next_attribute; + attr->prev_attribute_c = place; + place->next_attribute = attr; + } + + inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->prev_attribute_c->next_attribute) + place->prev_attribute_c->next_attribute = attr; + else + node->first_attribute = attr; + + attr->prev_attribute_c = place->prev_attribute_c; + attr->next_attribute = place; + place->prev_attribute_c = attr; + } + + inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + if (attr->next_attribute) + attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; + else + node->first_attribute->prev_attribute_c = attr->prev_attribute_c; + + if (attr->prev_attribute_c->next_attribute) + attr->prev_attribute_c->next_attribute = attr->next_attribute; + else + node->first_attribute = attr->next_attribute; + + attr->prev_attribute_c = 0; + attr->next_attribute = 0; + } + + PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + { + if (!alloc.reserve()) return 0; + + xml_node_struct* child = allocate_node(alloc, type); + if (!child) return 0; + + append_node(child, node); + + return child; + } + + PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) + { + if (!alloc.reserve()) return 0; + + xml_attribute_struct* attr = allocate_attribute(alloc); + if (!attr) return 0; + + append_attribute(attr, node); + + return attr; + } +PUGI__NS_END + +// Helper classes for code generation +PUGI__NS_BEGIN + struct opt_false + { + enum { value = 0 }; + }; + + struct opt_true + { + enum { value = 1 }; + }; +PUGI__NS_END + +// Unicode utilities +PUGI__NS_BEGIN + inline uint16_t endian_swap(uint16_t value) + { + return static_cast(((value & 0xff) << 8) | (value >> 8)); + } + + inline uint32_t endian_swap(uint32_t value) + { + return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); + } + + struct utf8_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) return result + 1; + // U+0080..U+07FF + else if (ch < 0x800) return result + 2; + // U+0800..U+FFFF + else return result + 3; + } + + static value_type high(value_type result, uint32_t) + { + // U+10000..U+10FFFF + return result + 4; + } + }; + + struct utf8_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) + { + *result = static_cast(ch); + return result + 1; + } + // U+0080..U+07FF + else if (ch < 0x800) + { + result[0] = static_cast(0xC0 | (ch >> 6)); + result[1] = static_cast(0x80 | (ch & 0x3F)); + return result + 2; + } + // U+0800..U+FFFF + else + { + result[0] = static_cast(0xE0 | (ch >> 12)); + result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[2] = static_cast(0x80 | (ch & 0x3F)); + return result + 3; + } + } + + static value_type high(value_type result, uint32_t ch) + { + // U+10000..U+10FFFF + result[0] = static_cast(0xF0 | (ch >> 18)); + result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); + result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[3] = static_cast(0x80 | (ch & 0x3F)); + return result + 4; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf16_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 2; + } + }; + + struct utf16_writer + { + typedef uint16_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + uint32_t msh = static_cast(ch - 0x10000) >> 10; + uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; + + result[0] = static_cast(0xD800 + msh); + result[1] = static_cast(0xDC00 + lsh); + + return result + 2; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf32_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 1; + } + }; + + struct utf32_writer + { + typedef uint32_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type any(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + }; + + struct latin1_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch > 255 ? '?' : ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + (void)ch; + + *result = '?'; + + return result + 1; + } + }; + + struct utf8_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + const uint8_t utf8_byte_mask = 0x3f; + + while (size) + { + uint8_t lead = *data; + + // 0xxxxxxx -> U+0000..U+007F + if (lead < 0x80) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + + // process aligned single-byte (ascii) blocks + if ((reinterpret_cast(data) & 3) == 0) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) + { + result = Traits::low(result, data[0]); + result = Traits::low(result, data[1]); + result = Traits::low(result, data[2]); + result = Traits::low(result, data[3]); + data += 4; + size -= 4; + } + } + } + // 110xxxxx -> U+0080..U+07FF + else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); + data += 2; + size -= 2; + } + // 1110xxxx -> U+0800-U+FFFF + else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); + data += 3; + size -= 3; + } + // 11110xxx -> U+10000..U+10FFFF + else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) + { + result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); + data += 4; + size -= 4; + } + // 10xxxxxx or 11111xxx -> invalid + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf16_decoder + { + typedef uint16_t type; + + template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+D7FF + if (lead < 0xD800) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+E000..U+FFFF + else if (static_cast(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // surrogate pair lead + else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if (static_cast(next - 0xDC00) < 0x400) + { + result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); + data += 2; + size -= 2; + } + else + { + data += 1; + size -= 1; + } + } + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf32_decoder + { + typedef uint32_t type; + + template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+FFFF + if (lead < 0x10000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+10000..U+10FFFF + else + { + result = Traits::high(result, lead); + data += 1; + size -= 1; + } + } + + return result; + } + }; + + struct latin1_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + result = Traits::low(result, *data); + data += 1; + size -= 1; + } + + return result; + } + }; + + template struct wchar_selector; + + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + typedef utf16_decoder decoder; + }; + + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + typedef utf32_decoder decoder; + }; + + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + struct wchar_decoder + { + typedef wchar_t type; + + template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) + { + typedef wchar_selector::decoder decoder; + + return decoder::process(reinterpret_cast(data), size, result, traits); + } + }; + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) + { + for (size_t i = 0; i < length; ++i) + result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); + } +#endif +PUGI__NS_END + +PUGI__NS_BEGIN + enum chartype_t + { + ct_parse_pcdata = 1, // \0, &, \r, < + ct_parse_attr = 2, // \0, &, \r, ', " + ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab + ct_space = 8, // \r, \n, space, tab + ct_parse_cdata = 16, // \0, ], >, \r + ct_parse_comment = 32, // \0, -, >, \r + ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . + ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : + }; + + static const unsigned char chartype_table[256] = + { + 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 + + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 + }; + + enum chartypex_t + { + ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > + ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " + ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ + ctx_digit = 8, // 0-9 + ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . + }; + + static const unsigned char chartypex_table[256] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 + 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 + + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 + + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 + }; + +#ifdef PUGIXML_WCHAR_MODE + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) +#endif + + #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) + #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) + + PUGI__FN bool is_little_endian() + { + unsigned int ui = 1; + + return *reinterpret_cast(&ui) == 1; + } + + PUGI__FN xml_encoding get_wchar_encoding() + { + PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + if (sizeof(wchar_t) == 2) + return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + else + return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + } + + PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) + { + #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } + #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; } + + // check if we have a non-empty XML declaration + if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space))) + return false; + + // scan XML declaration until the encoding field + for (size_t i = 6; i + 1 < size; ++i) + { + // declaration can not contain ? in quoted values + if (data[i] == '?') + return false; + + if (data[i] == 'e' && data[i + 1] == 'n') + { + size_t offset = i; + + // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed + PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o'); + PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g'); + + // S? = S? + PUGI__SCANCHARTYPE(ct_space); + PUGI__SCANCHAR('='); + PUGI__SCANCHARTYPE(ct_space); + + // the only two valid delimiters are ' and " + uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; + + PUGI__SCANCHAR(delimiter); + + size_t start = offset; + + out_encoding = data + offset; + + PUGI__SCANCHARTYPE(ct_symbol); + + out_length = offset - start; + + PUGI__SCANCHAR(delimiter); + + return true; + } + } + + return false; + + #undef PUGI__SCANCHAR + #undef PUGI__SCANCHARTYPE + } + + PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) + { + // skip encoding autodetection if input buffer is too small + if (size < 4) return encoding_utf8; + + uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + + // look for BOM in first few bytes + if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; + if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; + if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; + if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; + if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; + + // look for <, (contents); + + return guess_buffer_encoding(data, size); + } + + PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + out_buffer = static_cast(const_cast(contents)); + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + if (contents) + memcpy(buffer, contents, length * sizeof(char_t)); + else + assert(length == 0); + + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) + { + return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || + (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); + } + + PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const char_t* data = static_cast(contents); + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + char_t* buffer = const_cast(data); + + convert_wchar_endian_swap(buffer, data, length); + + out_buffer = buffer; + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + convert_wchar_endian_swap(buffer, data, length); + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in wchar_t units + size_t length = D::process(data, data_length, 0, wchar_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to wchar_t + wchar_writer::value_type obegin = reinterpret_cast(buffer); + wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // get native encoding + xml_encoding wchar_encoding = get_wchar_encoding(); + + // fast path: no conversion required + if (encoding == wchar_encoding) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // only endian-swapping is required + if (need_endian_swap_utf(encoding, wchar_encoding)) + return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf8 + if (encoding == encoding_utf8) + return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); + + assert(false && "Invalid encoding"); + return false; + } +#else + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in utf8 units + size_t length = D::process(data, data_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to utf8 + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) + { + for (size_t i = 0; i < size; ++i) + if (data[i] > 127) + return i; + + return size; + } + + PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const uint8_t* data = static_cast(contents); + size_t data_length = size; + + // get size of prefix that does not need utf8 conversion + size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); + assert(prefix_length <= data_length); + + const uint8_t* postfix = data + prefix_length; + size_t postfix_length = data_length - prefix_length; + + // if no conversion is needed, just return the original buffer + if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // first pass: get length in utf8 units + size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert latin1 input to utf8 + memcpy(buffer, data, prefix_length); + + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // fast path: no conversion required + if (encoding == encoding_utf8) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); + + assert(false && "Invalid encoding"); + return false; + } +#endif + + PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) + { + // get length in utf8 characters + return wchar_decoder::process(str, length, 0, utf8_counter()); + } + + PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + { + // convert to utf8 + uint8_t* begin = reinterpret_cast(buffer); + uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); + + assert(begin + size == end); + (void)!end; + (void)!size; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) + { + // first pass: get length in utf8 characters + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + std::string result; + result.resize(size); + + // second pass: convert to utf8 + if (size > 0) as_utf8_end(&result[0], size, str, length); + + return result; + } + + PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) + { + const uint8_t* data = reinterpret_cast(str); + + // first pass: get length in wchar_t units + size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); + + // allocate resulting string + std::basic_string result; + result.resize(length); + + // second pass: convert to wchar_t + if (length > 0) + { + wchar_writer::value_type begin = reinterpret_cast(&result[0]); + wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); + + assert(begin + length == end); + (void)!end; + } + + return result; + } +#endif + + template + inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) + { + // never reuse shared memory + if (header & xml_memory_page_contents_shared_mask) return false; + + size_t target_length = strlength(target); + + // always reuse document buffer memory if possible + if ((header & header_mask) == 0) return target_length >= length; + + // reuse heap memory if waste is not too great + const size_t reuse_threshold = 32; + + return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); + } + + template + PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) + { + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (header & header_mask) alloc->deallocate_string(dest); + + // mark the string as not allocated + dest = 0; + header &= ~header_mask; + + return true; + } + else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) + { + // we can reuse old buffer, so just copy the new data (including zero terminator) + memcpy(dest, source, source_length * sizeof(char_t)); + dest[source_length] = 0; + + return true; + } + else + { + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (!alloc->reserve()) return false; + + // allocate new buffer + char_t* buf = alloc->allocate_string(source_length + 1); + if (!buf) return false; + + // copy the string (including zero terminator) + memcpy(buf, source, source_length * sizeof(char_t)); + buf[source_length] = 0; + + // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) + if (header & header_mask) alloc->deallocate_string(dest); + + // the string is now allocated, so set the flag + dest = buf; + header |= header_mask; + + return true; + } + } + + struct gap + { + char_t* end; + size_t size; + + gap(): end(0), size(0) + { + } + + // Push new gap, move s count bytes further (skipping the gap). + // Collapse previous gap. + void push(char_t*& s, size_t count) + { + if (end) // there was a gap already; collapse it + { + // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + } + + s += count; // end of current gap + + // "merge" two gaps + end = s; + size += count; + } + + // Collapse all gaps, return past-the-end pointer + char_t* flush(char_t* s) + { + if (end) + { + // Move [old_gap_end, current_pos) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + + return s - size; + } + else return s; + } + }; + + PUGI__FN char_t* strconv_escape(char_t* s, gap& g) + { + char_t* stre = s + 1; + + switch (*stre) + { + case '#': // &#... + { + unsigned int ucsc = 0; + + if (stre[1] == 'x') // &#x... (hex code) + { + stre += 2; + + char_t ch = *stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(ch - '0') <= 9) + ucsc = 16 * ucsc + (ch - '0'); + else if (static_cast((ch | ' ') - 'a') <= 5) + ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + else // &#... (dec code) + { + char_t ch = *++stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(static_cast(ch) - '0') <= 9) + ucsc = 10 * ucsc + (ch - '0'); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + + #ifdef PUGIXML_WCHAR_MODE + s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); + #else + s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); + #endif + + g.push(s, stre - s); + return stre; + } + + case 'a': // &a + { + ++stre; + + if (*stre == 'm') // &am + { + if (*++stre == 'p' && *++stre == ';') // & + { + *s++ = '&'; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + else if (*stre == 'p') // &ap + { + if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' + { + *s++ = '\''; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + break; + } + + case 'g': // &g + { + if (*++stre == 't' && *++stre == ';') // > + { + *s++ = '>'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'l': // &l + { + if (*++stre == 't' && *++stre == ';') // < + { + *s++ = '<'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'q': // &q + { + if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " + { + *s++ = '"'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + default: + break; + } + + return stre; + } + + // Parser utilities + #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } + #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) + #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } + #define PUGI__POPNODE() { cursor = cursor->parent; } + #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } + #define PUGI__SCANWHILE(X) { while (X) ++s; } + #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } + #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } + #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) + #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } + + PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here + { + *g.flush(s) = 0; + + return s + (s[2] == '>' ? 3 : 2); + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + typedef char_t* (*strconv_pcdata_t)(char_t*); + + template struct strconv_pcdata_impl + { + static char_t* parse(char_t* s) + { + gap g; + + char_t* begin = s; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); + + if (*s == '<') // PCDATA ends here + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s + 1; + } + else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (*s == 0) + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s; + } + else ++s; + } + } + }; + + PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); + + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) + { + case 0: return strconv_pcdata_impl::parse; + case 1: return strconv_pcdata_impl::parse; + case 2: return strconv_pcdata_impl::parse; + case 3: return strconv_pcdata_impl::parse; + case 4: return strconv_pcdata_impl::parse; + case 5: return strconv_pcdata_impl::parse; + case 6: return strconv_pcdata_impl::parse; + case 7: return strconv_pcdata_impl::parse; + default: assert(false); return 0; // should not get here + } + } + + typedef char_t* (*strconv_attribute_t)(char_t*, char_t); + + template struct strconv_attribute_impl + { + static char_t* parse_wnorm(char_t* s, char_t end_quote) + { + gap g; + + // trim leading whitespaces + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s; + + do ++str; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + g.push(s, str - s); + } + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); + + if (*s == end_quote) + { + char_t* str = g.flush(s); + + do *str-- = 0; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + *s++ = ' '; + + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s + 1; + while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; + + g.push(s, str - s); + } + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_wconv(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + if (*s == '\r') + { + *s++ = ' '; + + if (*s == '\n') g.push(s, 1); + } + else *s++ = ' '; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_eol(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == '\r') + { + *s++ = '\n'; + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_simple(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + }; + + PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + { + case 0: return strconv_attribute_impl::parse_simple; + case 1: return strconv_attribute_impl::parse_simple; + case 2: return strconv_attribute_impl::parse_eol; + case 3: return strconv_attribute_impl::parse_eol; + case 4: return strconv_attribute_impl::parse_wconv; + case 5: return strconv_attribute_impl::parse_wconv; + case 6: return strconv_attribute_impl::parse_wconv; + case 7: return strconv_attribute_impl::parse_wconv; + case 8: return strconv_attribute_impl::parse_wnorm; + case 9: return strconv_attribute_impl::parse_wnorm; + case 10: return strconv_attribute_impl::parse_wnorm; + case 11: return strconv_attribute_impl::parse_wnorm; + case 12: return strconv_attribute_impl::parse_wnorm; + case 13: return strconv_attribute_impl::parse_wnorm; + case 14: return strconv_attribute_impl::parse_wnorm; + case 15: return strconv_attribute_impl::parse_wnorm; + default: assert(false); return 0; // should not get here + } + } + + inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) + { + xml_parse_result result; + result.status = status; + result.offset = offset; + + return result; + } + + struct xml_parser + { + xml_allocator* alloc; + char_t* error_offset; + xml_parse_status error_status; + + xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) + { + } + + // DOCTYPE consists of nested sections of the following possible types: + // , , "...", '...' + // + // + // First group can not contain nested groups + // Second group can contain nested groups of the same type + // Third group can contain all other groups + char_t* parse_doctype_primitive(char_t* s) + { + if (*s == '"' || *s == '\'') + { + // quoted string + char_t ch = *s++; + PUGI__SCANFOR(*s == ch); + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s++; + } + else if (s[0] == '<' && s[1] == '?') + { + // + s += 2; + PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 2; + } + else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') + { + s += 4; + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 3; + } + else PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_ignore(char_t* s) + { + size_t depth = 0; + + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); + s += 3; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] == '[') + { + // nested ignore section + s += 3; + depth++; + } + else if (s[0] == ']' && s[1] == ']' && s[2] == '>') + { + // ignore section end + s += 3; + + if (depth == 0) + return s; + + depth--; + } + else s++; + } + + PUGI__THROW_ERROR(status_bad_doctype, s); + } + + char_t* parse_doctype_group(char_t* s, char_t endch) + { + size_t depth = 0; + + assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); + s += 2; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] != '-') + { + if (s[2] == '[') + { + // ignore + s = parse_doctype_ignore(s); + if (!s) return s; + } + else + { + // some control group + s += 2; + depth++; + } + } + else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') + { + // unknown tag (forbidden), or some primitive group + s = parse_doctype_primitive(s); + if (!s) return s; + } + else if (*s == '>') + { + if (depth == 0) + return s; + + depth--; + s++; + } + else s++; + } + + if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) + { + // parse node contents, starting with exclamation mark + ++s; + + if (*s == '-') // 'value = s; // Save the offset. + } + + if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) + { + s = strconv_comment(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); + } + else + { + // Scan for terminating '-->'. + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_comment, s); + + if (PUGI__OPTSET(parse_comments)) + *s = 0; // Zero-terminate this segment at the first terminating '-'. + + s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. + } + } + else PUGI__THROW_ERROR(status_bad_comment, s); + } + else if (*s == '[') + { + // 'value = s; // Save the offset. + + if (PUGI__OPTSET(parse_eol)) + { + s = strconv_cdata(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); + } + else + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + *s++ = 0; // Zero-terminate this segment. + } + } + else // Flagged for discard, but we still have to scan for the terminator. + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + ++s; + } + + s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. + } + else PUGI__THROW_ERROR(status_bad_cdata, s); + } + else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) + { + s -= 2; + + if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); + + char_t* mark = s + 9; + + s = parse_doctype_group(s, endch); + if (!s) return s; + + assert((*s == 0 && endch == '>') || *s == '>'); + if (*s) *s++ = 0; + + if (PUGI__OPTSET(parse_doctype)) + { + while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; + + PUGI__PUSHNODE(node_doctype); + + cursor->value = mark; + } + } + else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); + else PUGI__THROW_ERROR(status_unrecognized_tag, s); + + return s; + } + + char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) + { + // load into registers + xml_node_struct* cursor = ref_cursor; + char_t ch = 0; + + // parse node contents, starting with question mark + ++s; + + // read PI target + char_t* target = s; + + if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); + PUGI__CHECK_ERROR(status_bad_pi, s); + + // determine node type; stricmp / strcasecmp is not portable + bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; + + if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) + { + if (declaration) + { + // disallow non top-level declarations + if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__PUSHNODE(node_declaration); + } + else + { + PUGI__PUSHNODE(node_pi); + } + + cursor->name = target; + + PUGI__ENDSEG(); + + // parse value/attributes + if (ch == '?') + { + // empty node + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); + s += (*s == '>'); + + PUGI__POPNODE(); + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); + + // scan for tag end + char_t* value = s; + + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + if (declaration) + { + // replace ending ? with / so that 'element' terminates properly + *s = '/'; + + // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES + s = value; + } + else + { + // store value and step over > + cursor->value = value; + + PUGI__POPNODE(); + + PUGI__ENDSEG(); + + s += (*s == '>'); + } + } + else PUGI__THROW_ERROR(status_bad_pi, s); + } + else + { + // scan for tag end + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + s += (s[1] == '>' ? 2 : 1); + } + + // store from registers + ref_cursor = cursor; + + return s; + } + + char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) + { + strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); + strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); + + char_t ch = 0; + xml_node_struct* cursor = root; + char_t* mark = s; + + while (*s != 0) + { + if (*s == '<') + { + ++s; + + LOC_TAG: + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + { + PUGI__PUSHNODE(node_element); // Append a new node to the tree. + + cursor->name = s; + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (ch == '>') + { + // end of tag + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + LOC_ATTRIBUTES: + while (true) + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + { + xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. + if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); + + a->name = s; // Save the offset. + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); // Eat any whitespace. + + ch = *s; + ++s; + } + + if (ch == '=') // '<... #=...' + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (*s == '"' || *s == '\'') // '<... #="...' + { + ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. + ++s; // Step over the quote. + a->value = s; // Save the offset. + + s = strconv_attribute(s, ch); + + if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); + + // After this line the loop continues from the start; + // Whitespaces, / and > are ok, symbols and EOF are wrong, + // everything else will be detected + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else if (*s == '/') + { + ++s; + + if (*s == '>') + { + PUGI__POPNODE(); + s++; + break; + } + else if (*s == 0 && endch == '>') + { + PUGI__POPNODE(); + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '>') + { + ++s; + + break; + } + else if (*s == 0 && endch == '>') + { + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + + // !!! + } + else if (ch == '/') // '<#.../' + { + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); + + PUGI__POPNODE(); // Pop. + + s += (*s == '>'); + } + else if (ch == 0) + { + // we stepped over null terminator, backtrack & handle closing tag + --s; + + if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '/') + { + ++s; + + mark = s; + + char_t* name = cursor->name; + if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + + while (PUGI__IS_CHARTYPE(*s, ct_symbol)) + { + if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + if (*name) + { + if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); + else PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + PUGI__POPNODE(); // Pop. + + PUGI__SKIPWS(); + + if (*s == 0) + { + if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + } + else + { + if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + ++s; + } + } + else if (*s == '?') // 'first_child) continue; + } + } + + if (!PUGI__OPTSET(parse_trim_pcdata)) + s = mark; + + if (cursor->parent || PUGI__OPTSET(parse_fragment)) + { + if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) + { + cursor->value = s; // Save the offset. + } + else + { + PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. + + cursor->value = s; // Save the offset. + + PUGI__POPNODE(); // Pop since this is a standalone. + } + + s = strconv_pcdata(s); + + if (!*s) break; + } + else + { + PUGI__SCANFOR(*s == '<'); // '...<' + if (!*s) break; + + ++s; + } + + // We're after '<' + goto LOC_TAG; + } + } + + // check that last tag is closed + if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); + + return s; + } + + #ifdef PUGIXML_WCHAR_MODE + static char_t* parse_skip_bom(char_t* s) + { + unsigned int bom = 0xfeff; + return (s[0] == static_cast(bom)) ? s + 1 : s; + } + #else + static char_t* parse_skip_bom(char_t* s) + { + return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; + } + #endif + + static bool has_element_node_siblings(xml_node_struct* node) + { + while (node) + { + if (PUGI__NODETYPE(node) == node_element) return true; + + node = node->next_sibling; + } + + return false; + } + + static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) + { + // early-out for empty documents + if (length == 0) + return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); + + // get last child of the root before parsing + xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; + + // create parser on stack + xml_parser parser(static_cast(xmldoc)); + + // save last character and make buffer zero-terminated (speeds up parsing) + char_t endch = buffer[length - 1]; + buffer[length - 1] = 0; + + // skip BOM to make sure it does not end up as part of parse output + char_t* buffer_data = parse_skip_bom(buffer); + + // perform actual parsing + parser.parse_tree(buffer_data, root, optmsk, endch); + + xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); + assert(result.offset >= 0 && static_cast(result.offset) <= length); + + if (result) + { + // since we removed last character, we have to handle the only possible false positive (stray <) + if (endch == '<') + return make_parse_result(status_unrecognized_tag, length - 1); + + // check if there are any element nodes parsed + xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0; + + if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) + return make_parse_result(status_no_document_element, length - 1); + } + else + { + // roll back offset if it occurs on a null terminator in the source buffer + if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) + result.offset--; + } + + return result; + } + }; + + // Output facilities + PUGI__FN xml_encoding get_write_native_encoding() + { + #ifdef PUGIXML_WCHAR_MODE + return get_wchar_encoding(); + #else + return encoding_utf8; + #endif + } + + PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) + { + // replace wchar encoding with utf implementation + if (encoding == encoding_wchar) return get_wchar_encoding(); + + // replace utf16 encoding with utf16 with specific endianness + if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + // replace utf32 encoding with utf32 with specific endianness + if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + // only do autodetection if no explicit encoding is requested + if (encoding != encoding_auto) return encoding; + + // assume utf8 encoding + return encoding_utf8; + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + return static_cast(end - dest) * sizeof(*dest); + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + if (opt_swap) + { + for (typename T::value_type i = dest; i != end; ++i) + *i = endian_swap(*i); + } + + return static_cast(end - dest) * sizeof(*dest); + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 1) return 0; + + // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; + } + + PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + // only endian-swapping is required + if (need_endian_swap_utf(encoding, get_wchar_encoding())) + { + convert_wchar_endian_swap(r_char, data, length); + + return length * sizeof(char_t); + } + + // convert to utf8 + if (encoding == encoding_utf8) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); + + // convert to utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); + } + + // convert to utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); + } + + // convert to latin1 + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); + return 0; + } +#else + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 5) return 0; + + for (size_t i = 1; i <= 4; ++i) + { + uint8_t ch = static_cast(data[length - i]); + + // either a standalone character or a leading one + if ((ch & 0xc0) != 0x80) return length - i; + } + + // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk + return length; + } + + PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); + } + + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); + } + + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); + return 0; + } +#endif + + class xml_buffered_writer + { + xml_buffered_writer(const xml_buffered_writer&); + xml_buffered_writer& operator=(const xml_buffered_writer&); + + public: + xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) + { + PUGI__STATIC_ASSERT(bufcapacity >= 8); + } + + size_t flush() + { + flush(buffer, bufsize); + bufsize = 0; + return 0; + } + + void flush(const char_t* data, size_t size) + { + if (size == 0) return; + + // fast path, just write data + if (encoding == get_write_native_encoding()) + writer.write(data, size * sizeof(char_t)); + else + { + // convert chunk + size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); + assert(result <= sizeof(scratch)); + + // write data + writer.write(scratch.data_u8, result); + } + } + + void write_direct(const char_t* data, size_t length) + { + // flush the remaining buffer contents + flush(); + + // handle large chunks + if (length > bufcapacity) + { + if (encoding == get_write_native_encoding()) + { + // fast path, can just write data chunk + writer.write(data, length * sizeof(char_t)); + return; + } + + // need to convert in suitable chunks + while (length > bufcapacity) + { + // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer + // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) + size_t chunk_size = get_valid_length(data, bufcapacity); + assert(chunk_size); + + // convert chunk and write + flush(data, chunk_size); + + // iterate + data += chunk_size; + length -= chunk_size; + } + + // small tail is copied below + bufsize = 0; + } + + memcpy(buffer + bufsize, data, length * sizeof(char_t)); + bufsize += length; + } + + void write_buffer(const char_t* data, size_t length) + { + size_t offset = bufsize; + + if (offset + length <= bufcapacity) + { + memcpy(buffer + offset, data, length * sizeof(char_t)); + bufsize = offset + length; + } + else + { + write_direct(data, length); + } + } + + void write_string(const char_t* data) + { + // write the part of the string that fits in the buffer + size_t offset = bufsize; + + while (*data && offset < bufcapacity) + buffer[offset++] = *data++; + + // write the rest + if (offset < bufcapacity) + { + bufsize = offset; + } + else + { + // backtrack a bit if we have split the codepoint + size_t length = offset - bufsize; + size_t extra = length - get_valid_length(data - length, length); + + bufsize = offset - extra; + + write_direct(data - extra, strlength(data) + extra); + } + } + + void write(char_t d0) + { + size_t offset = bufsize; + if (offset > bufcapacity - 1) offset = flush(); + + buffer[offset + 0] = d0; + bufsize = offset + 1; + } + + void write(char_t d0, char_t d1) + { + size_t offset = bufsize; + if (offset > bufcapacity - 2) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + bufsize = offset + 2; + } + + void write(char_t d0, char_t d1, char_t d2) + { + size_t offset = bufsize; + if (offset > bufcapacity - 3) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + bufsize = offset + 3; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3) + { + size_t offset = bufsize; + if (offset > bufcapacity - 4) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + bufsize = offset + 4; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) + { + size_t offset = bufsize; + if (offset > bufcapacity - 5) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + bufsize = offset + 5; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) + { + size_t offset = bufsize; + if (offset > bufcapacity - 6) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + buffer[offset + 5] = d5; + bufsize = offset + 6; + } + + // utf8 maximum expansion: x4 (-> utf32) + // utf16 maximum expansion: x2 (-> utf32) + // utf32 maximum expansion: x1 + enum + { + bufcapacitybytes = + #ifdef PUGIXML_MEMORY_OUTPUT_STACK + PUGIXML_MEMORY_OUTPUT_STACK + #else + 10240 + #endif + , + bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) + }; + + char_t buffer[bufcapacity]; + + union + { + uint8_t data_u8[4 * bufcapacity]; + uint16_t data_u16[2 * bufcapacity]; + uint32_t data_u32[bufcapacity]; + char_t data_char[bufcapacity]; + } scratch; + + xml_writer& writer; + size_t bufsize; + xml_encoding encoding; + }; + + PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + { + while (*s) + { + const char_t* prev = s; + + // While *s is a usual symbol + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); + + writer.write_buffer(prev, static_cast(s - prev)); + + switch (*s) + { + case 0: break; + case '&': + writer.write('&', 'a', 'm', 'p', ';'); + ++s; + break; + case '<': + writer.write('&', 'l', 't', ';'); + ++s; + break; + case '>': + writer.write('&', 'g', 't', ';'); + ++s; + break; + case '"': + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = static_cast(*s++); + assert(ch < 32); + + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + } + } + } + } + + PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) + { + if (flags & format_no_escapes) + writer.write_string(s); + else + text_output_escaped(writer, s, type); + } + + PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) + { + do + { + writer.write('<', '!', '[', 'C', 'D'); + writer.write('A', 'T', 'A', '['); + + const char_t* prev = s; + + // look for ]]> sequence - we can't output it as is since it terminates CDATA + while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; + + // skip ]] if we stopped at ]]>, > will go to the next CDATA section + if (*s) s += 2; + + writer.write_buffer(prev, static_cast(s - prev)); + + writer.write(']', ']', '>'); + } + while (*s); + } + + PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) + { + switch (indent_length) + { + case 1: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0]); + break; + } + + case 2: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1]); + break; + } + + case 3: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2]); + break; + } + + case 4: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2], indent[3]); + break; + } + + default: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write_buffer(indent, indent_length); + } + } + } + + PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) + { + writer.write('<', '!', '-', '-'); + + while (*s) + { + const char_t* prev = s; + + // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body + while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(*s == '-'); + + writer.write('-', ' '); + ++s; + } + } + + writer.write('-', '-', '>'); + } + + PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) + { + while (*s) + { + const char_t* prev = s; + + // look for ?> sequence - we can't output it since ?> terminates PI + while (*s && !(s[0] == '?' && s[1] == '>')) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(s[0] == '?' && s[1] == '>'); + + writer.write('?', ' ', '>'); + s += 2; + } + } + } + + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + { + if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) + { + writer.write('\n'); + + text_output_indent(writer, indent, indent_length, depth + 1); + } + else + { + writer.write(' '); + } + + writer.write_string(a->name ? a->name + 0 : default_name); + writer.write('=', '"'); + + if (a->value) + text_output(writer, a->value, ctx_special_attr, flags); + + writer.write('"'); + } + } + + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<'); + writer.write_string(name); + + if (node->first_attribute) + node_output_attributes(writer, node, indent, indent_length, flags, depth); + + // element nodes can have value if parse_embed_pcdata was used + if (!node->value) + { + if (!node->first_child) + { + if (flags & format_no_empty_element_tags) + { + writer.write('>', '<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + if ((flags & format_raw) == 0) + writer.write(' '); + + writer.write('/', '>'); + + return false; + } + } + else + { + writer.write('>'); + + return true; + } + } + else + { + writer.write('>'); + + text_output(writer, node->value, ctx_special_pcdata, flags); + + if (!node->first_child) + { + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + return true; + } + } + } + + PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + } + + PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + switch (PUGI__NODETYPE(node)) + { + case node_pcdata: + text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); + break; + + case node_cdata: + text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_comment: + node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_pi: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + + if (node->value) + { + writer.write(' '); + node_output_pi_value(writer, node->value); + } + + writer.write('?', '>'); + break; + + case node_declaration: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); + writer.write('?', '>'); + break; + + case node_doctype: + writer.write('<', '!', 'D', 'O', 'C'); + writer.write('T', 'Y', 'P', 'E'); + + if (node->value) + { + writer.write(' '); + writer.write_string(node->value); + } + + writer.write('>'); + break; + + default: + assert(false && "Invalid node type"); + } + } + + enum indent_flags_t + { + indent_newline = 1, + indent_indent = 2 + }; + + PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) + { + size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; + unsigned int indent_flags = indent_indent; + + xml_node_struct* node = root; + + do + { + assert(node); + + // begin writing current node + if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) + { + node_output_simple(writer, node, flags); + + indent_flags = 0; + } + else + { + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + if (PUGI__NODETYPE(node) == node_element) + { + indent_flags = indent_newline | indent_indent; + + if (node_output_start(writer, node, indent, indent_length, flags, depth)) + { + // element nodes can have value if parse_embed_pcdata was used + if (node->value) + indent_flags = 0; + + node = node->first_child; + depth++; + continue; + } + } + else if (PUGI__NODETYPE(node) == node_document) + { + indent_flags = indent_indent; + + if (node->first_child) + { + node = node->first_child; + continue; + } + } + else + { + node_output_simple(writer, node, flags); + + indent_flags = indent_newline | indent_indent; + } + } + + // continue to the next node + while (node != root) + { + if (node->next_sibling) + { + node = node->next_sibling; + break; + } + + node = node->parent; + + // write closing node + if (PUGI__NODETYPE(node) == node_element) + { + depth--; + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + node_output_end(writer, node); + + indent_flags = indent_newline | indent_indent; + } + } + } + while (node != root); + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + } + + PUGI__FN bool has_declaration(xml_node_struct* node) + { + for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) + { + xml_node_type type = PUGI__NODETYPE(child); + + if (type == node_declaration) return true; + if (type == node_element) return false; + } + + return false; + } + + PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) + { + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + if (a == attr) + return true; + + return false; + } + + PUGI__FN bool allow_insert_attribute(xml_node_type parent) + { + return parent == node_element || parent == node_declaration; + } + + PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) + { + if (parent != node_document && parent != node_element) return false; + if (child == node_document || child == node_null) return false; + if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; + + return true; + } + + PUGI__FN bool allow_move(xml_node parent, xml_node child) + { + // check that child can be a child of parent + if (!allow_insert_child(parent.type(), child.type())) + return false; + + // check that node is not moved between documents + if (parent.root() != child.root()) + return false; + + // check that new parent is not in the child subtree + xml_node cur = parent; + + while (cur) + { + if (cur == child) + return false; + + cur = cur.parent(); + } + + return true; + } + + template + PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) + { + assert(!dest && (header & header_mask) == 0); + + if (source) + { + if (alloc && (source_header & header_mask) == 0) + { + dest = source; + + // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared + header |= xml_memory_page_contents_shared_mask; + source_header |= xml_memory_page_contents_shared_mask; + } + else + strcpy_insitu(dest, header, header_mask, source, strlength(source)); + } + } + + PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) + { + node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); + node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); + + for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) + { + xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); + + if (da) + { + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + } + } + + PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) + { + xml_allocator& alloc = get_allocator(dn); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; + + node_copy_contents(dn, sn, shared_alloc); + + xml_node_struct* dit = dn; + xml_node_struct* sit = sn->first_child; + + while (sit && sit != sn) + { + if (sit != dn) + { + xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); + + if (copy) + { + node_copy_contents(copy, sit, shared_alloc); + + if (sit->first_child) + { + dit = copy; + sit = sit->first_child; + continue; + } + } + } + + // continue to the next node + do + { + if (sit->next_sibling) + { + sit = sit->next_sibling; + break; + } + + sit = sit->parent; + dit = dit->parent; + } + while (sit != sn); + } + } + + PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + { + xml_allocator& alloc = get_allocator(da); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; + + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + + inline bool is_text_node(xml_node_struct* node) + { + xml_node_type type = PUGI__NODETYPE(node); + + return type == node_pcdata || type == node_cdata; + } + + // get value with conversion functions + template U string_to_integer(const char_t* value, U minneg, U maxpos) + { + U result = 0; + const char_t* s = value; + + while (PUGI__IS_CHARTYPE(*s, ct_space)) + s++; + + bool negative = (*s == '-'); + + s += (*s == '+' || *s == '-'); + + bool overflow = false; + + if (s[0] == '0' && (s[1] | ' ') == 'x') + { + s += 2; + + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 16 + (*s - '0'); + else if (static_cast((*s | ' ') - 'a') < 6) + result = result * 16 + ((*s | ' ') - 'a' + 10); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + overflow = digits > sizeof(U) * 2; + } + else + { + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 10 + (*s - '0'); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); + + const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; + const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; + const size_t high_bit = sizeof(U) * 8 - 1; + + overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); + } + + if (negative) + return (overflow || result > minneg) ? 0 - minneg : 0 - result; + else + return (overflow || result > maxpos) ? maxpos : result; + } + + PUGI__FN int get_value_int(const char_t* value) + { + return string_to_integer(value, 0 - static_cast(INT_MIN), INT_MAX); + } + + PUGI__FN unsigned int get_value_uint(const char_t* value) + { + return string_to_integer(value, 0, UINT_MAX); + } + + PUGI__FN double get_value_double(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return wcstod(value, 0); + #else + return strtod(value, 0); + #endif + } + + PUGI__FN float get_value_float(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return static_cast(wcstod(value, 0)); + #else + return static_cast(strtod(value, 0)); + #endif + } + + PUGI__FN bool get_value_bool(const char_t* value) + { + // only look at first char + char_t first = *value; + + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long get_value_llong(const char_t* value) + { + return string_to_integer(value, 0 - static_cast(LLONG_MIN), LLONG_MAX); + } + + PUGI__FN unsigned long long get_value_ullong(const char_t* value) + { + return string_to_integer(value, 0, ULLONG_MAX); + } +#endif + + template PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) + { + char_t* result = end - 1; + U rest = negative ? 0 - value : value; + + do + { + *result-- = static_cast('0' + (rest % 10)); + rest /= 10; + } + while (rest); + + assert(result >= begin); + (void)begin; + + *result = '-'; + + return result + !negative; + } + + // set value with conversion functions + template + PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) + { + #ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); + + size_t offset = 0; + for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; + + return strcpy_insitu(dest, header, header_mask, wbuf, offset); + #else + return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); + #endif + } + + template + PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) + { + char_t buf[64]; + char_t* end = buf + sizeof(buf) / sizeof(buf[0]); + char_t* begin = integer_to_string(buf, end, value, negative); + + return strcpy_insitu(dest, header, header_mask, begin, end - begin); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + { + char buf[128]; + sprintf(buf, "%.9g", value); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + { + char buf[128]; + sprintf(buf, "%.17g", value); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) + { + return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); + } + + PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) + { + // check input buffer + if (!contents && size) return make_parse_result(status_io_error); + + // get actual encoding + xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); + + // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself + if (own || buffer != contents) *out_buffer = buffer; + + // store buffer for offset_debug + doc->buffer = buffer; + + // parse + xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); + + // remember encoding + res.encoding = buffer_encoding; + + return res; + } + + // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick + PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) + { + #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + // there are 64-bit versions of fseek/ftell, let's use them + typedef __int64 length_type; + + _fseeki64(file, 0, SEEK_END); + length_type length = _ftelli64(file); + _fseeki64(file, 0, SEEK_SET); + #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) + // there are 64-bit versions of fseek/ftell, let's use them + typedef off64_t length_type; + + fseeko64(file, 0, SEEK_END); + length_type length = ftello64(file); + fseeko64(file, 0, SEEK_SET); + #else + // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. + typedef long length_type; + + fseek(file, 0, SEEK_END); + length_type length = ftell(file); + fseek(file, 0, SEEK_SET); + #endif + + // check for I/O errors + if (length < 0) return status_io_error; + + // check for overflow + size_t result = static_cast(length); + + if (static_cast(result) != length) return status_out_of_memory; + + // finalize + out_result = result; + + return status_ok; + } + + // This function assumes that buffer has extra sizeof(char_t) writable bytes after size + PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + { + // We only need to zero-terminate if encoding conversion does not do it for us + #ifdef PUGIXML_WCHAR_MODE + xml_encoding wchar_encoding = get_wchar_encoding(); + + if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) + { + size_t length = size / sizeof(char_t); + + static_cast(buffer)[length] = 0; + return (length + 1) * sizeof(char_t); + } + #else + if (encoding == encoding_utf8) + { + static_cast(buffer)[size] = 0; + return size + 1; + } + #endif + + return size; + } + + PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + if (!file) return make_parse_result(status_file_not_found); + + // get file size (can result in I/O errors) + size_t size = 0; + xml_parse_status size_status = get_file_size(file, size); + if (size_status != status_ok) return make_parse_result(size_status); + + size_t max_suffix_size = sizeof(char_t); + + // allocate buffer for the whole file + char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); + if (!contents) return make_parse_result(status_out_of_memory); + + // read file in memory + size_t read_size = fread(contents, 1, size, file); + + if (read_size != size) + { + xml_memory::deallocate(contents); + return make_parse_result(status_io_error); + } + + xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); + + return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); + } + + PUGI__FN void close_file(FILE* file) + { + fclose(file); + } + +#ifndef PUGIXML_NO_STL + template struct xml_stream_chunk + { + static xml_stream_chunk* create() + { + void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); + if (!memory) return 0; + + return new (memory) xml_stream_chunk(); + } + + static void destroy(xml_stream_chunk* chunk) + { + // free chunk chain + while (chunk) + { + xml_stream_chunk* next_ = chunk->next; + + xml_memory::deallocate(chunk); + + chunk = next_; + } + } + + xml_stream_chunk(): next(0), size(0) + { + } + + xml_stream_chunk* next; + size_t size; + + T data[xml_memory_page_size / sizeof(T)]; + }; + + template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + auto_deleter > chunks(0, xml_stream_chunk::destroy); + + // read file to a chunk list + size_t total = 0; + xml_stream_chunk* last = 0; + + while (!stream.eof()) + { + // allocate new chunk + xml_stream_chunk* chunk = xml_stream_chunk::create(); + if (!chunk) return status_out_of_memory; + + // append chunk to list + if (last) last = last->next = chunk; + else chunks.data = last = chunk; + + // read data to chunk + stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); + chunk->size = static_cast(stream.gcount()) * sizeof(T); + + // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // guard against huge files (chunk size is small enough to make this overflow check work) + if (total + chunk->size < total) return status_out_of_memory; + total += chunk->size; + } + + size_t max_suffix_size = sizeof(char_t); + + // copy chunk list to a contiguous buffer + char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); + if (!buffer) return status_out_of_memory; + + char* write = buffer; + + for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) + { + assert(write + chunk->size <= buffer + total); + memcpy(write, chunk->data, chunk->size); + write += chunk->size; + } + + assert(write == buffer + total); + + // return buffer + *out_buffer = buffer; + *out_size = total; + + return status_ok; + } + + template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + // get length of remaining data in stream + typename std::basic_istream::pos_type pos = stream.tellg(); + stream.seekg(0, std::ios::end); + std::streamoff length = stream.tellg() - pos; + stream.seekg(pos); + + if (stream.fail() || pos < 0) return status_io_error; + + // guard against huge files + size_t read_length = static_cast(length); + + if (static_cast(read_length) != length || length < 0) return status_out_of_memory; + + size_t max_suffix_size = sizeof(char_t); + + // read stream data into memory (guard against stream exceptions with buffer holder) + auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); + if (!buffer.data) return status_out_of_memory; + + stream.read(static_cast(buffer.data), static_cast(read_length)); + + // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // return buffer + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + *out_buffer = buffer.release(); + *out_size = actual_length * sizeof(T); + + return status_ok; + } + + template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + void* buffer = 0; + size_t size = 0; + xml_parse_status status = status_ok; + + // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) + if (stream.fail()) return make_parse_result(status_io_error); + + // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) + if (stream.tellg() < 0) + { + stream.clear(); // clear error flags that could be set by a failing tellg + status = load_stream_data_noseek(stream, &buffer, &size); + } + else + status = load_stream_data_seek(stream, &buffer, &size); + + if (status != status_ok) return make_parse_result(status); + + xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); + + return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); + } +#endif + +#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + return _wfopen(path, mode); + } +#else + PUGI__FN char* convert_path_heap(const wchar_t* str) + { + assert(str); + + // first pass: get length in utf8 characters + size_t length = strlength_wide(str); + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + char* result = static_cast(xml_memory::allocate(size + 1)); + if (!result) return 0; + + // second pass: convert to utf8 + as_utf8_end(result, size, str, length); + + // zero-terminate + result[size] = 0; + + return result; + } + + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + // there is no standard function to open wide paths, so our best bet is to try utf8 path + char* path_utf8 = convert_path_heap(path); + if (!path_utf8) return 0; + + // convert mode to ASCII (we mirror _wfopen interface) + char mode_ascii[4] = {0}; + for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); + + // try to open the utf8 path + FILE* result = fopen(path_utf8, mode_ascii); + + // free dummy buffer + xml_memory::deallocate(path_utf8); + + return result; + } +#endif + + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) + { + if (!file) return false; + + xml_writer_file writer(file); + doc.save(writer, indent, flags, encoding); + + return ferror(file) == 0; + } + + struct name_null_sentry + { + xml_node_struct* node; + char_t* name; + + name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) + { + node->name = 0; + } + + ~name_null_sentry() + { + node->name = name; + } + }; +PUGI__NS_END + +namespace pugi +{ + PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) + { + } + + PUGI__FN void xml_writer_file::write(const void* data, size_t size) + { + size_t result = fwrite(data, 1, size, static_cast(file)); + (void)!result; // unfortunately we can't do proper error handling here + } + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + { + } + + PUGI__FN void xml_writer_stream::write(const void* data, size_t size) + { + if (narrow_stream) + { + assert(!wide_stream); + narrow_stream->write(reinterpret_cast(data), static_cast(size)); + } + else + { + assert(wide_stream); + assert(size % sizeof(wchar_t) == 0); + + wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); + } + } +#endif + + PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) + { + } + + PUGI__FN xml_tree_walker::~xml_tree_walker() + { + } + + PUGI__FN int xml_tree_walker::depth() const + { + return _depth; + } + + PUGI__FN bool xml_tree_walker::begin(xml_node&) + { + return true; + } + + PUGI__FN bool xml_tree_walker::end(xml_node&) + { + return true; + } + + PUGI__FN xml_attribute::xml_attribute(): _attr(0) + { + } + + PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + { + } + + PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) + { + } + + PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const + { + return _attr ? unspecified_bool_xml_attribute : 0; + } + + PUGI__FN bool xml_attribute::operator!() const + { + return !_attr; + } + + PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const + { + return (_attr == r._attr); + } + + PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const + { + return (_attr != r._attr); + } + + PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const + { + return (_attr < r._attr); + } + + PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const + { + return (_attr > r._attr); + } + + PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const + { + return (_attr <= r._attr); + } + + PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const + { + return (_attr >= r._attr); + } + + PUGI__FN xml_attribute xml_attribute::next_attribute() const + { + return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_attribute::previous_attribute() const + { + return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const + { + return (_attr && _attr->value) ? _attr->value + 0 : def; + } + + PUGI__FN int xml_attribute::as_int(int def) const + { + return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; + } + + PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const + { + return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; + } + + PUGI__FN double xml_attribute::as_double(double def) const + { + return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; + } + + PUGI__FN float xml_attribute::as_float(float def) const + { + return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; + } + + PUGI__FN bool xml_attribute::as_bool(bool def) const + { + return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_attribute::as_llong(long long def) const + { + return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; + } + + PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const + { + return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; + } +#endif + + PUGI__FN bool xml_attribute::empty() const + { + return !_attr; + } + + PUGI__FN const char_t* xml_attribute::name() const + { + return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_attribute::value() const + { + return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN size_t xml_attribute::hash_value() const + { + return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); + } + + PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const + { + return _attr; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) + { + set_value(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) + { + set_value(rhs); + return *this; + } +#endif + + PUGI__FN bool xml_attribute::set_name(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(double rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(float rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(bool rhs) + { + if (!_attr) return false; + + return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_attribute::set_value(long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } +#endif + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node::xml_node(): _root(0) + { + } + + PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) + { + } + + PUGI__FN static void unspecified_bool_xml_node(xml_node***) + { + } + + PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const + { + return _root ? unspecified_bool_xml_node : 0; + } + + PUGI__FN bool xml_node::operator!() const + { + return !_root; + } + + PUGI__FN xml_node::iterator xml_node::begin() const + { + return iterator(_root ? _root->first_child + 0 : 0, _root); + } + + PUGI__FN xml_node::iterator xml_node::end() const + { + return iterator(0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const + { + return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const + { + return attribute_iterator(0, _root); + } + + PUGI__FN xml_object_range xml_node::children() const + { + return xml_object_range(begin(), end()); + } + + PUGI__FN xml_object_range xml_node::children(const char_t* name_) const + { + return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); + } + + PUGI__FN xml_object_range xml_node::attributes() const + { + return xml_object_range(attributes_begin(), attributes_end()); + } + + PUGI__FN bool xml_node::operator==(const xml_node& r) const + { + return (_root == r._root); + } + + PUGI__FN bool xml_node::operator!=(const xml_node& r) const + { + return (_root != r._root); + } + + PUGI__FN bool xml_node::operator<(const xml_node& r) const + { + return (_root < r._root); + } + + PUGI__FN bool xml_node::operator>(const xml_node& r) const + { + return (_root > r._root); + } + + PUGI__FN bool xml_node::operator<=(const xml_node& r) const + { + return (_root <= r._root); + } + + PUGI__FN bool xml_node::operator>=(const xml_node& r) const + { + return (_root >= r._root); + } + + PUGI__FN bool xml_node::empty() const + { + return !_root; + } + + PUGI__FN const char_t* xml_node::name() const + { + return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node_type xml_node::type() const + { + return _root ? PUGI__NODETYPE(_root) : node_null; + } + + PUGI__FN const char_t* xml_node::value() const + { + return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node xml_node::child(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + return xml_attribute(i); + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_node xml_node::next_sibling() const + { + return _root ? xml_node(_root->next_sibling) : xml_node(); + } + + PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const + { + xml_attribute_struct* hint = hint_._attr; + + // if hint is not an attribute of node, behavior is not defined + assert(!hint || (_root && impl::is_attribute_of(hint, _root))); + + if (!_root) return xml_attribute(); + + // optimistically search from hint up until the end + for (xml_attribute_struct* i = hint; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = i->next_attribute; + + return xml_attribute(i); + } + + // wrap around and search from the first attribute until the hint + // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails + for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) + if (j->name && impl::strequal(name_, j->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = j->next_attribute; + + return xml_attribute(j); + } + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::previous_sibling() const + { + if (!_root) return xml_node(); + + if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); + else return xml_node(); + } + + PUGI__FN xml_node xml_node::parent() const + { + return _root ? xml_node(_root->parent) : xml_node(); + } + + PUGI__FN xml_node xml_node::root() const + { + return _root ? xml_node(&impl::get_document(_root)) : xml_node(); + } + + PUGI__FN xml_text xml_node::text() const + { + return xml_text(_root); + } + + PUGI__FN const char_t* xml_node::child_value() const + { + if (!_root) return PUGIXML_TEXT(""); + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root->value; + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (impl::is_text_node(i) && i->value) + return i->value; + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const + { + return child(name_).child_value(); + } + + PUGI__FN xml_attribute xml_node::first_attribute() const + { + return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_node::last_attribute() const + { + return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN xml_node xml_node::first_child() const + { + return _root ? xml_node(_root->first_child) : xml_node(); + } + + PUGI__FN xml_node xml_node::last_child() const + { + return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + } + + PUGI__FN bool xml_node::set_name(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) + return false; + + return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_node::set_value(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) + return false; + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_node xml_node::append_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::append_child(const char_t* name_) + { + xml_node result = append_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) + { + xml_node result = prepend_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_after(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_before(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::append_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::append_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::prepend_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_after(moved._root, node._root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_before(moved._root, node._root); + + return moved; + } + + PUGI__FN bool xml_node::remove_attribute(const char_t* name_) + { + return remove_attribute(attribute(name_)); + } + + PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) + { + if (!_root || !a._attr) return false; + if (!impl::is_attribute_of(a._attr, _root)) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_attribute(a._attr, _root); + impl::destroy_attribute(a._attr, alloc); + + return true; + } + + PUGI__FN bool xml_node::remove_child(const char_t* name_) + { + return remove_child(child(name_)); + } + + PUGI__FN bool xml_node::remove_child(const xml_node& n) + { + if (!_root || !n._root || n._root->parent != _root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_node(n._root); + impl::destroy_node(n._root, alloc); + + return true; + } + + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + // append_buffer is only valid for elements/documents + if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); + + // get document node + impl::xml_document_struct* doc = &impl::get_document(_root); + + // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense + doc->header |= impl::xml_memory_page_contents_shared_mask; + + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) + impl::xml_memory_page* page = 0; + impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); + (void)page; + + if (!extra) return impl::make_parse_result(status_out_of_memory); + + // add extra buffer to the list + extra->buffer = 0; + extra->next = doc->extra_buffers; + doc->extra_buffers = extra; + + // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level + impl::name_null_sentry sentry(_root); + + return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) + { + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + } + + return xml_node(); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xml_node::path(char_t delimiter) const + { + if (!_root) return string_t(); + + size_t offset = 0; + + for (xml_node_struct* i = _root; i; i = i->parent) + { + offset += (i != _root); + offset += i->name ? impl::strlength(i->name) : 0; + } + + string_t result; + result.resize(offset); + + for (xml_node_struct* j = _root; j; j = j->parent) + { + if (j != _root) + result[--offset] = delimiter; + + if (j->name && *j->name) + { + size_t length = impl::strlength(j->name); + + offset -= length; + memcpy(&result[offset], j->name, length * sizeof(char_t)); + } + } + + assert(offset == 0); + + return result; + } +#endif + + PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const + { + xml_node found = *this; // Current search context. + + if (!_root || !path_ || !path_[0]) return found; + + if (path_[0] == delimiter) + { + // Absolute path; e.g. '/foo/bar' + found = found.root(); + ++path_; + } + + const char_t* path_segment = path_; + + while (*path_segment == delimiter) ++path_segment; + + const char_t* path_segment_end = path_segment; + + while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; + + if (path_segment == path_segment_end) return found; + + const char_t* next_segment = path_segment_end; + + while (*next_segment == delimiter) ++next_segment; + + if (*path_segment == '.' && path_segment + 1 == path_segment_end) + return found.first_element_by_path(next_segment, delimiter); + else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) + return found.parent().first_element_by_path(next_segment, delimiter); + else + { + for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + { + if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) + { + xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); + + if (subsearch) return subsearch; + } + } + + return xml_node(); + } + } + + PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) + { + walker._depth = -1; + + xml_node arg_begin = *this; + if (!walker.begin(arg_begin)) return false; + + xml_node cur = first_child(); + + if (cur) + { + ++walker._depth; + + do + { + xml_node arg_for_each = cur; + if (!walker.for_each(arg_for_each)) + return false; + + if (cur.first_child()) + { + ++walker._depth; + cur = cur.first_child(); + } + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + // Borland C++ workaround + while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) + { + --walker._depth; + cur = cur.parent(); + } + + if (cur != *this) + cur = cur.next_sibling(); + } + } + while (cur && cur != *this); + } + + assert(walker._depth == -1); + + xml_node arg_end = *this; + return walker.end(arg_end); + } + + PUGI__FN size_t xml_node::hash_value() const + { + return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); + } + + PUGI__FN xml_node_struct* xml_node::internal_object() const + { + return _root; + } + + PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + if (!_root) return; + + impl::xml_buffered_writer buffered_writer(writer, encoding); + + impl::node_output(buffered_writer, _root, indent, flags, depth); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding, depth); + } + + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding_wchar, depth); + } +#endif + + PUGI__FN ptrdiff_t xml_node::offset_debug() const + { + if (!_root) return -1; + + impl::xml_document_struct& doc = impl::get_document(_root); + + // we can determine the offset reliably only if there is exactly once parse buffer + if (!doc.buffer || doc.extra_buffers) return -1; + + switch (type()) + { + case node_document: + return 0; + + case node_element: + case node_declaration: + case node_pi: + return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; + + default: + return -1; + } + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) + { + } + + PUGI__FN xml_node_struct* xml_text::_data() const + { + if (!_root || impl::is_text_node(_root)) return _root; + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root; + + for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) + if (impl::is_text_node(node)) + return node; + + return 0; + } + + PUGI__FN xml_node_struct* xml_text::_data_new() + { + xml_node_struct* d = _data(); + if (d) return d; + + return xml_node(_root).append_child(node_pcdata).internal_object(); + } + + PUGI__FN xml_text::xml_text(): _root(0) + { + } + + PUGI__FN static void unspecified_bool_xml_text(xml_text***) + { + } + + PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const + { + return _data() ? unspecified_bool_xml_text : 0; + } + + PUGI__FN bool xml_text::operator!() const + { + return !_data(); + } + + PUGI__FN bool xml_text::empty() const + { + return _data() == 0; + } + + PUGI__FN const char_t* xml_text::get() const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_text::as_string(const char_t* def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : def; + } + + PUGI__FN int xml_text::as_int(int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_int(d->value) : def; + } + + PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_uint(d->value) : def; + } + + PUGI__FN double xml_text::as_double(double def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_double(d->value) : def; + } + + PUGI__FN float xml_text::as_float(float def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_float(d->value) : def; + } + + PUGI__FN bool xml_text::as_bool(bool def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_bool(d->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_text::as_llong(long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_llong(d->value) : def; + } + + PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_ullong(d->value) : def; + } +#endif + + PUGI__FN bool xml_text::set(const char_t* rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; + } + + PUGI__FN bool xml_text::set(int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(float rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(double rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(bool rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_text::set(long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } +#endif + + PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(double rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(float rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(bool rhs) + { + set(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_text& xml_text::operator=(long long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) + { + set(rhs); + return *this; + } +#endif + + PUGI__FN xml_node xml_text::data() const + { + return xml_node(_data()); + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_text& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node_iterator::xml_node_iterator() + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() + { + assert(_wrap._root); + _wrap._root = _wrap._root->next_sibling; + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) + { + xml_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() + { + _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) + { + xml_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator() + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + { + return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + { + return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const + { + assert(_wrap._attr); + return _wrap; + } + + PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const + { + assert(_wrap._attr); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() + { + assert(_wrap._attr); + _wrap._attr = _wrap._attr->next_attribute; + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) + { + xml_attribute_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() + { + _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) + { + xml_attribute_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) + { + } + + PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_named_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_named_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() + { + assert(_wrap._root); + _wrap = _wrap.next_sibling(_name); + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) + { + xml_named_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() + { + if (_wrap._root) + _wrap = _wrap.previous_sibling(_name); + else + { + _wrap = _parent.last_child(); + + if (!impl::strequal(_wrap.name(), _name)) + _wrap = _wrap.previous_sibling(_name); + } + + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) + { + xml_named_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + { + } + + PUGI__FN xml_parse_result::operator bool() const + { + return status == status_ok; + } + + PUGI__FN const char* xml_parse_result::description() const + { + switch (status) + { + case status_ok: return "No error"; + + case status_file_not_found: return "File was not found"; + case status_io_error: return "Error reading from file/stream"; + case status_out_of_memory: return "Could not allocate memory"; + case status_internal_error: return "Internal error occurred"; + + case status_unrecognized_tag: return "Could not determine tag type"; + + case status_bad_pi: return "Error parsing document declaration/processing instruction"; + case status_bad_comment: return "Error parsing comment"; + case status_bad_cdata: return "Error parsing CDATA section"; + case status_bad_doctype: return "Error parsing document type declaration"; + case status_bad_pcdata: return "Error parsing PCDATA section"; + case status_bad_start_element: return "Error parsing start element tag"; + case status_bad_attribute: return "Error parsing element attribute"; + case status_bad_end_element: return "Error parsing end element tag"; + case status_end_element_mismatch: return "Start-end tags mismatch"; + + case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; + + case status_no_document_element: return "No document element found"; + + default: return "Unknown error"; + } + } + + PUGI__FN xml_document::xml_document(): _buffer(0) + { + _create(); + } + + PUGI__FN xml_document::~xml_document() + { + _destroy(); + } + + PUGI__FN void xml_document::reset() + { + _destroy(); + _create(); + } + + PUGI__FN void xml_document::reset(const xml_document& proto) + { + reset(); + + for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) + append_copy(cur); + } + + PUGI__FN void xml_document::_create() + { + assert(!_root); + + #ifdef PUGIXML_COMPACT + const size_t page_offset = sizeof(uint32_t); + #else + const size_t page_offset = 0; + #endif + + // initialize sentinel page + PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); + + // prepare page structure + impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); + assert(page); + + page->busy_size = impl::xml_memory_page_size; + + // setup first page marker + #ifdef PUGIXML_COMPACT + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); + *page->compact_page_marker = sizeof(impl::xml_memory_page); + #endif + + // allocate new root + _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); + _root->prev_sibling_c = _root; + + // setup sentinel page + page->allocator = static_cast(_root); + + // setup hash table pointer in allocator + #ifdef PUGIXML_COMPACT + page->allocator->_hash = &static_cast(_root)->hash; + #endif + + // verify the document allocation + assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); + } + + PUGI__FN void xml_document::_destroy() + { + assert(_root); + + // destroy static storage + if (_buffer) + { + impl::xml_memory::deallocate(_buffer); + _buffer = 0; + } + + // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) + for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) + { + if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); + } + + // destroy dynamic storage, leave sentinel page (it's in static memory) + impl::xml_memory_page* root_page = PUGI__GETPAGE(_root); + assert(root_page && !root_page->prev); + assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); + + for (impl::xml_memory_page* page = root_page->next; page; ) + { + impl::xml_memory_page* next = page->next; + + impl::xml_allocator::deallocate_page(page); + + page = next; + } + + #ifdef PUGIXML_COMPACT + // destroy hash table + static_cast(_root)->hash.clear(); + #endif + + _root = 0; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); + } +#endif + + PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) + { + // Force native encoding (skip autodetection) + #ifdef PUGIXML_WCHAR_MODE + xml_encoding encoding = encoding_wchar; + #else + xml_encoding encoding = encoding_utf8; + #endif + + return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); + } + + PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) + { + return load_string(contents, options); + } + + PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, "rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); + } + + PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + impl::xml_buffered_writer buffered_writer(writer, encoding); + + if ((flags & format_write_bom) && encoding != encoding_latin1) + { + // BOM always represents the codepoint U+FEFF, so just write it in native encoding + #ifdef PUGIXML_WCHAR_MODE + unsigned int bom = 0xfeff; + buffered_writer.write(static_cast(bom)); + #else + buffered_writer.write('\xef', '\xbb', '\xbf'); + #endif + } + + if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) + { + buffered_writer.write_string(PUGIXML_TEXT("'); + if (!(flags & format_raw)) buffered_writer.write('\n'); + } + + impl::node_output(buffered_writer, _root, indent, flags, 0); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding); + } + + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding_wchar); + } +#endif + + PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN xml_node xml_document::document_element() const + { + assert(_root); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (PUGI__NODETYPE(i) == node_element) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + { + assert(str); + + return impl::as_utf8_impl(str, impl::strlength_wide(str)); + } + + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) + { + return impl::as_utf8_impl(str.c_str(), str.size()); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return impl::as_wide_impl(str, strlen(str)); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) + { + return impl::as_wide_impl(str.c_str(), str.size()); + } +#endif + + PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + { + impl::xml_memory::allocate = allocate; + impl::xml_memory::deallocate = deallocate; + } + + PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + { + return impl::xml_memory::allocate; + } + + PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + { + return impl::xml_memory::deallocate; + } +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#ifndef PUGIXML_NO_XPATH +// STL replacements +PUGI__NS_BEGIN + struct equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs == rhs; + } + }; + + struct not_equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs != rhs; + } + }; + + struct less + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } + }; + + struct less_equal + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs <= rhs; + } + }; + + template void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template I min_element(I begin, I end, const Pred& pred) + { + I result = begin; + + for (I it = begin + 1; it != end; ++it) + if (pred(*it, *result)) + result = it; + + return result; + } + + template void reverse(I begin, I end) + { + while (end - begin > 1) swap(*begin++, *--end); + } + + template I unique(I begin, I end) + { + // fast skip head + while (end - begin > 1 && *begin != *(begin + 1)) begin++; + + if (begin == end) return begin; + + // last written element + I write = begin++; + + // merge unique elements + while (begin != end) + { + if (*begin != *write) + *++write = *begin++; + else + begin++; + } + + // past-the-end (write points to live element) + return write + 1; + } + + template void copy_backwards(I begin, I end, I target) + { + while (begin != end) *--target = *--end; + } + + template void insertion_sort(I begin, I end, const Pred& pred, T*) + { + assert(begin != end); + + for (I it = begin + 1; it != end; ++it) + { + T val = *it; + + if (pred(val, *begin)) + { + // move to front + copy_backwards(begin, it, it + 1); + *begin = val; + } + else + { + I hole = it; + + // move hole backwards + while (pred(val, *(hole - 1))) + { + *hole = *(hole - 1); + hole--; + } + + // fill hole with element + *hole = val; + } + } + } + + // std variant for elements with == + template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) + { + I eqbeg = middle, eqend = middle + 1; + + // expand equal range + while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; + while (eqend != end && *eqend == *eqbeg) ++eqend; + + // process outer elements + I ltend = eqbeg, gtbeg = eqend; + + for (;;) + { + // find the element from the right side that belongs to the left one + for (; gtbeg != end; ++gtbeg) + if (!pred(*eqbeg, *gtbeg)) + { + if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); + else break; + } + + // find the element from the left side that belongs to the right one + for (; ltend != begin; --ltend) + if (!pred(*(ltend - 1), *eqbeg)) + { + if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); + else break; + } + + // scanned all elements + if (gtbeg == end && ltend == begin) + { + *out_eqbeg = eqbeg; + *out_eqend = eqend; + return; + } + + // make room for elements by moving equal area + if (gtbeg == end) + { + if (--ltend != --eqbeg) swap(*ltend, *eqbeg); + swap(*eqbeg, *--eqend); + } + else if (ltend == begin) + { + if (eqend != gtbeg) swap(*eqbeg, *eqend); + ++eqend; + swap(*gtbeg++, *eqbeg++); + } + else swap(*gtbeg++, *--ltend); + } + } + + template void median3(I first, I middle, I last, const Pred& pred) + { + if (pred(*middle, *first)) swap(*middle, *first); + if (pred(*last, *middle)) swap(*last, *middle); + if (pred(*middle, *first)) swap(*middle, *first); + } + + template void median(I first, I middle, I last, const Pred& pred) + { + if (last - first <= 40) + { + // median of three for small chunks + median3(first, middle, last, pred); + } + else + { + // median of nine + size_t step = (last - first + 1) / 8; + + median3(first, first + step, first + 2 * step, pred); + median3(middle - step, middle, middle + step, pred); + median3(last - 2 * step, last - step, last, pred); + median3(first + step, middle, last - step, pred); + } + } + + template void sort(I begin, I end, const Pred& pred) + { + // sort large chunks + while (end - begin > 32) + { + // find median element + I middle = begin + (end - begin) / 2; + median(begin, middle, end - 1, pred); + + // partition in three chunks (< = >) + I eqbeg, eqend; + partition(begin, middle, end, pred, &eqbeg, &eqend); + + // loop on larger half + if (eqbeg - begin > end - eqend) + { + sort(eqend, end, pred); + end = eqbeg; + } + else + { + sort(begin, eqbeg, pred); + begin = eqend; + } + } + + // insertion sort small chunk + if (begin != end) insertion_sort(begin, end, pred, &*begin); + } +PUGI__NS_END + +// Allocator used for AST and evaluation stacks +PUGI__NS_BEGIN + static const size_t xpath_memory_page_size = + #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE + PUGIXML_MEMORY_XPATH_PAGE_SIZE + #else + 4096 + #endif + ; + + static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); + + struct xpath_memory_block + { + xpath_memory_block* next; + size_t capacity; + + union + { + char data[xpath_memory_page_size]; + double alignment; + }; + }; + + class xpath_allocator + { + xpath_memory_block* _root; + size_t _root_size; + + public: + #ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf* error_handler; + #endif + + xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) + { + #ifdef PUGIXML_NO_EXCEPTIONS + error_handler = 0; + #endif + } + + void* allocate_nothrow(size_t size) + { + // round size up to block alignment boundary + size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + if (_root_size + size <= _root->capacity) + { + void* buf = &_root->data[0] + _root_size; + _root_size += size; + return buf; + } + else + { + // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests + size_t block_capacity_base = sizeof(_root->data); + size_t block_capacity_req = size + block_capacity_base / 4; + size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; + + size_t block_size = block_capacity + offsetof(xpath_memory_block, data); + + xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); + if (!block) return 0; + + block->next = _root; + block->capacity = block_capacity; + + _root = block; + _root_size = size; + + return block->data; + } + } + + void* allocate(size_t size) + { + void* result = allocate_nothrow(size); + + if (!result) + { + #ifdef PUGIXML_NO_EXCEPTIONS + assert(error_handler); + longjmp(*error_handler, 1); + #else + throw std::bad_alloc(); + #endif + } + + return result; + } + + void* reallocate(void* ptr, size_t old_size, size_t new_size) + { + // round size up to block alignment boundary + old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + // we can only reallocate the last object + assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); + + // adjust root size so that we have not allocated the object at all + bool only_object = (_root_size == old_size); + + if (ptr) _root_size -= old_size; + + // allocate a new version (this will obviously reuse the memory if possible) + void* result = allocate(new_size); + assert(result); + + // we have a new block + if (result != ptr && ptr) + { + // copy old data + assert(new_size >= old_size); + memcpy(result, ptr, old_size); + + // free the previous page if it had no other objects + if (only_object) + { + assert(_root->data == result); + assert(_root->next); + + xpath_memory_block* next = _root->next->next; + + if (next) + { + // deallocate the whole page, unless it was the first one + xml_memory::deallocate(_root->next); + _root->next = next; + } + } + } + + return result; + } + + void revert(const xpath_allocator& state) + { + // free all new pages + xpath_memory_block* cur = _root; + + while (cur != state._root) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + + // restore state + _root = state._root; + _root_size = state._root_size; + } + + void release() + { + xpath_memory_block* cur = _root; + assert(cur); + + while (cur->next) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + } + }; + + struct xpath_allocator_capture + { + xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) + { + } + + ~xpath_allocator_capture() + { + _target->revert(_state); + } + + xpath_allocator* _target; + xpath_allocator _state; + }; + + struct xpath_stack + { + xpath_allocator* result; + xpath_allocator* temp; + }; + + struct xpath_stack_data + { + xpath_memory_block blocks[2]; + xpath_allocator result; + xpath_allocator temp; + xpath_stack stack; + + #ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf error_handler; + #endif + + xpath_stack_data(): result(blocks + 0), temp(blocks + 1) + { + blocks[0].next = blocks[1].next = 0; + blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); + + stack.result = &result; + stack.temp = &temp; + + #ifdef PUGIXML_NO_EXCEPTIONS + result.error_handler = temp.error_handler = &error_handler; + #endif + } + + ~xpath_stack_data() + { + result.release(); + temp.release(); + } + }; +PUGI__NS_END + +// String class +PUGI__NS_BEGIN + class xpath_string + { + const char_t* _buffer; + bool _uses_heap; + size_t _length_heap; + + static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) + { + char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); + assert(result); + + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; + + return result; + } + + xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) + { + } + + public: + static xpath_string from_const(const char_t* str) + { + return xpath_string(str, false, 0); + } + + static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) + { + assert(begin <= end && *end == 0); + + return xpath_string(begin, true, static_cast(end - begin)); + } + + static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) + { + assert(begin <= end); + + size_t length = static_cast(end - begin); + + return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); + } + + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) + { + } + + void append(const xpath_string& o, xpath_allocator* alloc) + { + // skip empty sources + if (!*o._buffer) return; + + // fast append for constant empty target and constant source + if (!*_buffer && !_uses_heap && !o._uses_heap) + { + _buffer = o._buffer; + } + else + { + // need to make heap copy + size_t target_length = length(); + size_t source_length = o.length(); + size_t result_length = target_length + source_length; + + // allocate new buffer + char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); + assert(result); + + // append first string to the new buffer in case there was no reallocation + if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); + + // append second string to the new buffer + memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); + result[result_length] = 0; + + // finalize + _buffer = result; + _uses_heap = true; + _length_heap = result_length; + } + } + + const char_t* c_str() const + { + return _buffer; + } + + size_t length() const + { + return _uses_heap ? _length_heap : strlength(_buffer); + } + + char_t* data(xpath_allocator* alloc) + { + // make private heap copy + if (!_uses_heap) + { + size_t length_ = strlength(_buffer); + + _buffer = duplicate_string(_buffer, length_, alloc); + _uses_heap = true; + _length_heap = length_; + } + + return const_cast(_buffer); + } + + bool empty() const + { + return *_buffer == 0; + } + + bool operator==(const xpath_string& o) const + { + return strequal(_buffer, o._buffer); + } + + bool operator!=(const xpath_string& o) const + { + return !strequal(_buffer, o._buffer); + } + + bool uses_heap() const + { + return _uses_heap; + } + }; +PUGI__NS_END + +PUGI__NS_BEGIN + PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) + { + while (*pattern && *string == *pattern) + { + string++; + pattern++; + } + + return *pattern == 0; + } + + PUGI__FN const char_t* find_char(const char_t* s, char_t c) + { + #ifdef PUGIXML_WCHAR_MODE + return wcschr(s, c); + #else + return strchr(s, c); + #endif + } + + PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) + { + #ifdef PUGIXML_WCHAR_MODE + // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) + return (*p == 0) ? s : wcsstr(s, p); + #else + return strstr(s, p); + #endif + } + + // Converts symbol to lower case, if it is an ASCII one + PUGI__FN char_t tolower_ascii(char_t ch) + { + return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; + } + + PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + { + if (na.attribute()) + return xpath_string::from_const(na.attribute().value()); + else + { + xml_node n = na.node(); + + switch (n.type()) + { + case node_pcdata: + case node_cdata: + case node_comment: + case node_pi: + return xpath_string::from_const(n.value()); + + case node_document: + case node_element: + { + xpath_string result; + + // element nodes can have value if parse_embed_pcdata was used + if (n.value()[0]) + result.append(xpath_string::from_const(n.value()), alloc); + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + if (cur.type() == node_pcdata || cur.type() == node_cdata) + result.append(xpath_string::from_const(cur.value()), alloc); + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur != n) + cur = cur.parent(); + + if (cur != n) cur = cur.next_sibling(); + } + } + + return result; + } + + default: + return xpath_string(); + } + } + } + + PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) + { + assert(ln->parent == rn->parent); + + // there is no common ancestor (the shared parent is null), nodes are from different documents + if (!ln->parent) return ln < rn; + + // determine sibling order + xml_node_struct* ls = ln; + xml_node_struct* rs = rn; + + while (ls && rs) + { + if (ls == rn) return true; + if (rs == ln) return false; + + ls = ls->next_sibling; + rs = rs->next_sibling; + } + + // if rn sibling chain ended ln must be before rn + return !rs; + } + + PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) + { + // find common ancestor at the same depth, if any + xml_node_struct* lp = ln; + xml_node_struct* rp = rn; + + while (lp && rp && lp->parent != rp->parent) + { + lp = lp->parent; + rp = rp->parent; + } + + // parents are the same! + if (lp && rp) return node_is_before_sibling(lp, rp); + + // nodes are at different depths, need to normalize heights + bool left_higher = !lp; + + while (lp) + { + lp = lp->parent; + ln = ln->parent; + } + + while (rp) + { + rp = rp->parent; + rn = rn->parent; + } + + // one node is the ancestor of the other + if (ln == rn) return left_higher; + + // find common ancestor... again + while (ln->parent != rn->parent) + { + ln = ln->parent; + rn = rn->parent; + } + + return node_is_before_sibling(ln, rn); + } + + PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) + { + while (node && node != parent) node = node->parent; + + return parent && node == parent; + } + + PUGI__FN const void* document_buffer_order(const xpath_node& xnode) + { + xml_node_struct* node = xnode.node().internal_object(); + + if (node) + { + if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) + { + if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; + if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; + } + + return 0; + } + + xml_attribute_struct* attr = xnode.attribute().internal_object(); + + if (attr) + { + if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) + { + if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; + if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; + } + + return 0; + } + + return 0; + } + + struct document_order_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + // optimized document order based check + const void* lo = document_buffer_order(lhs); + const void* ro = document_buffer_order(rhs); + + if (lo && ro) return lo < ro; + + // slow comparison + xml_node ln = lhs.node(), rn = rhs.node(); + + // compare attributes + if (lhs.attribute() && rhs.attribute()) + { + // shared parent + if (lhs.parent() == rhs.parent()) + { + // determine sibling order + for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) + if (a == rhs.attribute()) + return true; + + return false; + } + + // compare attribute parents + ln = lhs.parent(); + rn = rhs.parent(); + } + else if (lhs.attribute()) + { + // attributes go after the parent element + if (lhs.parent() == rhs.node()) return false; + + ln = lhs.parent(); + } + else if (rhs.attribute()) + { + // attributes go after the parent element + if (rhs.parent() == lhs.node()) return true; + + rn = rhs.parent(); + } + + if (ln == rn) return false; + + if (!ln || !rn) return ln < rn; + + return node_is_before(ln.internal_object(), rn.internal_object()); + } + }; + + struct duplicate_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; + else return rhs.attribute() ? false : lhs.node() < rhs.node(); + } + }; + + PUGI__FN double gen_nan() + { + #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) + PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); + typedef uint32_t UI; // BCC5 workaround + union { float f; UI i; } u; + u.i = 0x7fc00000; + return u.f; + #else + // fallback + const volatile double zero = 0.0; + return zero / zero; + #endif + } + + PUGI__FN bool is_nan(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + return !!_isnan(value); + #elif defined(fpclassify) && defined(FP_NAN) + return fpclassify(value) == FP_NAN; + #else + // fallback + const volatile double v = value; + return v != v; + #endif + } + + PUGI__FN const char_t* convert_number_to_string_special(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; + if (_isnan(value)) return PUGIXML_TEXT("NaN"); + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) + switch (fpclassify(value)) + { + case FP_NAN: + return PUGIXML_TEXT("NaN"); + + case FP_INFINITE: + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + + case FP_ZERO: + return PUGIXML_TEXT("0"); + + default: + return 0; + } + #else + // fallback + const volatile double v = value; + + if (v == 0) return PUGIXML_TEXT("0"); + if (v != v) return PUGIXML_TEXT("NaN"); + if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + return 0; + #endif + } + + PUGI__FN bool convert_number_to_boolean(double value) + { + return (value != 0 && !is_nan(value)); + } + + PUGI__FN void truncate_zeros(char* begin, char* end) + { + while (begin != end && end[-1] == '0') end--; + + *end = 0; + } + + // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get base values + int sign, exponent; + _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); + + // truncate redundant zeros + truncate_zeros(buffer, buffer + strlen(buffer)); + + // fill results + *out_mantissa = buffer; + *out_exponent = exponent; + } +#else + PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get a scientific notation value with IEEE DBL_DIG decimals + sprintf(buffer, "%.*e", DBL_DIG, value); + assert(strlen(buffer) < buffer_size); + (void)!buffer_size; + + // get the exponent (possibly negative) + char* exponent_string = strchr(buffer, 'e'); + assert(exponent_string); + + int exponent = atoi(exponent_string + 1); + + // extract mantissa string: skip sign + char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; + assert(mantissa[0] != '0' && mantissa[1] == '.'); + + // divide mantissa by 10 to eliminate integer part + mantissa[1] = mantissa[0]; + mantissa++; + exponent++; + + // remove extra mantissa digits and zero-terminate mantissa + truncate_zeros(mantissa, exponent_string); + + // fill results + *out_mantissa = mantissa; + *out_exponent = exponent; + } +#endif + + PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) + { + // try special number conversion + const char_t* special = convert_number_to_string_special(value); + if (special) return xpath_string::from_const(special); + + // get mantissa + exponent form + char mantissa_buffer[32]; + + char* mantissa; + int exponent; + convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); + + // allocate a buffer of suitable length for the number + size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; + char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); + assert(result); + + // make the number! + char_t* s = result; + + // sign + if (value < 0) *s++ = '-'; + + // integer part + if (exponent <= 0) + { + *s++ = '0'; + } + else + { + while (exponent > 0) + { + assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); + *s++ = *mantissa ? *mantissa++ : '0'; + exponent--; + } + } + + // fractional part + if (*mantissa) + { + // decimal point + *s++ = '.'; + + // extra zeroes from negative exponent + while (exponent < 0) + { + *s++ = '0'; + exponent++; + } + + // extra mantissa digits + while (*mantissa) + { + assert(static_cast(*mantissa - '0') <= 9); + *s++ = *mantissa++; + } + } + + // zero-terminate + assert(s < result + result_size); + *s = 0; + + return xpath_string::from_heap_preallocated(result, s); + } + + PUGI__FN bool check_string_to_number_format(const char_t* string) + { + // parse leading whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + // parse sign + if (*string == '-') ++string; + + if (!*string) return false; + + // if there is no integer part, there should be a decimal part with at least one digit + if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; + + // parse integer part + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + + // parse decimal part + if (*string == '.') + { + ++string; + + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + } + + // parse trailing whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + return *string == 0; + } + + PUGI__FN double convert_string_to_number(const char_t* string) + { + // check string format + if (!check_string_to_number_format(string)) return gen_nan(); + + // parse string + #ifdef PUGIXML_WCHAR_MODE + return wcstod(string, 0); + #else + return strtod(string, 0); + #endif + } + + PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform conversion + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = convert_string_to_number(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } + + PUGI__FN double round_nearest(double value) + { + return floor(value + 0.5); + } + + PUGI__FN double round_nearest_nzero(double value) + { + // same as round_nearest, but returns -0 for [-0.5, -0] + // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) + return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); + } + + PUGI__FN const char_t* qualified_name(const xpath_node& node) + { + return node.attribute() ? node.attribute().name() : node.node().name(); + } + + PUGI__FN const char_t* local_name(const xpath_node& node) + { + const char_t* name = qualified_name(node); + const char_t* p = find_char(name, ':'); + + return p ? p + 1 : name; + } + + struct namespace_uri_predicate + { + const char_t* prefix; + size_t prefix_length; + + namespace_uri_predicate(const char_t* name) + { + const char_t* pos = find_char(name, ':'); + + prefix = pos ? name : 0; + prefix_length = pos ? static_cast(pos - name) : 0; + } + + bool operator()(xml_attribute a) const + { + const char_t* name = a.name(); + + if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; + + return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; + } + }; + + PUGI__FN const char_t* namespace_uri(xml_node node) + { + namespace_uri_predicate pred = node.name(); + + xml_node p = node; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) + { + namespace_uri_predicate pred = attr.name(); + + // Default namespace does not apply to attributes + if (!pred.prefix) return PUGIXML_TEXT(""); + + xml_node p = parent; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(const xpath_node& node) + { + return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); + } + + PUGI__FN char_t* normalize_space(char_t* buffer) + { + char_t* write = buffer; + + for (char_t* it = buffer; *it; ) + { + char_t ch = *it++; + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + // replace whitespace sequence with single space + while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; + + // avoid leading spaces + if (write != buffer) *write++ = ' '; + } + else *write++ = ch; + } + + // remove trailing space + if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) + { + char_t* write = buffer; + + while (*buffer) + { + PUGI__DMC_VOLATILE char_t ch = *buffer++; + + const char_t* pos = find_char(from, ch); + + if (!pos) + *write++ = ch; // do not process + else if (static_cast(pos - from) < to_length) + *write++ = to[pos - from]; // replace + } + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) + { + unsigned char table[128] = {0}; + + while (*from) + { + unsigned int fc = static_cast(*from); + unsigned int tc = static_cast(*to); + + if (fc >= 128 || tc >= 128) + return 0; + + // code=128 means "skip character" + if (!table[fc]) + table[fc] = static_cast(tc ? tc : 128); + + from++; + if (tc) to++; + } + + for (int i = 0; i < 128; ++i) + if (!table[i]) + table[i] = static_cast(i); + + void* result = alloc->allocate_nothrow(sizeof(table)); + + if (result) + { + memcpy(result, table, sizeof(table)); + } + + return static_cast(result); + } + + PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) + { + char_t* write = buffer; + + while (*buffer) + { + char_t ch = *buffer++; + unsigned int index = static_cast(ch); + + if (index < 128) + { + unsigned char code = table[index]; + + // code=128 means "skip character" (table size is 128 so 128 can be a special value) + // this code skips these characters without extra branches + *write = static_cast(code); + write += 1 - (code >> 7); + } + else + { + *write++ = ch; + } + } + + // zero-terminate + *write = 0; + + return write; + } + + inline bool is_xpath_attribute(const char_t* name) + { + return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); + } + + struct xpath_variable_boolean: xpath_variable + { + xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) + { + } + + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + xpath_variable_number(): xpath_variable(xpath_type_number), value(0) + { + } + + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + xpath_variable_string(): xpath_variable(xpath_type_string), value(0) + { + } + + ~xpath_variable_string() + { + if (value) xml_memory::deallocate(value); + } + + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_variable_node_set(): xpath_variable(xpath_type_node_set) + { + } + + xpath_node_set value; + char_t name[1]; + }; + + static const xpath_node_set dummy_node_set; + + PUGI__FN unsigned int hash_string(const char_t* str) + { + // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) + unsigned int result = 0; + + while (*str) + { + result += static_cast(*str++); + result += result << 10; + result ^= result >> 6; + } + + result += result << 3; + result ^= result >> 11; + result += result << 15; + + return result; + } + + template PUGI__FN T* new_xpath_variable(const char_t* name) + { + size_t length = strlength(name); + if (length == 0) return 0; // empty variable names are invalid + + // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters + void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); + if (!memory) return 0; + + T* result = new (memory) T(); + + memcpy(result->name, name, (length + 1) * sizeof(char_t)); + + return result; + } + + PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) + { + switch (type) + { + case xpath_type_node_set: + return new_xpath_variable(name); + + case xpath_type_number: + return new_xpath_variable(name); + + case xpath_type_string: + return new_xpath_variable(name); + + case xpath_type_boolean: + return new_xpath_variable(name); + + default: + return 0; + } + } + + template PUGI__FN void delete_xpath_variable(T* var) + { + var->~T(); + xml_memory::deallocate(var); + } + + PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) + { + switch (type) + { + case xpath_type_node_set: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_number: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_string: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_boolean: + delete_xpath_variable(static_cast(var)); + break; + + default: + assert(false && "Invalid variable type"); + } + } + + PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + { + switch (rhs->type()) + { + case xpath_type_node_set: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_number: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_string: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_boolean: + return lhs->set(static_cast(rhs)->value); + + default: + assert(false && "Invalid variable type"); + return false; + } + } + + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform lookup + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = set->get(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } +PUGI__NS_END + +// Internal node set class +PUGI__NS_BEGIN + PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) + { + if (end - begin < 2) + return xpath_node_set::type_sorted; + + document_order_comparator cmp; + + bool first = cmp(begin[0], begin[1]); + + for (const xpath_node* it = begin + 1; it + 1 < end; ++it) + if (cmp(it[0], it[1]) != first) + return xpath_node_set::type_unsorted; + + return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; + } + + PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) + { + xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + if (type == xpath_node_set::type_unsorted) + { + xpath_node_set::type_t sorted = xpath_get_order(begin, end); + + if (sorted == xpath_node_set::type_unsorted) + { + sort(begin, end, document_order_comparator()); + + type = xpath_node_set::type_sorted; + } + else + type = sorted; + } + + if (type != order) reverse(begin, end); + + return order; + } + + PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) + { + if (begin == end) return xpath_node(); + + switch (type) + { + case xpath_node_set::type_sorted: + return *begin; + + case xpath_node_set::type_sorted_reverse: + return *(end - 1); + + case xpath_node_set::type_unsorted: + return *min_element(begin, end, document_order_comparator()); + + default: + assert(false && "Invalid node set type"); + return xpath_node(); + } + } + + class xpath_node_set_raw + { + xpath_node_set::type_t _type; + + xpath_node* _begin; + xpath_node* _end; + xpath_node* _eos; + + public: + xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) + { + } + + xpath_node* begin() const + { + return _begin; + } + + xpath_node* end() const + { + return _end; + } + + bool empty() const + { + return _begin == _end; + } + + size_t size() const + { + return static_cast(_end - _begin); + } + + xpath_node first() const + { + return xpath_first(_begin, _end, _type); + } + + void push_back_grow(const xpath_node& node, xpath_allocator* alloc); + + void push_back(const xpath_node& node, xpath_allocator* alloc) + { + if (_end != _eos) + *_end++ = node; + else + push_back_grow(node, alloc); + } + + void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) + { + if (begin_ == end_) return; + + size_t size_ = static_cast(_end - _begin); + size_t capacity = static_cast(_eos - _begin); + size_t count = static_cast(end_ - begin_); + + if (size_ + count > capacity) + { + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); + assert(data); + + // finalize + _begin = data; + _end = data + size_; + _eos = data + size_ + count; + } + + memcpy(_end, begin_, count * sizeof(xpath_node)); + _end += count; + } + + void sort_do() + { + _type = xpath_sort(_begin, _end, _type, false); + } + + void truncate(xpath_node* pos) + { + assert(_begin <= pos && pos <= _end); + + _end = pos; + } + + void remove_duplicates() + { + if (_type == xpath_node_set::type_unsorted) + sort(_begin, _end, duplicate_comparator()); + + _end = unique(_begin, _end); + } + + xpath_node_set::type_t type() const + { + return _type; + } + + void set_type(xpath_node_set::type_t value) + { + _type = value; + } + }; + + PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) + { + size_t capacity = static_cast(_eos - _begin); + + // get new capacity (1.5x rule) + size_t new_capacity = capacity + capacity / 2 + 1; + + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); + assert(data); + + // finalize + _begin = data; + _end = data + capacity; + _eos = data + new_capacity; + + // push + *_end++ = node; + } +PUGI__NS_END + +PUGI__NS_BEGIN + struct xpath_context + { + xpath_node n; + size_t position, size; + + xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) + { + } + }; + + enum lexeme_t + { + lex_none = 0, + lex_equal, + lex_not_equal, + lex_less, + lex_greater, + lex_less_or_equal, + lex_greater_or_equal, + lex_plus, + lex_minus, + lex_multiply, + lex_union, + lex_var_ref, + lex_open_brace, + lex_close_brace, + lex_quoted_string, + lex_number, + lex_slash, + lex_double_slash, + lex_open_square_brace, + lex_close_square_brace, + lex_string, + lex_comma, + lex_axis_attribute, + lex_dot, + lex_double_dot, + lex_double_colon, + lex_eof + }; + + struct xpath_lexer_string + { + const char_t* begin; + const char_t* end; + + xpath_lexer_string(): begin(0), end(0) + { + } + + bool operator==(const char_t* other) const + { + size_t length = static_cast(end - begin); + + return strequalrange(other, begin, length); + } + }; + + class xpath_lexer + { + const char_t* _cur; + const char_t* _cur_lexeme_pos; + xpath_lexer_string _cur_lexeme_contents; + + lexeme_t _cur_lexeme; + + public: + explicit xpath_lexer(const char_t* query): _cur(query) + { + next(); + } + + const char_t* state() const + { + return _cur; + } + + void next() + { + const char_t* cur = _cur; + + while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; + + // save lexeme position for error reporting + _cur_lexeme_pos = cur; + + switch (*cur) + { + case 0: + _cur_lexeme = lex_eof; + break; + + case '>': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_greater_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_greater; + } + break; + + case '<': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_less_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_less; + } + break; + + case '!': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_not_equal; + } + else + { + _cur_lexeme = lex_none; + } + break; + + case '=': + cur += 1; + _cur_lexeme = lex_equal; + + break; + + case '+': + cur += 1; + _cur_lexeme = lex_plus; + + break; + + case '-': + cur += 1; + _cur_lexeme = lex_minus; + + break; + + case '*': + cur += 1; + _cur_lexeme = lex_multiply; + + break; + + case '|': + cur += 1; + _cur_lexeme = lex_union; + + break; + + case '$': + cur += 1; + + if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_var_ref; + } + else + { + _cur_lexeme = lex_none; + } + + break; + + case '(': + cur += 1; + _cur_lexeme = lex_open_brace; + + break; + + case ')': + cur += 1; + _cur_lexeme = lex_close_brace; + + break; + + case '[': + cur += 1; + _cur_lexeme = lex_open_square_brace; + + break; + + case ']': + cur += 1; + _cur_lexeme = lex_close_square_brace; + + break; + + case ',': + cur += 1; + _cur_lexeme = lex_comma; + + break; + + case '/': + if (*(cur+1) == '/') + { + cur += 2; + _cur_lexeme = lex_double_slash; + } + else + { + cur += 1; + _cur_lexeme = lex_slash; + } + break; + + case '.': + if (*(cur+1) == '.') + { + cur += 2; + _cur_lexeme = lex_double_dot; + } + else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) + { + _cur_lexeme_contents.begin = cur; // . + + ++cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else + { + cur += 1; + _cur_lexeme = lex_dot; + } + break; + + case '@': + cur += 1; + _cur_lexeme = lex_axis_attribute; + + break; + + case '"': + case '\'': + { + char_t terminator = *cur; + + ++cur; + + _cur_lexeme_contents.begin = cur; + while (*cur && *cur != terminator) cur++; + _cur_lexeme_contents.end = cur; + + if (!*cur) + _cur_lexeme = lex_none; + else + { + cur += 1; + _cur_lexeme = lex_quoted_string; + } + + break; + } + + case ':': + if (*(cur+1) == ':') + { + cur += 2; + _cur_lexeme = lex_double_colon; + } + else + { + _cur_lexeme = lex_none; + } + break; + + default: + if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + if (*cur == '.') + { + cur++; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':') + { + if (cur[1] == '*') // namespace test ncname:* + { + cur += 2; // :* + } + else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_string; + } + else + { + _cur_lexeme = lex_none; + } + } + + _cur = cur; + } + + lexeme_t current() const + { + return _cur_lexeme; + } + + const char_t* current_pos() const + { + return _cur_lexeme_pos; + } + + const xpath_lexer_string& contents() const + { + assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); + + return _cur_lexeme_contents; + } + }; + + enum ast_type_t + { + ast_unknown, + ast_op_or, // left or right + ast_op_and, // left and right + ast_op_equal, // left = right + ast_op_not_equal, // left != right + ast_op_less, // left < right + ast_op_greater, // left > right + ast_op_less_or_equal, // left <= right + ast_op_greater_or_equal, // left >= right + ast_op_add, // left + right + ast_op_subtract, // left - right + ast_op_multiply, // left * right + ast_op_divide, // left / right + ast_op_mod, // left % right + ast_op_negate, // left - right + ast_op_union, // left | right + ast_predicate, // apply predicate to set; next points to next predicate + ast_filter, // select * from left where right + ast_string_constant, // string constant + ast_number_constant, // number constant + ast_variable, // variable + ast_func_last, // last() + ast_func_position, // position() + ast_func_count, // count(left) + ast_func_id, // id(left) + ast_func_local_name_0, // local-name() + ast_func_local_name_1, // local-name(left) + ast_func_namespace_uri_0, // namespace-uri() + ast_func_namespace_uri_1, // namespace-uri(left) + ast_func_name_0, // name() + ast_func_name_1, // name(left) + ast_func_string_0, // string() + ast_func_string_1, // string(left) + ast_func_concat, // concat(left, right, siblings) + ast_func_starts_with, // starts_with(left, right) + ast_func_contains, // contains(left, right) + ast_func_substring_before, // substring-before(left, right) + ast_func_substring_after, // substring-after(left, right) + ast_func_substring_2, // substring(left, right) + ast_func_substring_3, // substring(left, right, third) + ast_func_string_length_0, // string-length() + ast_func_string_length_1, // string-length(left) + ast_func_normalize_space_0, // normalize-space() + ast_func_normalize_space_1, // normalize-space(left) + ast_func_translate, // translate(left, right, third) + ast_func_boolean, // boolean(left) + ast_func_not, // not(left) + ast_func_true, // true() + ast_func_false, // false() + ast_func_lang, // lang(left) + ast_func_number_0, // number() + ast_func_number_1, // number(left) + ast_func_sum, // sum(left) + ast_func_floor, // floor(left) + ast_func_ceiling, // ceiling(left) + ast_func_round, // round(left) + ast_step, // process set left with step + ast_step_root, // select root node + + ast_opt_translate_table, // translate(left, right, third) where right/third are constants + ast_opt_compare_attribute // @name = 'string' + }; + + enum axis_t + { + axis_ancestor, + axis_ancestor_or_self, + axis_attribute, + axis_child, + axis_descendant, + axis_descendant_or_self, + axis_following, + axis_following_sibling, + axis_namespace, + axis_parent, + axis_preceding, + axis_preceding_sibling, + axis_self + }; + + enum nodetest_t + { + nodetest_none, + nodetest_name, + nodetest_type_node, + nodetest_type_comment, + nodetest_type_pi, + nodetest_type_text, + nodetest_pi, + nodetest_all, + nodetest_all_in_namespace + }; + + enum predicate_t + { + predicate_default, + predicate_posinv, + predicate_constant, + predicate_constant_one + }; + + enum nodeset_eval_t + { + nodeset_eval_all, + nodeset_eval_any, + nodeset_eval_first + }; + + template struct axis_to_type + { + static const axis_t axis; + }; + + template const axis_t axis_to_type::axis = N; + + class xpath_ast_node + { + private: + // node type + char _type; + char _rettype; + + // for ast_step + char _axis; + + // for ast_step/ast_predicate/ast_filter + char _test; + + // tree node structure + xpath_ast_node* _left; + xpath_ast_node* _right; + xpath_ast_node* _next; + + union + { + // value for ast_string_constant + const char_t* string; + // value for ast_number_constant + double number; + // variable for ast_variable + xpath_variable* variable; + // node test for ast_step (node name/namespace/node type/pi target) + const char_t* nodetest; + // table for ast_opt_translate_table + const unsigned char* table; + } _data; + + xpath_ast_node(const xpath_ast_node&); + xpath_ast_node& operator=(const xpath_ast_node&); + + template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + { + if (lt == xpath_type_boolean || rt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number || rt == xpath_type_number) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_string || rt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string ls = lhs->eval_string(c, stack); + xpath_string rs = rhs->eval_string(c, stack); + + return comp(ls, rs); + } + } + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) + return true; + } + + return false; + } + else + { + if (lt == xpath_type_node_set) + { + swap(lhs, rhs); + swap(lt, rt); + } + + if (lt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string l = lhs->eval_string(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, string_value(*ri, stack.result))) + return true; + } + + return false; + } + } + + assert(false && "Wrong types"); + return false; + } + + static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) + { + return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; + } + + template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + double l = convert_string_to_number(string_value(*li, stack.result).c_str()); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture crii(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + } + + return false; + } + else if (lt != xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_node_set && rt != xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + double r = rhs->eval_number(c, stack); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) + return true; + } + + return false; + } + else + { + assert(false && "Wrong types"); + return false; + } + } + + static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() != xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_boolean(c, stack)) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_number(c, stack) == i) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + xpath_context c(xpath_node(), 1, size); + + double er = expr->eval_number(c, stack); + + if (er >= 1.0 && er <= size) + { + size_t eri = static_cast(er); + + if (er == eri) + { + xpath_node r = last[eri - 1]; + + *last++ = r; + } + } + + ns.truncate(last); + } + + void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) + { + if (ns.size() == first) return; + + assert(_type == ast_filter || _type == ast_predicate); + + if (_test == predicate_constant || _test == predicate_constant_one) + apply_predicate_number_const(ns, first, _right, stack); + else if (_right->rettype() == xpath_type_number) + apply_predicate_number(ns, first, _right, stack, once); + else + apply_predicate_boolean(ns, first, _right, stack, once); + } + + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) + { + if (ns.size() == first) return; + + bool last_once = eval_once(ns.type(), eval); + + for (xpath_ast_node* pred = _right; pred; pred = pred->_next) + pred->apply_predicate(ns, first, stack, !pred->_next && last_once); + } + + bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) + { + assert(a); + + const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); + + switch (_test) + { + case nodetest_name: + if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_type_node: + case nodetest_all: + if (is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + default: + ; + } + + return false; + } + + bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) + { + assert(n); + + xml_node_type type = PUGI__NODETYPE(n); + + switch (_test) + { + case nodetest_name: + if (type == node_element && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_node: + ns.push_back(xml_node(n), alloc); + return true; + + case nodetest_type_comment: + if (type == node_comment) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_text: + if (type == node_pcdata || type == node_cdata) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_pi: + if (type == node_pi) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_pi: + if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all: + if (type == node_element) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + default: + assert(false && "Unknown axis"); + } + + return false; + } + + template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_attribute: + { + for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) + if (step_push(ns, a, n, alloc) & once) + return; + + break; + } + + case axis_child: + { + for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_descendant: + case axis_descendant_or_self: + { + if (axis == axis_descendant_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->first_child; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (cur == n) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_following_sibling: + { + for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_preceding_sibling: + { + for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_following: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_preceding: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->prev_sibling_c; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child->prev_sibling_c; + else + { + // leaf node, can't be ancestor + if (step_push(ns, cur, alloc) & once) + return; + + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + + if (!node_is_ancestor(cur, n)) + if (step_push(ns, cur, alloc) & once) + return; + } + + cur = cur->prev_sibling_c; + } + } + + break; + } + + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->parent; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_self: + { + step_push(ns, n, alloc); + + break; + } + + case axis_parent: + { + if (n->parent) + step_push(ns, n->parent, alloc); + + break; + } + + default: + assert(false && "Unimplemented axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test + if (step_push(ns, a, p, alloc) & once) + return; + + xml_node_struct* cur = p; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_descendant_or_self: + case axis_self: + { + if (_test == nodetest_type_node) // reject attributes based on principal node type test + step_push(ns, a, p, alloc); + + break; + } + + case axis_following: + { + xml_node_struct* cur = p; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + + if (step_push(ns, cur, alloc) & once) + return; + } + + break; + } + + case axis_parent: + { + step_push(ns, p, alloc); + + break; + } + + case axis_preceding: + { + // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding + step_fill(ns, p, alloc, once, v); + break; + } + + default: + assert(false && "Unimplemented axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); + + if (xn.node()) + step_fill(ns, xn.node().internal_object(), alloc, once, v); + else if (axis_has_attributes && xn.attribute() && xn.parent()) + step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); + } + + template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) + { + const axis_t axis = T::axis; + const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); + const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + bool once = + (axis == axis_attribute && _test == nodetest_name) || + (!_right && eval_once(axis_type, eval)) || + (_right && !_right->_next && _right->_test == predicate_constant_one); + + xpath_node_set_raw ns; + ns.set_type(axis_type); + + if (_left) + { + xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); + + // self axis preserves the original order + if (axis == axis_self) ns.set_type(s.type()); + + for (const xpath_node* it = s.begin(); it != s.end(); ++it) + { + size_t size = ns.size(); + + // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes + if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); + + step_fill(ns, *it, stack.result, once, v); + if (_right) apply_predicates(ns, size, stack, eval); + } + } + else + { + step_fill(ns, c.n, stack.result, once, v); + if (_right) apply_predicates(ns, 0, stack, eval); + } + + // child, attribute and self axes always generate unique set of nodes + // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice + if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) + ns.remove_duplicates(); + + return ns; + } + + public: + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_string_constant); + _data.string = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_number_constant); + _data.number = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_variable); + _data.variable = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) + { + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) + { + assert(type == ast_step); + _data.nodetest = contents; + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) + { + assert(type == ast_filter || type == ast_predicate); + } + + void set_next(xpath_ast_node* value) + { + _next = value; + } + + void set_right(xpath_ast_node* value) + { + _right = value; + } + + bool eval_boolean(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_or: + return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); + + case ast_op_and: + return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); + + case ast_op_equal: + return compare_eq(_left, _right, c, stack, equal_to()); + + case ast_op_not_equal: + return compare_eq(_left, _right, c, stack, not_equal_to()); + + case ast_op_less: + return compare_rel(_left, _right, c, stack, less()); + + case ast_op_greater: + return compare_rel(_right, _left, c, stack, less()); + + case ast_op_less_or_equal: + return compare_rel(_left, _right, c, stack, less_equal()); + + case ast_op_greater_or_equal: + return compare_rel(_right, _left, c, stack, less_equal()); + + case ast_func_starts_with: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return starts_with(lr.c_str(), rr.c_str()); + } + + case ast_func_contains: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return find_substring(lr.c_str(), rr.c_str()) != 0; + } + + case ast_func_boolean: + return _left->eval_boolean(c, stack); + + case ast_func_not: + return !_left->eval_boolean(c, stack); + + case ast_func_true: + return true; + + case ast_func_false: + return false; + + case ast_func_lang: + { + if (c.n.attribute()) return false; + + xpath_allocator_capture cr(stack.result); + + xpath_string lang = _left->eval_string(c, stack); + + for (xml_node n = c.n.node(); n; n = n.parent()) + { + xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); + + if (a) + { + const char_t* value = a.value(); + + // strnicmp / strncasecmp is not portable + for (const char_t* lit = lang.c_str(); *lit; ++lit) + { + if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; + ++value; + } + + return *value == 0 || *value == '-'; + } + } + + return false; + } + + case ast_opt_compare_attribute: + { + const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); + + xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); + + return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_boolean) + return _data.variable->get_boolean(); + + // fallthrough to type conversion + } + + default: + { + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); + return false; + } + } + } + } + + double eval_number(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_add: + return _left->eval_number(c, stack) + _right->eval_number(c, stack); + + case ast_op_subtract: + return _left->eval_number(c, stack) - _right->eval_number(c, stack); + + case ast_op_multiply: + return _left->eval_number(c, stack) * _right->eval_number(c, stack); + + case ast_op_divide: + return _left->eval_number(c, stack) / _right->eval_number(c, stack); + + case ast_op_mod: + return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); + + case ast_op_negate: + return -_left->eval_number(c, stack); + + case ast_number_constant: + return _data.number; + + case ast_func_last: + return static_cast(c.size); + + case ast_func_position: + return static_cast(c.position); + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(string_value(c.n, stack.result).length()); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_string(c, stack).length()); + } + + case ast_func_number_0: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(string_value(c.n, stack.result).c_str()); + } + + case ast_func_number_1: + return _left->eval_number(c, stack); + + case ast_func_sum: + { + xpath_allocator_capture cr(stack.result); + + double r = 0; + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) + { + xpath_allocator_capture cri(stack.result); + + r += convert_string_to_number(string_value(*it, stack.result).c_str()); + } + + return r; + } + + case ast_func_floor: + { + double r = _left->eval_number(c, stack); + + return r == r ? floor(r) : r; + } + + case ast_func_ceiling: + { + double r = _left->eval_number(c, stack); + + return r == r ? ceil(r) : r; + } + + case ast_func_round: + return round_nearest_nzero(_left->eval_number(c, stack)); + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_number) + return _data.variable->get_number(); + + // fallthrough to type conversion + } + + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); + return 0; + } + + } + } + } + + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) + { + assert(_type == ast_func_concat); + + xpath_allocator_capture ct(stack.temp); + + // count the string number + size_t count = 1; + for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; + + // gather all strings + xpath_string static_buffer[4]; + xpath_string* buffer = static_buffer; + + // allocate on-heap for large concats + if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) + { + buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + assert(buffer); + } + + // evaluate all strings to temporary stack + xpath_stack swapped_stack = {stack.temp, stack.result}; + + buffer[0] = _left->eval_string(c, swapped_stack); + + size_t pos = 1; + for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); + assert(pos == count); + + // get total length + size_t length = 0; + for (size_t i = 0; i < count; ++i) length += buffer[i].length(); + + // create final string + char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); + assert(result); + + char_t* ri = result; + + for (size_t j = 0; j < count; ++j) + for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) + *ri++ = *bi; + + *ri = 0; + + return xpath_string::from_heap_preallocated(result, ri); + } + + xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_string_constant: + return xpath_string::from_const(_data.string); + + case ast_func_local_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_local_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_namespace_uri_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_namespace_uri_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_string_0: + return string_value(c.n, stack.result); + + case ast_func_string_1: + return _left->eval_string(c, stack); + + case ast_func_concat: + return eval_string_concat(c, stack); + + case ast_func_substring_before: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + + return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); + } + + case ast_func_substring_after: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + if (!pos) return xpath_string(); + + const char_t* rbegin = pos + p.length(); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_2: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + + if (is_nan(first)) return xpath_string(); // NaN + else if (first >= s_length + 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + assert(1 <= pos && pos <= s_length + 1); + + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_3: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + double last = first + round_nearest(_right->_next->eval_number(c, stack)); + + if (is_nan(first) || is_nan(last)) return xpath_string(); + else if (first >= s_length + 1) return xpath_string(); + else if (first >= last) return xpath_string(); + else if (last < 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + + assert(1 <= pos && pos <= end && end <= s_length + 1); + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + (end - 1); + + return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); + } + + case ast_func_normalize_space_0: + { + xpath_string s = string_value(c.n, stack.result); + + char_t* begin = s.data(stack.result); + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_normalize_space_1: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_translate: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, stack); + xpath_string from = _right->eval_string(c, swapped_stack); + xpath_string to = _right->_next->eval_string(c, swapped_stack); + + char_t* begin = s.data(stack.result); + char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_opt_translate_table: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + char_t* end = translate_table(begin, _data.table); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_string) + return xpath_string::from_const(_data.variable->get_string()); + + // fallthrough to type conversion + } + + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); + return xpath_string(); + } + } + } + } + + xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) + { + switch (_type) + { + case ast_op_union: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); + + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother + rs.set_type(xpath_node_set::type_unsorted); + + rs.append(ls.begin(), ls.end(), stack.result); + rs.remove_duplicates(); + + return rs; + } + + case ast_filter: + { + xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); + + // either expression is a number or it contains position() call; sort by document order + if (_test != predicate_posinv) set.sort_do(); + + bool once = eval_once(set.type(), eval); + + apply_predicate(set, 0, stack, once); + + return set; + } + + case ast_func_id: + return xpath_node_set_raw(); + + case ast_step: + { + switch (_axis) + { + case axis_ancestor: + return step_do(c, stack, eval, axis_to_type()); + + case axis_ancestor_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, eval, axis_to_type()); + + case axis_child: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_self: + return step_do(c, stack, eval, axis_to_type()); + + default: + assert(false && "Unknown axis"); + return xpath_node_set_raw(); + } + } + + case ast_step_root: + { + assert(!_right); // root step can't have any predicates + + xpath_node_set_raw ns; + + ns.set_type(xpath_node_set::type_sorted); + + if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); + else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); + + return ns; + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_node_set) + { + const xpath_node_set& s = _data.variable->get_node_set(); + + xpath_node_set_raw ns; + + ns.set_type(s.type()); + ns.append(s.begin(), s.end(), stack.result); + + return ns; + } + + // fallthrough to type conversion + } + + default: + assert(false && "Wrong expression for return type node set"); + return xpath_node_set_raw(); + } + } + + void optimize(xpath_allocator* alloc) + { + if (_left) + _left->optimize(alloc); + + if (_right) + _right->optimize(alloc); + + if (_next) + _next->optimize(alloc); + + optimize_self(alloc); + } + + void optimize_self(xpath_allocator* alloc) + { + // Rewrite [position()=expr] with [expr] + // Note that this step has to go before classification to recognize [position()=1] + if ((_type == ast_filter || _type == ast_predicate) && + _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) + { + _right = _right->_right; + } + + // Classify filter/predicate ops to perform various optimizations during evaluation + if (_type == ast_filter || _type == ast_predicate) + { + assert(_test == predicate_default); + + if (_right->_type == ast_number_constant && _right->_data.number == 1.0) + _test = predicate_constant_one; + else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) + _test = predicate_constant; + else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) + _test = predicate_posinv; + } + + // Rewrite descendant-or-self::node()/child::foo with descendant::foo + // The former is a full form of //foo, the latter is much faster since it executes the node test immediately + // Do a similar kind of rewrite for self/descendant/descendant-or-self axes + // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && + _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + is_posinv_step()) + { + if (_axis == axis_child || _axis == axis_descendant) + _axis = axis_descendant; + else + _axis = axis_descendant_or_self; + + _left = _left->_left; + } + + // Use optimized lookup table implementation for translate() with constant arguments + if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + { + unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); + + if (table) + { + _type = ast_opt_translate_table; + _data.table = table; + } + } + + // Use optimized path for @attr = 'value' or @attr = $value + if (_type == ast_op_equal && + _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && + (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) + { + _type = ast_opt_compare_attribute; + } + } + + bool is_posinv_expr() const + { + switch (_type) + { + case ast_func_position: + case ast_func_last: + return false; + + case ast_string_constant: + case ast_number_constant: + case ast_variable: + return true; + + case ast_step: + case ast_step_root: + return true; + + case ast_predicate: + case ast_filter: + return true; + + default: + if (_left && !_left->is_posinv_expr()) return false; + + for (xpath_ast_node* n = _right; n; n = n->_next) + if (!n->is_posinv_expr()) return false; + + return true; + } + } + + bool is_posinv_step() const + { + assert(_type == ast_step); + + for (xpath_ast_node* n = _right; n; n = n->_next) + { + assert(n->_type == ast_predicate); + + if (n->_test != predicate_posinv) + return false; + } + + return true; + } + + xpath_value_type rettype() const + { + return static_cast(_rettype); + } + }; + + struct xpath_parser + { + xpath_allocator* _alloc; + xpath_lexer _lexer; + + const char_t* _query; + xpath_variable_set* _variables; + + xpath_parse_result* _result; + + char_t _scratch[32]; + + #ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf _error_handler; + #endif + + void throw_error(const char* message) + { + _result->error = message; + _result->offset = _lexer.current_pos() - _query; + + #ifdef PUGIXML_NO_EXCEPTIONS + longjmp(_error_handler, 1); + #else + throw xpath_exception(*_result); + #endif + } + + void throw_error_oom() + { + #ifdef PUGIXML_NO_EXCEPTIONS + throw_error("Out of memory"); + #else + throw std::bad_alloc(); + #endif + } + + void* alloc_node() + { + void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); + + if (!result) throw_error_oom(); + + return result; + } + + const char_t* alloc_string(const xpath_lexer_string& value) + { + if (value.begin) + { + size_t length = static_cast(value.end - value.begin); + + char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); + if (!c) throw_error_oom(); + assert(c); // workaround for clang static analysis + + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; + + return c; + } + else return 0; + } + + xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) + { + assert(argc <= 1); + + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) + throw_error("Function has to be applied to node set"); + + return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); + } + + xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) + { + switch (name.begin[0]) + { + case 'b': + if (name == PUGIXML_TEXT("boolean") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); + + break; + + case 'c': + if (name == PUGIXML_TEXT("count") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) + throw_error("Function has to be applied to node set"); + + return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); + } + else if (name == PUGIXML_TEXT("contains") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("concat") && argc >= 2) + return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("ceiling") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); + + break; + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) + return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); + + break; + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("not") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); + + break; + + case 'p': + if (name == PUGIXML_TEXT("position") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("string-length") && argc <= 1) + return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("sum") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + + case 't': + if (name == PUGIXML_TEXT("translate") && argc == 3) + return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("true") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); + + break; + + default: + break; + } + + throw_error("Unrecognized function or wrong parameter count"); + + return 0; + } + + axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) + { + specified = true; + + switch (name.begin[0]) + { + case 'a': + if (name == PUGIXML_TEXT("ancestor")) + return axis_ancestor; + else if (name == PUGIXML_TEXT("ancestor-or-self")) + return axis_ancestor_or_self; + else if (name == PUGIXML_TEXT("attribute")) + return axis_attribute; + + break; + + case 'c': + if (name == PUGIXML_TEXT("child")) + return axis_child; + + break; + + case 'd': + if (name == PUGIXML_TEXT("descendant")) + return axis_descendant; + else if (name == PUGIXML_TEXT("descendant-or-self")) + return axis_descendant_or_self; + + break; + + case 'f': + if (name == PUGIXML_TEXT("following")) + return axis_following; + else if (name == PUGIXML_TEXT("following-sibling")) + return axis_following_sibling; + + break; + + case 'n': + if (name == PUGIXML_TEXT("namespace")) + return axis_namespace; + + break; + + case 'p': + if (name == PUGIXML_TEXT("parent")) + return axis_parent; + else if (name == PUGIXML_TEXT("preceding")) + return axis_preceding; + else if (name == PUGIXML_TEXT("preceding-sibling")) + return axis_preceding_sibling; + + break; + + case 's': + if (name == PUGIXML_TEXT("self")) + return axis_self; + + break; + + default: + break; + } + + specified = false; + return axis_child; + } + + nodetest_t parse_node_test_type(const xpath_lexer_string& name) + { + switch (name.begin[0]) + { + case 'c': + if (name == PUGIXML_TEXT("comment")) + return nodetest_type_comment; + + break; + + case 'n': + if (name == PUGIXML_TEXT("node")) + return nodetest_type_node; + + break; + + case 'p': + if (name == PUGIXML_TEXT("processing-instruction")) + return nodetest_type_pi; + + break; + + case 't': + if (name == PUGIXML_TEXT("text")) + return nodetest_type_text; + + break; + + default: + break; + } + + return nodetest_none; + } + + // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall + xpath_ast_node* parse_primary_expression() + { + switch (_lexer.current()) + { + case lex_var_ref: + { + xpath_lexer_string name = _lexer.contents(); + + if (!_variables) + throw_error("Unknown variable: variable set is not provided"); + + xpath_variable* var = 0; + if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) + throw_error_oom(); + + if (!var) + throw_error("Unknown variable: variable set does not contain the given name"); + + _lexer.next(); + + return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); + } + + case lex_open_brace: + { + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + if (_lexer.current() != lex_close_brace) + throw_error("Unmatched braces"); + + _lexer.next(); + + return n; + } + + case lex_quoted_string: + { + const char_t* value = alloc_string(_lexer.contents()); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); + _lexer.next(); + + return n; + } + + case lex_number: + { + double value = 0; + + if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) + throw_error_oom(); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); + _lexer.next(); + + return n; + } + + case lex_string: + { + xpath_ast_node* args[2] = {0}; + size_t argc = 0; + + xpath_lexer_string function = _lexer.contents(); + _lexer.next(); + + xpath_ast_node* last_arg = 0; + + if (_lexer.current() != lex_open_brace) + throw_error("Unrecognized function call"); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + args[argc++] = parse_expression(); + + while (_lexer.current() != lex_close_brace) + { + if (_lexer.current() != lex_comma) + throw_error("No comma between function arguments"); + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + if (argc < 2) args[argc] = n; + else last_arg->set_next(n); + + argc++; + last_arg = n; + } + + _lexer.next(); + + return parse_function(function, argc, args); + } + + default: + throw_error("Unrecognizable primary expression"); + + return 0; + } + } + + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + // Predicate ::= '[' PredicateExpr ']' + // PredicateExpr ::= Expr + xpath_ast_node* parse_filter_expression() + { + xpath_ast_node* n = parse_primary_expression(); + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + if (n->rettype() != xpath_type_node_set) + throw_error("Predicate has to be applied to node set"); + + n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + + _lexer.next(); + } + + return n; + } + + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep + // AxisSpecifier ::= AxisName '::' | '@'? + // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' + // NameTest ::= '*' | NCName ':' '*' | QName + // AbbreviatedStep ::= '.' | '..' + xpath_ast_node* parse_step(xpath_ast_node* set) + { + if (set && set->rettype() != xpath_type_node_set) + throw_error("Step has to be applied to node set"); + + bool axis_specified = false; + axis_t axis = axis_child; // implied child axis + + if (_lexer.current() == lex_axis_attribute) + { + axis = axis_attribute; + axis_specified = true; + + _lexer.next(); + } + else if (_lexer.current() == lex_dot) + { + _lexer.next(); + + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); + } + else if (_lexer.current() == lex_double_dot) + { + _lexer.next(); + + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); + } + + nodetest_t nt_type = nodetest_none; + xpath_lexer_string nt_name; + + if (_lexer.current() == lex_string) + { + // node name test + nt_name = _lexer.contents(); + _lexer.next(); + + // was it an axis name? + if (_lexer.current() == lex_double_colon) + { + // parse axis name + if (axis_specified) + throw_error("Two axis specifiers in one step"); + + axis = parse_axis_name(nt_name, axis_specified); + + if (!axis_specified) + throw_error("Unknown axis"); + + // read actual node test + _lexer.next(); + + if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + nt_name = xpath_lexer_string(); + _lexer.next(); + } + else if (_lexer.current() == lex_string) + { + nt_name = _lexer.contents(); + _lexer.next(); + } + else throw_error("Unrecognized node test"); + } + + if (nt_type == nodetest_none) + { + // node type test or processing-instruction + if (_lexer.current() == lex_open_brace) + { + _lexer.next(); + + if (_lexer.current() == lex_close_brace) + { + _lexer.next(); + + nt_type = parse_node_test_type(nt_name); + + if (nt_type == nodetest_none) + throw_error("Unrecognized node type"); + + nt_name = xpath_lexer_string(); + } + else if (nt_name == PUGIXML_TEXT("processing-instruction")) + { + if (_lexer.current() != lex_quoted_string) + throw_error("Only literals are allowed as arguments to processing-instruction()"); + + nt_type = nodetest_pi; + nt_name = _lexer.contents(); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + throw_error("Unmatched brace near processing-instruction()"); + _lexer.next(); + } + else + { + throw_error("Unmatched brace near node type test"); + } + } + // QName or NCName:* + else + { + if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* + { + nt_name.end--; // erase * + + nt_type = nodetest_all_in_namespace; + } + else + { + nt_type = nodetest_name; + } + } + } + } + else if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + _lexer.next(); + } + else + { + throw_error("Unrecognized node test"); + } + + const char_t* nt_name_copy = alloc_string(nt_name); + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, nt_name_copy); + + xpath_ast_node* last = 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + _lexer.next(); + + if (last) last->set_next(pred); + else n->set_right(pred); + + last = pred; + } + + return n; + } + + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step + xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) + { + xpath_ast_node* n = parse_step(set); + + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + + n = parse_step(n); + } + + return n; + } + + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath + // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath + xpath_ast_node* parse_location_path() + { + if (_lexer.current() == lex_slash) + { + _lexer.next(); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + + // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path + lexeme_t l = _lexer.current(); + + if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) + return parse_relative_location_path(n); + else + return n; + } + else if (_lexer.current() == lex_double_slash) + { + _lexer.next(); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + + return parse_relative_location_path(n); + } + + // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 + return parse_relative_location_path(0); + } + + // PathExpr ::= LocationPath + // | FilterExpr + // | FilterExpr '/' RelativeLocationPath + // | FilterExpr '//' RelativeLocationPath + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + // UnaryExpr ::= UnionExpr | '-' UnaryExpr + xpath_ast_node* parse_path_or_unary_expression() + { + // Clarification. + // PathExpr begins with either LocationPath or FilterExpr. + // FilterExpr begins with PrimaryExpr + // PrimaryExpr begins with '$' in case of it being a variable reference, + // '(' in case of it being an expression, string literal, number constant or + // function call. + + if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || + _lexer.current() == lex_string) + { + if (_lexer.current() == lex_string) + { + // This is either a function call, or not - if not, we shall proceed with location path + const char_t* state = _lexer.state(); + + while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; + + if (*state != '(') return parse_location_path(); + + // This looks like a function call; however this still can be a node-test. Check it. + if (parse_node_test_type(_lexer.contents()) != nodetest_none) + return parse_location_path(); + } + + xpath_ast_node* n = parse_filter_expression(); + + if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + { + if (n->rettype() != xpath_type_node_set) + throw_error("Step has to be applied to node set"); + + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + } + + // select from location path + return parse_relative_location_path(n); + } + + return n; + } + else if (_lexer.current() == lex_minus) + { + _lexer.next(); + + // precedence 7+ - only parses union expressions + xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); + + return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); + } + else + { + return parse_location_path(); + } + } + + struct binary_op_t + { + ast_type_t asttype; + xpath_value_type rettype; + int precedence; + + binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) + { + } + + binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) + { + } + + static binary_op_t parse(xpath_lexer& lexer) + { + switch (lexer.current()) + { + case lex_string: + if (lexer.contents() == PUGIXML_TEXT("or")) + return binary_op_t(ast_op_or, xpath_type_boolean, 1); + else if (lexer.contents() == PUGIXML_TEXT("and")) + return binary_op_t(ast_op_and, xpath_type_boolean, 2); + else if (lexer.contents() == PUGIXML_TEXT("div")) + return binary_op_t(ast_op_divide, xpath_type_number, 6); + else if (lexer.contents() == PUGIXML_TEXT("mod")) + return binary_op_t(ast_op_mod, xpath_type_number, 6); + else + return binary_op_t(); + + case lex_equal: + return binary_op_t(ast_op_equal, xpath_type_boolean, 3); + + case lex_not_equal: + return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); + + case lex_less: + return binary_op_t(ast_op_less, xpath_type_boolean, 4); + + case lex_greater: + return binary_op_t(ast_op_greater, xpath_type_boolean, 4); + + case lex_less_or_equal: + return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); + + case lex_greater_or_equal: + return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); + + case lex_plus: + return binary_op_t(ast_op_add, xpath_type_number, 5); + + case lex_minus: + return binary_op_t(ast_op_subtract, xpath_type_number, 5); + + case lex_multiply: + return binary_op_t(ast_op_multiply, xpath_type_number, 6); + + case lex_union: + return binary_op_t(ast_op_union, xpath_type_node_set, 7); + + default: + return binary_op_t(); + } + } + }; + + xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) + { + binary_op_t op = binary_op_t::parse(_lexer); + + while (op.asttype != ast_unknown && op.precedence >= limit) + { + _lexer.next(); + + xpath_ast_node* rhs = parse_path_or_unary_expression(); + + binary_op_t nextop = binary_op_t::parse(_lexer); + + while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) + { + rhs = parse_expression_rec(rhs, nextop.precedence); + + nextop = binary_op_t::parse(_lexer); + } + + if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) + throw_error("Union operator has to be applied to node sets"); + + lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); + + op = binary_op_t::parse(_lexer); + } + + return lhs; + } + + // Expr ::= OrExpr + // OrExpr ::= AndExpr | OrExpr 'or' AndExpr + // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr + // EqualityExpr ::= RelationalExpr + // | EqualityExpr '=' RelationalExpr + // | EqualityExpr '!=' RelationalExpr + // RelationalExpr ::= AdditiveExpr + // | RelationalExpr '<' AdditiveExpr + // | RelationalExpr '>' AdditiveExpr + // | RelationalExpr '<=' AdditiveExpr + // | RelationalExpr '>=' AdditiveExpr + // AdditiveExpr ::= MultiplicativeExpr + // | AdditiveExpr '+' MultiplicativeExpr + // | AdditiveExpr '-' MultiplicativeExpr + // MultiplicativeExpr ::= UnaryExpr + // | MultiplicativeExpr '*' UnaryExpr + // | MultiplicativeExpr 'div' UnaryExpr + // | MultiplicativeExpr 'mod' UnaryExpr + xpath_ast_node* parse_expression() + { + return parse_expression_rec(parse_path_or_unary_expression(), 0); + } + + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + { + } + + xpath_ast_node* parse() + { + xpath_ast_node* result = parse_expression(); + + // check if there are unparsed tokens left + if (_lexer.current() != lex_eof) + throw_error("Incorrect query"); + + return result; + } + + static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) + { + xpath_parser parser(query, variables, alloc, result); + + #ifdef PUGIXML_NO_EXCEPTIONS + int error = setjmp(parser._error_handler); + + return (error == 0) ? parser.parse() : 0; + #else + return parser.parse(); + #endif + } + }; + + struct xpath_query_impl + { + static xpath_query_impl* create() + { + void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); + if (!memory) return 0; + + return new (memory) xpath_query_impl(); + } + + static void destroy(xpath_query_impl* impl) + { + // free all allocated pages + impl->alloc.release(); + + // free allocator memory (with the first page) + xml_memory::deallocate(impl); + } + + xpath_query_impl(): root(0), alloc(&block) + { + block.next = 0; + block.capacity = sizeof(block.data); + } + + xpath_ast_node* root; + xpath_allocator alloc; + xpath_memory_block block; + }; + + PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) + { + if (!impl) return xpath_string(); + + #ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_string(); + #endif + + xpath_context c(n, 1, 1); + + return impl->root->eval_string(c, sd.stack); + } + + PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) + { + if (!impl) return 0; + + if (impl->root->rettype() != xpath_type_node_set) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return 0; + #else + xpath_parse_result res; + res.error = "Expression does not evaluate to node set"; + + throw xpath_exception(res); + #endif + } + + return impl->root; + } +PUGI__NS_END + +namespace pugi +{ +#ifndef PUGIXML_NO_EXCEPTIONS + PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) + { + assert(_result.error); + } + + PUGI__FN const char* xpath_exception::what() const throw() + { + return _result.error; + } + + PUGI__FN const xpath_parse_result& xpath_exception::result() const + { + return _result; + } +#endif + + PUGI__FN xpath_node::xpath_node() + { + } + + PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) + { + } + + PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) + { + } + + PUGI__FN xml_node xpath_node::node() const + { + return _attribute ? xml_node() : _node; + } + + PUGI__FN xml_attribute xpath_node::attribute() const + { + return _attribute; + } + + PUGI__FN xml_node xpath_node::parent() const + { + return _attribute ? _node : _node.parent(); + } + + PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) + { + } + + PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const + { + return (_node || _attribute) ? unspecified_bool_xpath_node : 0; + } + + PUGI__FN bool xpath_node::operator!() const + { + return !(_node || _attribute); + } + + PUGI__FN bool xpath_node::operator==(const xpath_node& n) const + { + return _node == n._node && _attribute == n._attribute; + } + + PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const + { + return _node != n._node || _attribute != n._attribute; + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) + { + assert(begin_ <= end_); + + size_t size_ = static_cast(end_ - begin_); + + if (size_ <= 1) + { + // deallocate old buffer + if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + + // use internal buffer + if (begin_ != end_) _storage = *begin_; + + _begin = &_storage; + _end = &_storage + size_; + _type = type_; + } + else + { + // make heap copy + xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + + memcpy(storage, begin_, size_ * sizeof(xpath_node)); + + // deallocate old buffer + if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + + // finalize + _begin = storage; + _end = storage + size_; + _type = type_; + } + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) + { + _type = rhs._type; + _storage = rhs._storage; + _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _end = _begin + (rhs._end - rhs._begin); + + rhs._type = type_unsorted; + rhs._begin = &rhs._storage; + rhs._end = rhs._begin; + } +#endif + + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + } + + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _assign(begin_, end_, type_); + } + + PUGI__FN xpath_node_set::~xpath_node_set() + { + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + } + + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _assign(ns._begin, ns._end, ns._type); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + { + if (this == &ns) return *this; + + _assign(ns._begin, ns._end, ns._type); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _move(rhs); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) + { + if (this == &rhs) return *this; + + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + + _move(rhs); + + return *this; + } +#endif + + PUGI__FN xpath_node_set::type_t xpath_node_set::type() const + { + return _type; + } + + PUGI__FN size_t xpath_node_set::size() const + { + return _end - _begin; + } + + PUGI__FN bool xpath_node_set::empty() const + { + return _begin == _end; + } + + PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const + { + assert(index < size()); + return _begin[index]; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const + { + return _begin; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const + { + return _end; + } + + PUGI__FN void xpath_node_set::sort(bool reverse) + { + _type = impl::xpath_sort(_begin, _end, _type, reverse); + } + + PUGI__FN xpath_node xpath_node_set::first() const + { + return impl::xpath_first(_begin, _end, _type); + } + + PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + { + } + + PUGI__FN xpath_parse_result::operator bool() const + { + return error == 0; + } + + PUGI__FN const char* xpath_parse_result::description() const + { + return error ? error : "No error"; + } + + PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) + { + } + + PUGI__FN const char_t* xpath_variable::name() const + { + switch (_type) + { + case xpath_type_node_set: + return static_cast(this)->name; + + case xpath_type_number: + return static_cast(this)->name; + + case xpath_type_string: + return static_cast(this)->name; + + case xpath_type_boolean: + return static_cast(this)->name; + + default: + assert(false && "Invalid variable type"); + return 0; + } + } + + PUGI__FN xpath_value_type xpath_variable::type() const + { + return _type; + } + + PUGI__FN bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast(this)->value : false; + } + + PUGI__FN double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); + } + + PUGI__FN const char_t* xpath_variable::get_string() const + { + const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; + return value ? value : PUGIXML_TEXT(""); + } + + PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; + } + + PUGI__FN bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + impl::xpath_variable_string* var = static_cast(this); + + // duplicate string + size_t size = (impl::strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(impl::xml_memory::allocate(size)); + if (!copy) return false; + + memcpy(copy, value, size); + + // replace old string + if (var->value) impl::xml_memory::deallocate(var->value); + var->value = copy; + + return true; + } + + PUGI__FN bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + } + + PUGI__FN xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _destroy(_data[i]); + } + + PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + + _assign(rhs); + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + { + if (this == &rhs) return *this; + + _assign(rhs); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _destroy(_data[i]); + + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + + return *this; + } +#endif + + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + { + xpath_variable_set temp; + + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) + return; + + _swap(temp); + } + + PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* chain = _data[i]; + + _data[i] = rhs._data[i]; + rhs._data[i] = chain; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var; + + return 0; + } + + PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + { + xpath_variable* last = 0; + + while (var) + { + // allocate storage for new variable + xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); + if (!nvar) return false; + + // link the variable to the result immediately to handle failures gracefully + if (last) + last->_next = nvar; + else + *out_result = nvar; + + last = nvar; + + // copy the value; this can fail due to out-of-memory conditions + if (!impl::copy_xpath_variable(nvar, var)) return false; + + var = var->_next; + } + + return true; + } + + PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + { + while (var) + { + xpath_variable* next = var->_next; + + impl::delete_xpath_variable(var->_type, var); + + var = next; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = impl::new_xpath_variable(type, name); + + if (result) + { + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) + { + xpath_variable* var = add(name, xpath_type_boolean); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) + { + xpath_variable* var = add(name, xpath_type_number); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) + { + xpath_variable* var = add(name, xpath_type_string); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) + { + xpath_variable* var = add(name, xpath_type_node_set); + return var ? var->set(value) : false; + } + + PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) + { + return _find(name); + } + + PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return _find(name); + } + + PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + { + impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); + + if (!qimpl) + { + #ifdef PUGIXML_NO_EXCEPTIONS + _result.error = "Out of memory"; + #else + throw std::bad_alloc(); + #endif + } + else + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); + + qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); + + if (qimpl->root) + { + qimpl->root->optimize(&qimpl->alloc); + + _impl = impl.release(); + _result.error = 0; + } + } + } + + PUGI__FN xpath_query::xpath_query(): _impl(0) + { + } + + PUGI__FN xpath_query::~xpath_query() + { + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) + { + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + } + + PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) + { + if (this == &rhs) return *this; + + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + + return *this; + } +#endif + + PUGI__FN xpath_value_type xpath_query::return_type() const + { + if (!_impl) return xpath_type_none; + + return static_cast(_impl)->root->rettype(); + } + + PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const + { + if (!_impl) return false; + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + #ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return false; + #endif + + return static_cast(_impl)->root->eval_boolean(c, sd.stack); + } + + PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const + { + if (!_impl) return impl::gen_nan(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + #ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return impl::gen_nan(); + #endif + + return static_cast(_impl)->root->eval_number(c, sd.stack); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const + { + impl::xpath_stack_data sd; + + impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); + + return string_t(r.c_str(), r.length()); + } +#endif + + PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + { + impl::xpath_stack_data sd; + + impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); + + size_t full_size = r.length() + 1; + + if (capacity > 0) + { + size_t size = (full_size < capacity) ? full_size : capacity; + assert(size > 0); + + memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); + buffer[size - 1] = 0; + } + + return full_size; + } + + PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node_set(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + #ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_node_set(); + #endif + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); + + return xpath_node_set(r.begin(), r.end(), r.type()); + } + + PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + #ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_node(); + #endif + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); + + return r.first(); + } + + PUGI__FN const xpath_parse_result& xpath_query::result() const + { + return _result; + } + + PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) + { + } + + PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const + { + return _impl ? unspecified_bool_xpath_query : 0; + } + + PUGI__FN bool xpath_query::operator!() const + { + return !_impl; + } + + PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_node(q); + } + + PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_nodes(q); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const + { + return query.evaluate_node_set(*this); + } + + PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_single_node(q); + } + + PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } +} + +#endif + +#ifdef __BORLANDC__ +# pragma option pop +#endif + +// Intel C++ does not properly keep warning state for function templates, +// so popping warning state at the end of translation unit leads to warnings in the middle. +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#endif + +// Undefine all local macros (makes sure we're not leaking macros in header-only mode) +#undef PUGI__NO_INLINE +#undef PUGI__UNLIKELY +#undef PUGI__STATIC_ASSERT +#undef PUGI__DMC_VOLATILE +#undef PUGI__MSVC_CRT_VERSION +#undef PUGI__NS_BEGIN +#undef PUGI__NS_END +#undef PUGI__FN +#undef PUGI__FN_NO_INLINE +#undef PUGI__GETHEADER_IMPL +#undef PUGI__GETPAGE_IMPL +#undef PUGI__GETPAGE +#undef PUGI__NODETYPE +#undef PUGI__IS_CHARTYPE_IMPL +#undef PUGI__IS_CHARTYPE +#undef PUGI__IS_CHARTYPEX +#undef PUGI__ENDSWITH +#undef PUGI__SKIPWS +#undef PUGI__OPTSET +#undef PUGI__PUSHNODE +#undef PUGI__POPNODE +#undef PUGI__SCANFOR +#undef PUGI__SCANWHILE +#undef PUGI__SCANWHILE_UNROLL +#undef PUGI__ENDSEG +#undef PUGI__THROW_ERROR +#undef PUGI__CHECK_ERROR + +#endif + +/** + * Copyright (c) 2006-2016 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/pugixml/pugixml.hpp b/src/pugixml/pugixml.hpp new file mode 100644 index 000000000..6288d4bd9 --- /dev/null +++ b/src/pugixml/pugixml.hpp @@ -0,0 +1,1434 @@ +/** + * pugixml parser - version 1.8 + * -------------------------------------------------------- + * Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef PUGIXML_VERSION +// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons +# define PUGIXML_VERSION 180 +#endif + +// Include user configuration file (this can define various configuration macros) +#include "pugiconfig.hpp" + +#ifndef HEADER_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +// Include stddef.h for size_t and ptrdiff_t +#include + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#endif + +// Include STL headers +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// Macro for deprecated features +#ifndef PUGIXML_DEPRECATED +# if defined(__GNUC__) +# define PUGIXML_DEPRECATED __attribute__((deprecated)) +# elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_DEPRECATED __declspec(deprecated) +# else +# define PUGIXML_DEPRECATED +# endif +#endif + +// If no API is defined, assume default +#ifndef PUGIXML_API +# define PUGIXML_API +#endif + +// If no API for classes is defined, assume default +#ifndef PUGIXML_CLASS +# define PUGIXML_CLASS PUGIXML_API +#endif + +// If no API for functions is defined, assume default +#ifndef PUGIXML_FUNCTION +# define PUGIXML_FUNCTION PUGIXML_API +#endif + +// If the platform is known to have long long support, enable long long functions +#ifndef PUGIXML_HAS_LONG_LONG +# if __cplusplus >= 201103 +# define PUGIXML_HAS_LONG_LONG +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define PUGIXML_HAS_LONG_LONG +# endif +#endif + +// If the platform is known to have move semantics support, compile move ctor/operator implementation +#ifndef PUGIXML_HAS_MOVE +# if __cplusplus >= 201103 +# define PUGIXML_HAS_MOVE +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_HAS_MOVE +# endif +#endif + +// If C++ is 2011 or higher, add 'override' qualifiers +#ifndef PUGIXML_OVERRIDE +# if __cplusplus >= 201103 +# define PUGIXML_OVERRIDE override +# else +# define PUGIXML_OVERRIDE +# endif +#endif + +// Character interface macros +#ifdef PUGIXML_WCHAR_MODE +# define PUGIXML_TEXT(t) L ## t +# define PUGIXML_CHAR wchar_t +#else +# define PUGIXML_TEXT(t) t +# define PUGIXML_CHAR char +#endif + +namespace pugi +{ + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE + typedef PUGIXML_CHAR char_t; + +#ifndef PUGIXML_NO_STL + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE + typedef std::basic_string, std::allocator > string_t; +#endif +} + +// The PugiXML namespace +namespace pugi +{ + // Tree node types + enum xml_node_type + { + node_null, // Empty (null) node handle + node_document, // A document tree's absolute root + node_element, // Element tag, i.e. '' + node_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' + }; + + // Parsing options + + // Minimal parsing mode (equivalent to turning all other flags off). + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. + const unsigned int parse_minimal = 0x0000; + + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. + const unsigned int parse_pi = 0x0001; + + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. + const unsigned int parse_comments = 0x0002; + + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. + const unsigned int parse_cdata = 0x0004; + + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata = 0x0008; + + // This flag determines if character and entity references are expanded during parsing. This flag is on by default. + const unsigned int parse_escapes = 0x0010; + + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. + const unsigned int parse_eol = 0x0020; + + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. + const unsigned int parse_wconv_attribute = 0x0040; + + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. + const unsigned int parse_wnorm_attribute = 0x0080; + + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. + const unsigned int parse_declaration = 0x0100; + + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. + const unsigned int parse_doctype = 0x0200; + + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only + // of whitespace is added to the DOM tree. + // This flag is off by default; turning it on may result in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata_single = 0x0400; + + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. + const unsigned int parse_trim_pcdata = 0x0800; + + // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document + // is a valid document. This flag is off by default. + const unsigned int parse_fragment = 0x1000; + + // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of + // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. + // This flag is off by default. + const unsigned int parse_embed_pcdata = 0x2000; + + // The default parsing mode. + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + + // The full parsing mode. + // Nodes of all types are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; + + // These flags determine the encoding of input data for XML document + enum xml_encoding + { + encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range + { + public: + typedef It const_iterator; + typedef It iterator; + + xml_object_range(It b, It e): _begin(b), _end(e) + { + } + + It begin() const { return _begin; } + It end() const { return _end; } + + private: + It _begin, _end; + }; + + // Writer interface for node printing (see xml_node::print) + class PUGIXML_CLASS xml_writer + { + public: + virtual ~xml_writer() {} + + // Write memory chunk into stream/file/whatever + virtual void write(const void* data, size_t size) = 0; + }; + + // xml_writer implementation for FILE* + class PUGIXML_CLASS xml_writer_file: public xml_writer + { + public: + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio + xml_writer_file(void* file); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + void* file; + }; + + #ifndef PUGIXML_NO_STL + // xml_writer implementation for streams + class PUGIXML_CLASS xml_writer_stream: public xml_writer + { + public: + // Construct writer from an output stream object + xml_writer_stream(std::basic_ostream >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + std::basic_ostream >* narrow_stream; + std::basic_ostream >* wide_stream; + }; + #endif + + // A light-weight handle for manipulating attributes in DOM tree + class PUGIXML_CLASS xml_attribute + { + friend class xml_attribute_iterator; + friend class xml_node; + + private: + xml_attribute_struct* _attr; + + typedef void (*unspecified_bool_type)(xml_attribute***); + + public: + // Default constructor. Constructs an empty attribute. + xml_attribute(); + + // Constructs attribute from internal pointer + explicit xml_attribute(xml_attribute_struct* attr); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped attribute pointers) + bool operator==(const xml_attribute& r) const; + bool operator!=(const xml_attribute& r) const; + bool operator<(const xml_attribute& r) const; + bool operator>(const xml_attribute& r) const; + bool operator<=(const xml_attribute& r) const; + bool operator>=(const xml_attribute& r) const; + + // Check if attribute is empty + bool empty() const; + + // Get attribute name/value, or "" if attribute is empty + const char_t* name() const; + const char_t* value() const; + + // Get attribute value, or the default value if attribute is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty + bool as_bool(bool def = false) const; + + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set_value(int rhs); + bool set_value(unsigned int rhs); + bool set_value(long rhs); + bool set_value(unsigned long rhs); + bool set_value(double rhs); + bool set_value(float rhs); + bool set_value(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set_value(long long rhs); + bool set_value(unsigned long long rhs); + #endif + + // Set attribute value (equivalent to set_value without error checking) + xml_attribute& operator=(const char_t* rhs); + xml_attribute& operator=(int rhs); + xml_attribute& operator=(unsigned int rhs); + xml_attribute& operator=(long rhs); + xml_attribute& operator=(unsigned long rhs); + xml_attribute& operator=(double rhs); + xml_attribute& operator=(float rhs); + xml_attribute& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_attribute& operator=(long long rhs); + xml_attribute& operator=(unsigned long long rhs); + #endif + + // Get next/previous attribute in the attribute list of the parent node + xml_attribute next_attribute() const; + xml_attribute previous_attribute() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_attribute_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); +#endif + + // A light-weight handle for manipulating nodes in DOM tree + class PUGIXML_CLASS xml_node + { + friend class xml_attribute_iterator; + friend class xml_node_iterator; + friend class xml_named_node_iterator; + + protected: + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_node***); + + public: + // Default constructor. Constructs an empty node. + xml_node(); + + // Constructs node from internal pointer + explicit xml_node(xml_node_struct* p); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped node pointers) + bool operator==(const xml_node& r) const; + bool operator!=(const xml_node& r) const; + bool operator<(const xml_node& r) const; + bool operator>(const xml_node& r) const; + bool operator<=(const xml_node& r) const; + bool operator>=(const xml_node& r) const; + + // Check if node is empty. + bool empty() const; + + // Get node type + xml_node_type type() const; + + // Get node name, or "" if node is empty or it has no name + const char_t* name() const; + + // Get node value, or "" if node is empty or it has no value + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. + const char_t* value() const; + + // Get attribute list + xml_attribute first_attribute() const; + xml_attribute last_attribute() const; + + // Get children list + xml_node first_child() const; + xml_node last_child() const; + + // Get next/previous sibling in the children list of the parent node + xml_node next_sibling() const; + xml_node previous_sibling() const; + + // Get parent node + xml_node parent() const; + + // Get root of DOM tree this node belongs to + xml_node root() const; + + // Get text object for the current node + xml_text text() const; + + // Get child, attribute or next/previous sibling with the specified name + xml_node child(const char_t* name) const; + xml_attribute attribute(const char_t* name) const; + xml_node next_sibling(const char_t* name) const; + xml_node previous_sibling(const char_t* name) const; + + // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) + xml_attribute attribute(const char_t* name, xml_attribute& hint) const; + + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA + const char_t* child_value() const; + + // Get child value of child with specified name. Equivalent to child(name).child_value(). + const char_t* child_value(const char_t* name) const; + + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. + xml_attribute append_attribute(const char_t* name); + xml_attribute prepend_attribute(const char_t* name); + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); + + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. + xml_attribute append_copy(const xml_attribute& proto); + xml_attribute prepend_copy(const xml_attribute& proto); + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); + + // Add child node with specified type. Returns added node, or empty node on errors. + xml_node append_child(xml_node_type type = node_element); + xml_node prepend_child(xml_node_type type = node_element); + xml_node insert_child_after(xml_node_type type, const xml_node& node); + xml_node insert_child_before(xml_node_type type, const xml_node& node); + + // Add child element with specified name. Returns added node, or empty node on errors. + xml_node append_child(const char_t* name); + xml_node prepend_child(const char_t* name); + xml_node insert_child_after(const char_t* name, const xml_node& node); + xml_node insert_child_before(const char_t* name, const xml_node& node); + + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. + xml_node append_copy(const xml_node& proto); + xml_node prepend_copy(const xml_node& proto); + xml_node insert_copy_after(const xml_node& proto, const xml_node& node); + xml_node insert_copy_before(const xml_node& proto, const xml_node& node); + + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. + xml_node append_move(const xml_node& moved); + xml_node prepend_move(const xml_node& moved); + xml_node insert_move_after(const xml_node& moved, const xml_node& node); + xml_node insert_move_before(const xml_node& moved, const xml_node& node); + + // Remove specified attribute + bool remove_attribute(const xml_attribute& a); + bool remove_attribute(const char_t* name); + + // Remove specified child + bool remove_child(const xml_node& n); + bool remove_child(const char_t* name); + + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Find attribute using predicate. Returns first attribute for which predicate returned true. + template xml_attribute find_attribute(Predicate pred) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) + if (pred(attrib)) + return attrib; + + return xml_attribute(); + } + + // Find child node using predicate. Returns first child for which predicate returned true. + template xml_node find_child(Predicate pred) const + { + if (!_root) return xml_node(); + + for (xml_node node = first_child(); node; node = node.next_sibling()) + if (pred(node)) + return node; + + return xml_node(); + } + + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. + template xml_node find_node(Predicate pred) const + { + if (!_root) return xml_node(); + + xml_node cur = first_child(); + + while (cur._root && cur._root != _root) + { + if (pred(cur)) return cur; + + if (cur.first_child()) cur = cur.first_child(); + else if (cur.next_sibling()) cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); + + if (cur._root != _root) cur = cur.next_sibling(); + } + } + + return xml_node(); + } + + // Find child node by attribute name/value + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; + + #ifndef PUGIXML_NO_STL + // Get the absolute node path from root as a text string. + string_t path(char_t delimiter = '/') const; + #endif + + // Search for a node by path consisting of node names and . or .. elements. + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; + + // Recursively traverse subtree with xml_tree_walker + bool traverse(xml_tree_walker& walker); + + #ifndef PUGIXML_NO_XPATH + // Select single node by evaluating XPath query. Returns first node from the resulting node set. + xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const xpath_query& query) const; + + // Select node set by evaluating XPath query + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const xpath_query& query) const; + + // (deprecated: use select_node instead) Select single node by evaluating XPath query. + xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_single_node(const xpath_query& query) const; + + #endif + + // Print subtree using a writer object + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + + #ifndef PUGIXML_NO_STL + // Print subtree to stream + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; + #endif + + // Child nodes iterators + typedef xml_node_iterator iterator; + + iterator begin() const; + iterator end() const; + + // Attribute iterators + typedef xml_attribute_iterator attribute_iterator; + + attribute_iterator attributes_begin() const; + attribute_iterator attributes_end() const; + + // Range-based for support + xml_object_range children() const; + xml_object_range children(const char_t* name) const; + xml_object_range attributes() const; + + // Get node offset in parsed file/string (in char_t units) for debugging purposes + ptrdiff_t offset_debug() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_node_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); +#endif + + // A helper for working with text inside PCDATA nodes + class PUGIXML_CLASS xml_text + { + friend class xml_node; + + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_text***); + + explicit xml_text(xml_node_struct* root); + + xml_node_struct* _data_new(); + xml_node_struct* _data() const; + + public: + // Default constructor. Constructs an empty object. + xml_text(); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Check if text object is empty + bool empty() const; + + // Get text, or "" if object is empty + const char_t* get() const; + + // Get text, or the default value if object is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get text as a number, or the default value if conversion did not succeed or object is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty + bool as_bool(bool def = false) const; + + // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t* rhs); + + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set(int rhs); + bool set(unsigned int rhs); + bool set(long rhs); + bool set(unsigned long rhs); + bool set(double rhs); + bool set(float rhs); + bool set(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set(long long rhs); + bool set(unsigned long long rhs); + #endif + + // Set text (equivalent to set without error checking) + xml_text& operator=(const char_t* rhs); + xml_text& operator=(int rhs); + xml_text& operator=(unsigned int rhs); + xml_text& operator=(long rhs); + xml_text& operator=(unsigned long rhs); + xml_text& operator=(double rhs); + xml_text& operator=(float rhs); + xml_text& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_text& operator=(long long rhs); + xml_text& operator=(unsigned long long rhs); + #endif + + // Get the data node (node_pcdata or node_cdata) for this object + xml_node data() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); +#endif + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + mutable xml_node _wrap; + xml_node _parent; + + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_node_iterator(); + + // Construct an iterator which points to the specified node + xml_node_iterator(const xml_node& node); + + // Iterator operators + bool operator==(const xml_node_iterator& rhs) const; + bool operator!=(const xml_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_node_iterator& operator++(); + xml_node_iterator operator++(int); + + const xml_node_iterator& operator--(); + xml_node_iterator operator--(int); + }; + + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) + class PUGIXML_CLASS xml_attribute_iterator + { + friend class xml_node; + + private: + mutable xml_attribute _wrap; + xml_node _parent; + + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_attribute value_type; + typedef xml_attribute* pointer; + typedef xml_attribute& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_attribute_iterator(); + + // Construct an iterator which points to the specified attribute + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); + + // Iterator operators + bool operator==(const xml_attribute_iterator& rhs) const; + bool operator!=(const xml_attribute_iterator& rhs) const; + + xml_attribute& operator*() const; + xml_attribute* operator->() const; + + const xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + const xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // Named node range helper + class PUGIXML_CLASS xml_named_node_iterator + { + friend class xml_node; + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_named_node_iterator(); + + // Construct an iterator which points to the specified node + xml_named_node_iterator(const xml_node& node, const char_t* name); + + // Iterator operators + bool operator==(const xml_named_node_iterator& rhs) const; + bool operator!=(const xml_named_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_named_node_iterator& operator++(); + xml_named_node_iterator operator++(int); + + const xml_named_node_iterator& operator--(); + xml_named_node_iterator operator--(int); + + private: + mutable xml_node _wrap; + xml_node _parent; + const char_t* _name; + + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); + }; + + // Abstract tree walker class (see xml_node::traverse) + class PUGIXML_CLASS xml_tree_walker + { + friend class xml_node; + + private: + int _depth; + + protected: + // Get current traversal depth + int depth() const; + + public: + xml_tree_walker(); + virtual ~xml_tree_walker(); + + // Callback that is called when traversal begins + virtual bool begin(xml_node& node); + + // Callback that is called for each node traversed + virtual bool for_each(xml_node& node) = 0; + + // Callback that is called when traversal ends + virtual bool end(xml_node& node); + }; + + // Parsing status, returned as part of xml_parse_result object + enum xml_parse_status + { + status_ok = 0, // No error + + status_file_not_found, // File was not found during load_file() + status_io_error, // Error reading from file/stream + status_out_of_memory, // Could not allocate memory + status_internal_error, // Internal error occurred + + status_unrecognized_tag, // Parser could not determine tag type + + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction + status_bad_comment, // Parsing error occurred while parsing comment + status_bad_cdata, // Parsing error occurred while parsing CDATA section + status_bad_doctype, // Parsing error occurred while parsing document type declaration + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section + status_bad_start_element, // Parsing error occurred while parsing start element tag + status_bad_attribute, // Parsing error occurred while parsing element attribute + status_bad_end_element, // Parsing error occurred while parsing end element tag + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) + + status_no_document_element // Parsing resulted in a document without element nodes + }; + + // Parsing result + struct PUGIXML_CLASS xml_parse_result + { + // Parsing status (see xml_parse_status) + xml_parse_status status; + + // Last parsed offset (in char_t units from start of input data) + ptrdiff_t offset; + + // Source document encoding + xml_encoding encoding; + + // Default constructor, initializes object to failed state + xml_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // Document class (DOM tree root) + class PUGIXML_CLASS xml_document: public xml_node + { + private: + char_t* _buffer; + + char _memory[192]; + + // Non-copyable semantics + xml_document(const xml_document&); + xml_document& operator=(const xml_document&); + + void _create(); + void _destroy(); + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + // Removes all nodes, leaving the empty document + void reset(); + + // Removes all nodes, then copies the entire contents of the specified document + void reset(const xml_document& proto); + + #ifndef PUGIXML_NO_STL + // Load document from stream. + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); + #endif + + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load(const char_t* contents, unsigned int options = parse_default); + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); + + // Load document from file + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + #ifndef PUGIXML_NO_STL + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; + #endif + + // Save XML to file + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + // Get document element + xml_node document_element() const; + }; + +#ifndef PUGIXML_NO_XPATH + // XPath query return type + enum xpath_value_type + { + xpath_type_none, // Unknown type (query failed to compile) + xpath_type_node_set, // Node set (xpath_node_set) + xpath_type_number, // Number + xpath_type_string, // String + xpath_type_boolean // Boolean + }; + + // XPath parsing result + struct PUGIXML_CLASS xpath_parse_result + { + // Error message (0 if no error) + const char* error; + + // Last parsed offset (in char_t units from string start) + ptrdiff_t offset; + + // Default constructor, initializes object to failed state + xpath_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // A single XPath variable + class PUGIXML_CLASS xpath_variable + { + friend class xpath_variable_set; + + protected: + xpath_value_type _type; + xpath_variable* _next; + + xpath_variable(xpath_value_type type); + + // Non-copyable semantics + xpath_variable(const xpath_variable&); + xpath_variable& operator=(const xpath_variable&); + + public: + // Get variable name + const char_t* name() const; + + // Get variable type + xpath_value_type type() const; + + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error + bool get_boolean() const; + double get_number() const; + const char_t* get_string() const; + const xpath_node_set& get_node_set() const; + + // Set variable value; no type conversion is performed, false is returned on type mismatch error + bool set(bool value); + bool set(double value); + bool set(const char_t* value); + bool set(const xpath_node_set& value); + }; + + // A set of XPath variables + class PUGIXML_CLASS xpath_variable_set + { + private: + xpath_variable* _data[64]; + + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); + + xpath_variable* _find(const char_t* name) const; + + static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs); + xpath_variable_set& operator=(xpath_variable_set&& rhs); + #endif + + // Add a new variable or get the existing one, if the types match + xpath_variable* add(const char_t* name, xpath_value_type type); + + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch + bool set(const char_t* name, bool value); + bool set(const char_t* name, double value); + bool set(const char_t* name, const char_t* value); + bool set(const char_t* name, const xpath_node_set& value); + + // Get existing variable by name + xpath_variable* get(const char_t* name); + const xpath_variable* get(const char_t* name) const; + }; + + // A compiled XPath query object + class PUGIXML_CLASS xpath_query + { + private: + void* _impl; + xpath_parse_result _result; + + typedef void (*unspecified_bool_type)(xpath_query***); + + // Non-copyable semantics + xpath_query(const xpath_query&); + xpath_query& operator=(const xpath_query&); + + public: + // Construct a compiled object from XPath expression. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. + explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + + // Constructor + xpath_query(); + + // Destructor + ~xpath_query(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_query(xpath_query&& rhs); + xpath_query& operator=(xpath_query&& rhs); + #endif + + // Get query expression return type + xpath_value_type return_type() const; + + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + bool evaluate_boolean(const xpath_node& n) const; + + // Evaluate expression as double value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + double evaluate_number(const xpath_node& n) const; + + #ifndef PUGIXML_NO_STL + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + string_t evaluate_string(const xpath_node& n) const; + #endif + + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. + xpath_node_set evaluate_node_set(const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // Return first node in document order, or empty node if node set is empty. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. + xpath_node evaluate_node(const xpath_node& n) const; + + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) + const xpath_parse_result& result() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + }; + + #ifndef PUGIXML_NO_EXCEPTIONS + // XPath exception class + class PUGIXML_CLASS xpath_exception: public std::exception + { + private: + xpath_parse_result _result; + + public: + // Construct exception from parse result + explicit xpath_exception(const xpath_parse_result& result); + + // Get error message + virtual const char* what() const throw() PUGIXML_OVERRIDE; + + // Get parse result + const xpath_parse_result& result() const; + }; + #endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef void (*unspecified_bool_type)(xpath_node***); + + public: + // Default constructor; constructs empty XPath node + xpath_node(); + + // Construct XPath node from XML node/attribute + xpath_node(const xml_node& node); + xpath_node(const xml_attribute& attribute, const xml_node& parent); + + // Get node/attribute, if any + xml_node node() const; + xml_attribute attribute() const; + + // Get parent of contained node/attribute + xml_node parent() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators + bool operator==(const xpath_node& n) const; + bool operator!=(const xpath_node& n) const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); +#endif + + // A fixed-size collection of XPath nodes + class PUGIXML_CLASS xpath_node_set + { + public: + // Collection type + enum type_t + { + type_unsorted, // Not ordered + type_sorted, // Sorted by document order (ascending) + type_sorted_reverse // Sorted by document order (descending) + }; + + // Constant iterator type + typedef const xpath_node* const_iterator; + + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work + typedef const xpath_node* iterator; + + // Default constructor. Constructs empty set. + xpath_node_set(); + + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); + + // Destructor + ~xpath_node_set(); + + // Copy constructor/assignment operator + xpath_node_set(const xpath_node_set& ns); + xpath_node_set& operator=(const xpath_node_set& ns); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_node_set(xpath_node_set&& rhs); + xpath_node_set& operator=(xpath_node_set&& rhs); + #endif + + // Get collection type + type_t type() const; + + // Get collection size + size_t size() const; + + // Indexing operator + const xpath_node& operator[](size_t index) const; + + // Collection iterators + const_iterator begin() const; + const_iterator end() const; + + // Sort the collection in ascending/descending order by document order + void sort(bool reverse = false); + + // Get first node in the collection by document order + xpath_node first() const; + + // Check if collection is empty + bool empty() const; + + private: + type_t _type; + + xpath_node _storage; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs); + }; +#endif + +#ifndef PUGIXML_NO_STL + // Convert wide string to UTF8 + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); + + // Convert UTF8 to wide string + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); +#endif + + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure + typedef void* (*allocation_function)(size_t size); + + // Memory deallocation function interface + typedef void (*deallocation_function)(void* ptr); + + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); + + // Get current memory management functions + allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); +} +#endif + +#endif + +// Make sure implementation is included in header-only mode +// Use macro expansion in #include to work around QMake (QTBUG-11923) +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) +# define PUGIXML_SOURCE "pugixml.cpp" +# include PUGIXML_SOURCE +#endif + +/** + * Copyright (c) 2006-2016 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/pugixml/pugixml_c.cpp b/src/pugixml/pugixml_c.cpp new file mode 100644 index 000000000..925ec6e81 --- /dev/null +++ b/src/pugixml/pugixml_c.cpp @@ -0,0 +1 @@ +#include "pugixml_c.hpp" diff --git a/src/pugixml/pugixml_c.hpp b/src/pugixml/pugixml_c.hpp new file mode 100644 index 000000000..92e95edac --- /dev/null +++ b/src/pugixml/pugixml_c.hpp @@ -0,0 +1,97 @@ +#include + +#include "pugixml.hpp" + +using namespace pugi; + +extern "C" { + // xml_node functions + + const char* xml_node_name(xml_node_struct* node){ + return xml_node(node).name(); + } + + xml_node_struct* xml_node_child(xml_node_struct* node, char* name){ + return xml_node(node).child(name).internal_object(); + } + + xml_node_struct* xml_node_next_sibling(xml_node_struct* node, char* name){ + return xml_node(node).next_sibling(name).internal_object(); + } + + xml_attribute_struct* xml_node_attribute(xml_node_struct* node, char* name){ + return xml_node(node).attribute(name).internal_object(); + } + + const char* xml_node_child_value(xml_node_struct* node){ + return xml_node(node).child_value(); + } + + xml_node_struct* xml_node_text(xml_node_struct* node){ + return xml_node(node).internal_object(); + } + + // xml_attribute functions + + const char* xml_attribute_name(xml_attribute_struct* attribute){ + return xml_attribute(attribute).name(); + } + + const char* xml_attribute_value(xml_attribute_struct* attribute){ + return xml_attribute(attribute).value(); + } + + xml_attribute_struct* xml_attribute_next_attribute(xml_attribute_struct* attribute){ + return xml_attribute(attribute).next_attribute().internal_object(); + } + + bool xml_attribute_as_bool(xml_attribute_struct* attribute){ + return xml_attribute(attribute).as_bool(); + } + + int xml_attribute_as_int(xml_attribute_struct* attribute){ + return xml_attribute(attribute).as_int(); + } + + long long xml_attribute_as_llong(xml_attribute_struct* attribute){ + return xml_attribute(attribute).as_llong(); + } + + double xml_attribute_as_double(xml_attribute_struct* attribute){ + return xml_attribute(attribute).as_double(); + } + + // xml_text functions + + bool xml_text_as_bool(xml_node_struct* node){ + return xml_node(node).text().as_bool(); + } + + int xml_text_as_int(xml_node_struct* node){ + return xml_node(node).text().as_int(); + } + + long long xml_text_as_llong(xml_node_struct* node){ + return xml_node(node).text().as_llong(); + } + + double xml_text_as_double(xml_node_struct* node){ + return xml_node(node).text().as_double(); + } + + // xml_document functions + + xml_document* xml_document_load_file(char* filename){ + xml_document * doc = new xml_document(); + xml_parse_result result = doc->load_file(filename); + return doc; + } + + xml_node_struct* xml_document_document_element(xml_document* doc){ + return doc->document_element().internal_object(); + } + + void xml_document_clear(xml_document* doc){ + delete doc; + } +} diff --git a/src/pugixml/pugixml_f.F90 b/src/pugixml/pugixml_f.F90 new file mode 100644 index 000000000..bb45b79c2 --- /dev/null +++ b/src/pugixml/pugixml_f.F90 @@ -0,0 +1,451 @@ +module pugixml + + use, intrinsic :: ISO_C_BINDING + + implicit none + private + + interface + function strlen(str) result(sz) bind(C) + import C_PTR, C_SIZE_T + type(C_PTR), value :: str + integer(C_SIZE_T) :: sz + end function strlen + end interface + + !============================================================================= + ! XMLNode C interface and derived type + + interface + function xml_node_name(node) result(name) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR) :: name + end function xml_node_name + + function xml_node_name_size(base) result(sz) bind(C) + import C_PTR, C_SIZE_T + type(C_PTR), value :: base + integer(C_SIZE_T) :: sz + end function xml_node_name_size + + function xml_node_child(node, name) result(child) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR), value :: name + type(C_PTR) :: child + end function xml_node_child + + function xml_node_next_sibling(node, name) result(next) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR), value :: name + type(C_PTR) :: next + end function xml_node_next_sibling + + function xml_node_attribute(node, name) result(attribute) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR), value :: name + type(C_PTR) :: attribute + end function xml_node_attribute + + function xml_node_child_value(node) result(val) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR) :: val + end function xml_node_child_value + + function xml_node_text(node) result(text) bind(C) + import C_PTR + type(C_PTR), value :: node + type(C_PTR) :: text + end function xml_node_text + end interface + + type :: XMLNode + type(C_PTR) :: ptr + contains + procedure :: name => xmlnode_name + procedure :: child => xmlnode_child + procedure :: next_sibling => xmlnode_next_sibling + procedure :: attribute => xmlnode_attribute + procedure :: child_value => xmlnode_child_value + procedure :: text => xmlnode_text + procedure :: associated => xmlnode_associated + end type XMLNode + + !============================================================================= + ! XMLAttribute C interface and derived type + + interface + function xml_attribute_name(attribute) result(name) bind(C) + import C_PTR + type(C_PTR), value :: attribute + type(C_PTR) :: name + end function xml_attribute_name + + function xml_attribute_name_size(base) result(sz) bind(C) + import C_PTR, C_SIZE_T + type(C_PTR), value :: base + integer(C_SIZE_T) :: sz + end function xml_attribute_name_size + + function xml_attribute_value(attribute) result(val) bind(C) + import C_PTR + type(C_PTR), value :: attribute + type(C_PTR) :: val + end function xml_attribute_value + + function xml_attribute_next_attribute(attribute) result(next) bind(C) + import C_PTR + type(C_PTR), value :: attribute + type(C_PTR) :: next + end function xml_attribute_next_attribute + + function xml_attribute_as_bool(attribute) result(x) bind(C) + import C_PTR, C_BOOL + type(C_PTR), value :: attribute + logical(C_BOOL) :: x + end function xml_attribute_as_bool + + function xml_attribute_as_int(attribute) result(x) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: attribute + integer(C_INT) :: x + end function xml_attribute_as_int + + function xml_attribute_as_llong(attribute) result(x) bind(C) + import C_PTR, C_LONG_LONG + type(C_PTR), value :: attribute + integer(C_LONG_LONG) :: x + end function xml_attribute_as_llong + + function xml_attribute_as_double(attribute) result(x) bind(C) + import C_PTR, C_DOUBLE + type(C_PTR), value :: attribute + real(C_DOUBLE) :: x + end function xml_attribute_as_double + end interface + + type :: XMLAttribute + type(C_PTR) :: ptr + contains + procedure :: name => xmlattribute_name + procedure :: value => xmlattribute_value + procedure :: next_attribute => xmlattribute_next_attribute + procedure :: as_bool => xmlattribute_as_bool + procedure :: as_int => xmlattribute_as_int + procedure :: as_llong => xmlattribute_as_llong + procedure :: as_double => xmlattribute_as_double + procedure :: associated => xmlattribute_associated + end type XMLAttribute + + !============================================================================= + ! XMLText C interface and derived type + + interface + function xml_text_as_bool(text) result(x) bind(C) + import C_PTR, C_BOOL + type(C_PTR), value :: text + logical(C_BOOL) :: x + end function xml_text_as_bool + + function xml_text_as_int(text) result(x) bind(C) + import C_PTR, C_INT + type(C_PTR), value :: text + integer(C_INT) :: x + end function xml_text_as_int + + function xml_text_as_llong(text) result(x) bind(C) + import C_PTR, C_LONG_LONG + type(C_PTR), value :: text + integer(C_LONG_LONG) :: x + end function xml_text_as_llong + + function xml_text_as_double(text) result(x) bind(C) + import C_PTR, C_DOUBLE + type(C_PTR), value :: text + real(C_DOUBLE) :: x + end function xml_text_as_double + end interface + + type :: XMLText + type(C_PTR) :: ptr + contains + procedure :: as_bool => xmltext_as_bool + procedure :: as_int => xmltext_as_int + procedure :: as_llong => xmltext_as_llong + procedure :: as_double => xmltext_as_double + end type XMLText + + !============================================================================= + ! XMLDocument C interface and derived type + + interface + function xml_document_load_file(filename) result(doc) bind(C) + import C_PTR + type(C_PTR), value :: filename + type(C_PTR) :: doc + end function xml_document_load_file + + function xml_document_document_element(doc) result(node) bind(C) + import C_PTR + type(C_PTR), value :: doc + type(C_PTR) :: node + end function xml_document_document_element + + subroutine xml_document_clear(doc) bind(C) + import C_PTR + type(C_PTR), value :: doc + end subroutine xml_document_clear + end interface + + type, extends(XMLNode) :: XMLDocument + contains + procedure :: load_file => xmldocument_load_file + procedure :: document_element => xmldocument_document_element + procedure :: clear => xmldocument_clear + end type XMLDocument + + public :: XMLNode, XMLAttribute, XMLText, XMLDocument + +contains + + !============================================================================= + ! XMLNode Implementation + + function xmlnode_name(this) result(name) + class(XMLNode), intent(in) :: this + character(len=:, kind=C_CHAR), allocatable :: name + + character(kind=C_CHAR), pointer :: string(:) + integer(C_SIZE_T) :: size_string + integer(C_SIZE_T) :: i + type(C_PTR) :: name_ptr + + name_ptr = xml_node_name(this % ptr) + size_string = strlen(name_ptr) + call c_f_pointer(name_ptr, string, [size_string]) + allocate(character(len=size_string, kind=C_CHAR) :: name) + do i = 1, size_string + name(i:i) = string(i) + end do + end function xmlnode_name + + function xmlnode_child(this, name) result(child) + class(XMLNode), intent(in) :: this + character(len=*), optional :: name + type(XMLNode) :: child + + integer :: i, n + character(len=:, kind=C_CHAR), target, allocatable :: string + + if (present(name)) then + allocate(character(len=len_trim(name) + 1, kind=C_CHAR) :: string) + n = len_trim(name) + do i = 1, n + string(i:i) = name(i:i) + end do + string(n+1:n+1) = C_NULL_CHAR + child % ptr = xml_node_child(this % ptr, c_loc(string)) + else + child % ptr = xml_node_child(this % ptr, C_NULL_PTR) + end if + end function xmlnode_child + + function xmlnode_next_sibling(this, name) result(next) + class(XMLNode), intent(in) :: this + character(len=*), optional :: name + type(XMLNode) :: next + + integer :: i, n + character(len=:, kind=C_CHAR), target, allocatable :: string + + if (present(name)) then + allocate(character(len=len_trim(name) + 1, kind=C_CHAR) :: string) + n = len_trim(name) + do i = 1, n + string(i:i) = name(i:i) + end do + string(n+1:n+1) = C_NULL_CHAR + next % ptr = xml_node_next_sibling(this % ptr, c_loc(string)) + else + next % ptr = xml_node_next_sibling(this % ptr, C_NULL_PTR) + end if + end function xmlnode_next_sibling + + function xmlnode_attribute(this, name) result(attribute) + class(XMLNode), intent(in) :: this + character(len=*), intent(in) :: name + type(XMLAttribute) :: attribute + + integer :: i, n + character(kind=C_CHAR), target :: string(len_trim(name) + 1) + + n = len_trim(name) + do i = 1, n + string(i) = name(i:i) + end do + string(n+1) = C_NULL_CHAR + attribute % ptr = xml_node_attribute(this % ptr, c_loc(string)) + end function xmlnode_attribute + + function xmlnode_child_value(this) result(val) + class(XMLNode), intent(in) :: this + character(len=:, kind=C_CHAR), allocatable :: val + + character(kind=C_CHAR), pointer :: string(:) + integer(C_SIZE_T) :: size_string + integer(C_SIZE_T) :: i + type(C_PTR) :: text + + text = xml_node_child_value(this % ptr) + size_string = strlen(text) + call c_f_pointer(text, string, [size_string]) + allocate(character(len=size_string, kind=C_CHAR) :: val) + do i = 1, size_string + val(i:i) = string(i) + end do + end function xmlnode_child_value + + function xmlnode_text(this) result(text) + class(XMLNode), intent(in) :: this + type(XMLText) :: text + + text % ptr = xml_node_text(this % ptr) + end function xmlnode_text + + logical function xmlnode_associated(this) + class(XMLNode), intent(in) :: this + + xmlnode_associated = c_associated(this % ptr) + end function xmlnode_associated + + !============================================================================= + ! XMLAttribute Implementation + + function xmlattribute_name(this) result(name) + class(XMLAttribute), intent(in) :: this + character(len=:, kind=C_CHAR), allocatable :: name + + character(kind=C_CHAR), pointer :: string(:) + integer(C_SIZE_T) :: size_string + integer(C_SIZE_T) :: i + type(C_PTR) :: name_ptr + + name_ptr = xml_attribute_name(this % ptr) + size_string = strlen(name_ptr) + call c_f_pointer(name_ptr, string, [size_string]) + allocate(character(len=size_string, kind=C_CHAR) :: name) + do i = 1, size_string + name(i:i) = string(i) + end do + end function xmlattribute_name + + function xmlattribute_value(this) result(val) + class(XMLAttribute), intent(in) :: this + character(len=:, kind=C_CHAR), allocatable :: val + + character(kind=C_CHAR), pointer :: string(:) + integer(C_SIZE_T) :: size_string + integer(C_SIZE_T) :: i + type(C_PTR) :: val_ptr + + val_ptr = xml_attribute_value(this % ptr) + size_string = strlen(val_ptr) + call c_f_pointer(val_ptr, string, [size_string]) + allocate(character(len=size_string, kind=C_CHAR) :: val) + do i = 1, size_string + val(i:i) = string(i) + end do + end function xmlattribute_value + + function xmlattribute_next_attribute(this) result(next) + class(XMLAttribute), intent(in) :: this + type(XMLAttribute) :: next + + next % ptr = xml_attribute_next_attribute(this % ptr) + end function xmlattribute_next_attribute + + logical(C_BOOL) function xmlattribute_as_bool(this) + class(XMLAttribute), intent(in) :: this + xmlattribute_as_bool = xml_attribute_as_bool(this % ptr) + end function xmlattribute_as_bool + + integer(C_INT) function xmlattribute_as_int(this) + class(XMLAttribute), intent(in) :: this + xmlattribute_as_int = xml_attribute_as_int(this % ptr) + end function xmlattribute_as_int + + integer(C_LONG_LONG) function xmlattribute_as_llong(this) + class(XMLAttribute), intent(in) :: this + xmlattribute_as_llong = xml_attribute_as_llong(this % ptr) + end function xmlattribute_as_llong + + real(C_DOUBLE) function xmlattribute_as_double(this) + class(XMLAttribute), intent(in) :: this + xmlattribute_as_double = xml_attribute_as_double(this % ptr) + end function xmlattribute_as_double + + logical function xmlattribute_associated(this) + class(XMLAttribute), intent(in) :: this + + xmlattribute_associated = c_associated(this % ptr) + end function xmlattribute_associated + + !============================================================================= + ! XMLText Implementation + + logical(C_BOOL) function xmltext_as_bool(this) + class(XMLText), intent(in) :: this + xmltext_as_bool = xml_text_as_bool(this % ptr) + end function xmltext_as_bool + + integer(C_INT) function xmltext_as_int(this) + class(XMLText), intent(in) :: this + xmltext_as_int = xml_text_as_int(this % ptr) + end function xmltext_as_int + + integer(C_LONG_LONG) function xmltext_as_llong(this) + class(XMLText), intent(in) :: this + xmltext_as_llong = xml_text_as_llong(this % ptr) + end function xmltext_as_llong + + real(C_DOUBLE) function xmltext_as_double(this) + class(XMLText), intent(in) :: this + xmltext_as_double = xml_text_as_double(this % ptr) + end function xmltext_as_double + + !============================================================================= + ! XMLDocument Implementation + + subroutine xmldocument_load_file(this, filename) + class(XMLDocument), intent(inout) :: this + character(*), intent(in) :: filename + + integer :: i, n + character(kind=C_CHAR), target :: string(len_trim(filename) + 1) + + n = len_trim(filename) + do i = 1, n + string(i) = filename(i:i) + end do + string(n+1) = C_NULL_CHAR + this % ptr = xml_document_load_file(c_loc(string)) + end subroutine xmldocument_load_file + + function xmldocument_document_element(this) result(node) + class(XMLDocument), intent(in) :: this + type(XMLNode) :: node + + node % ptr = xml_document_document_element(this % ptr) + end function xmldocument_document_element + + subroutine xmldocument_clear(this) + class(XMLDocument), intent(inout) :: this + + call xml_document_clear(this % ptr) + end subroutine xmldocument_clear + +end module pugixml diff --git a/src/simulation.F90 b/src/simulation.F90 index 1274c2b00..13cc9b669 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -46,10 +46,10 @@ contains ! Display header if (master) then if (run_mode == MODE_FIXEDSOURCE) then - call header("FIXED SOURCE TRANSPORT SIMULATION", level=1) + call header("FIXED SOURCE TRANSPORT SIMULATION", 3) elseif (run_mode == MODE_EIGENVALUE) then - call header("K EIGENVALUE SIMULATION", level=1) - call print_columns() + call header("K EIGENVALUE SIMULATION", 3) + if (verbosity >= 7) call print_columns() end if end if @@ -110,8 +110,6 @@ contains ! ========================================================================== ! END OF RUN WRAPUP - if (master) call header("SIMULATION FINISHED", level=1) - call finalize_simulation() ! Clear particle @@ -172,7 +170,7 @@ contains if (run_mode == MODE_FIXEDSOURCE) then call write_message("Simulating batch " // trim(to_str(current_batch)) & - // "...", 1) + // "...", 6) end if ! Reset total starting particle weight used for normalizing tallies @@ -273,7 +271,8 @@ contains call calculate_average_keff() ! Write generation output - if (master .and. current_gen /= gen_per_batch) call print_generation() + if (master .and. current_gen /= gen_per_batch .and. verbosity >= 7) & + call print_generation() elseif (run_mode == MODE_FIXEDSOURCE) then ! For fixed-source mode, we need to sample the external source if (path_source == '') then @@ -310,7 +309,7 @@ contains if (cmfd_on) call execute_cmfd() ! Display output - if (master) call print_batch_keff() + if (master .and. verbosity >= 7) call print_batch_keff() ! Calculate combined estimate of k-effective if (master) call calculate_combined_keff() @@ -356,7 +355,7 @@ contains ! Write message at beginning if (current_batch == 1) then - call write_message("Replaying history from state point...", 1) + call write_message("Replaying history from state point...", 6) end if if (run_mode == MODE_EIGENVALUE) then @@ -365,17 +364,19 @@ contains call calculate_average_keff() ! print out batch keff - if (current_gen < gen_per_batch) then - if (master) call print_generation() - else - if (master) call print_batch_keff() + if (verbosity >= 7) then + if (current_gen < gen_per_batch) then + if (master) call print_generation() + else + if (master) call print_batch_keff() + end if end if end do end if ! Write message at end if (current_batch == restart_batch) then - call write_message("Resuming simulation...", 1) + call write_message("Resuming simulation...", 6) end if end subroutine replay_batch_history @@ -403,8 +404,8 @@ contains call time_finalize%stop() call time_total%stop() if (master) then - call print_runtime() - call print_results() + if (verbosity >= 6) call print_runtime() + if (verbosity >= 4) call print_results() if (check_overlaps) call print_overlap_check() end if diff --git a/src/source.F90 b/src/source.F90 index 4c0fe9891..f77fe1c05 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -40,7 +40,7 @@ contains character(MAX_FILE_LEN) :: filename type(Bank), pointer :: src ! source bank site - call write_message("Initializing source particles...", 6) + call write_message("Initializing source particles...", 5) if (path_source /= '') then ! Read the source from a binary file instead of sampling from some @@ -84,7 +84,7 @@ contains ! Write out initial source if (write_initial_source) then - call write_message('Writing out initial source...', 1) + call write_message('Writing out initial source...', 5) filename = trim(path_output) // 'initial_source.h5' file_id = file_create(filename, parallel=.true.) call write_source_bank(file_id) diff --git a/src/state_point.F90 b/src/state_point.F90 index f3b452953..c6790a3ef 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -56,7 +56,7 @@ contains filename = trim(filename) // '.h5' ! Write message - call write_message("Creating state point " // trim(filename) // "...", 1) + call write_message("Creating state point " // trim(filename) // "...", 5) if (master) then ! Create statepoint file @@ -440,7 +440,7 @@ contains & zero_padded(current_batch, count_digits(n_max_batches)) filename = trim(filename) // '.h5' call write_message("Creating source file " // trim(filename) & - // "...", 1) + // "...", 5) ! Create separate source file if (master .or. parallel) then @@ -464,7 +464,7 @@ contains ! Also check to write source separately in overwritten file if (source_latest) then filename = trim(path_output) // 'source' // '.h5' - call write_message("Creating source file " // trim(filename) // "...", 1) + call write_message("Creating source file " // trim(filename) // "...", 5) if (master .or. parallel) then file_id = file_create(filename, parallel=.true.) call write_dataset(file_id, "filetype", 'source') @@ -627,7 +627,7 @@ contains ! Write message call write_message("Loading state point " // trim(path_state_point) & - // "...", 1) + // "...", 5) ! Open file for reading file_id = file_open(path_state_point, 'r', parallel=.true.) @@ -781,7 +781,7 @@ contains ! Write message call write_message("Loading source file " // trim(path_source_point) & - // "...", 1) + // "...", 5) ! Open source file file_id = file_open(path_source_point, 'r', parallel=.true.) diff --git a/src/string.F90 b/src/string.F90 index b51763a61..efb0dd982 100644 --- a/src/string.F90 +++ b/src/string.F90 @@ -1,5 +1,7 @@ module string + use, intrinsic :: ISO_C_BINDING + use constants, only: MAX_WORDS, MAX_LINE_LEN, ERROR_INT, ERROR_REAL, & OP_LEFT_PAREN, OP_RIGHT_PAREN, OP_COMPLEMENT, OP_INTERSECTION, OP_UNION use error, only: fatal_error, warning @@ -482,4 +484,34 @@ contains end function real_to_str +!=============================================================================== +! WORD_COUNT determines the number of words in a string +!=============================================================================== + + function word_count(str) result(n) + character(*), intent(in) :: str + integer :: n + + integer :: i + character(kind=C_CHAR) :: chr + logical :: inword + + ! Count number of words + inword = .false. + n = 0 + do i = 1, len_trim(str) + chr = str(i:i) + select case (chr) + case (' ', C_HORIZONTAL_TAB, C_NEW_LINE, C_CARRIAGE_RETURN) + if (inword) then + inword = .false. + n = n + 1 + end if + case default + inword = .true. + end select + end do + if (inword) n = n + 1 + end function word_count + end module string diff --git a/src/summary.F90 b/src/summary.F90 index 278127308..354f1db55 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -122,7 +122,7 @@ contains integer(HID_T) :: universes_group, univ_group integer(HID_T) :: lattices_group, lattice_group real(8), allocatable :: coeffs(:) - character(REGION_SPEC_LEN) :: region_spec + character(:), allocatable :: region_spec character(MAX_LINE_LEN), allocatable :: paths(:) character(MAX_LINE_LEN) :: path type(Cell), pointer :: c diff --git a/src/tally.F90 b/src/tally.F90 index 650e91b66..ebca2bb5d 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -443,7 +443,7 @@ contains if (micro_xs(p % event_nuclide) % absorption > ZERO) then score = p % absorb_wgt * micro_xs(p % event_nuclide) % fission & * nuclides(p % event_nuclide) % nu(E, EMISSION_PROMPT) & - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux else score = ZERO end if @@ -456,7 +456,7 @@ contains ! bank as prompt neutrons. Since this was weighted by 1/keff, we ! multiply by keff to get the proper score. score = keff * p % wgt_bank * (ONE - sum(p % n_delayed_bank) & - / real(p % n_bank, 8)) + / real(p % n_bank, 8)) * flux end if else @@ -534,7 +534,7 @@ contains ! Compute the score and tally to bin score = p % absorb_wgt * yield & * micro_xs(p % event_nuclide) % fission & - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux call score_fission_delayed_dg(t, d_bin, score, score_index) end do cycle SCORE_LOOP @@ -545,7 +545,7 @@ contains ! delayed-nu-fission xs to the absorption xs score = p % absorb_wgt * micro_xs(p % event_nuclide) % fission & * nuclides(p % event_nuclide) % nu(E, EMISSION_DELAYED) & - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux end if end if else @@ -574,7 +574,7 @@ contains ! Compute the score and tally to bin score = keff * p % wgt_bank / p % n_bank * & - p % n_delayed_bank(d) + p % n_delayed_bank(d) * flux call score_fission_delayed_dg(t, d_bin, score, score_index) end do @@ -583,7 +583,8 @@ contains else ! Add the contribution from all delayed groups - score = keff * p % wgt_bank / p % n_bank * sum(p % n_delayed_bank) + score = keff * p % wgt_bank / p % n_bank * & + sum(p % n_delayed_bank) * flux end if end if else @@ -719,7 +720,7 @@ contains score = p % absorb_wgt * yield * & micro_xs(p % event_nuclide) % fission & / micro_xs(p % event_nuclide) % absorption & - * rxn % products(1 + d) % decay_rate + * rxn % products(1 + d) % decay_rate * flux end associate ! Tally to bin @@ -747,7 +748,7 @@ contains score = score + rxn % products(1 + d) % decay_rate * & p % absorb_wgt * micro_xs(p % event_nuclide) % fission *& nuclides(p % event_nuclide) % nu(E, EMISSION_DELAYED, d)& - / micro_xs(p % event_nuclide) % absorption + / micro_xs(p % event_nuclide) % absorption * flux end do end associate end if @@ -782,7 +783,7 @@ contains ! determine score based on bank site weight and keff. score = score + keff * fission_bank(n_bank - p % n_bank + k) & - % wgt * rxn % products(1 + g) % decay_rate + % wgt * rxn % products(1 + g) % decay_rate * flux end associate ! if the delayed group filter is present, tally to corresponding @@ -1638,7 +1639,7 @@ contains ! bank. Since this was weighted by 1/keff, we multiply by keff ! to get the proper score. score = keff * p % wgt_bank * (ONE - sum(p % n_delayed_bank) & - / real(p % n_bank, 8)) + / real(p % n_bank, 8)) * flux if (i_nuclide > 0) then score = score * atom_density * & nucxs % get_xs('fission', p_g, UVW=p_uvw) / & @@ -1738,7 +1739,7 @@ contains d = filt % groups(d_bin) score = keff * p % wgt_bank / p % n_bank * & - p % n_delayed_bank(d) + p % n_delayed_bank(d) * flux if (i_nuclide > 0) then score = score * atom_density * & @@ -1751,7 +1752,7 @@ contains cycle SCORE_LOOP end select else - score = keff * p % wgt_bank / p % n_bank * sum(p % n_delayed_bank) + score = keff * p % wgt_bank / p % n_bank * sum(p % n_delayed_bank) * flux if (i_nuclide > 0) then score = score * atom_density * & nucxs % get_xs('fission', p_g, UVW=p_uvw) / & @@ -1851,12 +1852,12 @@ contains score = score + p % absorb_wgt * & nucxs % get_xs('decay rate', p_g, UVW=p_uvw, dg=d) * & nucxs % get_xs('delayed-nu-fission', p_g, UVW=p_uvw, & - dg=d) / matxs % get_xs('absorption', p_g, UVW=p_uvw) + dg=d) / matxs % get_xs('absorption', p_g, UVW=p_uvw) * flux else score = score + p % absorb_wgt * & matxs % get_xs('decay rate', p_g, UVW=p_uvw, dg=d) * & matxs % get_xs('delayed-nu-fission', p_g, UVW=p_uvw, & - dg=d) / matxs % get_xs('absorption', p_g, UVW=p_uvw) + dg=d) / matxs % get_xs('absorption', p_g, UVW=p_uvw) * flux end if end do end if @@ -1887,11 +1888,11 @@ contains fission_bank(n_bank - p % n_bank + k) % wgt * & nucxs % get_xs('decay rate', p_g, UVW=p_uvw, dg=g) * & nucxs % get_xs('fission', p_g, UVW=p_uvw) / & - matxs % get_xs('fission', p_g, UVW=p_uvw) + matxs % get_xs('fission', p_g, UVW=p_uvw) * flux else score = score + keff * & fission_bank(n_bank - p % n_bank + k) % wgt * & - matxs % get_xs('decay rate', p_g, UVW=p_uvw, dg=g) + matxs % get_xs('decay rate', p_g, UVW=p_uvw, dg=g) * flux end if ! if the delayed group filter is present, tally to corresponding @@ -2523,9 +2524,13 @@ contains if (.not. run_CE .and. eo_filt % matches_transport_groups) then - ! determine outgoing energy from fission bank + ! determine outgoing energy group from fission bank g_out = int(fission_bank(n_bank - p % n_bank + k) % E) + ! modify the value so that g_out = 1 corresponds to the highest + ! energy bin + g_out = size(eo_filt % bins) - g_out + ! change outgoing energy bin matching_bins(i) = g_out @@ -2554,12 +2559,11 @@ contains ! determine scoring index and weight for this filter combination i_filter = sum((matching_bins(1:size(t%filters)) - 1) * t % stride) & + 1 - filter_weight = product(filter_weights(:size(t % filters))) ! Add score to tally !$omp atomic t % results(RESULT_VALUE, i_score, i_filter) = & - t % results(RESULT_VALUE, i_score, i_filter) + score * filter_weight + t % results(RESULT_VALUE, i_score, i_filter) + score ! Case for tallying delayed emissions else if (score_bin == SCORE_DELAYED_NU_FISSION .and. g /= 0) then @@ -2583,7 +2587,15 @@ contains ! check whether the delayed group of the particle is equal to ! the delayed group of this bin if (d == g) then - call score_fission_delayed_dg(t, d_bin, score, i_score) + + ! determine scoring index and weight for this filter + ! combination + i_filter = sum((matching_bins(1:size(t%filters)) - 1) * & + t % stride) + 1 + filter_weight = product(filter_weights(:size(t % filters))) + + call score_fission_delayed_dg(t, d_bin, & + score * filter_weight, i_score) end if end do end select @@ -2624,7 +2636,6 @@ contains integer :: bin_original ! original bin index integer :: filter_index ! index for matching filter bin combination - real(8) :: filter_weight ! combined weight of all filters ! save original delayed group bin bin_original = matching_bins(t % find_filter(FILTER_DELAYEDGROUP)) @@ -2633,11 +2644,10 @@ contains ! determine scoring index and weight on the modified matching_bins filter_index = sum((matching_bins(1:size(t % filters)) - 1) * t % stride) & + 1 - filter_weight = product(filter_weights(:size(t % filters))) !$omp atomic t % results(RESULT_VALUE, score_index, filter_index) = & - t % results(RESULT_VALUE, score_index, filter_index) + score * filter_weight + t % results(RESULT_VALUE, score_index, filter_index) + score ! reset original delayed group bin matching_bins(t % find_filter(FILTER_DELAYEDGROUP)) = bin_original diff --git a/src/trigger.F90 b/src/trigger.F90 index bf22327d6..503b53ca8 100644 --- a/src/trigger.F90 +++ b/src/trigger.F90 @@ -46,16 +46,16 @@ contains ! When trigger threshold is reached, write information if (satisfy_triggers) then call write_message("Triggers satisfied for batch " // & - trim(to_str(current_batch))) + trim(to_str(current_batch)), 7) ! When trigger is not reached write convergence info for user elseif (name == "eigenvalue") then call write_message("Triggers unsatisfied, max unc./thresh. is " // & - trim(to_str(max_ratio)) // " for " // trim(name)) + trim(to_str(max_ratio)) // " for " // trim(name), 7) else call write_message("Triggers unsatisfied, max unc./thresh. is " // & trim(to_str(max_ratio)) // " for " // trim(name) // & - " in tally " // trim(to_str(tally_id))) + " in tally " // trim(to_str(tally_id)), 7) end if ! If batch_interval is not set, estimate batches till triggers are satisfied @@ -75,7 +75,7 @@ contains " -- greater than max batches. ") else call write_message("The estimated number of batches is " // & - trim(to_str(n_pred_batches))) + trim(to_str(n_pred_batches)), 7) end if end if end subroutine check_triggers diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index 5fb4cb5b4..d63955017 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -42,7 +42,7 @@ contains type(VectorReal), allocatable :: uncertainty_vec(:) ! uncertainty of total # of atoms if (master) then - call header("STOCHASTIC VOLUME CALCULATION", level=1) + call header("STOCHASTIC VOLUME CALCULATION", 3) call time_volume % start() end if @@ -54,7 +54,7 @@ contains if (master) then call write_message("Running volume calculation " // trim(to_str(i)) & - // "...") + // "...", 4) end if call get_volume(volume_calcs(i), volume, nuclide_vec, atoms_vec, & @@ -74,9 +74,10 @@ contains do j = 1, size(volume_calcs(i) % domain_id) call write_message(trim(domain_type) // " " // trim(to_str(& volume_calcs(i) % domain_id(j))) // ": " // trim(to_str(& - volume(1,j))) // " +/- " // trim(to_str(volume(2,j))) // " cm^3") + volume(1,j))) // " +/- " // trim(to_str(volume(2,j))) // & + " cm^3", 4) end do - call write_message("") + call write_message("", 4) filename = trim(path_output) // 'volume_' // trim(to_str(i)) // '.h5' call write_volume(volume_calcs(i), filename, volume, nuclide_vec, & @@ -90,7 +91,7 @@ contains if (master) then call time_volume % stop() call write_message("Elapsed time: " // trim(to_str(time_volume % & - get_value())) // " s") + get_value())) // " s", 6) end if end subroutine run_volume_calculations diff --git a/src/volume_header.F90 b/src/volume_header.F90 index c70345f15..a01b664df 100644 --- a/src/volume_header.F90 +++ b/src/volume_header.F90 @@ -20,7 +20,7 @@ contains subroutine volume_from_xml(this, node_vol) class(VolumeCalculation), intent(out) :: this - type(Node), pointer :: node_vol + type(XMLNode), intent(in) :: node_vol integer :: num_domains character(10) :: temp_str @@ -41,7 +41,7 @@ contains ! Read cell IDs if (check_for_node(node_vol, "domain_ids")) then - num_domains = get_arraysize_integer(node_vol, "domain_ids") + num_domains = node_word_count(node_vol, "domain_ids") else call fatal_error("Must specify at least one cell for a volume calculation") end if diff --git a/src/xml/fox b/src/xml/fox deleted file mode 160000 index bdc852f4f..000000000 --- a/src/xml/fox +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bdc852f4f43d969fb1b179cba79295c1e095a455 diff --git a/src/xml/openmc_fox.F90 b/src/xml/openmc_fox.F90 deleted file mode 100644 index 461c6bc2b..000000000 --- a/src/xml/openmc_fox.F90 +++ /dev/null @@ -1,194 +0,0 @@ -module openmc_fox - - use fox_m_fsys_array_str, only: str_vs, vs_str, vs_str_alloc - use fox_m_fsys_format, only: operator(//) - use fox_m_fsys_string, only: toLower - use fox_m_utils_uri, only: URI, parseURI, destroyURI, isAbsoluteURI, & - rebaseURI, expressURI - use m_common_charset, only: checkChars, XML1_0, XML1_1 - use m_common_element, only: element_t, get_element, attribute_t, & - attribute_has_default, get_attribute_declaration, get_attlist_size - use m_common_namecheck, only: checkQName, prefixOfQName, localPartOfQName, & - checkName, checkPublicId, checkNCName - use m_common_struct, only: xml_doc_state, init_xml_doc_state, destroy_xml_doc_state - - use m_dom_error, only: DOMException, throw_exception, inException, getExceptionCode, & - NO_MODIFICATION_ALLOWED_ERR, NOT_FOUND_ERR, HIERARCHY_REQUEST_ERR, & - WRONG_DOCUMENT_ERR, FoX_INTERNAL_ERROR, FoX_NODE_IS_NULL, FoX_LIST_IS_NULL, & - INUSE_ATTRIBUTE_ERR, FoX_MAP_IS_NULL, INVALID_CHARACTER_ERR, NAMESPACE_ERR, & - FoX_INVALID_PUBLIC_ID, FoX_INVALID_SYSTEM_ID, FoX_IMPL_IS_NULL, FoX_INVALID_NODE, & - FoX_INVALID_CHARACTER, FoX_INVALID_COMMENT, FoX_INVALID_CDATA_SECTION, & - FoX_INVALID_PI_DATA, NOT_SUPPORTED_ERR, FoX_INVALID_ENTITY, & - INDEX_SIZE_ERR, FoX_NO_SUCH_ENTITY, FoX_HIERARCHY_REQUEST_ERR, & - FoX_INVALID_URI - - use m_dom_dom - use fox_dom - use fox_m_fsys_count_parse_input, only: countrts - - implicit none - - contains - - function getChildrenByTagName(doc, tagName, name, ex)result(list) - type(DOMException), intent(out), optional :: ex - type(Node), pointer :: doc - character(len=*), intent(in), optional :: tagName, name - type(NodeList), pointer :: list - - type(NodeListPtr), pointer :: nll(:), temp_nll(:) - type(Node), pointer :: arg, this, treeroot - logical :: doneChildren, doneAttributes, allElements - integer :: i, i_tree - - list => null() - - if (.not.associated(doc)) then - if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then - call throw_exception(FoX_NODE_IS_NULL, "getElementsByTagName", ex) - if (present(ex)) then - if (inException(ex)) then - return - endif - endif -endif - - endif - - if (doc%nodeType==DOCUMENT_NODE) then - if (present(name).or..not.present(tagName)) then - if (getFoX_checks().or.FoX_INVALID_NODE<200) then - call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) - if (present(ex)) then - if (inException(ex)) then - return - endif - endif -endif - - endif - elseif (doc%nodeType==ELEMENT_NODE) then - if (present(name).or..not.present(tagName)) then - if (getFoX_checks().or.FoX_INVALID_NODE<200) then - call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) - if (present(ex)) then - if (inException(ex)) then - return - endif - endif -endif - - endif - else - if (getFoX_checks().or.FoX_INVALID_NODE<200) then - call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) - if (present(ex)) then - if (inException(ex)) then - return - endif - endif -endif - - endif - - if (doc%nodeType==DOCUMENT_NODE) then - arg => getDocumentElement(doc) - else - arg => doc - endif - - allocate(list) - allocate(list%nodes(0)) - list%element => doc - if (present(name)) list%nodeName => vs_str_alloc(name) - if (present(tagName)) list%nodeName => vs_str_alloc(tagName) - - allElements = (str_vs(list%nodeName)=="*") - - if (doc%nodeType==DOCUMENT_NODE) then - nll => doc%docExtras%nodelists - elseif (doc%nodeType==ELEMENT_NODE) then - nll => doc%ownerDocument%docExtras%nodelists - endif - allocate(temp_nll(size(nll)+1)) - do i = 1, size(nll) - temp_nll(i)%this => nll(i)%this - enddo - temp_nll(i)%this => list - deallocate(nll) - if (doc%nodeType==DOCUMENT_NODE) then - doc%docExtras%nodelists => temp_nll - elseif (doc%nodeType==ELEMENT_NODE) then - doc%ownerDocument%docExtras%nodelists => temp_nll - endif - - treeroot => arg - - i_tree = 0 - doneChildren = .false. - doneAttributes = .false. - this => treeroot - do - if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then - if (this%nodeType==ELEMENT_NODE) then - if ((allElements .or. str_vs(this%nodeName)==tagName) & - .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & - call append(list, this) - doneAttributes = .true. - endif - - else - if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then - doneAttributes = .true. - else - - endif - endif - - - if (.not.doneChildren) then - if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then - if (getLength(getAttributes(this))>0) then - this => item(getAttributes(this), 0) - else - doneAttributes = .true. - endif - elseif (hasChildNodes(this) .and. .not. associated(getParentNode(this), treeroot)) then - this => getFirstChild(this) - doneChildren = .false. - doneAttributes = .false. - else - doneChildren = .true. - doneAttributes = .false. - endif - - else ! if doneChildren - - if (associated(this, treeroot)) exit - if (getNodeType(this)==ATTRIBUTE_NODE) then - if (i_tree item(getAttributes(getOwnerElement(this)), i_tree) - doneChildren = .false. - else - i_tree= 0 - this => getOwnerElement(this) - doneAttributes = .true. - doneChildren = .false. - endif - elseif (associated(getNextSibling(this))) then - - this => getNextSibling(this) - doneChildren = .false. - doneAttributes = .false. - else - this => getParentNode(this) - endif - endif - - enddo - - - - end function getChildrenByTagName -end module openmc_fox diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 8f0370b15..a9dc5224b 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -1,30 +1,23 @@ module xml_interface - use constants, only: MAX_LINE_LEN - use error, only: fatal_error - use openmc_fox + use, intrinsic :: ISO_C_BINDING + + use error, only: fatal_error + use pugixml + use string, only: word_count implicit none private - public :: Node - public :: NodeList - public :: open_xmldoc - public :: close_xmldoc + public :: XMLDocument, XMLNode, XMLText, XMLAttribute public :: check_for_node - public :: get_node_ptr public :: get_node_list - public :: get_list_size - public :: get_list_item public :: get_node_value public :: get_node_array - public :: get_arraysize_integer - public :: get_arraysize_double - public :: get_arraysize_string - - integer, parameter :: ATTR_NODE = 0 - integer, parameter :: ELEM_NODE = 1 + public :: node_value_string + public :: node_word_count interface get_node_value + module procedure get_node_value_bool module procedure get_node_value_integer module procedure get_node_value_long module procedure get_node_value_double @@ -39,493 +32,305 @@ module xml_interface contains -!=============================================================================== -! OPEN_XMLDOC opens and parses an XML and returns a pointer to the document -!=============================================================================== - - subroutine open_xmldoc(ptr, filename) - - character(len=*) :: filename - type(Node), pointer :: ptr - - ptr => parseFile(trim(filename)) ! Pointer to the whole XML document - ptr => getDocumentElement(ptr) ! Grabs root element of XML document - - end subroutine open_xmldoc - -!=============================================================================== -! CLOSE_XMLDOC closes and destroys all memory associated with document -!=============================================================================== - - subroutine close_xmldoc(ptr) - - type(Node), pointer :: ptr - - ptr => getParentNode(ptr) ! Go from root element ptr to document ptr - call destroy(ptr) ! Deallocates all nodes recursively - - end subroutine close_xmldoc - !=============================================================================== ! CHECK_FOR_NODE checks for an attribute or sub-element node with the given ! node name. This should only be used for checking a single occurance of a -! sub-element node. To check for sub-element nodes that repeat, use -! get_node_list and get_list_size. This is to minimize number of calls -! to getChildrenByTagName. +! sub-element node. !=============================================================================== - function check_for_node(ptr, node_name) result(found) - - character(len=*), intent(in) :: node_name + function check_for_node(node, name) result(found) + type(XMLNode), intent(in) :: node + character(len=*), intent(in) :: name logical :: found - type(Node), pointer, intent(in) :: ptr - type(Node), pointer :: temp_ptr - type(NodeList), pointer :: elem_list + type(XMLAttribute) :: attr + type(XMLNode) :: child - ! Default that we found it and attribute - found = .true. - - ! Get the attribute node - temp_ptr => getAttributeNode(ptr, trim(node_name)) - - ! Check if node exists - if (associated(temp_ptr)) return - - ! Check for a sub-element - elem_list => getChildrenByTagName(ptr, trim(node_name)) - - ! Get the length of the list - if (getLength(elem_list) == 0) then - found = .false. - return + ! Check if an attribute or exists + attr = node % attribute(name) + if (attr % associated()) then + found = .true. + else + child = node % child(name) + if (child % associated()) then + found = .true. + else + found = .false. + end if end if - end function check_for_node -!=============================================================================== -! GET_NODE_PTR returns a Node pointer to an attribute or sub-element node. -! Note, this should only be used for sub-element nodes that occur only once. -! For repeated nodes, use get_node_list and then get_item_item. -!=============================================================================== - - subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) - - character(len=*), intent(in) :: node_name - logical, intent(out), optional :: found - type(Node), pointer, intent(in) :: in_ptr - type(Node), pointer, intent(out) :: out_ptr - - logical :: found_ - type(NodeList), pointer :: elem_list - - ! Set found to false - found_ = .false. - - ! Check for a sub-element - elem_list => getChildrenByTagName(in_ptr, trim(node_name)) - - ! Get the length of the list - if (getLength(elem_list) == 0) return - - ! Point to the new element - out_ptr => item(elem_list, 0) - found_ = .true. - - ! Check to output found - if (present(found)) found = found_ - - end subroutine get_node_ptr - !=============================================================================== ! GET_NODE_LIST is used to get a pointer to a list of sub-element nodes !=============================================================================== - subroutine get_node_list(in_ptr, node_name, out_ptr) + subroutine get_node_list(node, name, node_list) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + type(XMLNode), allocatable, intent(out) :: node_list(:) - character(len=*), intent(in) :: node_name - type(Node), pointer, intent(in) :: in_ptr - type(NodeList), pointer, intent(out) :: out_ptr + integer :: i, n + type(XMLNode) :: first, current - ! Check for a sub-element - out_ptr => getChildrenByTagName(in_ptr, trim(node_name)) + first = node % child(name) + ! Determine number of nodes + n = 0 + current = first + do while (current % associated()) + n = n + 1 + current = current % next_sibling(name) + end do + + ! Allocate nodes + allocate(node_list(n)) + current = first + do i = 1, n + node_list(i) = current + current = current % next_sibling(name) + end do end subroutine get_node_list !=============================================================================== -! GET_LIST_SIZE is used to get the number of elements from a node list +! GET_NODE_VALUE_BOOL returns a logical value from an attribute or node !=============================================================================== - function get_list_size(in_ptr) result(n_size) + subroutine get_node_value_bool(node, name, val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + logical, intent(out) :: val - integer :: n_size - type(NodeList), pointer, intent(in) :: in_ptr + type(XMLAttribute) :: attr + type(XMLNode) :: child + type(XMLText) :: text - ! Get the size of the list - n_size = getLength(in_ptr) - - end function get_list_size - -!=============================================================================== -! GET_LIST_ITEM is used to select a specific item from a node list -!=============================================================================== - - subroutine get_list_item(in_ptr, idx, out_ptr) - - integer, intent(in) :: idx - type(NodeList), pointer, intent(in) :: in_ptr - type(Node), pointer, intent(out) :: out_ptr - - ! Check for a sub-element - out_ptr => item(in_ptr, idx - 1) - - end subroutine get_list_item + attr = node % attribute(name) + if (attr % associated()) then + val = attr % as_bool() + else + child = node % child(name) + if (child % associated()) then + text = child % text() + val = text % as_bool() + else + call fatal_error("No child XML node named '" // trim(name) // "'.") + end if + end if + end subroutine get_node_value_bool !=============================================================================== ! GET_NODE_VALUE_INTEGER returns a integer value from an attribute or node !=============================================================================== - subroutine get_node_value_integer(ptr, node_name, result_int) + subroutine get_node_value_integer(node, name, val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + integer, intent(out) :: val - character(len=*), intent(in) :: node_name - integer :: result_int - type(Node), pointer, intent(in) :: ptr + type(XMLAttribute) :: attr + type(XMLNode) :: child + type(XMLText) :: text - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") - end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_int) + attr = node % attribute(name) + if (attr % associated()) then + val = attr % as_int() else - call extractDataContent(temp_ptr, result_int) + child = node % child(name) + if (child % associated()) then + text = child % text() + val = text % as_int() + else + call fatal_error("No XML node named '" // trim(name) // "'.") + end if end if - end subroutine get_node_value_integer !=============================================================================== ! GET_NODE_VALUE_LONG returns an 8-byte integer from attribute or node !=============================================================================== - subroutine get_node_value_long(ptr, node_name, result_long) + subroutine get_node_value_long(node, name, val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + integer(C_LONG_LONG), intent(out) :: val - character(len=*), intent(in) :: node_name - integer(8) :: result_long - type(Node), pointer, intent(in) :: ptr + type(XMLAttribute) :: attr + type(XMLNode) :: child + type(XMLText) :: text - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") - end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_long) + attr = node % attribute(name) + if (attr % associated()) then + val = attr % as_llong() else - call extractDataContent(temp_ptr, result_long) + child = node % child(name) + if (child % associated()) then + text = child % text() + val = text % as_llong() + else + call fatal_error("No XML node named '" // trim(name) // "'.") + end if end if - end subroutine get_node_value_long !=============================================================================== ! GET_NODE_VALUE_DOUBLE returns a double precision real from attr. or node !=============================================================================== - subroutine get_node_value_double(ptr, node_name, result_double) + subroutine get_node_value_double(node, name, val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + real(C_DOUBLE), intent(out) :: val - character(len=*), intent(in) :: node_name - real(8) :: result_double - type(Node), pointer, intent(in) :: ptr + type(XMLAttribute) :: attr + type(XMLNode) :: child + type(XMLText) :: text - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") - end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_double) + attr = node % attribute(name) + if (attr % associated()) then + val = attr % as_double() else - call extractDataContent(temp_ptr, result_double) + child = node % child(name) + if (child % associated()) then + text = child % text() + val = text % as_double() + else + call fatal_error("No XML node named '" // trim(name) // "'.") + end if end if - end subroutine get_node_value_double +!=============================================================================== +! NODE_VALUE_STRING returns a single string from attr. or node +!=============================================================================== + + function node_value_string(node, name) result(val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + character(len=:, kind=C_CHAR), allocatable :: val + + type(XMLAttribute) :: attr + type(XMLNode) :: child + + attr = node % attribute(name) + if (attr % associated()) then + val = adjustl(attr % value()) + else + child = node % child(name) + if (child % associated()) then + val = adjustl(child % child_value()) + else + call fatal_error("No child XML node named '" // trim(name) // "'.") + end if + end if + end function node_value_string + +!=============================================================================== +! GET_NODE_VALUE_STRING returns a single string from attr. or node +!=============================================================================== + + subroutine get_node_value_string(node, name, val) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + character(*), intent(out) :: val + + val = node_value_string(node, name) + end subroutine get_node_value_string + +!=============================================================================== +! NODE_WORD_COUNT returns the number of words in a text node or attribute value +!=============================================================================== + + integer function node_word_count(node, name) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + + node_word_count = word_count(node_value_string(node, name)) + end function node_word_count + !=============================================================================== ! GET_NODE_ARRAY_INTEGER returns a 1-D array of integers !=============================================================================== - subroutine get_node_array_integer(ptr, node_name, result_int) + subroutine get_node_array_integer(node, name, array) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + integer, intent(out) :: array(:) - character(len=*), intent(in) :: node_name - integer :: result_int(:) - type(Node), pointer, intent(in) :: ptr + integer :: stat + character(len=:, kind=C_CHAR), allocatable :: str - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr + ! Get value of text node/attribute + str = node_value_string(node, name) - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") + ! Read numbers into array + read(UNIT=str, FMT=*, IOSTAT=stat) array + if (stat > 0) then + call fatal_error("Error converting XML text: " // trim(str)) end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_int) - else - call extractDataContent(temp_ptr, result_int) - end if - end subroutine get_node_array_integer !=============================================================================== ! GET_NODE_ARRAY_DOUBLE returns a 1-D array of double precision reals !=============================================================================== - subroutine get_node_array_double(ptr, node_name, result_double) + subroutine get_node_array_double(node, name, array) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + real(C_DOUBLE), intent(out) :: array(:) - character(len=*), intent(in) :: node_name - real(8) :: result_double(:) - type(Node), pointer, intent(in) :: ptr + integer :: stat + character(len=:, kind=C_CHAR), allocatable :: str - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr + ! Get value of text node/attribute + str = node_value_string(node, name) - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") + ! Read numbers into array + read(UNIT=str, FMT=*, IOSTAT=stat) array + if (stat > 0) then + call fatal_error("Error converting XML text: " // trim(str)) end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_double) - else - call extractDataContent(temp_ptr, result_double) - end if - end subroutine get_node_array_double !=============================================================================== ! GET_NODE_ARRAY_STRING returns a 1-D array of strings !=============================================================================== - subroutine get_node_array_string(ptr, node_name, result_string) + subroutine get_node_array_string(node, name, array) + type(XMLNode), intent(in) :: node + character(*), intent(in) :: name + character(len=*), intent(out) :: array(:) - character(len=*), intent(in) :: node_name - character(len=*) :: result_string(:) - type(Node), pointer, intent(in) :: ptr + integer :: i, n + integer :: start + logical :: inword + character(kind=C_CHAR) :: chr + character(len=:, kind=C_CHAR), allocatable :: str - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr + ! Get value of text node/attribute + str = node_value_string(node, name) - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") + inword = .false. + n = 0 + do i = 1, len(str) + chr = str(i:i) + select case (chr) + case (' ', C_HORIZONTAL_TAB, C_NEW_LINE, C_CARRIAGE_RETURN) + if (inword) then + inword = .false. + n = n + 1 + array(n) = str(start:i-1) + end if + case default + if (.not. inword) then + start = i + inword = .true. + end if + end select + end do + if (inword) then + n = n + 1 + array(n) = str(start:len(str)) end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_string) - else - call extractDataContent(temp_ptr, result_string) - end if - end subroutine get_node_array_string -!=============================================================================== -! GET_NODE_VALUE_STRING returns a single string from attr. or node -!=============================================================================== - - subroutine get_node_value_string(ptr, node_name, result_str) - - character(len=*), intent(in) :: node_name - character(len=*) :: result_str - type(Node), pointer, intent(in) :: ptr - - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " // & - getNodeName(ptr) // ".") - end if - - ! Extract value - if (node_type == ATTR_NODE) then - call extractDataAttribute(ptr, node_name, result_str) - else - call extractDataContent(temp_ptr, result_str) - end if - - end subroutine get_node_value_string - -!=============================================================================== -! GET_NODE_ARRAYSIZE_INTEGER returns the size of the integer array -!=============================================================================== - - function get_arraysize_integer(ptr, node_name) result(n) - - character(len=*), intent(in) :: node_name - integer :: n - type(Node), pointer, intent(in) :: ptr - - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") - end if - - ! Get the size - n = countrts(getTextContent(temp_ptr), 0) - - end function get_arraysize_integer - -!=============================================================================== -! GET_NODE_ARRAYSIZE_DOUBLE returns the size of double prec. real array -!=============================================================================== - - function get_arraysize_double(ptr, node_name) result(n) - - character(len=*), intent(in) :: node_name - integer :: n - type(Node), pointer, intent(in) :: ptr - - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " & - &// getNodeName(ptr) // ".") - end if - - ! Get the size - n = countrts(getTextContent(temp_ptr), 0.0_8) - - end function get_arraysize_double - -!=============================================================================== -! GET_NODE_ARRAYSIZE_STRING returns the size of string array -!=============================================================================== - - function get_arraysize_string(ptr, node_name) result(n) - - character(len=*), intent(in) :: node_name - integer :: n - type(Node), pointer, intent(in) :: ptr - - integer :: node_type - logical :: found - type(Node), pointer :: temp_ptr - - ! Get pointer to the node - call get_node(ptr, node_name, temp_ptr, node_type, found) - - ! Leave if it was not found - if (.not. found) then - call fatal_error("Node " // node_name // " not part of Node " // & - getNodeName(ptr) // ".") - end if - - ! Get the size - n = countrts(getTextContent(temp_ptr), 'a') - - end function get_arraysize_string - -!=============================================================================== -! GET_NODE private routine that gets a pointer to a specific node -!=============================================================================== - - subroutine get_node(in_ptr, node_name, out_ptr, node_type, found) - - character(len=*), intent(in) :: node_name - integer, intent(out) :: node_type - logical, intent(out) :: found - type(Node), pointer, intent(in) :: in_ptr - type(Node), pointer, intent(out) :: out_ptr - - type(NodeList), pointer :: elem_list - - ! Default that we found it and attribute - found = .true. - node_type = ATTR_NODE - - ! Get the attribute node - out_ptr => getAttributeNode(in_ptr, trim(node_name)) - - ! Check if node exists - if (associated(out_ptr)) return - - ! Check for a sub-element - elem_list => getChildrenByTagName(in_ptr, trim(node_name)) - - ! Get the length of the list - if (getLength(elem_list) == 0) then - found = .false. - return - end if - - ! Point to the new element - node_type = ELEM_NODE - out_ptr => item(elem_list, 0) - - end subroutine get_node - end module xml_interface diff --git a/tests/test_cmfd_feed/cmfd.xml b/tests/test_cmfd_feed/cmfd.xml index 479655d87..ae50243c0 100644 --- a/tests/test_cmfd_feed/cmfd.xml +++ b/tests/test_cmfd_feed/cmfd.xml @@ -8,8 +8,8 @@ 0.0 0.0 1.0 1.0 1.0 1.0 - 5 - dominance - true - 1.e-15 1.e-20 + 5 + dominance + true + 1.e-15 1.e-20 diff --git a/tests/test_cmfd_feed/settings.xml b/tests/test_cmfd_feed/settings.xml index eab90b70f..3e8b77b39 100644 --- a/tests/test_cmfd_feed/settings.xml +++ b/tests/test_cmfd_feed/settings.xml @@ -7,9 +7,6 @@ 10 1000 - - - @@ -26,6 +23,6 @@ - true + true diff --git a/tests/test_cmfd_nofeed/settings.xml b/tests/test_cmfd_nofeed/settings.xml index eab90b70f..3e8b77b39 100644 --- a/tests/test_cmfd_nofeed/settings.xml +++ b/tests/test_cmfd_nofeed/settings.xml @@ -7,9 +7,6 @@ 10 1000 - - - @@ -26,6 +23,6 @@ - true + true diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/test_mg_convert/test_mg_convert.py index d8a9665d8..45e703b0e 100755 --- a/tests/test_mg_convert/test_mg_convert.py +++ b/tests/test_mg_convert/test_mg_convert.py @@ -154,6 +154,11 @@ class MGXSTestHarness(PyAPITestHarness): form = '{0:12.6E} {1:12.6E}\n' outstr += form.format(sp.k_combined[0], sp.k_combined[1]) + # Enforce closing statepoint and summary files so HDF5 + # does not throw an error during the next OpenMC execution + sp._f.close() + sp._summary._f.close() + return outstr def _get_results(self, outstr, hash_output=False): diff --git a/tests/test_mg_tallies/inputs_true.dat b/tests/test_mg_tallies/inputs_true.dat index 244d92b06..8120c8681 100644 --- a/tests/test_mg_tallies/inputs_true.dat +++ b/tests/test_mg_tallies/inputs_true.dat @@ -137,43 +137,95 @@ scatter nu-scatter nu-fission + + + total absorption flux fission nu-fission scatter nu-scatter + analog + + + + + total absorption flux fission nu-fission + collision + + + + + total absorption flux fission nu-fission + tracklength + + + + + + scatter nu-scatter nu-fission + + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu total absorption fission nu-fission analog - + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu total absorption fission nu-fission tracklength - + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu total absorption fission nu-fission scatter nu-scatter analog - + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu total absorption fission nu-fission collision - + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu total absorption fission nu-fission tracklength - + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu scatter nu-scatter nu-fission + + + + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + total absorption fission nu-fission scatter nu-scatter + analog + + + + + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + total absorption fission nu-fission + collision + + + + + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + total absorption fission nu-fission + tracklength + + + + + + uo2_ang uo2_ang_mu uo2_iso uo2_iso_mu clad_ang clad_ang_mu clad_iso clad_iso_mu lwtr_ang lwtr_ang_mu lwtr_iso lwtr_iso_mu + scatter nu-scatter nu-fission + diff --git a/tests/test_mg_tallies/results_true.dat b/tests/test_mg_tallies/results_true.dat index af328ff8a..db7735cb4 100644 --- a/tests/test_mg_tallies/results_true.dat +++ b/tests/test_mg_tallies/results_true.dat @@ -1 +1 @@ -1817642a0d20d8ef437c7970cae2e77c265a566ef9843ac1b7cd33f76af09365dc8bd5c045f4297360b38d509864bd629b06dfc4d043b4b1ae7d8a5e2e93fae3 \ No newline at end of file +0a17877c7cf6dbd3e432b1997669a47c588d2a4074a29406deef27332eb7a0f736ea1fdc2037fc02f8ebca39f00332563bbe2172adc26bfaab2d6144d259daee \ No newline at end of file diff --git a/tests/test_mg_tallies/test_mg_tallies.py b/tests/test_mg_tallies/test_mg_tallies.py index 257accb60..580eb60c3 100644 --- a/tests/test_mg_tallies/test_mg_tallies.py +++ b/tests/test_mg_tallies/test_mg_tallies.py @@ -23,6 +23,12 @@ class MGTalliesTestHarness(HashedPyAPITestHarness): # Instantiate some tally filters energy_filter = openmc.EnergyFilter([0.0, 20.0e6]) energyout_filter = openmc.EnergyoutFilter([0.0, 20.0e6]) + matching_energy_filter = openmc.EnergyFilter([1e-5, 0.0635, 10.0, + 1.0e2, 1.0e3, 0.5e6, + 1.0e6, 20.0e6]) + matching_eout_filter = openmc.EnergyoutFilter([1e-5, 0.0635, 10.0, + 1.0e2, 1.0e3, 0.5e6, + 1.0e6, 20.0e6]) mesh_filter = openmc.MeshFilter(mesh) mat_ids = [mat.id for mat in self._input_set.materials] @@ -34,6 +40,7 @@ class MGTalliesTestHarness(HashedPyAPITestHarness): True: ['total', 'absorption', 'fission', 'nu-fission']} tallies = [] + for do_nuclides in [False, True]: tallies.append(openmc.Tally()) tallies[-1].filters = [mesh_filter] @@ -49,34 +56,43 @@ class MGTalliesTestHarness(HashedPyAPITestHarness): if do_nuclides: tallies[-1].nuclides = nuclides - tallies.append(openmc.Tally()) - tallies[-1].filters = [mat_filter, energy_filter] - tallies[-1].estimator = 'analog' - tallies[-1].scores = scores[do_nuclides] + ['scatter', - 'nu-scatter'] - if do_nuclides: - tallies[-1].nuclides = nuclides + # Impose energy bins that dont match the MG structure and those + # that do + for match_energy_bins in [False, True]: + if match_energy_bins: + e_filter = matching_energy_filter + eout_filter = matching_eout_filter + else: + e_filter = energy_filter + eout_filter = energyout_filter - tallies.append(openmc.Tally()) - tallies[-1].filters = [mat_filter, energy_filter] - tallies[-1].estimator = 'collision' - tallies[-1].scores = scores[do_nuclides] - if do_nuclides: - tallies[-1].nuclides = nuclides + tallies.append(openmc.Tally()) + tallies[-1].filters = [mat_filter, e_filter] + tallies[-1].estimator = 'analog' + tallies[-1].scores = scores[do_nuclides] + ['scatter', + 'nu-scatter'] + if do_nuclides: + tallies[-1].nuclides = nuclides - tallies.append(openmc.Tally()) - tallies[-1].filters = [mat_filter, energy_filter] - tallies[-1].estimator = 'tracklength' - tallies[-1].scores = scores[do_nuclides] - if do_nuclides: - tallies[-1].nuclides = nuclides + tallies.append(openmc.Tally()) + tallies[-1].filters = [mat_filter, e_filter] + tallies[-1].estimator = 'collision' + tallies[-1].scores = scores[do_nuclides] + if do_nuclides: + tallies[-1].nuclides = nuclides - tallies.append(openmc.Tally()) - tallies[-1].filters = [mat_filter, energy_filter, - energyout_filter] - tallies[-1].scores = ['scatter', 'nu-scatter', 'nu-fission'] - if do_nuclides: - tallies[-1].nuclides = nuclides + tallies.append(openmc.Tally()) + tallies[-1].filters = [mat_filter, e_filter] + tallies[-1].estimator = 'tracklength' + tallies[-1].scores = scores[do_nuclides] + if do_nuclides: + tallies[-1].nuclides = nuclides + + tallies.append(openmc.Tally()) + tallies[-1].filters = [mat_filter, e_filter, eout_filter] + tallies[-1].scores = ['scatter', 'nu-scatter', 'nu-fission'] + if do_nuclides: + tallies[-1].nuclides = nuclides self._input_set.tallies = openmc.Tallies(tallies) diff --git a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py index f9f9a7509..343a48a83 100644 --- a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py +++ b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py @@ -71,6 +71,11 @@ class MGXSTestHarness(PyAPITestHarness): if os.path.exists('./tallies.xml'): os.remove('./tallies.xml') + # Enforce closing statepoint and summary files so HDF5 + # does not throw an error during the next OpenMC execution + sp._f.close() + sp._summary._f.close() + # Re-run MG mode. if self._opts.mpi_exec is not None: mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] diff --git a/tests/test_mgxs_library_mesh/results_true.dat b/tests/test_mgxs_library_mesh/results_true.dat index 28203268a..fd4c972e2 100644 --- a/tests/test_mgxs_library_mesh/results_true.dat +++ b/tests/test_mgxs_library_mesh/results_true.dat @@ -180,30 +180,30 @@ 3 2 2 1 1 1 total 0.011105 0.003806 mesh 1 delayedgroup group in nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.000005 1.004630e-06 -1 1 1 1 2 1 total 0.000025 5.397934e-06 -2 1 1 1 3 1 total 0.000025 5.275008e-06 -3 1 1 1 4 1 total 0.000058 1.230434e-05 -4 1 1 1 5 1 total 0.000026 5.548702e-06 -5 1 1 1 6 1 total 0.000011 2.307003e-06 -6 1 2 1 1 1 total 0.000004 1.779307e-06 -7 1 2 1 2 1 total 0.000024 9.648450e-06 -8 1 2 1 3 1 total 0.000024 9.479453e-06 -9 1 2 1 4 1 total 0.000056 2.231366e-05 -10 1 2 1 5 1 total 0.000026 1.028267e-05 -11 1 2 1 6 1 total 0.000011 4.268087e-06 -12 2 1 1 1 1 total 0.000006 7.462931e-07 -13 2 1 1 2 1 total 0.000031 3.842628e-06 -14 2 1 1 3 1 total 0.000031 3.666739e-06 -15 2 1 1 4 1 total 0.000071 8.228956e-06 -16 2 1 1 5 1 total 0.000031 3.416811e-06 -17 2 1 1 6 1 total 0.000013 1.429034e-06 -18 2 2 1 1 1 total 0.000005 1.049553e-06 -19 2 2 1 2 1 total 0.000025 5.392757e-06 -20 2 2 1 3 1 total 0.000024 5.145263e-06 -21 2 2 1 4 1 total 0.000056 1.156820e-05 -22 2 2 1 5 1 total 0.000025 4.880083e-06 -23 2 2 1 6 1 total 0.000010 2.037276e-06 +0 1 1 1 1 1 total 0.000005 1.004638e-06 +1 1 1 1 2 1 total 0.000025 5.397978e-06 +2 1 1 1 3 1 total 0.000025 5.275050e-06 +3 1 1 1 4 1 total 0.000058 1.230443e-05 +4 1 1 1 5 1 total 0.000026 5.548743e-06 +5 1 1 1 6 1 total 0.000011 2.307020e-06 +6 1 2 1 1 1 total 0.000005 1.838266e-06 +7 1 2 1 2 1 total 0.000025 9.941991e-06 +8 1 2 1 3 1 total 0.000025 9.753804e-06 +9 1 2 1 4 1 total 0.000058 2.290678e-05 +10 1 2 1 5 1 total 0.000026 1.050498e-05 +11 1 2 1 6 1 total 0.000011 4.361882e-06 +12 2 1 1 1 1 total 0.000006 7.462654e-07 +13 2 1 1 2 1 total 0.000031 3.842479e-06 +14 2 1 1 3 1 total 0.000031 3.666594e-06 +15 2 1 1 4 1 total 0.000071 8.228616e-06 +16 2 1 1 5 1 total 0.000031 3.416659e-06 +17 2 1 1 6 1 total 0.000013 1.428970e-06 +18 2 2 1 1 1 total 0.000005 1.117984e-06 +19 2 2 1 2 1 total 0.000026 5.744801e-06 +20 2 2 1 3 1 total 0.000025 5.480526e-06 +21 2 2 1 4 1 total 0.000058 1.231604e-05 +22 2 2 1 5 1 total 0.000026 5.181495e-06 +23 2 2 1 6 1 total 0.000011 2.163732e-06 mesh 1 delayedgroup group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 0.0 0.000000 @@ -238,50 +238,50 @@ 3 1 1 1 4 1 total 0.002835 0.000748 4 1 1 1 5 1 total 0.001300 0.000340 5 1 1 1 6 1 total 0.000540 0.000141 -6 1 2 1 1 1 total 0.000218 0.000074 -7 1 2 1 2 1 total 0.001182 0.000400 -8 1 2 1 3 1 total 0.001160 0.000393 -9 1 2 1 4 1 total 0.002726 0.000926 -10 1 2 1 5 1 total 0.001249 0.000428 -11 1 2 1 6 1 total 0.000519 0.000177 +6 1 2 1 1 1 total 0.000226 0.000076 +7 1 2 1 2 1 total 0.001220 0.000412 +8 1 2 1 3 1 total 0.001197 0.000404 +9 1 2 1 4 1 total 0.002809 0.000949 +10 1 2 1 5 1 total 0.001284 0.000436 +11 1 2 1 6 1 total 0.000533 0.000181 12 2 1 1 1 1 total 0.000226 0.000033 13 2 1 1 2 1 total 0.001207 0.000174 14 2 1 1 3 1 total 0.001175 0.000167 15 2 1 1 4 1 total 0.002721 0.000378 16 2 1 1 5 1 total 0.001207 0.000160 17 2 1 1 6 1 total 0.000502 0.000067 -18 2 2 1 1 1 total 0.000220 0.000068 -19 2 2 1 2 1 total 0.001178 0.000355 -20 2 2 1 3 1 total 0.001150 0.000343 -21 2 2 1 4 1 total 0.002675 0.000783 -22 2 2 1 5 1 total 0.001199 0.000341 -23 2 2 1 6 1 total 0.000499 0.000142 +18 2 2 1 1 1 total 0.000228 0.000071 +19 2 2 1 2 1 total 0.001221 0.000373 +20 2 2 1 3 1 total 0.001190 0.000360 +21 2 2 1 4 1 total 0.002767 0.000822 +22 2 2 1 5 1 total 0.001238 0.000357 +23 2 2 1 6 1 total 0.000515 0.000149 mesh 1 delayedgroup group in nuclide mean std. dev. x y z 0 1 1 1 1 1 total 0.013362 0.003586 1 1 1 1 2 1 total 0.032554 0.008645 2 1 1 1 3 1 total 0.121179 0.031941 3 1 1 1 4 1 total 0.306858 0.079971 -4 1 1 1 5 1 total 0.865303 0.220417 -5 1 1 1 6 1 total 2.906554 0.741589 -6 1 2 1 1 1 total 0.013362 0.004521 -7 1 2 1 2 1 total 0.032556 0.011045 -8 1 2 1 3 1 total 0.121174 0.041226 -9 1 2 1 4 1 total 0.306816 0.104995 -10 1 2 1 5 1 total 0.865155 0.301124 -11 1 2 1 6 1 total 2.906049 1.010025 +4 1 1 1 5 1 total 0.865303 0.220418 +5 1 1 1 6 1 total 2.906554 0.741592 +6 1 2 1 1 1 total 0.013361 0.004515 +7 1 2 1 2 1 total 0.032560 0.010992 +8 1 2 1 3 1 total 0.121165 0.040913 +9 1 2 1 4 1 total 0.306728 0.103786 +10 1 2 1 5 1 total 0.864851 0.295633 +11 1 2 1 6 1 total 2.905009 0.992039 12 2 1 1 1 1 total 0.013353 0.002006 -13 2 1 1 2 1 total 0.032614 0.004677 -14 2 1 1 3 1 total 0.121052 0.016785 -15 2 1 1 4 1 total 0.305600 0.040199 -16 2 1 1 5 1 total 0.860792 0.101713 -17 2 1 1 6 1 total 2.891182 0.344217 -18 2 2 1 1 1 total 0.013356 0.004047 -19 2 2 1 2 1 total 0.032596 0.009453 -20 2 2 1 3 1 total 0.121091 0.034091 -21 2 2 1 4 1 total 0.305990 0.082530 -22 2 2 1 5 1 total 0.862223 0.216936 -23 2 2 1 6 1 total 2.896051 0.731528 +13 2 1 1 2 1 total 0.032614 0.004676 +14 2 1 1 3 1 total 0.121052 0.016784 +15 2 1 1 4 1 total 0.305600 0.040195 +16 2 1 1 5 1 total 0.860792 0.101702 +17 2 1 1 6 1 total 2.891182 0.344181 +18 2 2 1 1 1 total 0.013355 0.004172 +19 2 2 1 2 1 total 0.032599 0.009758 +20 2 2 1 3 1 total 0.121084 0.035213 +21 2 2 1 4 1 total 0.305920 0.085294 +22 2 2 1 5 1 total 0.861968 0.224059 +23 2 2 1 6 1 total 2.895182 0.755656 mesh 1 delayedgroup group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 1 total 0.000000 0.000000 diff --git a/tests/test_particle_restart_eigval/settings.xml b/tests/test_particle_restart_eigval/settings.xml index da37b4fc2..64b1a4dd4 100644 --- a/tests/test_particle_restart_eigval/settings.xml +++ b/tests/test_particle_restart_eigval/settings.xml @@ -1,6 +1,7 @@ + 1 eigenvalue 12 5