New Upstream Snapshot - ismrmrd

Ready changes

Summary

Merged new upstream version: 1.13.2+git20230213.1.3cb9cab (was: 1.8.0).

Diff

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..d1186c1
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,86 @@
+#########################################################
+# file-normalizer stage
+# In order to use BuildKit remote caching, input files must have
+# not only the right content hash, but also the right permissions.
+# Git only tracks whether the owner can execute a file.
+# Here we bring in all files that are going to be used in the
+# subsequent stage and normalize the permissions.
+#########################################################
+
+ARG USERNAME="vscode"
+ARG USER_UID=1000
+ARG USER_GID=$USER_UID
+
+FROM mcr.microsoft.com/oss/busybox/busybox:1.33.1 as file-normalizer
+
+COPY environment.yml \
+     /data/
+
+RUN chmod -R 555 /data/
+
+#########################################################
+# devcontainer stage
+# Installs all dependencies and tooling for development.
+#########################################################
+
+FROM mcr.microsoft.com/vscode/devcontainers/base:0.201.8-focal AS devcontainer
+
+# Install needed packages and setup non-root user.
+ARG USERNAME
+ARG USER_UID
+ARG USER_GID
+
+ARG CONDA_GID=900
+ARG CONDA_ENVIRONMENT_NAME=ismrmrd
+ARG VSCODE_DEV_CONTAINERS_SCRIPT_LIBRARY_VERSION=v0.229.0
+
+RUN apt-get update && apt-get install -y \
+    libc6-dbg \
+    && rm -rf /var/lib/apt/lists/*
+
+# Setting the ENTRYPOINT to docker-init.sh will configure non-root access to
+# the Docker socket if "overrideCommand": false is set in devcontainer.json.
+# The script will also execute CMD if you need to alter startup behaviors.
+ENTRYPOINT [ "/usr/local/share/docker-init.sh" ]
+CMD [ "sleep", "infinity" ]
+
+ARG MAMBA_VERSION=0.22.1
+
+# Based on https://github.com/ContinuumIO/docker-images/blob/master/miniconda3/debian/Dockerfile.
+RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh \
+    && mkdir -p /opt \
+    && sh miniconda.sh -b -p /opt/conda \
+    && ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh \
+    && find /opt/conda/ -follow -type f -name '*.a' -delete \
+    && find /opt/conda/ -follow -type f -name '*.js.map' -delete \
+    && /opt/conda/bin/conda install -n base -c conda-forge mamba=${MAMBA_VERSION} \
+    && /opt/conda/bin/conda clean -afy \
+    && groupadd -r conda --gid ${CONDA_GID} \
+    && usermod -aG conda ${USERNAME} \
+    && chown -R :conda /opt/conda \
+    && chmod -R g+w /opt/conda \
+    && find /opt -type d | xargs -n 1 chmod g+s
+
+# Create a conda environment from the environment file in the repo root.
+COPY --from=file-normalizer --chown=$USER_UID:conda /data/environment.yml /tmp/build/
+RUN umask 0002 \
+    && /opt/conda/bin/mamba env create -f /tmp/build/environment.yml \
+    && /opt/conda/bin/mamba clean -fy \
+    && sudo chown -R :conda /opt/conda/envs
+
+# Add a section to /etc/bash.bashrc that ensures that a section is present at the end of ~/.bashrc.
+# We can't just write to .bashrc from here because it will be overwritten if the devcontainer user has
+# opted to use their own dotfiles repo. The dotfiles repo is cloned after the postCreateCommand
+# in the devcontainer.json file is executed.
+RUN echo "\n\
+if ! grep -q \"^source /opt/conda/etc/profile.d/conda.sh\" /home/${USERNAME}/.bashrc; then\n\
+	echo \"source /opt/conda/etc/profile.d/conda.sh\" >> /home/${USERNAME}/.bashrc\n\
+	echo \"conda activate $(grep 'name:' /tmp/build/environment.yml | awk '{print $2}')\" >> /home/${USERNAME}/.bashrc\n\
+fi\n" >> /etc/bash.bashrc
+
+ENV CMAKE_GENERATOR=Ninja
+
+# Create a kits file for the VSCode CMake Tools extension, so you are not prompted for which kit to select whenever you open VSCode
+RUN mkdir -p /home/vscode/.local/share/CMakeTools \
+    && echo '[{"name":"GCC-11","compilers":{"C":"/opt/conda/envs/ismrmrd/bin/x86_64-conda_cos6-linux-gnu-gcc","CXX":"/opt/conda/envs/ismrmrd/bin/x86_64-conda_cos6-linux-gnu-g++"}}]' > /home/vscode/.local/share/CMakeTools/cmake-tools-kits.json \
+    && chown vscode:conda /home/vscode/.local/share/CMakeTools/cmake-tools-kits.json
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..0089892
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,85 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
+// https://github.com/microsoft/vscode-dev-containerdevcotns/tree/v0.238.0/containers/go
+{
+  "name": "ismrmrd",
+  "build": {
+    "dockerfile": "./Dockerfile",
+    "target": "devcontainer",
+    "context": ".."
+  },
+  "runArgs": [
+    "--cap-add=SYS_PTRACE",
+    "--security-opt",
+    "seccomp=unconfined",
+    "--net=host"
+  ],
+
+  // Configure tool-specific properties.
+  "customizations": {
+    // Configure properties specific to VS Code.
+    "vscode": {
+      // Set *default* container specific settings.json values on container create.
+      "settings": {
+        "files.trimFinalNewlines": true,
+        "files.trimTrailingWhitespace": true,
+        "files.insertFinalNewline": true,
+
+        "[cpp]": {
+          "editor.formatOnSave": true
+        },
+
+        "[json]": {
+          "editor.defaultFormatter": "esbenp.prettier-vscode",
+          "editor.formatOnSave": true
+        },
+
+        "[jsonc]": {
+          "editor.defaultFormatter": "esbenp.prettier-vscode",
+          "editor.formatOnSave": true
+        },
+
+        "cmake.sourceDirectory": "${workspaceFolder}",
+        "cmake.buildDirectory": "${workspaceFolder}/build",
+        "cmake.installPrefix": "/opt/conda/envs/ismrmrd",
+
+        // Python linting.
+        "python.analysis.typeCheckingMode": "basic",
+        "python.linting.ignorePatterns": ["**/conftest.py", "**/test_*.py"],
+        "python.defaultInterpreterPath": "/opt/conda/envs/ismrmrd/bin/python",
+        "python.formatting.autopep8Path": "/opt/conda/envs/ismrmrd/bin/autopep8",
+        "python.terminal.activateEnvironment": false // Disable the extension calling activate when the integrated terminal launches. We take care of this in ~/.bashrc.
+      },
+
+      // Add the IDs of extensions you want installed when the container is created.
+      "extensions": [
+        "eamodio.gitlens",
+        "esbenp.prettier-vscode",
+        "mhutchie.git-graph",
+        "ms-python.python",
+        "ms-vscode.cmake-tools",
+        "ms-vscode.cpptools",
+        "timonwong.shellcheck",
+        "twxs.cmake"
+      ]
+    }
+  },
+
+  // Use 'forwardPorts' to make a list of ports inside the container available locally.
+  // "forwardPorts": [],
+
+  // Use 'postCreateCommand' to run commands after the container is created.
+  // "postCreateCommand": "go version",
+
+  // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
+  "remoteUser": "vscode",
+  "features": {
+    "common": {
+      "username": "automatic"
+    },
+    "docker-from-docker": {
+      "version": "latest",
+      "moby": "false"
+    },
+    "git": "os-provided"
+  }
+}
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index bab465a..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior, e.g.:
-1. Start with clean Ubuntu 20.04 VM '...'
-2. Install '....'
-3. Type '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Logs/Screenshots**
-If applicable, add screenshots or logs to help explain your problem.
-
-**System Information:**
- - OS: [e.g. Linux, Ubuntu 20.04]
- - Install/deployment method [e.g. docker image, conda environment, local compile]
- - Version [include the hash of the branch you are working on]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature-proposal--spec-.md b/.github/ISSUE_TEMPLATE/feature-proposal--spec-.md
deleted file mode 100644
index 695cfc8..0000000
--- a/.github/ISSUE_TEMPLATE/feature-proposal--spec-.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-name: Feature proposal (spec)
-about: Describe a plan for implementing a new feature
-title: ''
-labels: ''
-assignees: ''
-
----
-
-Provide a high-level summary of the feature
-
-
-**Justification**
-Explain WHY we are building this feature. WHO is this for?
-
-
-**Scenarios**
-List the scenarios this will enable. These should map to acceptance criteria and associated tests. Think of user stories.
-
-**Design and implementation**
-Describe the design with enough detail that code reviewers and stakeholders are not surprised.
-
-
-**Test Strategy**
-Describe the test strategy. How will we make sure it works and that it keeps working.
-
-
-**Maintenance**
-What maintenance issues might there be with this feature and WHO will maintain it ?
-
-**Other**
-Any other important issues
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index bbcbbe7..0000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/ismrmrd_build.yml b/.github/workflows/ismrmrd_build.yml
deleted file mode 100644
index 27b0765..0000000
--- a/.github/workflows/ismrmrd_build.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: CMake
-
-on:
-  push:
-    branches:
-      - master
-  
-  pull_request:
-    branches:
-      - master
-
-env:
-  BUILD_TYPE: Release
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-
-    - name: Create Build Environment
-      run: cmake -E make_directory ${{github.workspace}}/build
-
-    - name: Install dependencies
-      run: |
-        sudo apt-get update
-        sudo apt-get -y install doxygen git-core graphviz libboost-all-dev libfftw3-dev libhdf5-serial-dev
-
-    - name: Configure CMake
-      working-directory: ${{github.workspace}}/build
-      shell: bash
-      run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
-
-    - name: Build
-      working-directory: ${{github.workspace}}/build
-      shell: bash
-      run: |
-        cmake --build . --config $BUILD_TYPE
-        cmake --build . --target doc --config $BUILD_TYPE
-
-    - name: Test
-      working-directory: ${{github.workspace}}/build/tests
-      shell: bash
-      run: ./test_ismrmrd
diff --git a/.github/workflows/ismrmrd_matlab.yml b/.github/workflows/ismrmrd_matlab.yml
deleted file mode 100644
index 7533267..0000000
--- a/.github/workflows/ismrmrd_matlab.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: Matlab
-
-on:
-  push:
-    branches:
-      - master
-  
-  pull_request:
-    branches:
-      - master
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-
-    - name: Set up MATLAB
-      uses: matlab-actions/setup-matlab@v1
-    - name: Run tests and generate artifacts
-      uses: matlab-actions/run-tests@v1
-      with:
-        test-results-junit: test-results/results.xml
-        source-folder: matlab
\ No newline at end of file
diff --git a/.github/workflows/ismrmrd_windows_build.yml b/.github/workflows/ismrmrd_windows_build.yml
deleted file mode 100644
index 92359cb..0000000
--- a/.github/workflows/ismrmrd_windows_build.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: WindowsCMake
-
-on:
-  push:
-    branches:
-      - master
-  
-  pull_request:
-    branches:
-      - master
-
-env:
-  BUILD_TYPE: Release
-  VCPKG_DEFAULT_TRIPLET: x64-windows
-
-jobs:
-  windows-build:
-    runs-on: windows-latest
-
-    steps:
-    - uses: actions/checkout@v2
-
-    - name: Create Build Environment
-      shell: pwsh
-      run: cmake -E make_directory ${{github.workspace}}/build
-  
-    - name: Install dependencies
-      shell: pwsh
-      run: |
-        vcpkg install fftw3 boost-test boost-program-options boost-random hdf5
-        vcpkg list
-        dir c:\
-        dir d:\
-    
-    - name: Configure CMake
-      working-directory: ${{github.workspace}}/build
-      shell: pwsh
-      run: cmake $env:GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$env:BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake
-    
-    - name: Build
-      working-directory: ${{github.workspace}}/build
-      shell: pwsh
-      run: cmake --build . --config $env:BUILD_TYPE
-
-    - name: Test
-      working-directory: ${{github.workspace}}/build
-      shell: pwsh
-      run: |
-        cp .\tests\Release\* .\Release\
-        cd .\Release\
-        .\test_ismrmrd.exe
-
-
diff --git a/.github/workflows/rpkg_build.yml b/.github/workflows/rpkg_build.yml
deleted file mode 100644
index 95501da..0000000
--- a/.github/workflows/rpkg_build.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: RPKG
-
-on:
-  push:
-    branches:
-      - master
-  
-  pull_request:
-    branches:
-      - master
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    
-    container:
-      image: fedora:latest
-    steps:
-    - name: Install dependencies
-      run: sudo dnf install rpkg git dnf-plugins-core -y
-
-    - uses: actions/checkout@v2
-    - name: Run RPKG 
-      run: |
-        cd "$GITHUB_WORKSPACE" 
-        rpkg spec --outdir ./
-        sudo dnf builddep -y ismrmrd.spec
-        rm ismrmrd.spec
-        rpkg local 
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 0f21618..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-*~
-*swp
-*.so
-build/
-.DS_Store
-.idea/
-.project
-.cproject
-# autogenerated
-cmake/cpack_options.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 341042f..1396d2d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,6 @@ set (ISMRMRD_CMAKE_DIR ${PROJECT_SOURCE_DIR}/cmake CACHE PATH
   "Location of CMake scripts")
 
 # command line options
-option(USE_SYSTEM_PUGIXML "Use pugixml installed on the system" OFF)
 option(USE_HDF5_DATASET_SUPPORT "Compile with support for reading and writing datasets to HDF5 files" ON)
 option(BUILD_TESTS "Build unit tests " ON)
 option(BUILD_UTILITIES "Build utilities tests " ON)
@@ -40,7 +39,7 @@ endif()
 
 # compiler flags
 if (WIN32)
-    add_definitions(-DWIN32 -D_WIN32 -D_WINDOWS)
+    add_definitions(-DWIN32 -D_WIN32 -D_WINDOWS -DBOOST_UUID_FORCE_AUTO_LINK)
     add_definitions(-DUNICODE -D_UNICODE)
     add_definitions(-D_CRT_SECURE_NO_WARNINGS)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
@@ -80,12 +79,31 @@ endif ()
 #in the utility libraries, that don't affect the data format itself.
 # For more information see http://semver.org/
 set(ISMRMRD_VERSION_MAJOR 1)
-set(ISMRMRD_VERSION_MINOR 8)
-set(ISMRMRD_VERSION_PATCH 0)
+set(ISMRMRD_VERSION_MINOR 13)
+set(ISMRMRD_VERSION_PATCH 3)
+
 set(ISMRMRD_VERSION_STRING ${ISMRMRD_VERSION_MAJOR}.${ISMRMRD_VERSION_MINOR}.${ISMRMRD_VERSION_PATCH})
 set(ISMRMRD_SOVERSION ${ISMRMRD_VERSION_MAJOR}.${ISMRMRD_VERSION_MINOR})
 
-set(ISMRMRD_XML_SCHEMA_SHA1 "5464ea590ded027471b0740bba7472c4919b895b")
+if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.19)
+file(READ ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg.json VCPKG_JSON)
+string(JSON VCPKG_VERSION_STRING GET ${VCPKG_JSON} version)
+
+if (NOT VCPKG_VERSION_STRING STREQUAL ISMRMRD_VERSION_STRING)
+    message("")
+    message("-----------------------------------------------")
+    message("            !! VCPKG VERSION ERROR!!           ")
+    message(" Vcpkg version in vcpkg.json must match ISMRMRD")
+    message("           version in CMakeLists.txt           ")
+    message("-----------------------------------------------")
+    message("")
+    message(FATAL_ERROR "     FATAL VCPKG VERSION ERROR")
+endif ()
+
+endif()
+
+
+set(ISMRMRD_XML_SCHEMA_SHA1  "40036f175a77542b50acbcfb0f019094c90b5e18")
 
 #Remove line breaks and white space that does not change the meaning of the schema
 file(STRINGS ${CMAKE_SOURCE_DIR}/schema/ismrmrd.xsd SCHEMA_STRINGS) #Read all strings from file
@@ -182,6 +200,7 @@ set(ISMRMRD_TARGET_SOURCES
   libsrc/ismrmrd.cpp
   libsrc/xml.cpp
   libsrc/meta.cpp
+  libsrc/serialization.cpp
   libsrc/waveform.cpp
   libsrc/waveform.c
   ${ISMRMRD_DATASET_SOURCES}
@@ -189,23 +208,17 @@ set(ISMRMRD_TARGET_SOURCES
 
 set(ISMRMRD_TARGET_LINK_LIBS ${ISMRMRD_DATASET_LIBRARIES})
 
-# optional handling of system-installed pugixml
-if (USE_SYSTEM_PUGIXML)
-
-    if (VCPKG_TARGET_TRIPLET) #VCPKG .
-        find_package(PugiXML CONFIG REQUIRED)
-        list(APPEND ISMRMRD_TARGET_LINK_LIBS pugixml)
-    else ()
-        find_package(PugiXML REQUIRED)
-
-        message("Found system pugixml: ${PugiXML_INCLUDE_DIR} ${PugiXML_LIBRARY}")
-        list(APPEND ISMRMRD_TARGET_INCLUDE_DIRS ${PugiXML_INCLUDE_DIR})
-        list(APPEND ISMRMRD_TARGET_LINK_LIBS ${PugiXML_LIBRARY})
-        list(APPEND ISMRMRD_TARGET_INCLUDE_DIRS ${PugiXML_INCLUDE_DIR})
-        list(APPEND ISMRMRD_TARGET_LINK_LIBS ${PugiXML_LIBRARY})
-    endif ()
+if (VCPKG_TARGET_TRIPLET) #VCPKG .
+    find_package(PugiXML CONFIG REQUIRED)
+    list(APPEND ISMRMRD_TARGET_LINK_LIBS pugixml)
 else ()
-    list(APPEND ISMRMRD_TARGET_SOURCES libsrc/pugixml.cpp)
+    find_package(PugiXML REQUIRED)
+
+    message("Found system pugixml: ${PugiXML_INCLUDE_DIR} ${PugiXML_LIBRARY}")
+    list(APPEND ISMRMRD_TARGET_INCLUDE_DIRS ${PugiXML_INCLUDE_DIR})
+    list(APPEND ISMRMRD_TARGET_LINK_LIBS ${PugiXML_LIBRARY})
+    list(APPEND ISMRMRD_TARGET_INCLUDE_DIRS ${PugiXML_INCLUDE_DIR})
+    list(APPEND ISMRMRD_TARGET_LINK_LIBS ${PugiXML_LIBRARY})
 endif ()
 
 if (build4GE)
@@ -238,7 +251,7 @@ include(GNUInstallDirs)
 install(TARGETS ismrmrd EXPORT ISMRMRDTargets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 
+   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    COMPONENT Devel
 )
 
@@ -265,6 +278,8 @@ endif ()
 # TODO: make this work on Windows
 if (BUILD_TESTS)
    if (NOT build4GE)
+
+       enable_testing()
       add_subdirectory(tests)
    endif ()
 endif ()
diff --git a/cmake/ISMRMRDConfig.cmake.in b/cmake/ISMRMRDConfig.cmake.in
index 700b295..364dbca 100644
--- a/cmake/ISMRMRDConfig.cmake.in
+++ b/cmake/ISMRMRDConfig.cmake.in
@@ -34,11 +34,9 @@ include(CMakeFindDependencyMacro)
 
 list(INSERT CMAKE_MODULE_PATH 0 ${ISMRMRD_CMAKE_DIR})
 
-if(USE_SYSTEM_PUGIXML)
-  find_package(PugiXML CONFIG)
-  if (NOT PugiXML_FOUND)
+find_package(PugiXML CONFIG)
+if (NOT PugiXML_FOUND)
     find_dependency(PugiXML)
-  endif()
 endif()
 
 if (@USE_HDF5_DATASET_SUPPORT@)
diff --git a/conda/.gitignore b/conda/.gitignore
new file mode 100644
index 0000000..846c4f9
--- /dev/null
+++ b/conda/.gitignore
@@ -0,0 +1,3 @@
+build_pkg/
+.pytest_cache/
+__pycache__/
\ No newline at end of file
diff --git a/conda/build.sh b/conda/build.sh
new file mode 100755
index 0000000..f337148
--- /dev/null
+++ b/conda/build.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -euo pipefail
+
+mkdir -p build
+cd build
+
+cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} \
+      ../
+
+ninja
+cd tests && ./test_ismrmrd && cd ../
+ninja install
diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml
new file mode 100644
index 0000000..4b672a5
--- /dev/null
+++ b/conda/conda_build_config.yaml
@@ -0,0 +1,3 @@
+boost:
+    - 1.76.0
+    - 1.80.0
diff --git a/conda/environment.yml b/conda/environment.yml
new file mode 100644
index 0000000..4fd259a
--- /dev/null
+++ b/conda/environment.yml
@@ -0,0 +1,7 @@
+name: ismrmrd-build
+channels:
+  - conda-forge
+dependencies:
+  - conda-build
+  - anaconda-client
+
diff --git a/conda/meta.yaml b/conda/meta.yaml
new file mode 100644
index 0000000..015adf3
--- /dev/null
+++ b/conda/meta.yaml
@@ -0,0 +1,45 @@
+{% set major = load_file_regex(load_file='CMakeLists.txt', regex_pattern='(?<=set\(ISMRMRD_VERSION_MAJOR )([^\)]+)') %}
+{% set minor = load_file_regex(load_file='CMakeLists.txt', regex_pattern='(?<=set\(ISMRMRD_VERSION_MINOR )([^\)]+)') %}
+{% set patch = load_file_regex(load_file='CMakeLists.txt', regex_pattern='(?<=set\(ISMRMRD_VERSION_PATCH )([^\)]+)') %}
+
+package:
+  name: ismrmrd
+  version: {{ major.group(1) }}.{{ minor.group(1) }}.{{ patch.group(1) }}
+
+source:
+  path: ../
+
+requirements:
+  build:
+    - boost {{ boost }}
+    - clang>=13.0.1        # [osx]
+    - clangxx>=13.0.1      # [osx]
+    - cmake>=3.20.0
+    - fftw=3.3.9
+    - gcc_linux-64>=9.0.0  # [linux64]
+    - gxx_linux-64>=9.0.0  # [linux64]
+    - git
+    - hdf5=1.10.6
+    - ninja=1.10.*
+    - pugixml=1.11.4
+  run:
+    - boost
+    - hdf5=1.10.6
+    - fftw=3.3.9
+    - pugixml=1.11.4
+
+test:
+  requires:
+    - numpy
+    - h5py
+    - pytest
+
+about:
+  home: https://github.com/ismrmrd/ismrmrd
+  license: MIT
+  summary: 'ISMRM Raw Data Format (ISMRMRD)'
+  description: |
+    Libraries and tools for working with data in the ISMRM Raw Data (ISMRMRD or MRD).
+  dev_url: https://github.com/ismrmrd/ismrmrd
+  doc_url: https://github.com/ismrmrd/ismrmrd
+  doc_source_url: https://github.com/ismrmrd/ismrmrd/blob/master/README.md
diff --git a/conda/package.sh b/conda/package.sh
new file mode 100755
index 0000000..908f6a3
--- /dev/null
+++ b/conda/package.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+set -euo pipefail
+
+usage()
+{
+  cat << EOF
+
+Builds the ismrmrd conda package.
+
+Usage: $0
+EOF
+}
+
+output_path="$(dirname "$0")/build_pkg"
+
+# Build up channel directives
+channels=(
+  conda-forge
+)
+
+channel_directives=$(printf -- "-c %s " "${channels[@]}")
+
+mkdir -p "$output_path"
+bash -c "conda build --no-anaconda-upload --output-folder $output_path $channel_directives $(dirname "$0")"
diff --git a/conda/publish_package.sh b/conda/publish_package.sh
new file mode 100755
index 0000000..6c3b922
--- /dev/null
+++ b/conda/publish_package.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+set -euo pipefail
+
+usage()
+{
+  cat << EOF
+
+Publishes a conda package.
+
+Usage: $0 [options]
+
+Options:
+  -p|--package_path <path>               Path to the package (tar.gz) to push
+  -u|--user <user>                       Anaconda.org channeluser or organization
+  -t|--token <token>                     Token for uploading to anaconda.org
+  -f|--force                             Force push even if package exists
+  -h| --help  Brings up this menu
+EOF
+}
+
+while [[ $# -gt 0 ]]; do
+  key="$1"
+
+  case $key in
+    -p|--package_path)
+      package_path="$2"
+      shift
+      shift
+      ;;
+    -u|--user)
+      user="$2"
+      shift
+      shift
+      ;;
+    -t|--token)
+      token="$2"
+      shift
+      shift
+      ;;
+    --force)
+      force=1
+      shift
+      ;;
+    -h|--help)
+      usage
+      exit
+      ;;
+    *)
+      echo "ERROR: unknown option \"$key\""
+      usage
+      exit 1
+      ;;
+  esac
+done
+
+if [[ -z "${package_path:-}" ]]; then
+  echo "You cannot push to anaconda without a package"
+  echo "Please supply a package path with the --package_path argument"
+  exit 1
+fi
+if [[ -z "${token:-}" ]]; then
+  echo "You cannot push to anaconda without a token"
+  echo "Please supply a token with the --token argument"
+  exit 1
+fi
+if [[ -z "${user:-}" ]]; then
+  echo "You cannot push to anaconda without a user"
+  echo "Please supply a user with the --user argument"
+  exit 1
+fi
+
+force_directive="--skip-existing"
+if [[ -n ${force:-} ]]; then
+  force_directive="--force"
+fi
+
+anaconda -t "$token" upload -u "$user" $force_directive "$package_path"
diff --git a/conda/run_test.py b/conda/run_test.py
new file mode 100644
index 0000000..c9241f7
--- /dev/null
+++ b/conda/run_test.py
@@ -0,0 +1,33 @@
+import os
+import h5py
+import numpy as np
+import pytest
+from pathlib import Path
+def test_basic_recon():
+    testdata_filename = Path('testdata.h5')
+
+    # Generate test data with no noise
+    os.system("ismrmrd_generate_cartesian_shepp_logan -o " + str(testdata_filename) + " -n 0.0")
+
+    # Reconstruct
+    os.system("ismrmrd_recon_cartesian_2d " + str(testdata_filename))
+
+    input_data = h5py.File(str(testdata_filename))
+    coil_images = input_data['dataset']['coil_images']
+
+    
+    coil_images = np.squeeze(coil_images['real'] + 1j*coil_images['imag']) 
+    recon_reference = np.abs(np.sqrt(np.sum(coil_images * np.conj(coil_images), 0)))
+    ro_length = recon_reference.shape[-1]
+    recon_reference = recon_reference[:, int(ro_length/4):int(ro_length/4+ro_length/2)]
+
+    # We need to scale the images, since the recon does not
+    reconstruction = np.squeeze(np.array(input_data['dataset']['cpp']['data'])) / np.sqrt(coil_images.shape[-1]*coil_images.shape[-2])
+
+    assert np.linalg.norm(reconstruction - recon_reference) / np.linalg.norm(recon_reference) < 2e-5
+
+    testdata_filename.unlink()
+
+
+# We will invoke pytest here
+pytest.main([__file__])
\ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
index 7c3f5d3..1ebbf13 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ismrmrd (1.13.2+git20230213.1.3cb9cab-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 26 Feb 2023 11:08:29 -0000
+
 ismrmrd (1.8.0-2) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/Add-support-for-multiarch.patch b/debian/patches/Add-support-for-multiarch.patch
index 5db6541..9b6baf4 100644
--- a/debian/patches/Add-support-for-multiarch.patch
+++ b/debian/patches/Add-support-for-multiarch.patch
@@ -6,9 +6,11 @@ Subject: Add support for multiarch.
  CMakeLists.txt | 10 ++++++----
  1 file changed, 6 insertions(+), 4 deletions(-)
 
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -288,7 +288,7 @@ configure_file(cmake/ISMRMRDConfig.cmake
+Index: ismrmrd.git/CMakeLists.txt
+===================================================================
+--- ismrmrd.git.orig/CMakeLists.txt
++++ ismrmrd.git/CMakeLists.txt
+@@ -303,7 +303,7 @@ configure_file(cmake/ISMRMRDConfig.cmake
  
  set(CONFIG_ISMRMRD_SCHEMA_DIR   ${CMAKE_INSTALL_PREFIX}/share/ismrmrd/schema)
  set(CONFIG_ISMRMRD_TARGET_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include)
diff --git a/debian/patches/Disable-HTML-timestamps.patch b/debian/patches/Disable-HTML-timestamps.patch
index 63190a9..764942a 100644
--- a/debian/patches/Disable-HTML-timestamps.patch
+++ b/debian/patches/Disable-HTML-timestamps.patch
@@ -7,10 +7,10 @@ Reason: makes build reproducible.
  doc/Doxyfile.in | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
-index ff07a28..5346e9a 100644
---- a/doc/Doxyfile.in
-+++ b/doc/Doxyfile.in
+Index: ismrmrd.git/doc/Doxyfile.in
+===================================================================
+--- ismrmrd.git.orig/doc/Doxyfile.in
++++ ismrmrd.git/doc/Doxyfile.in
 @@ -1130,7 +1130,7 @@ HTML_COLORSTYLE_GAMMA  = 80
  # The default value is: YES.
  # This tag requires that the tag GENERATE_HTML is set to YES.
diff --git a/debian/patches/add_static_lib.patch b/debian/patches/add_static_lib.patch
index d7e9b2e..82b2447 100644
--- a/debian/patches/add_static_lib.patch
+++ b/debian/patches/add_static_lib.patch
@@ -2,11 +2,11 @@ Author: Andreas Tille <tille@debian.org>
 Last-Update: Sun, 20 Sep 2020 08:09:49 +0200
 Description: Add static library
 
-Index: ismrmrd/CMakeLists.txt
+Index: ismrmrd.git/CMakeLists.txt
 ===================================================================
---- ismrmrd.orig/CMakeLists.txt
-+++ ismrmrd/CMakeLists.txt
-@@ -214,7 +214,9 @@ endif ()
+--- ismrmrd.git.orig/CMakeLists.txt
++++ ismrmrd.git/CMakeLists.txt
+@@ -227,7 +227,9 @@ endif ()
  
  # main library
  add_library(ismrmrd SHARED ${ISMRMRD_TARGET_SOURCES})
@@ -16,7 +16,7 @@ Index: ismrmrd/CMakeLists.txt
  if (USE_HDF5_DATASET_SUPPORT)
    target_compile_options(ismrmrd PUBLIC ${HDF5_DEFINITIONS})
  endif()
-@@ -228,7 +230,12 @@ set_target_properties(ismrmrd
+@@ -241,7 +243,12 @@ set_target_properties(ismrmrd
    PROPERTIES
    EXPORT_NAME ISMRMRD)
  
@@ -29,8 +29,8 @@ Index: ismrmrd/CMakeLists.txt
  list(APPEND ISMRMRD_LIBRARIES ismrmrd) # Add to list of libraries to be found
  list(APPEND ISMRMRD_LIBRARY_DIRS ${CMAKE_BINARY_DIR} ) # Add to list of directories to find libraries
  
-@@ -241,6 +248,7 @@ install(TARGETS ismrmrd EXPORT ISMRMRDTa
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 
+@@ -254,6 +261,7 @@ install(TARGETS ismrmrd EXPORT ISMRMRDTa
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     COMPONENT Devel
  )
 +install(TARGETS ismrmrd_static DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/debian/patches/remove-travis-build-status.patch b/debian/patches/remove-travis-build-status.patch
index 3c8b23b..7291284 100644
--- a/debian/patches/remove-travis-build-status.patch
+++ b/debian/patches/remove-travis-build-status.patch
@@ -1,8 +1,10 @@
 Author: Nilesh Patra <npatra974@gail.com>
 Description: Remove travis build status causing privacy breaches. This is anyway a useless information for debian package documentation
 Last-Changed: September 20, 2020
---- a/README.md
-+++ b/README.md
+Index: ismrmrd.git/README.md
+===================================================================
+--- ismrmrd.git.orig/README.md
++++ ismrmrd.git/README.md
 @@ -1,5 +1,3 @@
 -![Build Status](https://github.com/ismrmrd/ismrmrd/workflows/CMake/badge.svg?branch=master)
 -
diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css
new file mode 100644
index 0000000..7c0b84a
--- /dev/null
+++ b/doc/_static/css/custom.css
@@ -0,0 +1,9 @@
+.wy-table-responsive table td, .wy-table-responsive table th {
+    white-space: inherit;
+}
+
+.wy-table-backed, .wy-table-odd td, .wy-table-striped tr:nth-child(2n-1) td {
+    background-color: #ef0606 !important;
+}
+
+/* .wy-table-striped tr:nth-child(2n-1) td{background-color:inherit} */
\ No newline at end of file
diff --git a/doc/building.md b/doc/building.md
index 91b7aaf..53cf278 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -2,22 +2,22 @@
 
 The ISMRM Raw Data format is described by an XML schema and some C-style structs with fixed memory layout and as such does not have dependencies. However, it uses HDF5 files for storage and a C++ library for reading and writing the ISMRMRD files is included in this distribution. Furthermore, since the XML header is defined with an XML schema, we encourage using XML data binding when writing software using the format. To compile all components of this distribution you need:
 
-* HDF5 (version 1.8 or higher) libraries. Available from http://www.hdfgroup.org/downloads/index.html.
-* Boost (http://www.boost.org/)
-* Cmake build tool (http://www.cmake.org/)
-* Git if you would like to use the source code archive (http://git-scm.com/)
-* FFTW if you would like to compile some of the example applications
-  (http://www.fftw.org)
-* Doxygen if you would like to generate API documentation (http://www.doxygen.org)
+* [HDF5](http://www.hdfgroup.org/downloads/index.html) (version 1.8 or higher) libraries.
+* [Boost](http://www.boost.org/)
+* [Pugixml](https://pugixml.org)
+* [Cmake build tool](http://www.cmake.org/)
+* [Git](http://git-scm.com/) if you would like to use the source code archive
+* [FFTW](http://www.fftw.org) if you would like to compile some of the example applications
+* [Doxygen](http://www.doxygen.org) if you would like to generate API documentation
 
-> It is only necessary to install the dependencies if you wish to develop compiled C/C++ software, which uses the ISMRMRD format. The format can be read in Matlab or Python without installing any additional software.
+It is only necessary to install the dependencies if you wish to develop compiled C/C++ software, which uses the ISMRMRD format. The format can be read in Matlab or Python without installing any additional software.
 
 ## Linux installation
 
 The dependencies mentioned above should be included in most linux distributions. On Ubuntu you can install all required dependencies with::
 
 ```bash
-sudo apt-get -y install doxygen git-core graphviz libboost-all-dev libfftw3-dev libhdf5-serial-dev
+sudo apt-get -y install doxygen git-core graphviz libboost-all-dev libfftw3-dev libhdf5-serial-dev libxml2-utils libpugixml-dev
 ```
 
 After installation of dependencies, the library can be installed with:
@@ -51,4 +51,4 @@ then when configuring your package use set the following cmake variables (comman
 
 ```
 cmake -DISMRMRD_DIR:PATH=<path to build/install tree of ISMRMRD> <path to my source tree>
-```
\ No newline at end of file
+```
diff --git a/doc/conf.py b/doc/conf.py
index 0dd2a33..697d9a5 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -53,7 +53,7 @@ author = 'ISMRMRD Working Group'
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = ['breathe', 'myst_parser']
+extensions = ['breathe', 'myst_parser', 'sphinxcontrib.mermaid']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -74,4 +74,19 @@ html_theme = 'sphinx_rtd_theme'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
\ No newline at end of file
+html_static_path = ['_static']
+
+# Override wide tables issue: https://github.com/readthedocs/sphinx_rtd_theme/issues/117
+# html_context = {
+#     'css_files': [
+#         '_static/theme_overrides.css',  # overrides for wide tables in RTD theme
+#         ],
+#     }
+html_css_files = [
+    'css/custom.css',
+]
+
+# -- Custom CSS --------------------------------------------------------------
+# def setup(app):
+#    app.add_javascript("custom.js")
+#    app.add_css_file("theme_overrides.css")
\ No newline at end of file
diff --git a/doc/cpplibrary.md b/doc/cpplibrary.md
new file mode 100644
index 0000000..ee08aac
--- /dev/null
+++ b/doc/cpplibrary.md
@@ -0,0 +1,44 @@
+# C++ Support Library
+To enable easy prototyping of C++ software using the ISMRMRD data format, a simple C++ wrapper class is provided (defined in [dataset.h](../include/ismrmrd/dataset.h)).
+
+Using this wrapper, C++ applications can be programmed as:
+```C++
+// Open dataset
+ISMRMRD::Dataset d(datafile.c_str(), "dataset", false);
+
+std::string xml;
+d.readHeader(xml);
+ISMRMRD::IsmrmrdHeader hdr;
+ISMRMRD::deserialize(xml.c_str(),hdr);
+
+// Do something with the header
+
+unsigned int number_of_acquisitions = d.getNumberOfAcquisitions();
+ISMRMRD::Acquisition acq;
+for (unsigned int i = 0; i < number_of_acquisitions; i++) {
+    // Read one acquisition at a time
+    d.readAcquisition(i, acq);
+
+    // Do something with the data
+}
+```
+
+Since the XML header is defined in the [schema/ismrmrd.xsd](../schema/ismrmrd.xsd) file, it can be parsed with numerous xml parsing libraries. The ISMRMRD library includes an API that allows for programmatically deserializing, manipulating, and serializing the XML header. See the code in the [utilities](https://github.com/ismrmrd/ismrmrd/blob/master/utilities) directory for examples of how to use the XML API.
+
+# C++ Example Applications
+The distribution includes two example applications, one that creates a simple 2D single-channel dataset from scratch and one that reconstructs this dataset (you need FFTW installed to compile these test applications). The data generation application can be found in [utilities/generate_cartesian_shepp_logan.cpp](../utilities/generate_cartesian_shepp_logan.cpp):
+
+To reconstruct this synthetic dataset, you can use the test reconstruction application [utilities/recon_cartesian_2d.cpp](../utilities/recon_cartesian_2d.cpp).
+
+
+# External use of ISMRMRD C++ library in other projects
+To use ISMRMRD for your externally developed projects, add the following to your CMakeLists.txt file:
+```
+find_package( ISMRMRD REQUIRED )
+include_directories( ${ISMRMRD_INCLUDE_DIR} )
+target_link_libraries( mytarget ISMRMRD::ISMRMRD )
+```
+then when configuring your package use set the following cmake variables (command line variant shown):
+```
+cmake <path to my source tree>
+```
\ No newline at end of file
diff --git a/doc/cpplibrary.rst b/doc/cpplibrary.rst
deleted file mode 100644
index 0adad38..0000000
--- a/doc/cpplibrary.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-C++ Support Library
-####################
-
-To enable easy prototyping of C++ software using the ISMRMRD data format, a simple C++ wrapper class is provided (defined in `dataset.h <https://github.com/ismrmrd/ismrmrd/blob/master/include/ismrmrd/dataset.h>_.
-
-Using this wrapper, C++ applications can be programmed as::
-
-    // Open dataset
-    ISMRMRD::Dataset d(datafile.c_str(), "dataset", false);
-
-    std::string xml;
-    d.readHeader(xml);
-    ISMRMRD::IsmrmrdHeader hdr;
-    ISMRMRD::deserialize(xml.c_str(),hdr);
-
-    // Do something with the header
-
-    unsigned int number_of_acquisitions = d.getNumberOfAcquisitions();
-    ISMRMRD::Acquisition acq;
-    for (unsigned int i = 0; i < number_of_acquisitions; i++) {
-        // Read one acquisition at a time
-        d.readAcquisition(i, acq);
-
-        // Do something with the data
-    }
-
-Since the XML header is defined in the `schema/ismrmrd.xsd` file, it can be
-parsed with numerous xml parsing libraries. The ISMRMRD library includes an API
-that allows for programmatically deserializing, manipulating, and serializing the
-XML header. See the code in the `utilities <https://github.com/ismrmrd/ismrmrd/blob/master/utilities/>`_ directory for examples of how to use the XML API.
-
-C++ Example Applications
-************************
-
-The distribution includes two example applications, one that creates a simple 2D single-channel dataset from scratch and one that reconstructs this dataset (you need FFTW installed to compile these test applications). The data generation application can be found in `utilities/generate_cartesian_shepp_logan.cpp <https://github.com/ismrmrd/ismrmrd/blob/master/utilities/generate_cartesian_shepp_logan.cpp>`_:
-
-To reconstruct this synthetic dataset, you can use the test reconstruction application `utilities/recon_cartesian_2d.cpp <https://github.com/ismrmrd/ismrmrd/blob/master/utilities/recon_cartesian_2d.cpp>`_.
-
-
-External use of ISMRMRD C++ library in other projects
-******************************************************
-
-To use ISMRMRD for your externally developed projects, add the following to your CMakeLists.txt file::
-
-  find_package( ISMRMRD REQUIRED )
-  include_directories( ${ISMRMRD_INCLUDE_DIR} )
-  target_link_libraries( mytarget ISMRMRD::ISMRMRD )
-
-then when configuring your package use set the following cmake variables (command line variant shown)::
-
-  cmake <path to my source tree>
diff --git a/doc/filestorage.rst b/doc/filestorage.rst
deleted file mode 100644
index 42c889d..0000000
--- a/doc/filestorage.rst
+++ /dev/null
@@ -1,86 +0,0 @@
-HDF5 File Storage
-************
-
-The ISMRM Raw Data format can really be stored in any format that you choose, but most commonly it is stored in `HDF5 format https://www.hdfgroup.org/solutions/hdf5`_. Briefly it is a hierarchical data format (much like a file system), which can contain multiple variable organized in groups (like folders in a file system). The variables can contain arrays of data values, custom defined structs, or simple text fields. It is the convention (but not a requirement) that the ISMRMRD datasets are stored in a group called `/dataset`. The XML configuration is stored in the variable `/dataset/xml` and the data is stored in `/dataset/data`. HDF5 files can be viewed with the HDFView application which is available on the HDF5 website for multiple platforms. Many programming languages also have support for working with HDF5 files. 
-
-In Python, the `h5py <https://www.h5py.org/>`_ package can be used to read the files::
-
-    import h5py
-    import numpy as np
-    f = h5py.File('testdata.h5')
-    acq = np.array(f['dataset']['data'][0])
-    { k: acq['head'][k] for k in acq['head'].dtype.fields.keys() }
-
-    {'version': array(1, dtype=uint16),
-    'flags': array(64, dtype=uint64),
-    'measurement_uid': array(0, dtype=uint32),
-    'scan_counter': array(0, dtype=uint32),
-    'acquisition_time_stamp': array(0, dtype=uint32),
-    'physiology_time_stamp': array([0, 0, 0], dtype=uint32),
-    'number_of_samples': array(512, dtype=uint16),
-    'available_channels': array(8, dtype=uint16),
-    'active_channels': array(8, dtype=uint16),
-    'channel_mask': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=uint64),
-    'discard_pre': array(0, dtype=uint16),
-    'discard_post': array(0, dtype=uint16),
-    'center_sample': array(256, dtype=uint16),
-    'encoding_space_ref': array(0, dtype=uint16),
-    'trajectory_dimensions': array(0, dtype=uint16),
-    'sample_time_us': array(5., dtype=float32),
-    'position': array([0., 0., 0.], dtype=float32),
-    'read_dir': array([0., 0., 0.], dtype=float32),
-    'phase_dir': array([0., 0., 0.], dtype=float32),
-    'slice_dir': array([0., 0., 0.], dtype=float32),
-    'patient_table_position': array([0., 0., 0.], dtype=float32),
-    'idx': array((0, 0, 0, 0, 0, 0, 0, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0]),
-        dtype=[('kspace_encode_step_1', '<u2'), ('kspace_encode_step_2', '<u2'), ('average', '<u2'), ('slice', '<u2'), ('contrast', '<u2'), ('phase', '<u2'), ('repetition', '<u2'), ('set', '<u2'), ('segment', '<u2'), ('user', '<u2', (8,))]),
-    'user_int': array([0, 0, 0, 0, 0, 0, 0, 0], dtype=int32),
-    'user_float': array([0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)}
-
-There is also a dedicated python libarary for working with the ISMRMRD files. The Python implementation of the API is maintained in `ismrmrd-python <https://www.github.com/ismrmrd/ismrmrd-python>_`
-
-Files can also be read directly in Matlab, in fact Matlab uses (since file format v7.3) HDF5 as its internal data format in the `.mat` files. As an example the data from an ISMRMRD file with name `myfile.h5` can be read in Matlab with a command like::
-
-    >> data = h5read('simple_gre.h5', '/dataset/data');
-    >> data
-
-    data =
-
-    head: [1x1 struct]
-    traj: {1x1281 cell}
-    data: {1x1281 cell}
-
-      >> data.head
-
-      ans =
-
-                    version: [1x1281 uint16]
-                      flags: [1x1281 uint64]
-            measurement_uid: [1x1281 uint32]
-                scan_counter: [1x1281 uint32]
-      acquisition_time_stamp: [1x1281 uint32]
-      physiology_time_stamp: [3x1281 uint32]
-          number_of_samples: [1x1281 uint16]
-          available_channels: [1x1281 uint16]
-            active_channels: [1x1281 uint16]
-                channel_mask: [16x1281 uint64]
-                discard_pre: [1x1281 uint16]
-                discard_post: [1x1281 uint16]
-              center_sample: [1x1281 uint16]
-          encoding_space_ref: [1x1281 uint16]
-      trajectory_dimensions: [1x1281 uint16]
-              sample_time_us: [1x1281 single]
-                    position: [3x1281 single]
-                    read_dir: [3x1281 single]
-                  phase_dir: [3x1281 single]
-                  slice_dir: [3x1281 single]
-      patient_table_position: [3x1281 single]
-                        idx: [1x1 struct]
-                    user_int: [8x1281 int32]
-                  user_float: [8x1281 single]
-
-      >>
-
-The HDF5 file format can be accessed from C, C++, and java using the libraries provided on the HDF5 website. The ISMRMRD distribution also comes with some C++ wrappers that can be used for easy access (read and write) from C++ programs. 
-
-
diff --git a/doc/index.rst b/doc/index.rst
index dbe74bd..39be0e9 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,6 +1,6 @@
-ISMRM Raw Data Format (ISMRMRD)
 ################################
-
+ISMRM Data Format (MRD)
+################################
 A prerequisite for sharing magnetic resonance (imaging) reconstruction algorithms and code is a common raw data format. The ISMRMRD project describes such a common raw data format, which attempts to capture the data fields that are required to describe the magnetic resonance experiment with enough detail to reconstruct images. The repository also contains a C/C++ library for working with the format. This standard was developed by a subcommittee of the ISMRM Sedona 2013 workshop and is described in detail in:
 
 Inati SJ, Naegele JD, Zwart NR, Roopchansingh V, Lizak MJ, Hansen DC, Liu CY, Atkinson D, Kellman P, Kozerke S, Xue H, Campbell-Washburn AE, Sørensen TS, Hansen MS. ISMRM Raw data format: A proposed standard for MRI raw datasets. `Magn Reson Med. 2017 Jan;77(1):411-421. <https://onlinelibrary.wiley.com/doi/10.1002/mrm.26089>`_
@@ -9,11 +9,31 @@ Please cite this paper if you use the format.
 
 .. toctree::
   :maxdepth: 2
-  :caption: Contents:
+  :caption: General
 
   overview
+  mrd_files
+
+.. toctree::
+  :maxdepth: 2
+  :caption: MRD Data Primatives
+
+  mrd_header
+  mrd_raw_data
+  mrd_image_data
+  mrd_waveform_data
+
+.. toctree::
+  :maxdepth: 2
+  :caption: MRD Streaming Protocol
+
+  mrd_streaming_protocol
+  mrd_messages
+
+.. toctree::
+  :maxdepth: 2
+  :caption: API Libraries
+
   building
-  filestorage
   cpplibrary
   api
-
diff --git a/doc/mrd_files.md b/doc/mrd_files.md
new file mode 100644
index 0000000..2a841c2
--- /dev/null
+++ b/doc/mrd_files.md
@@ -0,0 +1,186 @@
+# MRD File Format
+MRD data can be stored in various formats, but the [HDF5](https://www.hdfgroup.org/solutions/hdf5) format is commonly used due to its good compatibility across programming languages and platforms.  HDF5 is a hierarchical data format (much like a file system), which can contain multiple variable organized in groups (like folders in a file system). The variables can contain arrays of data values, custom defined structs, or simple text fields.  Interface libraries are provided for C++, Python, and MATLAB to simplify usage.  MRD HDF5 files can also be opened using standard HDF tools such as [HDFView](https://www.hdfgroup.org/downloads/hdfview/) or HDF5 packages such as [h5py](https://www.h5py.org/) for Python or the built-in [h5read](https://www.mathworks.com/help/matlab/ref/h5read.html) and associated functions in MATLAB.  
+
+The extension ``.mrd`` is used to indicate an HDF5 file containing MRD formatted data as follows:
+```
+/dataset/xml                 text of MRD header
+/dataset/data                array of raw data (data + AcquisitionHeader + optional trajectory)
+/dataset/waveforms           array of waveform (e.g. PMU) data
+/dataset/image_0/data        array of image data
+/dataset/image_0/header      array of ImageHeaders
+/dataset/image_0/attributes  array of image MetaAttributes (xml text)
+/dataset/config              text of configuration parameters for reconstruction or image analysis (optional)
+/dataset/config_file         file name of configuration parameters for reconstruction or image analysis (optional)
+```
+
+All data from a complete acquisition are stored in a group (``dataset`` in the above example).  An MRD file may contain multiple acquisitions in separate groups, usually in the case of related or dependent acquisitions.
+
+## Reading MRD data in Python
+The [ismrmrd-python](https://www.github.com/ismrmrd/ismrmrd-python) library provides a convenient interface for working with MRD files.  It can either be compiled from source or installed from a pip package using the command ``pip install ismrmrd``.  The following code shows an example of getting the number of readout lines from a dataset and reading the first line of k-space data:
+```python
+>>> import ismrmrd
+>>> dset = ismrmrd.Dataset('data.mrd')
+>>> nacq = dset.number_of_acquisitions()
+>>> acq = dset.read_acquisition(0)
+>>> dset.close()
+>>> print(acq.getHead())
+version: 1
+flags: 0
+measurement_uid: 281
+scan_counter: 3
+acquisition_time_stamp: 23187655
+physiology_time_stamp: 4304495, 0, 0
+number_of_samples: 256
+available_channels: 2
+active_channels: 2
+channel_mask: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+discard_pre: 0
+discard_post: 0
+center_sample: 128
+encoding_space_ref: 0
+trajectory_dimensions: 0
+sample_time_us: 15.0
+position: 0.0, 0.0, 0.0
+read_dir: -0.9999999403953552, 5.960464477539063e-08, 0.0
+phase_dir: 5.960464477539063e-08, 0.9999999403953552, 0.0
+slice_dir: 0.0, 0.0, 1.0
+patient_table_position: 0.0, 0.0, -1374995.0
+idx: kspace_encode_step_1: 2
+kspace_encode_step_2: 0
+average: 0
+slice: 0
+contrast: 0
+phase: 0
+repetition: 0
+set: 0
+segment: 0
+user: 0, 0, 0, 0, 0, 64, 0, 0
+
+user_int: 0, 0, 0, 0, 0, 0, 0, 0
+user_float: 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
+
+>>> acq.data.shape
+(2, 256)
+```
+
+The basic [h5py](https://www.h5py.org) package Python can also be used to read the files:
+```python
+  >>> import h5py
+  >>> import numpy as np
+  >>> f = h5py.File('data.mrd')
+  >>> acq = np.array(f['dataset']['data'][0])
+  >>> { k: acq['head'][k] for k in acq['head'].dtype.fields.keys() }
+
+  {'version': array(1, dtype=uint16),
+  'flags': array(64, dtype=uint64),
+  'measurement_uid': array(0, dtype=uint32),
+  'scan_counter': array(0, dtype=uint32),
+  'acquisition_time_stamp': array(0, dtype=uint32),
+  'physiology_time_stamp': array([0, 0, 0], dtype=uint32),
+  'number_of_samples': array(512, dtype=uint16),
+  'available_channels': array(8, dtype=uint16),
+  'active_channels': array(8, dtype=uint16),
+  'channel_mask': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=uint64),
+  'discard_pre': array(0, dtype=uint16),
+  'discard_post': array(0, dtype=uint16),
+  'center_sample': array(256, dtype=uint16),
+  'encoding_space_ref': array(0, dtype=uint16),
+  'trajectory_dimensions': array(0, dtype=uint16),
+  'sample_time_us': array(5., dtype=float32),
+  'position': array([0., 0., 0.], dtype=float32),
+  'read_dir': array([0., 0., 0.], dtype=float32),
+  'phase_dir': array([0., 0., 0.], dtype=float32),
+  'slice_dir': array([0., 0., 0.], dtype=float32),
+  'patient_table_position': array([0., 0., 0.], dtype=float32),
+  'idx': array((0, 0, 0, 0, 0, 0, 0, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0]),
+      dtype=[('kspace_encode_step_1', '<u2'), ('kspace_encode_step_2', '<u2'), ('average', '<u2'), ('slice', '<u2'), ('contrast', '<u2'), ('phase', '<u2'), ('repetition', '<u2'), ('set', '<u2'), ('segment', '<u2'), ('user', '<u2', (8,))]),
+  'user_int': array([0, 0, 0, 0, 0, 0, 0, 0], dtype=int32),
+  'user_float': array([0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)}
+```
+
+## Reading MRD data in MATLAB
+A MATLAB package is also provided in this repository to facilitate easy usage of MRD files.  To use it, add the ``matlab`` folder in this repository to the MATLAB path.  The following code shows an example of getting the number of readout lines from a dataset and reading the first line of k-space data:
+```matlab
+>> dset = ismrmrd.Dataset('data.mrd');
+>> nacq = dset.getNumberOfAcquisitions()
+nacq =
+   128
+>> acq = dset.readAcquisition(1)
+acq = 
+  Acquisition with properties:
+
+    head: [1×1 ismrmrd.AcquisitionHeader]
+    traj: {[0×256 single]}
+    data: {[256×2 single]}
+>> dset.close();
+>> acq.head
+ans = 
+  AcquisitionHeader with properties:
+
+                   version: 1
+                     flags: 64
+           measurement_uid: 281
+              scan_counter: 1
+    acquisition_time_stamp: 23187639
+     physiology_time_stamp: [3×1 uint32]
+         number_of_samples: 256
+        available_channels: 2
+           active_channels: 2
+              channel_mask: [16×1 uint64]
+               discard_pre: 0
+              discard_post: 0
+             center_sample: 128
+        encoding_space_ref: 0
+     trajectory_dimensions: 0
+            sample_time_us: 15
+                  position: [3×1 single]
+                  read_dir: [3×1 single]
+                 phase_dir: [3×1 single]
+                 slice_dir: [3×1 single]
+    patient_table_position: [3×1 single]
+                       idx: [1×1 struct]
+                  user_int: [8×1 int32]
+                user_float: [8×1 single]
+                     FLAGS: [1×1 struct]
+```
+
+MATLAB also provides [native HDF5 support](https://www.mathworks.com/help/matlab/import_export/import-hdf5-files.html) which can be used to read the data without an external library:
+```matlab
+>> data = h5read('data.mrd', '/dataset/data')
+data = 
+  struct with fields:
+
+    head: [1×1 struct]
+    traj: {128×1 cell}
+    data: {128×1 cell}
+
+>> data.head
+ans = 
+  struct with fields:
+
+                   version: [128×1 uint16]
+                     flags: [128×1 uint64]
+           measurement_uid: [128×1 uint32]
+              scan_counter: [128×1 uint32]
+    acquisition_time_stamp: [128×1 uint32]
+     physiology_time_stamp: [3×128 uint32]
+         number_of_samples: [128×1 uint16]
+        available_channels: [128×1 uint16]
+           active_channels: [128×1 uint16]
+              channel_mask: [16×128 uint64]
+               discard_pre: [128×1 uint16]
+              discard_post: [128×1 uint16]
+             center_sample: [128×1 uint16]
+        encoding_space_ref: [128×1 uint16]
+     trajectory_dimensions: [128×1 uint16]
+            sample_time_us: [128×1 single]
+                  position: [3×128 single]
+                  read_dir: [3×128 single]
+                 phase_dir: [3×128 single]
+                 slice_dir: [3×128 single]
+    patient_table_position: [3×128 single]
+                       idx: [1×1 struct]
+                  user_int: [8×128 int32]
+                user_float: [8×128 single]
+>> 
+```
diff --git a/doc/mrd_header.md b/doc/mrd_header.md
new file mode 100644
index 0000000..c5ea893
--- /dev/null
+++ b/doc/mrd_header.md
@@ -0,0 +1,33 @@
+# MRD Header
+The flexible data structure is defined by the xml schema definition in [schema/ismrmrd.xsd](../schema/ismrmrd.xsd). An example of an XML file for a Cartesian 3D acquisition can be found [schema/ismrmrd_example.xml](../schema/ismrmrd_example.xml).
+
+The most critical elements for image reconstruction are contained in the ``<encoding>`` section of the document, which describes the encoded spaced and also the target reconstructed space. Along with the ``<encodingLimits>``, this section allows the reconstruction program to determine matrix sizes, oversampling factors, partial Fourier, etc. In the example above, data is acquired with two-fold oversampling in the read-out (``x``) direction, which is reflected in the larger matrix size in the encoded space compared to the reconstruction space. The field of view is also twice as large in the encoded space. For the first phase encoding dimension (``y``), we have a combination of oversampling (20%), reduced phase resolution (only 83 lines of k-space acquired, and partial Fourier sampling, which is reflected in the asymmetric center of the encoding limits of the ``<kspace_encoding_step_1>``. Specifically, the data lines would be placed into the encoding space like this::
+
+    0                                     70                                         139
+    |-------------------------------------|-------------------------------------------|
+                            ****************************************************
+                            ^               ^                                  ^
+                            0              28                                  83
+
+After FFT, only the central 116 lines are kept, i.e. there is a reduced field of view in the phase encoding direction. Center and encoding limits for the readout dimension is not given in the XML header. This is to accommodate sequences where the center of the readout may change from readout to readout (alternating directions of readout). There is a field on the individual data headers (see below) to indicate the center of the readout.
+
+An experiment can have multiple encoding spaces and it is possible to indicate on each acquired data readout, which encoding space the data belongs to (see below).
+
+In addition to the defined field in the xml header, it is possible to add an arbitrary number of user defined parameters to accommodate special sequence parameters. Please consult the xml [schema](../schema/ismrmrd.xsd) to see how user parameters are defined. Briefly, the XML header can have a section at the end which looks like:
+
+```xml
+<userParameters>
+  <userParameterLong>
+    <name>MyVar1</name>
+    <value>1003</value>
+  </userParameterLong>
+  <userParameterLong>
+    <name>MyVar2</name>
+    <value>1999</value>
+  </userParameterLong>
+  <userParameterDouble>
+    <name>MyDoubleVar</name>
+    <value>87.6676</value>
+  </userParameterDouble>
+</userParameters>
+```
\ No newline at end of file
diff --git a/doc/mrd_image_data.md b/doc/mrd_image_data.md
new file mode 100644
index 0000000..b2eabdf
--- /dev/null
+++ b/doc/mrd_image_data.md
@@ -0,0 +1,163 @@
+# Image Data
+MRD images are stored as a combination of [image data](ImageData), a fixed [ImageHeader](ImageHeader) of common properties, and an extensible set of [MetaAttributes](MetaAttributes).  Images can stores as individual 2D images or 3D volumes and may include multiple channels for individual receiver coils.
+
+(ImageHeader)=
+## ImageHeader
+| Field                  | Description                                                                                                                                        | Type                | Offset    |
+| --                     | --                                                                                                                                                 | --                  | --        |
+| version                | Major version number (currently 1)                                                                                                                 | uint16              |   0 bytes |
+| data_type              | Data type of the image data, e.g. short, float, complex float, etc., as defined in [MRD Image Data Types](DataTypes)                               | uint16              |   2 bytes |
+| flags                  | A bit mask of common attributes applicable to individual images                                                                                    | uint64              |   4 bytes |
+| measurement_uid        | Unique ID corresponding to the image                                                                                                               | uint32              |  12 bytes |
+| matrix_size            | Number of pixels in each of the 3 dimensions in the image                                                                                          | uint16 (x3)         |  16 bytes |
+| field_of_view          | Physical size (in mm) in each of the 3 dimensions in the image                                                                                     | float (32 bit) (x3) |  22 bytes |
+| channels               | Number of receiver channels in image data (stored in the 4th dimension)                                                                            | uint16              |  34 bytes |
+| position               | Center of the excited volume, in (left, posterior, superior) (LPS) coordinates relative to isocenter in millimeters.  NB this is different than DICOM's ImageOrientationPatient, which defines the center of the first (typically top-left) voxel.                                                                                                                                                                                         | float (32 bit) (x3) |  36 bytes |
+| read_dir               | Directional cosine of readout/frequency encoding.  If the image is [flipped or rotated to bring them into standard DICOM orientation](http://gdcm.sourceforge.net/wiki/index.php/Orientation), ***this field still corresponds to the acquisition readout/frequency direction***, but the ``ImageRowDir`` must be set in the MetaAttributes.                                                                                               | float (32 bit) (x3) |  48 bytes |
+| phase_dir              | Directional cosine of phase encoding (2D).  If the image is [flipped or rotated to bring them into standard DICOM orientation](http://gdcm.sourceforge.net/wiki/index.php/Orientation), ***this field still corresponds to the 2D phase encoding direction***, but the ``ImageColumnDir`` must be set in the MetaAttributes.                                                                                                               | float (32 bit) (x3) |  60 bytes |
+| slice_dir              | For 3D data, the directional cosine of 3D phase encoding direction.  For 3D data, the slice normal, i.e. cross-product of ``read_dir`` and ``phase_dir``.  If the image is [flipped or rotated to bring them into standard DICOM orientation](http://gdcm.sourceforge.net/wiki/index.php/Orientation), ***this field still corresponds to the 3D phase encoding direction***, but the ``ImageSliceDir`` must be set in the MetaAttributes. | float (32 bit) (x3) |  72 bytes |
+| patient_table_position | Offset position of the patient table, in LPS coordinates                                                                                           | float (32 bit) (x3) |  84 bytes |
+| average                | Signal average                                                                                                                                     | uint16              |  96 bytes |
+| slice                  | Slice number (multi-slice 2D)                                                                                                                      | uint16              |  98 bytes |
+| contrast               | Echo number in multi-echo                                                                                                                          | uint16              | 100 bytes |
+| phase                  | Cardiac phase                                                                                                                                      | uint16              | 102 bytes |
+| repetition             | Counter in repeated/dynamic acquisitions                                                                                                           | uint16              | 104 bytes |
+| set                    | Sets of different preparation, e.g. flow encoding, diffusion weighting                                                                             | uint16              | 106 bytes |
+| acquisition_time_stamp | Clock time stamp (e.g. milliseconds since midnight)                                                                                                | uint32              | 108 bytes |
+| physiology_time_stamp  | Time stamps relative to physiological triggering, e.g. ECG, pulse oximetry, respiratory. Multiplicity defined by ISMRMRD_PHYS_STAMPS (currently 3) | uint32 (x3)         | 112 bytes |
+| image_type             | Interpretation type of the image, e.g. magnitude, phase, as defined in [MRD Image Types](ImageTypes)                                               | uint16              | 124 bytes |
+| image_index            | Image index number within a series of images, corresponding to DICOM InstanceNumber (0020,0013)                                                    | uint16              | 126 bytes |
+| image_series_index     | Series index, used to separate images into different series, corresponding to DICOM SeriesNumber (0020,0011)                                       | uint16              | 128 bytes |
+| user_int               | User-defined integer parameters, multiplicity defined by MRD_USER_INTS (currently 8)                                                               |  int32 (x8)         | 130 bytes |
+| user_float             | User-defined float parameters, multiplicity defined by MRD_USER_FLOATS (currently 8)                                                               | float (32 bit) (x8) | 162 bytes |
+| attribute_string_len   | Length of serialized MetaAttributes text                                                                                                           | uint32              | 194 bytes |
+|                        |                                                                                                                                         **Total**  | **198 bytes**       |           |
+
+A reference implementation for serialization/deserialization of the ImageHeader can be found in [serialization.cpp](../libsrc/serialization.cpp).
+
+(DataTypes)=
+### Data Types
+The ``data_type`` field of the ImageHeader describes the data type and precision of the image data.  The following types are supported:
+| Value        | Name         | Type           | Size        |
+| --           | --           | --             | --          |
+| 1            | MRD_USHORT   | uint16_t       |     2 bytes |
+| 2            | MRD_SHORT    | int16_t        |     2 bytes |
+| 3            | MRD_UINT     | uint32_t       |     4 bytes |
+| 4            | MRD_INT      | int32_t        |     4 bytes |
+| 5            | MRD_FLOAT    | float          |     4 bytes |
+| 6            | MRD_DOUBLE   | double         |     8 bytes |
+| 7            | MRD_CXFLOAT  | complex float  | 2 * 4 bytes |
+| 8            | MRD_CXDOUBLE | complex double | 2 * 8 bytes |
+
+(ImageTypes)=
+### Image Types
+The ``image_type`` field of the ImageHeader is an enum describing the image type with the following values:
+| Value        | Name                 |
+| --           | --                   |
+| 1            | MRD_IMTYPE_MAGNITUDE |
+| 2            | MRD_IMTYPE_PHASE     |
+| 3            | MRD_IMTYPE_REAL      |
+| 4            | MRD_IMTYPE_IMAG      |
+| 5            | MRD_IMTYPE_COMPLEX   |
+| 6            | MRD_IMTYPE_RGB       |
+
+A value of ``6`` is used for 8-bit RGB color images, which have the following settings:
+- ``image_type`` is set to ``MRD_IMTYPE_RGB``
+- ``data_type`` is set to ``MRD_USHORT``
+- ``channels`` is set to 3, representing the red, green, and blue channels of the RGB image
+- image data values are in the range 0-255 (8-bit color depth)
+
+(MetaAttributes)=
+## MetaAttributes
+Image metadata can be stored in the extensible MRD MetaContainer format.  This is serialized as XML text such as:
+```xml
+<ismrmrdMeta>
+    <meta>
+        <name>DataRole</name>
+        <value>Image</value>
+        <value>AVE</value>
+        <value>NORM</value>
+        <value>MAGIR</value>
+    </meta>
+    <meta>
+        <name>ImageNumber</name>
+        <value>1</value>
+    </meta>
+</ismrmrdMeta>
+```
+A variable number of "meta" elements can be defined, each with a single name and one or more value sub-elements.  The following table lists standardized attributes which should be used when appropriate, but custom "meta" elements can also be added.
+
+| MRD Element Name  | Format       | DICOM Tag                                                                                 | Interpretation                                      |
+| --                | --           | --                                                                                        | --                                                  |
+| DataRole          | text array   | N/A                                                                                       | Characteristics of the image. <br><br> A value of “Quantitative” indicates that pixel values in the image are parametric and to be interpreted directly (e.g. T1 values, velocity, etc.). If this role is present, pixel values are not further modified in the ICE chain, e.g. by normalization. |
+| SeriesDescription | text array   | [SeriesDescription](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0008,103E))           | Brief characteristics of the image. <br><br> The DICOM SeriesDescription (0008,103E) field is constructed by combining this array of values, delimited by "\_" (underscores). |
+| SeriesDescriptionAdditional | text array   | [SeriesDescription](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0008,103E)) | Brief characteristics of the image. <br><br> The existing DICOM SeriesDescription (0008,103E) field is appended each string in this array, delimited by "\_" (underscores). |
+| ImageComments     | text array   | [ImageComments](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0020,4000))                | Remarks about the image. <br><br> This array of values is stored in the DICOM ImageComment (0020,4000) field, delimited by "\_" (underscores). |
+| ImageType         | text array   | [ImageType](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0008,0008))                   | Characteristics of the image. <br><br> This array of values is appended to the DICOM ImageType (0008,0008) field starting in position 4, delimited by “\” (backslash). |
+| ImageRowDir       | double array | N/A                                                                                       | A (1x3) vector in indicating the direction along row dimension.  For images reconstructed from raw data and not undergoing any [flipping or rotating to bring them into standard DICOM orientation](http://gdcm.sourceforge.net/wiki/index.php/Orientation), this value is equivalent to the AcquisitionHeader read_dir field. |
+| ImageColumnDir    | double array | N/A                                                                                       | A (1x3) vector in indicating the direction along column dimension.  For images reconstructed from raw data and not undergoing any [flipping or rotating to bring them into standard DICOM orientation](http://gdcm.sourceforge.net/wiki/index.php/Orientation), this value is equivalent to the AcquisitionHeader phase_dir field. |
+| RescaleIntercept  | double       | [RescaleIntercept](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0028,1052))            | Intercept for image pixel values, used in conjunction with RescaleSlope. <br><br> Pixel values are to be interpreted as: ***value = RescaleSlope\*pixelValue + RescaleIntercept***. This value is set in the DICOM RescaleIntercept (0028,1052) field. |
+| RescaleSlope      | double       | [RescaleSlope](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0028,1053))                | Scaling factor for image pixel values, used in conjunction with RescaleIntercept. <br><br> Pixel values are to be interpreted as: ***value = RescaleSlope\*pixelValue + RescaleIntercept***. This value is set in the DICOM RescaleSlope (0028,1053) field. |
+| WindowCenter      | long         | [WindowCenter](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0028,1050))                | The window center in the rendered image, used in conjunction with WindowWidth. <br><br> If RescaleIntercept and RescaleSlope are defined, WindowCenter and WindowWidth are applied to rescaled values. This value is set in the DICOM WindowCenter (0028,1050) field. |
+| WindowWidth       | long         | [WindowWidth](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0028,1051))                 | The window center in the rendered image, used in conjunction with WindowCenter. <br><br> If RescaleIntercept and RescaleSlope are defined, WindowCenter and WindowWidth are applied to rescaled values. This value is set in the DICOM WindowWidth (0028,1051) field. |
+| LUTFileName       | text         | PhotometricInterpretation, [RedPaletteColorLookupTable, RedPaletteColorLookupTable, RedPaletteColorLookupTable](http://dicom.nema.org/medical/Dicom/2018d/output/chtml/part03/sect_C.7.9.html) | Path to a color lookup table file to be used for this image. <br><br> LUT files must be in Siemens .pal format and stored in C:\MedCom\config\MRI\ColorLUT. If a value is provided, the DICOM field PhotometricInterpretation (0028,0004) is set to “PALETTE COLOR” |
+| EchoTime          | double       | [EchoTime](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0018,0081))                    | Echo time of the image in ms. <br><br> This value is set in the DICOM EchoTime (0018,0081) field.
+| InversionTime     | double       | [InversionTime](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0018,0082))               | Inversion time of the image in ms. <br><br> This value is set in the DICOM InversionTime (0018,0082) field.
+| ROI               | double array | N/A                                                                                       | Region of interest polygon. <br><br> For multiple ROIs, the MetaAttribute element names shall start with “ROI_”. These ROIs are stored in a format compatible with the Siemens syngo viewer. The first 6 values are meta attributes of the ROI:
+|                   |              |                                                                                           |   1. Red color (normalized to 1)
+|                   |              |                                                                                           |   2. Green color (normalized to 1)
+|                   |              |                                                                                           |   3. Blue color (normalized to 1)
+|                   |              |                                                                                           |   4. Line thickness (default is 1)
+|                   |              |                                                                                           |   5. Line style (0 = solid, 1 = dashed)
+|                   |              |                                                                                           |   6. Visibility (0 = false, 1 = true)
+|                   |              |                                                                                           | The remaining values are (row,col) coordinates for each ROI point, with values between 0 and the number of rows/columns. Data is organized as (point 1<sub>row</sub>, point 1<sub>col</sub>, point2<sub>row</sub>, point 2<sub>col</sub>, etc). The last point should be a duplicate of the first point if a closed ROI is desired.
+
+(ImageData)=
+## Image Data
+Image data is organized by looping through ``matrix_size[0]``, ``matrix_size[1]``, ``matrix_size[2]``, then ``channels``. For example, 2D image data would be formatted as:
+<style>
+ .smalltable td {
+   font-size:       80%;
+   border-collapse: collapse;
+   border-spacing:  0;
+   border-width:    0;
+   padding:         3px;
+   border:          1px solid lightgray
+ }
+</style>
+
+<table class="smalltable">
+  <tr>
+    <td style="text-align: center" colspan="9">Channel 1</td>
+    <td style="text-align: center" rowspan="3">...</td>
+    <td style="text-align: center" colspan="9">Channel n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center" colspan="3">y<sub>1</sub></td>
+    <td style="text-align: center" colspan="3">...</td>
+    <td style="text-align: center" colspan="3">y<sub>n</sub></td>
+    <td style="text-align: center" colspan="3">y<sub>1</sub></td>
+    <td style="text-align: center" colspan="3">...</td>
+    <td style="text-align: center" colspan="3">y<sub>n</sub></td>
+  </tr>
+  <tr>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+  </tr>
+</table>
diff --git a/doc/mrd_messages.md b/doc/mrd_messages.md
new file mode 100644
index 0000000..893c977
--- /dev/null
+++ b/doc/mrd_messages.md
@@ -0,0 +1,321 @@
+# Message Types
+<style>
+    .mrdMsgTable2 {
+        border-collapse: collapse;
+        width:           100%;
+        text-align:      center;
+    }
+    .mrdMsgTable2 tr th:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable2 tr td:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable2 tr th:nth-child(2) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable2 tr td:nth-child(2) { text-align: center; background-color: #ebf0de !important;}
+
+
+    .mrdMsgTable3 {
+        border-collapse: collapse;
+        border-color:    black !important;
+        width:           100%;
+        text-align:      center;
+    }
+    
+    .mrdMsgTable3 tr th:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable3 tr td:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable3 tr th:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable3 tr td:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable3 tr th:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable3 tr td:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+
+
+    .mrdMsgTable4 {
+        border-collapse: collapse;
+        width:           100%;
+        text-align:      center;
+    }
+
+    .mrdMsgTable4 tr th:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable4 tr td:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable4 tr th:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable4 tr td:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable4 tr th:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable4 tr td:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable4 tr th:nth-child(4) { text-align: center; background-color: #e5e0eb !important;}
+    .mrdMsgTable4 tr td:nth-child(4) { text-align: center; background-color: #e5e0eb !important;}
+
+    .mrdMsgTable5 {
+        border-collapse: collapse;
+        width:           100%;
+        text-align:      center;
+    }
+
+    .mrdMsgTable5 tr th:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable5 tr td:nth-child(1) { text-align: center; background-color: #dbe5f0 !important;}
+    .mrdMsgTable5 tr th:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable5 tr td:nth-child(2) { text-align: center; background-color: #f1dbdb !important;}
+    .mrdMsgTable5 tr th:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable5 tr td:nth-child(3) { text-align: center; background-color: #ebf0de !important;}
+    .mrdMsgTable5 tr th:nth-child(4) { text-align: center; background-color: #c4d49f !important;}
+    .mrdMsgTable5 tr td:nth-child(4) { text-align: center; background-color: #c4d49f !important;}
+    .mrdMsgTable5 tr th:nth-child(5) { text-align: center; background-color: #e5e0eb !important;}
+    .mrdMsgTable5 tr td:nth-child(5) { text-align: center; background-color: #e5e0eb !important;}
+</style>
+
+
+(MRD_MESSAGE_CONFIG_FILE)=
+## ID 1: MRD_MESSAGE_CONFIG_FILE
+<div class="mrdMsgTable2">
+
+| ID             | Config File Name |
+| --             | --               |
+| 2 bytes        | 1024 bytes       |
+| unsigned short | char             |
+</div>
+
+This message type is used to send the file name of a configuration file (local on the server file system) to be used during reconstruction. The file name must not exceed 1023 characters and is formatted as a null-terminated, UTF-8 encoded char string.
+
+(MRD_MESSAGE_CONFIG_TEXT)=
+## ID 2: MRD_MESSAGE_CONFIG_TEXT
+<div class="mrdMsgTable3">
+
+| ID             | Length   | Config Text     |
+| --             | --       | --              |
+| 2 bytes        | 4 bytes  | length * 1 byte |
+| unsigned short | uint32_t | char            |
+</div>
+
+Alternatively, the text contents of a configuration file can be sent directly via the data stream. The length is sent as an uint32_t. Configuration text is sent as a UTF-8 encoded char string.
+
+In addition to specifying a configuration "preset" to be executed on the server, it is often desirable to modify specific parameters of the configuration, such as filter strength or the toggling of intermediate outputs for debugging purposes.  While individual parameters are specific to a given pipeline and server, the format of this configuration is standardized to enable interoperable communications between various clients and servers.
+
+(MRD_MESSAGE_HEADER)=
+## ID 3: MRD_MESSAGE_HEADER
+<div class="mrdMsgTable3">
+
+| ID             | Length   | XML Header Text |
+| --             | --       | --              |
+| 2 bytes        | 4 bytes  | length * 1 byte |
+| unsigned short | uint32_t | char            |
+</div>
+
+Metadata for MRD datasets are stored in a flexible XML formatted [MRD header](mrd_header.md). The header length is sent as an uint32_t and the text is sent as a UTF-8 encoded char string.
+
+(MRD_MESSAGE_CLOSE)=
+## ID 4: MRD_MESSAGE_CLOSE
+<div class="mrdMsgTable2">
+
+| ID             |
+| --             |
+| 2 bytes        |
+| unsigned short |
+</div>
+
+This message type consists only of an ID with no following data. It is used to indicate that all data related to an acquisition/reconstruction has been sent. The client will send this message after sending the last data (raw, image, or waveform) message.  The server will also send this message after sending the last data (raw, image, or waveform) message back to the client.
+
+(MRD_MESSAGE_TEXT)=
+## ID 5: MRD_MESSAGE_TEXT
+<div class="mrdMsgTable3">
+
+| ID             | Length   | Text            |
+| --             | --       | --              |
+| 2 bytes        | 4 bytes  | length * 1 byte |
+| unsigned short | uint32_t | char            |
+</div>
+
+Informational (logging) text can be sent using this message type, typically from the reconstruction side to the acquisition/client side. The length of message text is sent as an uint32_t while the text is sent as a UTF-8 encoded char string. These messages are optional and their timing is not guaranteed.
+
+(MRD_MESSAGE_ACQUISITION)=
+## ID 1008: MRD_MESSAGE_ACQUISITION
+<div class="mrdMsgTable4">
+
+| ID             | Fixed Raw Data Header   | Trajectory       | Raw Data |
+| --             | --                      | --               | --       |
+| 2 bytes        | 340 bytes               | number_of_samples * trajectory_dimensions * 4 bytes | number_of_channels * number_of_samples * 8 bytes |
+| unsigned short | mixed                   | float             | float |
+</div>
+
+This message type is used to send raw (k-space) acquisition data. A separate message is sent for each readout. A fixed [AcquisitionHeader](AcquisitionHeader) contains metadata such as [encoding loop counters](EncodingCounters). Three fields of the data header must be parsed in order to read the rest of the message:
+- **trajectory_dimensions**: defines the number of dimensions in the k-space trajectory data component. For 2D acquisitions (k<sub>x</sub>, k<sub>y</sub>), this is set to 2, while for 3D acquisitions (k<sub>x</sub>, k<sub>y</sub>, k<sub>z</sub>), this is set to 3. If set to 0, the trajectory component is omitted.
+- **number_of_samples**: number of readout samples.
+- **active_channels**: number of channels for which raw data is acquired.
+
+<style>
+ .smalltable td {
+   font-size:       80%;
+   border-collapse: collapse;
+   border-spacing:  0;
+   border-width:    0;
+   padding:         3px;
+   border:          1px solid lightgray
+ }
+</style>
+
+Trajectory data is organized by looping through the dimensions first then the samples:
+  - For 2D trajectory data:
+    <table class="smalltable">
+      <tr>
+        <td style="text-align: center" colspan="2">Sample 1</td>
+        <td style="text-align: center" colspan="2">Sample 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="2">Sample n</td>
+      </tr>
+      <tr>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+      </tr>
+    </table>
+
+  - For 3D trajectory data:
+    <table class="smalltable">
+      <tr>
+        <td style="text-align: center" colspan="3">Sample 1</td>
+        <td style="text-align: center" colspan="3">Sample 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="3">Sample n</td>
+      </tr>
+      <tr>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+      </tr>
+    </table>
+
+Raw data is organized by looping through real/imaginary data, samples, then channels:
+
+<table class="smalltable">
+  <tr>
+    <td style="text-align: center" colspan="6">Channel 1</td>
+    <td style="text-align: center" colspan="6">Channel 2</td>
+    <td style="text-align: center" rowspan="3">...</td>
+    <td style="text-align: center" colspan="6">Channel n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+  </tr>
+</table>
+
+(MRD_MESSAGE_IMAGE)=
+## ID 1022: MRD_MESSAGE_IMAGE
+<div class="mrdMsgTable5">
+
+| ID             | Fixed Image Header  | Attribute Length | Attribute Data  |Image Data |
+| --             | --                  | --               | --              | --        |
+| 2 bytes        | 198 bytes           | 8 bytes          | length * 1 byte | matrix_size[0] * matrix_size[1] * matrix_size[2] * channels * sizeof(data_type) |
+| unsigned short | mixed               | uint_64          | char            | data_type |
+</div>
+
+Image data is sent using this message type. The fixed image header contains metadata including fields such as the [ImageType](ImageTypes) (magnitude, phase, etc.) and indices such as slice and repetition number. It is defined by the [ImageHeader](ImageHeader) struct. Within this header, there are 3 fields that must be interpreted to parse the rest of the message:
+- **matrix_size**: This 3 element array indicates the size of each dimension of the image data.
+- **channels**: This value indicates the number of (receive) channels for which image data is sent
+- **data_type**: This value is an MRD_DataTypes enum that indicates the type of data sent. The following types are supported:
+    | Value        | Name         | Type           | Size        |
+    | --           | --           | --             | --          |
+    | 1            | MRD_USHORT   | uint16_t       |     2 bytes |
+    | 2            | MRD_SHORT    | int16_t        |     2 bytes |
+    | 3            | MRD_UINT     | uint32_t       |     4 bytes |
+    | 4            | MRD_INT      | int32_t        |     4 bytes |
+    | 5            | MRD_FLOAT    | float          |     4 bytes |
+    | 6            | MRD_DOUBLE   | double         |     8 bytes |
+    | 7            | MRD_CXFLOAT  | complex float  | 2 * 4 bytes |
+    | 8            | MRD_CXDOUBLE | complex double | 2 * 8 bytes |
+
+Attributes are used to declare additional image metadata that is not present in the fixed image header. In general, this data is sent as a UTF-8 encoded char string (not null-terminated), with the length sent first as an uint_64 (not uint_32!). These are interpreted as an XML formatted set of image [MetaAttributes](MetaAttributes).
+
+Image data is organized by looping through ``matrix_size[0]``, ``matrix_size[1]``, ``matrix_size[2]``, then ``channels``. For example, 2D image data would be formatted as:
+
+<table class="smalltable">
+  <tr>
+    <td style="text-align: center" colspan="9">Channel 1</td>
+    <td style="text-align: center" rowspan="3">...</td>
+    <td style="text-align: center" colspan="9">Channel n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center" colspan="3">y<sub>1</sub></td>
+    <td style="text-align: center" colspan="3">...</td>
+    <td style="text-align: center" colspan="3">y<sub>n</sub></td>
+    <td style="text-align: center" colspan="3">y<sub>1</sub></td>
+    <td style="text-align: center" colspan="3">...</td>
+    <td style="text-align: center" colspan="3">y<sub>n</sub></td>
+  </tr>
+  <tr>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+    <td style="text-align: center">x<sub>1</sub></td>
+    <td style="text-align: center">...</td>
+    <td style="text-align: center">x<sub>n</sub></td>
+  </tr>
+</table>
+
+(MRD_MESSAGE_WAVEFORM)=
+## ID 1026: MRD_MESSAGE_WAVEFORM
+<div class="mrdMsgTable3">
+
+| ID             | Fixed Waveform Header | Waveform Data                        |
+| --             | --                    | --                                   |
+| 2 bytes        | 40 bytes              | channels * number of samples * bytes |
+| unsigned short | mixed                 | uint32_t                             |
+</div>
+
+This message type is used to send arbitrary waveform data (e.g. physio signals, gradient waveforms, etc.). The fixed waveform data header contains metadata as defined by the [WaveformHeader](WaveformHeader).
+
+The ``channels`` and ``number_of_samples`` members fields must be parsed in order to read the rest of the message. Waveform data is sent as an uint32_t array, ordered by looping through samples and then through channels:
+
+<table class="smalltable">
+      <tr>
+        <td style="text-align: center" colspan="3">Channel 1</td>
+        <td style="text-align: center" colspan="3">Channel 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="3">Channel n</td>
+      </tr>
+      <tr>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+      </tr>
+    </table>
diff --git a/doc/mrd_raw_data.md b/doc/mrd_raw_data.md
new file mode 100644
index 0000000..a255a4a
--- /dev/null
+++ b/doc/mrd_raw_data.md
@@ -0,0 +1,190 @@
+# Raw Acquisition Data
+Raw k-space data is stored in MRD format as individual readout acquisitions.  Each readout contains the complex raw data for all channels, a fixed AcquisitionHeader, and optionally corresponding k-space trajectory information.  Most datasets will be comprised of many acquisitions, each stored indvidiually with its own [AcquisitionHeader](AcquisitionHeader), optional [trajectory](Trajectory), and [raw data](RawData).
+
+(AcquisitionHeader)=
+## AcquisitionHeader
+An MRD AcquisitionHeader accompanies each readout containing metadata common to most data.  It is of a fixed size and thus fields cannot be added, removed, or otherwise repurposed.  It contains the following information:
+| Field                  | Description                                                                                                                                        | Type                | Offset    |
+| --                     | --                                                                                                                                                 | --                  | --        |
+| version                | Major version number (currently 1)                                                                                                                 | uint16              |   0 bytes |
+| flags                  | A bit mask of common attributes applicable to individual acquisition readouts                                                                      | uint64              |   2 bytes |
+| measurement_uid        | Unique ID corresponding to the readout                                                                                                             | uint32              |  10 bytes |
+| scan_counter           | Zero-indexed incrementing counter for readouts                                                                                                     | uint32              |  14 bytes |
+| acquisition_time_stamp | Clock time stamp (e.g. milliseconds since midnight)                                                                                                | uint32              |  18 bytes |
+| physiology_time_stamp  | Time stamps relative to physiological triggering, e.g. ECG, pulse oximetry, respiratory. Multiplicity defined by ISMRMRD_PHYS_STAMPS (currently 3) | uint32 (x3)         |  22 bytes |
+| number_of_samples      | Number of digitized readout samples                                                                                                                | uint16              |  34 bytes |
+| available_channels     | Number of possible receiver coils (channels)                                                                                                       | uint16              |  36 bytes |
+| active_channels        | Number of active receiver coils                                                                                                                    | uint16              |  38 bytes |
+| channel_mask           | Bit mask indicating active coils (64\*16 = 1024 bits)                                                                                              | uint64 (x16)        |  40 bytes |
+| discard_pre            | Number of readout samples to be discarded at the beginning (e.g. if the ADC is active during gradient events)                                      | uint16              | 168 bytes |
+| discard_post           | Number of readout samples to be discarded at the end (e.g. if the ADC is active during gradient events)                                            | uint16              | 170 bytes |
+| center_sample          | Index of the readout sample corresponing to k-space center (zero indexed)                                                                          | uint16              | 172 bytes |
+| encoding_space_ref     | Indexed reference to the encoding spaces enumerated in the MRD (xml) header                                                                        | uint16              | 174 bytes |
+| trajectory_dimensions  | Dimensionality of the k-space trajectory vector (e.g. 2 for 2D radial (k<sub>x</sub>, k<sub>y</sub>), 0 for no trajectory data)                    | uint16              | 176 bytes |
+| sample_time_us         | Readout bandwidth, as time between samples in microseconds                                                                                         | float (32 bit)      | 178 bytes |
+| position               | Center of the excited volume, in (left, posterior, superior) (LPS) coordinates relative to isocenter in millimeters                                | float (32 bit) (x3) | 182 bytes |
+| read_dir               | Directional cosine of readout/frequency encoding                                                                                                   | float (32 bit) (x3) | 194 bytes |
+| phase_dir              | Directional cosine of phase encoding (2D)                                                                                                          | float (32 bit) (x3) | 206 bytes |
+| slice_dir              | Directional cosine of slice normal, i.e. cross-product of read_dir and phase_dir                                                                   | float (32 bit) (x3) | 218 bytes |
+| patient_table_position | Offset position of the patient table, in LPS coordinates                                                                                           | float (32 bit) (x3) | 230 bytes |
+| idx                    | Encoding loop counters, as defined [below](EncodingCounters)                                                                                       | uint16 (x17)        | 242 bytes |
+| user_int               | User-defined integer parameters, multiplicity defined by ISMRMRD_USER_INTS (currently 8)                                                           |  int32 (x8)         | 276 bytes |
+| user_float             | User-defined float parameters, multiplicity defined by ISMRMRD_USER_FLOATS (currently 8)                                                           | float (32 bit) (x8) | 308 bytes |
+|                        |                                                                                                                                          **Total** | **340 bytes**       |           |
+
+A reference implementation for serialization/deserialization of the AcquisitionHeader can be found in [serialization.cpp](../libsrc/serialization.cpp).
+
+(EncodingCounters)=
+### MRD EncodingCounters
+MR acquisitions often loop through a set of counters (e.g. phase encodes) in a complete experiment.  The following encoding counters are referred to by the ``idx`` field in the AcquisitionHeader.
+
+| Field                  | Format       | Description                                                                    | Type         | Offset   |
+| --                     | --           | --                                                                             | --           | --       |
+| kspace_encode_step_1   | uint16       | Phase encoding line                                                            | uint16       |  0 bytes |
+| kspace_encode_step_2   | uint16       | Partition encoding                                                             | uint16       |  2 bytes |
+| average                | uint16       | Signal average                                                                 | uint16       |  4 bytes |
+| slice                  | uint16       | Slice number (multi-slice 2D)                                                  | uint16       |  6 bytes |
+| contrast               | uint16       | Echo number in multi-echo                                                      | uint16       |  8 bytes |
+| phase                  | uint16       | Cardiac phase                                                                  | uint16       | 10 bytes |
+| repetition             | uint16       | Counter in repeated/dynamic acquisitions                                       | uint16       | 12 bytes |
+| set                    | uint16       | Sets of different preparation, e.g. flow encoding, diffusion weighting         | uint16       | 14 bytes |
+| segment                | uint16       | Counter for segmented acquisitions                                             | uint16       | 16 bytes |
+| user                   | uint16 (x8)  | User defined counters, multiplicity defined by ISMRMRD_USER_INTS (currently 8) | uint16 (x8)  | 18 bytes |
+|                        |              |                                                                      **Total** | **34 bytes** |          |
+
+A reference implementation for serialization/deserialization of the EncodingCounters can be found in [serialization.cpp](../libsrc/serialization.cpp).
+
+### MRD AcquisitionFlags
+The ``flags`` field in the AcquisitionHeader is a 64 bit mask that can be used to indicate specific attributes of the corresponding readout.  One usage of these flags is to trigger the processing of data when a condition is met, e.g. the last readout for the current slice.  The following flags are defined in the ``ISMRMRD_AcquisitionFlags`` enum of [ismrmrd.h](../include/ismrmrd/ismrmrd.h):
+```
+    ISMRMRD_ACQ_FIRST_IN_ENCODE_STEP1               =  1,
+    ISMRMRD_ACQ_LAST_IN_ENCODE_STEP1                =  2,
+    ISMRMRD_ACQ_FIRST_IN_ENCODE_STEP2               =  3,
+    ISMRMRD_ACQ_LAST_IN_ENCODE_STEP2                =  4,
+    ISMRMRD_ACQ_FIRST_IN_AVERAGE                    =  5,
+    ISMRMRD_ACQ_LAST_IN_AVERAGE                     =  6,
+    ISMRMRD_ACQ_FIRST_IN_SLICE                      =  7,
+    ISMRMRD_ACQ_LAST_IN_SLICE                       =  8,
+    ISMRMRD_ACQ_FIRST_IN_CONTRAST                   =  9,
+    ISMRMRD_ACQ_LAST_IN_CONTRAST                    = 10,
+    ISMRMRD_ACQ_FIRST_IN_PHASE                      = 11,
+    ISMRMRD_ACQ_LAST_IN_PHASE                       = 12,
+    ISMRMRD_ACQ_FIRST_IN_REPETITION                 = 13,
+    ISMRMRD_ACQ_LAST_IN_REPETITION                  = 14,
+    ISMRMRD_ACQ_FIRST_IN_SET                        = 15,
+    ISMRMRD_ACQ_LAST_IN_SET                         = 16,
+    ISMRMRD_ACQ_FIRST_IN_SEGMENT                    = 17,
+    ISMRMRD_ACQ_LAST_IN_SEGMENT                     = 18,
+    ISMRMRD_ACQ_IS_NOISE_MEASUREMENT                = 19,
+    ISMRMRD_ACQ_IS_PARALLEL_CALIBRATION             = 20,
+    ISMRMRD_ACQ_IS_PARALLEL_CALIBRATION_AND_IMAGING = 21,
+    ISMRMRD_ACQ_IS_REVERSE                          = 22,
+    ISMRMRD_ACQ_IS_NAVIGATION_DATA                  = 23,
+    ISMRMRD_ACQ_IS_PHASECORR_DATA                   = 24,
+    ISMRMRD_ACQ_LAST_IN_MEASUREMENT                 = 25,
+    ISMRMRD_ACQ_IS_HPFEEDBACK_DATA                  = 26,
+    ISMRMRD_ACQ_IS_DUMMYSCAN_DATA                   = 27,
+    ISMRMRD_ACQ_IS_RTFEEDBACK_DATA                  = 28,
+    ISMRMRD_ACQ_IS_SURFACECOILCORRECTIONSCAN_DATA   = 29,
+    ISMRMRD_ACQ_COMPRESSION1                        = 53,
+    ISMRMRD_ACQ_COMPRESSION2                        = 54,
+    ISMRMRD_ACQ_COMPRESSION3                        = 55,
+    ISMRMRD_ACQ_COMPRESSION4                        = 56,
+    ISMRMRD_ACQ_USER1                               = 57,
+    ISMRMRD_ACQ_USER2                               = 58,
+    ISMRMRD_ACQ_USER3                               = 59,
+    ISMRMRD_ACQ_USER4                               = 60,
+    ISMRMRD_ACQ_USER5                               = 61,
+    ISMRMRD_ACQ_USER6                               = 62,
+    ISMRMRD_ACQ_USER7                               = 63,
+    ISMRMRD_ACQ_USER8                               = 64
+```
+(Trajectory)=
+## k-space Trajectory
+<style>
+ .smalltable td {
+   font-size:       80%;
+   border-collapse: collapse;
+   border-spacing:  0;
+   border-width:    0;
+   padding:         3px;
+   border:          1px solid lightgray
+ }
+</style>
+
+k-space trajectory information is optionally included with each readout, with dimensionality specified by the ``trajectory_dimensions`` field in the AcquisitionHeader.  Common values are ``2`` for 2D radial (k<sub>x</sub>, k<sub>y</sub>), ``3`` for 3D radial (k<sub>x</sub>, k<sub>y</sub>, k<sub>z</sub>).  Trajectory information is omitted if ``trajectory_dimensions`` is set to ``0``.
+
+Trajectory data is organized by looping through the dimensions first then the samples:
+  - For 2D trajectory data:
+    <table class="smalltable">
+      <tr>
+        <td style="text-align: center" colspan="2">Sample 1</td>
+        <td style="text-align: center" colspan="2">Sample 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="2">Sample n</td>
+      </tr>
+      <tr>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+      </tr>
+    </table>
+
+  - For 3D trajectory data:
+    <table class="smalltable">
+      <tr>
+        <td style="text-align: center" colspan="3">Sample 1</td>
+        <td style="text-align: center" colspan="3">Sample 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="3">Sample n</td>
+      </tr>
+      <tr>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+        <td style="text-align: center">k<sub>x</sub></td>
+        <td style="text-align: center">k<sub>y</sub></td>
+        <td style="text-align: center">k<sub>z</sub></td>
+      </tr>
+    </table>
+
+(RawData)=
+## Raw Data
+MR acquisition raw data are stored as complex valued floats.  Data from all receiver channels are included in a single readout object.  Data is organized by looping through real/imaginary data, samples, then channels:
+
+<table class="smalltable">
+  <tr>
+    <td style="text-align: center" colspan="6">Channel 1</td>
+    <td style="text-align: center" colspan="6">Channel 2</td>
+    <td style="text-align: center" rowspan="3">...</td>
+    <td style="text-align: center" colspan="6">Channel n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+    <td style="text-align: center" colspan="2">Sample 1</td>
+    <td style="text-align: center" colspan="2">...</td>
+    <td style="text-align: center" colspan="2">Sample n</td>
+  </tr>
+  <tr>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+    <td style="text-align: center">Re</td> <td style="text-align: center">Im</td>
+  </tr>
+</table>
diff --git a/doc/mrd_streaming_protocol.md b/doc/mrd_streaming_protocol.md
new file mode 100644
index 0000000..66e0df8
--- /dev/null
+++ b/doc/mrd_streaming_protocol.md
@@ -0,0 +1,51 @@
+# Session Protocol
+The MR Data (MRD) streaming protocol describes the communication of data ([k-space](mrd_raw_data.md), [image](mrd_image_data.md), or [waveform](mrd_waveform_data.md)) between a client and a server pair.  It consists of a series of [messages](mrd_messages.md) that are sent through a TCP/IP socket in sessions with the protocol defined as follow:
+1. The server is started and listens for incoming connections on a designated TCP port (9002 by default).  A client initiates a session by connecting to the TCP port above.
+1. The client sends a configuration message to indicate analysis that should be performed by the server.  The message may be either (but not both):
+    - [MRD_MESSAGE_CONFIG_FILE](MRD_MESSAGE_CONFIG_FILE), corresponding to the name of a config file that exists on the server
+    - [MRD_MESSAGE_CONFIG_TEXT](MRD_MESSAGE_CONFIG_TEXT), configuration parameters for the server, in XML text format.
+1. The client sends the MRD acquisition parameter header, [MRD_MESSAGE_HEADER](MRD_MESSAGE_HEADER), containing information pertaining to the entire acquisition.  This information is sent as XML formatted text that conforms to a standardized [ismrmrd.xsd schema](../schema/ismrmrd.xsd).
+1. The client sends raw k-space, image, waveform or text data.  Not all types of data may be sent, depending on the analysis pipeline to be performed.  For example, image processing pipelines may not contain k-space or waveform data.  Data of each type must sent in order by acquisition time, but the order between messages of different types is not guaranteed.  For eample, a waveform message corresponding to a specific time may be sent before or after the raw k-space data from that time.  The data types are:
+    - [MRD_MESSAGE_ACQUISITION](MRD_MESSAGE_ACQUISITION): Data from a single k-space readout, including a fixed raw data [AcquisitionHeader](AcquisitionHeader) and optional k-space trajectory information.
+    - [MRD_MESSAGE_IMAGE](MRD_MESSAGE_IMAGE): Image data as a 2D or 3D array, including both a fixed image data [ImageHeader](ImageHeader) and a flexible set of image [MetaAttributes](MetaAttributes) formatted as an XML text string.
+    - [MRD_MESSAGE_WAVEFORM](MRD_MESSAGE_WAVEFORM): Waveform data such as physiological monitoring (ECG, pulse oximeter, respiratory motion) including a fixed waveform [WaveformHeader](WaveformHeader).
+    - [MRD_MESSAGE_TEXT](MRD_MESSAGE_TEXT): Informational text for the other party.  This text may provide logging information about the status of the analysis or client, but is optional and should not be used for workflow control.
+1. At any point after a config message is received, the server may send back raw k-space, image, waveform, or text data as described above.
+1. When all data has been sent, the client sends [MRD_MESSAGE_CLOSE](MRD_MESSAGE_CLOSE) to indicate that no further data will be sent by the client.
+1. When the server has sent all data from its side, it also sends [MRD_MESSAGE_CLOSE](MRD_MESSAGE_CLOSE).  This usually occurs after the client has sent MRD_MESSAGE_CLOSE, but can also occur if the server encounters an unrecoverable error and no further data can be processed.
+1.  The TCP session may be closed by either side and the MRD streaming session is complete.
+
+```{mermaid}
+
+sequenceDiagram
+    autonumber
+    rect rgba(0, 255, 0, 0.05)
+        Note over Client,Server: Startup
+        Client->>+Server: Establish connection
+        Client-->>Server: Configuration information (xml text or filename)
+        Client-->>Server: MRD header (xml text)
+    end
+
+    rect rgba(0, 0, 255, 0.05)
+        Note over Client,Server: Data transfer
+        par
+            loop Sending data
+                Client-->>+Server: k-space, image, waveform, text message data
+            end
+        and
+            loop Receiving data
+                Server-->>-Client: k-space, image, waveform, text message data
+            end
+        end
+    end
+
+    rect rgba(255, 0, 0, 0.05)
+        Note over Client,Server: Shutdown
+        par
+            Client->>Server: Session complete message
+        and
+            Server->>-Client: Session complete message
+        end
+        Server->Client: TCP session closed by either side
+    end
+```
diff --git a/doc/mrd_waveform_data.md b/doc/mrd_waveform_data.md
new file mode 100644
index 0000000..e8fa7e8
--- /dev/null
+++ b/doc/mrd_waveform_data.md
@@ -0,0 +1,84 @@
+# Physiological Waveforms
+Physiological monitoring data such as electrocardiograms, pulse oximetry, or external triggering may accompany MR acquisitions.  These physiological data are stored in MRD as a combination of a fixed [WaveformHeader](WaveformHeader) and the [raw physiological waveforms](WaveformData).
+
+(WaveformHeader)=
+## WaveformHeader
+The WaveformHeader contains metadata associated with a set of waveform data and has the following fields:
+| Field             | Description                                              | Type      | Offset            |
+| --                | --                                                       | --        | --                |
+| version           | Version number                                           | uint16_t  |  0 bytes          |
+| flags             | Bit field with flags                                     | uint64_t  |  8 bytes (not 2!) |
+| measurement_uid   | Unique ID for this measurement                           | uint32_t  | 16 bytes          |
+| scan_counter      | Number of the acquisition after this waveform            | uint32_t  | 20 bytes          |
+| time_stamp        | Starting timestamp of this waveform                      | uint32_t  | 24 bytes          |
+| number_of_samples | Number of samples acquired                               | uint16_t  | 28 bytes          |
+| channels          | Active channels                                          | uint16_t  | 30 bytes          |
+| sample_time_us    | Time between samples in microseconds                     | float     | 32 bytes          |
+| waveform_id       | [ID matching types specified in XML header](WaveformIDs) | uint16_t  | 36 bytes          |
+|                   |                                                **Total** | **40 bytes (2 bytes padding at the end!)** |                 |
+
+A reference implementation for serialization/deserialization of the WaveformHeader can be found in [serialization.cpp](../libsrc/serialization.cpp).
+
+(WaveformIDs)=
+### Waveform IDs
+The ``waveform_id`` field in the WaveformHeader describes the type of physiological data stored.  The following ID numbers are standardized:
+| Value | Name                                  |
+| --    | --                                    |
+|  0    | ECG                                   |
+|  1    | Pulse Oximetry                        |
+|  2    | Respiratory                           |
+|  3    | External Waveform 1                   |
+|  4    | External Waveform 2                   |
+
+For each type of ``waveform_id`` included in the dataset, a corresponding ``WaveformInformation`` entry is found in the MRD header to describe the data interpretation.  For example:
+```xml
+<waveformInformation>
+    <waveformId>0</waveformName>
+    <waveformName>ECG1</waveformName>
+    <waveformTriggerChannel>4</waveformTriggerChannel>
+</waveformInformation>
+```
+
+Physiological data used for triggering may have an associated "trigger" channel as detected by the MR system.  The ``waveformTriggerChannel`` indicates the channel index (0-indexed) which contains the detected triggers and is omitted if no trigger data is present.
+
+Waveform ID numbers less than 1024 are reserved while numbers greater than or equal to 1024 can be used to define custom physiological data.  For custom waveform_ids, corresponding WaveformInformation entries should be added to the MRD header to describe the data interpretation.  For example:
+```xml
+<waveformInformation>
+    <waveformId>1024</waveformName>
+    <waveformName>CustomName</waveformName>
+</waveformInformation>
+```
+
+(WaveformData)=
+## Waveform Data
+Waveform data is sent as an uint32_t array, ordered by looping through samples and then through channels:
+<style>
+ .smalltable td {
+   font-size:       80%;
+   border-collapse: collapse;
+   border-spacing:  0;
+   border-width:    0;
+   padding:         3px;
+   border:          1px solid lightgray
+ }
+</style>
+
+<table class="smalltable">
+    <tr>
+        <td style="text-align: center" colspan="3">Channel 1</td>
+        <td style="text-align: center" colspan="3">Channel 2</td>
+        <td style="text-align: center" rowspan="2">...</td>
+        <td style="text-align: center" colspan="3">Channel n</td>
+    </tr>
+    <tr>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+        <td style="text-align: center">w<sub>1</sub></td>
+        <td style="text-align: center">...</td>
+        <td style="text-align: center">w<sub>n</sub></td>
+    </tr>
+</table>
diff --git a/doc/overview.md b/doc/overview.md
new file mode 100644
index 0000000..da0de65
--- /dev/null
+++ b/doc/overview.md
@@ -0,0 +1,10 @@
+# Magnetic Resonance Data (MRD) Format
+The Magnetic Resonance Data (MRD) format is a vendor neutral standard for describing data from MR acquisitions and reconstructions.  It consists of 4 major components:
+
+1. An [MRD header](mrd_header.md) containing general metadata describing the acquisition, including MR system details and k-space sampling.  The header contains a small set of mandatory parameters common to all MR acquisitions, but is extensible to parameters for specialized acquisitions such as b-values, venc, magnetization preparation durations, etc.  The MRD header is in XML format and described by an [XML schema file](../schema/ismrmrd.xsd).
+
+2. [Raw k-space data](mrd_raw_data.md) is stored as individual readout acquisitions.  Each readout contains the [complex raw data](RawData) for all channels, a fixed [AcquisitionHeader](AcquisitionHeader) for metadata including [encoding loop counters](EncodingCounters), and optionally corresponding [k-space trajectory](Trajectory) information.  Most datasets will be comprised of many acquisitions, each stored individually with its own AcquisitionHeader, optional trajectory, and raw data.
+
+3. [Image data](mrd_image_data.md) is stored as either sets of 2D or 3D arrays with a fixed [ImageHeader](ImageHeader) of common properties and an extensible set of image [MetaAttributes](MetaAttributes).  Images can be organized into series of common types and multi-channel data is supported for non-coil-combined images.
+
+4. Physiological data such as electrocardiograms, pulse oximetry, or external triggering sources are stored as individual [waveforms](mrd_waveform_data.md) along with a fixed [WaveformHeader](WaveformHeader) for metadata.
\ No newline at end of file
diff --git a/doc/overview.rst b/doc/overview.rst
deleted file mode 100644
index a21f997..0000000
--- a/doc/overview.rst
+++ /dev/null
@@ -1,81 +0,0 @@
-Overview
-#########
-
-The raw data format combines a mix of flexible data structures (XML header) and fixed structures (equivalent to C-structs). A raw data set consist mainly of 2 sections:
-
-1. A flexible XML format document that can contain an arbitrary number of fields and accommodate everything from simple values (b-values, etc.) to entire vendor protocols, etc. This purpose of this XML document is to provide parameters that may be meaningful for some experiments but not for others. This XML format is defined by an XML Schema Definition file `schema/ismrmrd.xsd <https://github.com/ismrmrd/ismrmrd/blob/master/schema/ismrmrd.xsd>`_.
-2. Raw data section. This section contains all the acquired data in the experiment. Each data item is preceded by a C-struct with encoding numbers, etc. Following this data header is a channel header and data for each acquired channel. The raw data headers are defined in a C/C++ header file `ismrmrd.h <https://github.com/ismrmrd/ismrmrd/blob/master/include/ismrmrd/ismrmrd.h>`_
-
-In addition to these sections, the ISMRMRD format also specifies an image header for storing reconstructed images and the accompanying C++ library provides a convenient way of writing such images into HDF5 files along with generic arrays for storing less well defined data structures, e.g. coil sensitivity maps or other calibration data.
-
-Flexible Data Header
-********************
-
-The flexible data structure is defined by the xml schema definition in `schema/ismrmrd.xsd <https://github.com/ismrmrd/ismrmrd/blob/master/schema/ismrmrd.xsd>`_. An example of an XML file for a Cartesian 3D acquisition can be found `schema/ismrmrd_example.xml <https://github.com/ismrmrd/ismrmrd/blob/master/schema/ismrmrd_example.xml>_
-
-The most critical elements for image reconstruction are contained in the `<encoding>` section of the document, which describes the encoded spaced and also the target reconstructed space. Along with the `<encodingLimits>` this section allows the reconstruction program to determine matrix sizes, oversampling factors, partial Fourier, etc. In the example above, data is acquired with two-fold oversampling in the read-out (`x`) direction, which is reflected in the larger matrix size in the encoded space compared to the reconstruction space. The field of view is also twice as large in the encoded space. For the first phase encoding dimension (`y`), we have a combination of oversampling (20%), reduced phase resolution (only 83 lines of k-space acquired, and partial Fourier sampling, which is reflected in the asymmetric center of the encoding limits of the `<kspace_encoding_step_1>`. Specifically, the data lines would be placed into the encoding space like this::
-
-    0                                     70                                         139
-    |-------------------------------------|-------------------------------------------|
-                            ****************************************************
-                            ^               ^                                  ^
-                            0              28                                  83
-
-After FFT, only the central 116 lines are kept, i.e. there is a reduced field of view in the phase encoding direction. Center and encoding limits for the readout dimension is not given in the XML header. This is to accommodate sequences where the center of the readout may change from readout to readout (alternating directions of readout). There is a field on the individual data headers (see below) to indicate the center of the readout.
-
-An experiment can have multiple encoding spaces and it is possible to indicate on each acquired data readout, which encoding space the data belongs to (see below).
-
-In addition to the defined field in the xml header, it is possible to add an arbitrary number of user defined parameters to accommodate special sequence parameters. Please consult the xml `schema <https://github.com/ismrmrd/ismrmrd/blob/master/schema/ismrmrd.xsd>`_ to see how user parameters are defined. Briefly, the XML header can have a section at the end which looks like::
-
-    <userParameters>
-      <userParameterLong>
-        <name>MyVar1</name><value>1003</value>
-      </userParameterLong>
-      <userParameterLong>
-        <name>MyVar2</name><value>1999</value>
-      </userParameterLong>
-      <userParameterDouble>
-        <name>MyDoubleVar</name><value>87.6676</value>
-      </userParameterDouble>
-    </userParameters>
-
-Fixed Data structures
-**********************
-
-Each raw data acquisition is preceded by the following fixed layout structures in `ismrmrd.h <https://github.com/ismrmrd/ismrmrd/blob/master/include/ismrmrd/ismrmrd.h#L225>`_.
-
-The interpretation of some of these fields may vary from sequence to sequence, i.e. for a Cartesian sequence, `kspace_encode_step_1` would be the phase encoding step, for a spiral sequence where phase encoding direction does not make sense, it would be the spiral interleave number. The `encoding_space_ref` enables the user to tie an acquisition to a specific encoding space (see above) in case there are multiple, e.g. in situations where a calibration scan may be integrated in the acquisition.
-
-The flags field is a bit mask, which in principle can be used freely by the user, but suggested flag values are given in `ismrmrd.h`, it is recommended not to use already designated flag bits for custom purposes. There are a set of bits reserved for prototyping (bits 57-64), please see `ismrmrd.h` for details.
-
-The header contains a `trajectory_dimensions` field. If the value of this field is larger than 0, it means that trajectories are stored with each individual acquisition. For a 2D acquisition, the `trajectory_dimensions` would typically be 2 and the convention (for memory layout) is that the header is followed immediately by the trajectory before the complex data. There is an example of how this memory layout could be implemented with a C++ class in the `ismrmrd.h` file::
-
-    class Acquisition
-    {
-
-    //....
-
-    AcquisitionHeader head_; //Header, see above
-
-    float* traj_;            //Trajectory, elements = head_.trajectory_dimensions*head_.number_of_samples
-                            //   [kx,ky,kx,ky.....]        (for head_.trajectory_dimensions = 2)
-                            //   [kx,ky,kz,kx,ky,kz,.....] (for head_.trajectory_dimensions = 3)
-
-    float* data_;            //Actual data, elements = head_.number_of_samples*head_.active_channels*2
-                            //   [re,im,re,im,.....,re,im,re,im,.....,re,im,re,im,.....]
-                            //    ---channel 1-------channel 2---------channel 3-----
-
-    };
-
-This suggested memory layout is only a suggestion. The HDF5 interface (see below) can be used to read the data into many different data structures. In fact, the user can choose to read only part of the header or not read the data, etc.
-
-As mentioned above, the ISMRMRD format also suggests a way to store reconstructed images (or maybe image data used for calibration). An `ImageHeader` structure is defined in `ismrmrd.h <https://github.com/ismrmrd/ismrmrd/blob/master/include/ismrmrd/ismrmrd.h#L286>_.
-
-In a similar fashion to the raw data acquisition data, the intention is to store a header followed by the image data. Since image data can be in several different format (e.g. float, complex, etc.), the memory layout is less well defined but can be described as::
-
-    template <typename T> class Image {
-
-    ImageHeader head_;     //ImageHeader as defined above
-    T* data_;              //Data, array of size (matrix_size[0]*matrix_size[1]*matrix_size[2]*channels),
-                            //first spatial dimension is fastest changing array index, channels outer most (slowest changing).
-    };
diff --git a/environment.yml b/environment.yml
new file mode 100644
index 0000000..d8d7878
--- /dev/null
+++ b/environment.yml
@@ -0,0 +1,29 @@
+name: ismrmrd
+channels:
+  - ismrmrd
+  - conda-forge
+  - defaults
+dependencies:
+  - boost=1.80.0
+  - cmake=3.21.3
+  - fftw=3.3.9
+  - fmt=8.1.1
+  - gcc_linux-64=11.2.0
+  - gdb=11.1
+  - gxx_linux-64=11.2.0
+  - h5py=3.2.1
+  - hdf5=1.10.6
+  - jupyter=1.0.0
+  - libblas=*=*mkl
+  - libcurl=7.79.1
+  - libxml2=2.9
+  - matplotlib=3.4.3
+  - mkl>=2022.0.0
+  - mkl-include>=2022.0.0
+  - ninja=1.10.2
+  - numpy=1.22.1
+  - pugixml=1.11.4
+  - pyfftw=0.12.0
+  - python=3.9
+  - scipy=1.7.1
+  - xsdata=22.2
diff --git a/include/ismrmrd/dataset.h b/include/ismrmrd/dataset.h
index e19c970..f8a49e0 100644
--- a/include/ismrmrd/dataset.h
+++ b/include/ismrmrd/dataset.h
@@ -31,6 +31,7 @@ typedef struct ISMRMRD_Dataset {
     char *filename;
     char *groupname;
     hid_t fileid;
+    hid_t transfer_properties;
 } ISMRMRD_Dataset;
 
 /**
diff --git a/include/ismrmrd/ismrmrd.h b/include/ismrmrd/ismrmrd.h
index dc13d47..43f2af9 100644
--- a/include/ismrmrd/ismrmrd.h
+++ b/include/ismrmrd/ismrmrd.h
@@ -9,6 +9,8 @@
 /*    Souheil Inati     (souheil.inati@nih.gov)            */
 /*    Joseph Naegele    (joseph.naegele@nih.gov)           */
 
+// clang-format off
+
 /**
  * @file ismrmrd.h
  * @defgroup capi C API
@@ -21,19 +23,12 @@
 
 /* Language and cross platform section for defining types */
 /* integers */
-#ifdef _MSC_VER /* MS compiler */
-#ifndef HAS_INT_TYPE
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#endif
-#else /* non MS C or C++ compiler */
 #include <stdint.h>
 #include <stddef.h>     /* for size_t */
-#endif /* _MSC_VER */
+
+#if __cplusplus > 199711L
+#include <type_traits>
+#endif
 
 /* Complex numbers */
 #ifdef __cplusplus
@@ -194,15 +189,27 @@ enum ISMRMRD_ImageTypes {
  * Image Flags
  */
 enum ISMRMRD_ImageFlags {
-    ISMRMRD_IMAGE_IS_NAVIGATION_DATA =  1,
-    ISMRMRD_IMAGE_USER1              = 57,
-    ISMRMRD_IMAGE_USER2              = 58,
-    ISMRMRD_IMAGE_USER3              = 59,
-    ISMRMRD_IMAGE_USER4              = 60,
-    ISMRMRD_IMAGE_USER5              = 61,
-    ISMRMRD_IMAGE_USER6              = 62,
-    ISMRMRD_IMAGE_USER7              = 63,
-    ISMRMRD_IMAGE_USER8              = 64
+    ISMRMRD_IMAGE_IS_NAVIGATION_DATA  =  1,
+    ISMRMRD_IMAGE_FIRST_IN_AVERAGE    =  5,
+    ISMRMRD_IMAGE_LAST_IN_AVERAGE     =  6,
+    ISMRMRD_IMAGE_FIRST_IN_SLICE      =  7,
+    ISMRMRD_IMAGE_LAST_IN_SLICE       =  8,
+    ISMRMRD_IMAGE_FIRST_IN_CONTRAST   =  9,
+    ISMRMRD_IMAGE_LAST_IN_CONTRAST    = 10,
+    ISMRMRD_IMAGE_FIRST_IN_PHASE      = 11,
+    ISMRMRD_IMAGE_LAST_IN_PHASE       = 12,
+    ISMRMRD_IMAGE_FIRST_IN_REPETITION = 13,
+    ISMRMRD_IMAGE_LAST_IN_REPETITION  = 14,
+    ISMRMRD_IMAGE_FIRST_IN_SET        = 15,
+    ISMRMRD_IMAGE_LAST_IN_SET         = 16,
+    ISMRMRD_IMAGE_USER1               = 57,
+    ISMRMRD_IMAGE_USER2               = 58,
+    ISMRMRD_IMAGE_USER3               = 59,
+    ISMRMRD_IMAGE_USER4               = 60,
+    ISMRMRD_IMAGE_USER5               = 61,
+    ISMRMRD_IMAGE_USER6               = 62,
+    ISMRMRD_IMAGE_USER7               = 63,
+    ISMRMRD_IMAGE_USER8               = 64
 };
 
 /**
@@ -221,6 +228,23 @@ typedef struct ISMRMRD_EncodingCounters {
     uint16_t user[ISMRMRD_USER_INTS]; /**< Free user parameters */
 } ISMRMRD_EncodingCounters;
 
+#if __cplusplus > 199711L // Static assert requires C++11
+// Check standard layout
+static_assert(std::is_standard_layout<ISMRMRD_EncodingCounters>::value, "ISMRMRD_EncodingCounters is not a standard layout type");
+// Check for size and offset of EncodingCounters struct members
+static_assert(sizeof(ISMRMRD_EncodingCounters) == 34, "ISMRMRD_EncodingCounters is not the expected size");
+static_assert(offsetof(ISMRMRD_EncodingCounters, kspace_encode_step_1) == 0, "kspace_encode_step_1 is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, kspace_encode_step_2) == 2, "kspace_encode_step_2 is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, average) == 4, "average is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, slice) == 6, "slice is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, contrast) == 8, "contrast is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, phase) == 10, "phase is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, repetition) == 12, "repetition is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, set) == 14, "set is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, segment) == 16, "segment is not at the expected offset");
+static_assert(offsetof(ISMRMRD_EncodingCounters, user) == 18, "user is not at the expected offset");
+#endif
+
 /**
  * Header for each MR acquisition.
  */
@@ -251,6 +275,37 @@ typedef struct ISMRMRD_AcquisitionHeader {
     float user_float[ISMRMRD_USER_FLOATS];               /**< Free user parameters */
 } ISMRMRD_AcquisitionHeader;
 
+#if __cplusplus > 199711L // Static assert requires C++11
+// Check standard layout
+static_assert(std::is_standard_layout<ISMRMRD_AcquisitionHeader>::value, "ISMRMRD_AcquisitionHeader is not a standard layout type");
+// Check for size and offset of AcquisitionHeader struct members
+static_assert(sizeof(ISMRMRD_AcquisitionHeader) == 340, "ISMRMRD_AcquisitionHeader is not the expected size");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, version) == 0, "version is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, flags) == 2, "flags is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, measurement_uid) == 10, "measurement_uid is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, scan_counter) == 14, "scan_counter is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, acquisition_time_stamp) == 18, "acquisition_time_stamp is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, physiology_time_stamp) == 22, "physiology_time_stamp is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, number_of_samples) == 34, "number_of_samples is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, available_channels) == 36, "available_channels is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, active_channels) == 38, "active_channels is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, channel_mask) == 40, "channel_mask is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, discard_pre) == 168, "discard_pre is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, discard_post) == 170, "discard_post is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, center_sample) == 172, "center_sample is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, encoding_space_ref) == 174, "encoding_space_ref is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, trajectory_dimensions) == 176, "trajectory_dimensions is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, sample_time_us) == 178, "sample_time_us is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, position) == 182, "position is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, read_dir) == 194, "read_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, phase_dir) == 206, "phase_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, slice_dir) == 218, "slice_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, patient_table_position) == 230, "patient_table_position is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, idx) == 242, "idx is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, user_int) == 276, "user_int is not at the expected offset");
+static_assert(offsetof(ISMRMRD_AcquisitionHeader, user_float) == 308, "user_float is not at the expected offset");
+#endif
+
 /**
  * Initialize an Acquisition Header
  * @ingroup capi
@@ -314,6 +369,39 @@ typedef struct ISMRMRD_ImageHeader {
     uint32_t attribute_string_len;                       /**< Length of attributes string */
 } ISMRMRD_ImageHeader;
 
+#if __cplusplus > 199711L // Static assert requires C++11
+// Check standard layout
+static_assert(std::is_standard_layout<ISMRMRD_ImageHeader>::value, "ISMRMRD_ImageHeader is not a standard layout type");
+// Check size and offsets of ISMRMRD_ImageHeader
+static_assert(sizeof(ISMRMRD_ImageHeader) == 198, "ISMRMRD_ImageHeader is not the expected size");
+static_assert(offsetof(ISMRMRD_ImageHeader, version) == 0, "version is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, data_type) == 2, "data_type is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, flags) == 4, "flags is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, measurement_uid) == 12, "measurement_uid is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, matrix_size) == 16, "matrix_size is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, field_of_view) == 22, "field_of_view is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, channels) == 34, "channels is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, position) == 36, "position is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, read_dir) == 48, "read_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, phase_dir) == 60, "phase_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, slice_dir) == 72, "slice_dir is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, patient_table_position) == 84, "patient_table_position is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, average) == 96, "average is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, slice) == 98, "slice is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, contrast) == 100, "contrast is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, phase) == 102, "phase is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, repetition) == 104, "repetition is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, set) == 106, "set is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, acquisition_time_stamp) == 108, "acquisition_time_stamp is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, physiology_time_stamp) == 112, "physiology_time_stamp is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, image_type) == 124, "image_type is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, image_index) == 126, "image_index is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, image_series_index) == 128, "image_series_index is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, user_int) == 130, "user_int is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, user_float) == 162, "user_float is not at the expected offset");
+static_assert(offsetof(ISMRMRD_ImageHeader, attribute_string_len) == 194, "attribute_string_len is not at the expected offset");
+#endif
+
 /** @ingroup capi */
 EXPORTISMRMRD int ismrmrd_init_image_header(ISMRMRD_ImageHeader *hdr);
 
@@ -500,6 +588,11 @@ public:
 
 };
 
+#if __cplusplus > 199711L // Static assert and is_standard_layout requires C++11
+static_assert(sizeof(AcquisitionHeader) == sizeof(ISMRMRD_AcquisitionHeader), "AcquisitionHeader size mismatch");
+static_assert(std::is_standard_layout<AcquisitionHeader>::value, "AcquisitionHeader is not a standard layout type");
+#endif
+
 /// MR Acquisition type
 class EXPORTISMRMRD Acquisition {
     friend class Dataset;
@@ -547,7 +640,7 @@ public:
     // Header, data and trajectory accessors
     const AcquisitionHeader &getHead() const;
     void setHead(const AcquisitionHeader &other);
-    
+
     /**
      * Returns a pointer to the data
      */
@@ -556,45 +649,45 @@ public:
 
     /**
      * Returns a reference to the data
-     */    
+     */
     complex_float_t & data(uint16_t sample, uint16_t channel);
 
     /**
      * Sets the datay.  Must set sizes properly first
-     */    
+     */
     void setData(complex_float_t * data);
 
     /**
      * Returns an iterator to the beginning of the data
      */
     complex_float_t * data_begin() const;
-    
+
     /**
      * Returns an iterator of the end of the data
      */
     complex_float_t * data_end() const;
-    
+
     /**
      * Returns a pointer to the trajectory
      */
     const float * getTrajPtr() const;
     float * getTrajPtr();
-    
+
     /**
      * Returns a reference to the trajectory
      */
     float & traj(uint16_t dimension, uint16_t sample);
-    
+
     /**
      * Sets the trajectory.  Must set sizes properly first
      */
     void setTraj(float * traj);
-    
+
     /**
      * Returns an iterator to the beginning of the trajectories
      */
     float * traj_begin() const;
-    
+
     /**
      * Returns an iterator to the end of the trajectories
      */
@@ -634,6 +727,11 @@ public:
 
 };
 
+#if __cplusplus > 199711L // Static assert and is_standard_layout requires C++11
+static_assert(sizeof(ImageHeader) == sizeof(ISMRMRD_ImageHeader), "ImageHeader size mismatch");
+static_assert(std::is_standard_layout<ImageHeader>::value, "ImageHeader is not a standard layout type");
+#endif
+
 /// MR Image type
 template <typename T> class EXPORTISMRMRD Image {
     friend class Dataset;
@@ -666,7 +764,7 @@ public:
     void setFieldOfViewZ(float f);
 
     // Positions and orientations
-    void setPosition(float x, float y, float z);    
+    void setPosition(float x, float y, float z);
     float getPositionX() const;
     void setPositionX(float x);
     float getPositionY() const;
@@ -681,7 +779,7 @@ public:
     void setReadDirectionY(float y);
     float getReadDirectionZ() const;
     void setReadDirectionZ(float z);
-    
+
     void setPhaseDirection(float x, float y, float z);
     float getPhaseDirectionX() const;
     void setPhaseDirectionX(float x);
@@ -697,7 +795,7 @@ public:
     void setSliceDirectionY(float y);
     float getSliceDirectionZ() const;
     void setSliceDirectionZ(float z);
-    
+
     void setPatientTablePosition(float x, float y, float z);
     float getPatientTablePositionX() const;
     void setPatientTablePositionX(float x);
@@ -706,7 +804,7 @@ public:
     float getPatientTablePositionZ() const;
     void setPatientTablePositionZ(float z);
 
-    
+
     // Attributes
     uint16_t getVersion() const;
     ISMRMRD_DataTypes getDataType() const;
@@ -720,13 +818,13 @@ public:
 
     uint16_t getSlice() const;
     void setSlice(uint16_t slice);
-    
+
     uint16_t getContrast() const;
     void setContrast(uint16_t contrast);
 
     uint16_t getPhase() const;
     void setPhase(uint16_t phase);
-    
+
     uint16_t getRepetition() const;
     void setRepetition(uint16_t repetition);
 
@@ -738,7 +836,7 @@ public:
 
     uint32_t getPhysiologyTimeStamp(unsigned int stamp_id) const;
     void setPhysiologyTimeStamp(unsigned int stamp_id, uint32_t value);
-    
+
     uint16_t getImageType() const;
     void setImageType(uint16_t image_type);
 
@@ -747,7 +845,7 @@ public:
 
     uint16_t getImageSeriesIndex() const;
     void setImageSeriesIndex(uint16_t image_series_index);
-    
+
     // User parameters
     float getUserFloat(unsigned int index) const;
     void setUserFloat(unsigned int index, float value);
@@ -767,14 +865,14 @@ public:
     ImageHeader & getHead();
     const ImageHeader & getHead() const;
     void setHead(const ImageHeader& head);
-    
+
     // Attribute string
     void getAttributeString(std::string &attr) const;
     const char *getAttributeString() const;
     void setAttributeString(const std::string &attr);
     void setAttributeString(const char *attr);
     size_t getAttributeStringLength() const;
-    
+
     // Data
     T * getDataPtr();
     const T * getDataPtr() const;
@@ -817,7 +915,7 @@ public:
     size_t getNumberOfElements() const;
     T * getDataPtr();
     const T * getDataPtr() const;
-    
+
     /** Returns iterator to the beginning of the array **/
     T * begin();
 
diff --git a/include/ismrmrd/serialization.h b/include/ismrmrd/serialization.h
new file mode 100644
index 0000000..f580cc1
--- /dev/null
+++ b/include/ismrmrd/serialization.h
@@ -0,0 +1,146 @@
+#ifndef ISMRMRDSERIALIZATION_H
+#define ISMRMRDSERIALIZATION_H
+
+#include <cstdint>
+#include <exception>
+#include <iostream>
+
+#include "ismrmrd/export.h"
+#include "ismrmrd/ismrmrd.h"
+#include "ismrmrd/waveform.h"
+#include "ismrmrd/xml.h"
+
+/**
+ * @file serialization.h
+ *
+ * @brief Serialization functions for ISMRMRD data structures
+ *
+ * This file contains functions for serializing and deserializing ISMRMRD data
+ * The standalone function that serialize to and deserialize from a stream will
+ * serrialize the data structures without adding "message id" identifiers.
+ * The ProtocolSerializer and ProtocolDeserializer classes are used to to create streams
+ * that include the message id in front of each message.
+ *
+ */
+
+namespace ISMRMRD {
+
+enum ISMRMRD_MESSAGE_ID {
+    ISMRMRD_MESSAGE_UNPEEKED = 0,
+    ISMRMRD_MESSAGE_CONFIG_FILE = 1,
+    ISMRMRD_MESSAGE_CONFIG_TEXT = 2,
+    ISMRMRD_MESSAGE_HEADER = 3,
+    ISMRMRD_MESSAGE_CLOSE = 4,
+    ISMRMRD_MESSAGE_TEXT = 5,
+    ISMRMRD_MESSAGE_ACQUISITION = 1008,
+    ISMRMRD_MESSAGE_IMAGE = 1022,
+    ISMRMRD_MESSAGE_WAVEFORM = 1026
+};
+
+// A wrapper interface, which we can implement, e.g., for std::istream
+class ReadableStreamView {
+public:
+    virtual void read(char *buffer, size_t count) = 0;
+
+    virtual bool eof() = 0;
+};
+
+// A wrapper interface, which we can implement, e.g., for std::ostream
+class WritableStreamView {
+public:
+    virtual void write(const char *buffer, size_t count) = 0;
+
+    virtual bool bad() = 0;
+};
+
+// We define a few wrapper structs here to make the serialization code a bit
+// more readable.
+struct ConfigFile {
+    char config[1024];
+};
+
+struct ConfigText {
+    std::string config_text;
+};
+
+struct TextMessage {
+    std::string message;
+};
+
+// serialize Acquisition to ostream
+EXPORTISMRMRD void serialize(const Acquisition &acq, WritableStreamView &ws);
+
+// serialize Image<T> to ostream
+template <typename T>
+EXPORTISMRMRD void serialize(const Image<T> &img, WritableStreamView &ws);
+
+// serialize Waveform to ostream
+EXPORTISMRMRD void serialize(const Waveform &wfm, WritableStreamView &ws);
+
+// serialize const length (1024) char array to ostream. Used for CONFIG FILE
+EXPORTISMRMRD void serialize(const ConfigFile &cfg, WritableStreamView &ws);
+
+// serialize a string
+EXPORTISMRMRD void serialize(const std::string &str, WritableStreamView &ws);
+
+// deserialize Acquisition from istream
+EXPORTISMRMRD void deserialize(Acquisition &acq, ReadableStreamView &rs);
+
+// deserialize Image<T> from istream
+template <typename T>
+EXPORTISMRMRD void deserialize(Image<T> &img, ReadableStreamView &rs);
+
+// deserialize Waveform from istream
+EXPORTISMRMRD void deserialize(Waveform &wfm, ReadableStreamView &rs);
+
+// deserialize const length (1024) char array from istream. Used for CONFIG FILE
+EXPORTISMRMRD void deserialize(ConfigFile &cfg, ReadableStreamView &rs);
+
+// deserialize a string
+EXPORTISMRMRD void deserialize(std::string &str, ReadableStreamView &rs);
+
+class ProtocolStreamClosed : public std::exception {};
+
+class EXPORTISMRMRD ProtocolSerializer {
+public:
+    ProtocolSerializer(WritableStreamView &ws);
+    void serialize(const ConfigFile &cf);
+    void serialize(const ConfigText &ct);
+    void serialize(const TextMessage &tm);
+    void serialize(const IsmrmrdHeader &hdr);
+    void serialize(const Acquisition &acq);
+    template <typename T>
+    void serialize(const Image<T> &img);
+    void serialize(const Waveform &wfm);
+    void close();
+
+protected:
+    void write_msg_id(uint16_t id);
+    WritableStreamView &_ws;
+};
+
+class EXPORTISMRMRD ProtocolDeserializer {
+public:
+    ProtocolDeserializer(ReadableStreamView &rs);
+    void deserialize(ConfigFile &cf);
+    void deserialize(ConfigText &ct);
+    void deserialize(TextMessage &tm);
+    void deserialize(IsmrmrdHeader &hdr);
+    void deserialize(Acquisition &acq);
+    template <typename T>
+    void deserialize(Image<T> &img);
+    void deserialize(Waveform &wfm);
+
+    // Peek at the next data type in the stream
+    uint16_t peek();
+    int peek_image_data_type();
+
+protected:
+    ReadableStreamView &_rs;
+    uint16_t _peeked;
+    ImageHeader _peeked_image_header;
+};
+
+} // namespace ISMRMRD
+
+#endif // ISMRMRDSERIALIZATION_H
diff --git a/include/ismrmrd/serialization_iostream.h b/include/ismrmrd/serialization_iostream.h
new file mode 100644
index 0000000..2daf3cc
--- /dev/null
+++ b/include/ismrmrd/serialization_iostream.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <iostream>
+#include <ismrmrd/serialization.h>
+
+namespace ISMRMRD {
+
+class IStreamView : public ReadableStreamView {
+public:
+    IStreamView(std::istream &is) : _is(is) {}
+
+    virtual void read(char *buffer, size_t count) {
+        _is.read(buffer, count);
+    }
+
+    virtual bool eof() {
+        return _is.eof();
+    }
+
+protected:
+    std::istream &_is;
+};
+
+class OStreamView : public WritableStreamView {
+public:
+    OStreamView(std::ostream &os) : _os(os) {}
+
+    void write(const char *buffer, size_t count) {
+        _os.write(buffer, count);
+    }
+
+    bool bad() {
+        return _os.bad();
+    }
+
+private:
+    std::ostream &_os;
+};
+
+} // namespace ISMRMRD
diff --git a/include/ismrmrd/waveform.h b/include/ismrmrd/waveform.h
index bfbb8c1..a29f194 100644
--- a/include/ismrmrd/waveform.h
+++ b/include/ismrmrd/waveform.h
@@ -2,18 +2,26 @@
 // Created by dch on 26/02/18.
 //
 
+// clang-format off
+
 #ifndef ISMRMRD_WAVEFORM_H
 #define ISMRMRD_WAVEFORM_H
 
+#if __cplusplus > 199711L
+#include <type_traits>
+#endif
+
 #include "export.h"
 #ifdef __cplusplus
 #include <cstdint>
+#include <cstddef>
 namespace ISMRMRD {
 extern "C" {
 #else
 #include <stdint.h>
 #endif
 
+// Note: This header does not use 2-byte packing like the rest of the ISMRMRD
 typedef struct ISMRMRD_WaveformHeader
 {
     uint16_t version;
@@ -36,6 +44,21 @@ typedef struct ISMRMRD_WaveformHeader
 	/**< Id matching the types specified in the xml header */
 } ISMRMRD_WaveformHeader;
 
+
+#if __cplusplus > 199711L // Static assert requires C++11
+// Check size and offsets of WaveformHeader
+static_assert(sizeof(ISMRMRD_WaveformHeader) == 40, "ISMRMRD_WaveformHeader is not 40 bytes");
+static_assert(offsetof(ISMRMRD_WaveformHeader, version) == 0, "ISMRMRD WaveformHeader version offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, flags) == 8, "ISMRMRD WaveformHeader flags offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, measurement_uid) == 16, "ISMRMRD WaveformHeader measurement_uid offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, scan_counter) == 20, "ISMRMRD WaveformHeader scan_counter offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, time_stamp) == 24, "ISMRMRD WaveformHeader time_stamp offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, number_of_samples) == 28, "ISMRMRD WaveformHeader number_of_samples offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, channels) == 30, "ISMRMRD WaveformHeader channels offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, sample_time_us) == 32, "ISMRMRD WaveformHeader sample_time_us offset is not correct");
+static_assert(offsetof(ISMRMRD_WaveformHeader, waveform_id) == 36, "ISMRMRD WaveformHeader waveform_id offset is not correct");
+#endif
+
 typedef struct ISMRMRD_Waveform
 {
     ISMRMRD_WaveformHeader head;
@@ -62,6 +85,13 @@ EXPORTISMRMRD int ismrmrd_copy_waveform(ISMRMRD_Waveform* dest, const ISMRMRD_Wa
         void clearAllFlags();
 
     };
+
+    #if __cplusplus > 199711L // Static assert and is_standard_layout requires C++11
+    // check size of WaveformHeader
+    static_assert(sizeof(WaveformHeader) == sizeof(ISMRMRD_WaveformHeader), "WaveformHeader is not the same size as ISMRMRD_WaveformHeader");
+    static_assert(std::is_standard_layout<WaveformHeader>::value, "WaveformHeader is not a standard layout type");
+    #endif
+
     struct EXPORTISMRMRD Waveform : public ISMRMRD_Waveform {
         Waveform();
         Waveform(const Waveform &other);
diff --git a/include/ismrmrd/xml.h b/include/ismrmrd/xml.h
index 6c8460c..16b3218 100644
--- a/include/ismrmrd/xml.h
+++ b/include/ismrmrd/xml.h
@@ -7,7 +7,7 @@
 #define ISMRMRDXML_H
 
 #include "ismrmrd/export.h"
-
+#include "ismrmrd.h"
 #include <cstddef>
 #include <new> //For std::badalloc
 #include <stdexcept> //For std::length_error
@@ -16,6 +16,7 @@
 #include <iostream>
 #include <string>
 #include <vector>
+#include <array>
 
 /**
   TODO LIST
@@ -142,7 +143,16 @@ namespace ISMRMRD
       return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(default_value));
   }
 
+    bool operator==(const Optional<T>& other) const {
+      if (this->present_ && other.present_) return this->get() == *other;
+      if ((!this->present_) && (!other.present_)) return true;
+      return false;
+    }
 
+    bool operator==(const T& val) const {
+      if (this->present_) return this->get() == val;
+      return false; 
+    }
 
 
   T& operator()() {
@@ -161,7 +171,7 @@ namespace ISMRMRD
   }; 
 
   struct threeDimensionalFloat
-  {    
+  {
     float x;
     float y;
     float z;
@@ -171,6 +181,7 @@ namespace ISMRMRD
   {
     Optional<std::string> patientName;
     Optional<float> patientWeight_kg;
+    Optional<float> patientHeight_m;
     Optional<std::string> patientID;
     Optional<std::string> patientBirthdate;
     Optional<std::string> patientGender;
@@ -178,24 +189,27 @@ namespace ISMRMRD
 
   struct StudyInformation
   {
+
     Optional<std::string> studyDate;
     Optional<std::string> studyTime;
     Optional<std::string> studyID;
-    Optional<long> accessionNumber;
+    Optional<std::int64_t> accessionNumber;
     Optional<std::string> referringPhysicianName;
     Optional<std::string> studyDescription;
     Optional<std::string> studyInstanceUID;
+    Optional<std::string> bodyPartExamined;
   };
 
   struct MeasurementDependency
   {
-    std::string dependencyType;
+
+      std::string dependencyType;
     std::string measurementID;
   };
 
   struct ReferencedImageSequence
   {
-    std::string referencedSOPInstanceUID;
+      std::string referencedSOPInstanceUID;
   };
   
   struct MeasurementInformation
@@ -205,8 +219,9 @@ namespace ISMRMRD
     Optional<std::string> seriesTime;
     std::string patientPosition;
     Optional<threeDimensionalFloat> relativeTablePosition;
-    Optional<long int> initialSeriesNumber;
+    Optional<std::int64_t> initialSeriesNumber;
     Optional<std::string> protocolName;
+    Optional<std::string> sequenceName;
     Optional<std::string> seriesDescription;
     std::vector<MeasurementDependency> measurementDependency;
     Optional<std::string> seriesInstanceUIDRoot;
@@ -216,7 +231,7 @@ namespace ISMRMRD
 
   struct CoilLabel
   {
-    unsigned short coilNumber;
+    std::uint16_t coilNumber;
     std::string coilName;
   };
   
@@ -226,17 +241,18 @@ namespace ISMRMRD
     Optional<std::string> systemModel;
     Optional<float> systemFieldStrength_T;
     Optional<float> relativeReceiverNoiseBandwidth;
-    Optional<unsigned short> receiverChannels;
+    Optional<std::uint16_t> receiverChannels;
     std::vector<CoilLabel> coilLabel;
     Optional<std::string> institutionName;
     Optional<std::string> stationName;
     Optional<std::string> deviceID;
+    Optional<std::string> deviceSerialNumber;
   };
 
 
   struct ExperimentalConditions
   {
-    long int H1resonanceFrequency_Hz;
+      std::int64_t H1resonanceFrequency_Hz;
   };
 
   struct MatrixSize
@@ -249,7 +265,7 @@ namespace ISMRMRD
 
     }
     
-    MatrixSize(unsigned short x, unsigned short y)
+    MatrixSize(std::uint16_t x, std::uint16_t y)
     : x(x)
     , y(y)
     , z(1)
@@ -257,17 +273,17 @@ namespace ISMRMRD
 
     }
     
-    MatrixSize(unsigned short x, unsigned short y, unsigned short z)
+    MatrixSize(std::uint16_t x, std::uint16_t y, std::uint16_t z)
     : x(x)
     , y(y)
     , z(z)
     {
 
     }
-    
-    unsigned short x;
-    unsigned short y;
-    unsigned short z;
+
+    std::uint16_t x;
+    std::uint16_t y;
+    std::uint16_t z;
   };
 
   struct FieldOfView_mm
@@ -294,17 +310,17 @@ namespace ISMRMRD
 
     }
     
-    Limit(unsigned short minimum, unsigned short maximum, unsigned short center) 
+    Limit(std::uint16_t minimum, std::uint16_t maximum, std::uint16_t center) 
     : minimum(minimum)
     , maximum(maximum)
     , center(center)
     {
 
     }
-    
-    unsigned short minimum;
-    unsigned short maximum;
-    unsigned short center;
+
+    std::uint16_t minimum;
+    std::uint16_t maximum;
+    std::uint16_t center;
   };
 
   struct EncodingLimits
@@ -319,13 +335,14 @@ namespace ISMRMRD
     Optional<Limit> repetition;
     Optional<Limit> set;
     Optional<Limit> segment;
+    std::array<Optional<Limit>,ISMRMRD_USER_INTS> user;
   };
 
 
   struct UserParameterLong
   {
     std::string name;
-    long value;
+    std::int64_t value;
   };
 
   struct UserParameterDouble
@@ -335,9 +352,8 @@ namespace ISMRMRD
   };
   
   struct UserParameterString
-
   {
-    std::string name;
+      std::string name;
     std::string value;
   };
 
@@ -354,21 +370,16 @@ namespace ISMRMRD
     std::string identifier;
     std::vector<UserParameterLong> userParameterLong;
     std::vector<UserParameterDouble> userParameterDouble;
+    std::vector<UserParameterString> userParameterString;
     Optional<std::string> comment; 
   };
 
   struct AccelerationFactor
   {
-    unsigned short kspace_encoding_step_1;
-    unsigned short kspace_encoding_step_2;
+    std::uint16_t kspace_encoding_step_1;
+    std::uint16_t kspace_encoding_step_2;
   };
 
-  struct ParallelImaging
-  {
-    AccelerationFactor accelerationFactor;
-    Optional<std::string> calibrationMode;
-    Optional<std::string> interleavingDimension;
-  };
 
   enum class TrajectoryType {
       CARTESIAN,
@@ -379,6 +390,34 @@ namespace ISMRMRD
       OTHER
   };
 
+
+
+  enum class MultibandCalibrationType {
+    SEPARABLE2D,
+    FULL3D,
+    OTHER
+  };
+
+  struct MultibandSpacing {
+      std::vector<float> dZ;
+  };
+
+  struct Multiband{
+    std::vector<MultibandSpacing> spacing;
+    float deltaKz;
+    std::uint32_t multiband_factor;
+    MultibandCalibrationType calibration;
+    std::uint64_t calibration_encoding;
+  };
+
+  struct ParallelImaging
+  {
+    AccelerationFactor accelerationFactor;
+    Optional<std::string> calibrationMode;
+    Optional<std::string> interleavingDimension;
+    Optional<Multiband> multiband;
+  };
+
   struct Encoding
   {
     EncodingSpace encodedSpace;
@@ -387,7 +426,36 @@ namespace ISMRMRD
     TrajectoryType trajectory;
     Optional<TrajectoryDescription> trajectoryDescription;
     Optional<ParallelImaging> parallelImaging;
-    Optional<long> echoTrainLength;
+    Optional<std::int64_t> echoTrainLength;
+  };
+
+   struct GradientDirection {
+      //In patient coordinate system. Unit vector
+      float rl;
+      float ap;
+      float fh;
+  };
+
+  enum class DiffusionDimension {
+      AVERAGE,
+      CONTRAST,
+      PHASE,
+      REPETITION,
+      SET,
+      SEGMENT,
+      USER_0,
+      USER_1,
+      USER_2,
+      USER_3,
+      USER_4,
+      USER_5,
+      USER_6,
+      USER_7
+  };
+
+  struct Diffusion {
+      float bvalue;
+      GradientDirection gradientDirection;
   };
 
   struct SequenceParameters
@@ -398,6 +466,9 @@ namespace ISMRMRD
     Optional<std::vector<float> > flipAngle_deg;
     Optional<std::string> sequence_type;
     Optional<std::vector<float> > echo_spacing;
+    Optional<DiffusionDimension> diffusionDimension;
+    Optional<std::vector<Diffusion>> diffusion;
+    Optional<std::string> diffusionScheme;
   };
 
   enum class WaveformType {
@@ -418,7 +489,7 @@ namespace ISMRMRD
 
   struct IsmrmrdHeader
   {
-    Optional<long> version;
+    Optional<std::int64_t> version;
     Optional<SubjectInformation> subjectInformation;
     Optional<StudyInformation> studyInformation;
     Optional<MeasurementInformation> measurementInformation;
@@ -431,8 +502,70 @@ namespace ISMRMRD
   };
 
 
-  EXPORTISMRMRD void deserialize(const char* xml, IsmrmrdHeader& h);
-  EXPORTISMRMRD void serialize(const IsmrmrdHeader& h, std::ostream& o);
+    EXPORTISMRMRD void deserialize(const char* xml, IsmrmrdHeader& h);
+    EXPORTISMRMRD void serialize(const IsmrmrdHeader& h, std::ostream& o);
+
+    EXPORTISMRMRD std::ostream& operator<<(std::ostream & os, const IsmrmrdHeader&);
+
+ EXPORTISMRMRD bool operator==(const IsmrmrdHeader&, const IsmrmrdHeader&);
+ EXPORTISMRMRD bool operator!=(const IsmrmrdHeader &lhs, const IsmrmrdHeader &rhs);
+ EXPORTISMRMRD bool operator==(const SubjectInformation &lhs, const SubjectInformation &rhs);
+ EXPORTISMRMRD bool operator!=(const SubjectInformation &lhs, const SubjectInformation &rhs);
+ EXPORTISMRMRD bool operator==(const StudyInformation &lhs, const StudyInformation &rhs);
+ EXPORTISMRMRD bool operator!=(const StudyInformation &lhs, const StudyInformation &rhs);
+ EXPORTISMRMRD bool operator==(const ReferencedImageSequence &lhs, const ReferencedImageSequence &rhs);
+ EXPORTISMRMRD bool operator!=(const ReferencedImageSequence &lhs, const ReferencedImageSequence &rhs);
+ EXPORTISMRMRD bool operator==(const MeasurementInformation &lhs, const MeasurementInformation &rhs);
+ EXPORTISMRMRD bool operator!=(const MeasurementInformation &lhs, const MeasurementInformation &rhs);
+ EXPORTISMRMRD bool operator==(const CoilLabel &lhs, const CoilLabel &rhs);
+ EXPORTISMRMRD bool operator!=(const CoilLabel &lhs, const CoilLabel &rhs);
+ EXPORTISMRMRD bool operator==(const AcquisitionSystemInformation &lhs, const AcquisitionSystemInformation &rhs);
+ EXPORTISMRMRD bool operator!=(const AcquisitionSystemInformation &lhs, const AcquisitionSystemInformation &rhs);
+ EXPORTISMRMRD bool operator==(const ExperimentalConditions &lhs, const ExperimentalConditions &rhs);
+ EXPORTISMRMRD bool operator!=(const ExperimentalConditions &lhs, const ExperimentalConditions &rhs);
+ EXPORTISMRMRD bool operator==(const MatrixSize &lhs, const MatrixSize &rhs);
+ EXPORTISMRMRD bool operator!=(const MatrixSize &lhs, const MatrixSize &rhs);
+ EXPORTISMRMRD bool operator==(const FieldOfView_mm &lhs, const FieldOfView_mm &rhs);
+ EXPORTISMRMRD bool operator!=(const FieldOfView_mm &lhs, const FieldOfView_mm &rhs);
+ EXPORTISMRMRD bool operator==(const EncodingSpace &lhs, const EncodingSpace &rhs);
+ EXPORTISMRMRD bool operator!=(const EncodingSpace &lhs, const EncodingSpace &rhs);
+ EXPORTISMRMRD bool operator==(const Limit &lhs, const Limit &rhs);
+ EXPORTISMRMRD bool operator!=(const Limit &lhs, const Limit &rhs);
+ EXPORTISMRMRD bool operator==(const EncodingLimits &lhs, const EncodingLimits &rhs);
+ EXPORTISMRMRD bool operator!=(const EncodingLimits &lhs, const EncodingLimits &rhs);
+ EXPORTISMRMRD bool operator==(const UserParameterLong &lhs, const UserParameterLong &rhs);
+ EXPORTISMRMRD bool operator!=(const UserParameterLong &lhs, const UserParameterLong &rhs);
+ EXPORTISMRMRD bool operator==(const UserParameterDouble &lhs, const UserParameterDouble &rhs);
+ EXPORTISMRMRD bool operator!=(const UserParameterDouble &lhs, const UserParameterDouble &rhs);
+ EXPORTISMRMRD bool operator==(const UserParameterString &lhs, const UserParameterString &rhs);
+ EXPORTISMRMRD bool operator!=(const UserParameterString &lhs, const UserParameterString &rhs);
+ EXPORTISMRMRD bool operator==(const UserParameters &lhs, const UserParameters &rhs);
+ EXPORTISMRMRD bool operator!=(const UserParameters &lhs, const UserParameters &rhs);
+ EXPORTISMRMRD bool operator==(const TrajectoryDescription &lhs, const TrajectoryDescription &rhs);
+ EXPORTISMRMRD bool operator!=(const TrajectoryDescription &lhs, const TrajectoryDescription &rhs);
+ EXPORTISMRMRD bool operator==(const AccelerationFactor &lhs, const AccelerationFactor &rhs);
+ EXPORTISMRMRD bool operator!=(const AccelerationFactor &lhs, const AccelerationFactor &rhs);
+ EXPORTISMRMRD bool operator==(const ParallelImaging &lhs, const ParallelImaging &rhs);
+ EXPORTISMRMRD bool operator!=(const ParallelImaging &lhs, const ParallelImaging &rhs);
+ EXPORTISMRMRD bool operator==(const Multiband &lhs, const Multiband &rhs);
+ EXPORTISMRMRD bool operator!=(const Multiband &lhs, const Multiband &rhs);
+ EXPORTISMRMRD bool operator==(const MultibandSpacing &lhs, const MultibandSpacing &rhs);
+ EXPORTISMRMRD bool operator!=(const MultibandSpacing &lhs, const MultibandSpacing &rhs);
+ EXPORTISMRMRD bool operator==(const Encoding &lhs, const Encoding &rhs);
+ EXPORTISMRMRD bool operator!=(const Encoding &lhs, const Encoding &rhs);
+ EXPORTISMRMRD bool operator==(const SequenceParameters &lhs, const SequenceParameters &rhs);
+ EXPORTISMRMRD bool operator!=(const SequenceParameters &lhs, const SequenceParameters &rhs);
+ EXPORTISMRMRD bool operator==(const WaveformInformation &lhs, const WaveformInformation &rhs);
+ EXPORTISMRMRD bool operator!=(const WaveformInformation &lhs, const WaveformInformation &rhs);
+ EXPORTISMRMRD bool operator==(const threeDimensionalFloat &lhs, const threeDimensionalFloat &rhs);
+ EXPORTISMRMRD bool operator!=(const threeDimensionalFloat &lhs, const threeDimensionalFloat &rhs);
+ EXPORTISMRMRD bool operator==(const MeasurementDependency &lhs, const MeasurementDependency &rhs);
+ EXPORTISMRMRD bool operator!=(const MeasurementDependency &lhs, const MeasurementDependency &rhs);
+ EXPORTISMRMRD bool operator==(const GradientDirection &lhs, const GradientDirection &rhs);
+ EXPORTISMRMRD bool operator!=(const GradientDirection &lhs, const GradientDirection &rhs);
+ EXPORTISMRMRD bool operator==(const Diffusion &lhs, const Diffusion &rhs);
+ EXPORTISMRMRD bool operator!=(const Diffusion &lhs, const Diffusion &rhs);
+
 
 /** @} */
 
diff --git a/libsrc/dataset.c b/libsrc/dataset.c
index 110b1b8..6087553 100644
--- a/libsrc/dataset.c
+++ b/libsrc/dataset.c
@@ -152,6 +152,11 @@ static int delete_var(const ISMRMRD_Dataset *dset, const char *var) {
     return status;
 }
 
+#define ISMRMRD_READ_BUFFER_SIZE 1024*1024 //HDF5 default buffer size
+
+//Static buffers here means ISMRMRD is not threadsafe. Howevever, neither is HDF5, so we don't loose anything here. 
+static char ismrmrd_conversion_buffer[ISMRMRD_READ_BUFFER_SIZE];
+static char ismrmrd_transfer_buffer[ISMRMRD_READ_BUFFER_SIZE];
 /*********************************************/
 /* Private (Static) Functions for HDF5 Types */
 /*********************************************/
@@ -735,7 +740,7 @@ static int append_element(const ISMRMRD_Dataset * dset, const char * path,
 
     /* Write it */
     /* since this is a 1 element array we can just pass the pointer to the header */
-    h5status = H5Dwrite(dataset, datatype, memspace, filespace, H5P_DEFAULT, elem);
+    h5status = H5Dwrite(dataset, datatype, memspace, filespace, dset->transfer_properties, elem);
     if (h5status < 0) {
         H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL);
         return ISMRMRD_PUSH_ERR(ISMRMRD_HDF5ERROR, "Failed to write dataset");
@@ -830,9 +835,9 @@ static int get_array_properties(const ISMRMRD_Dataset *dset, const char *path,
 
 }
 
+
 int read_element(const ISMRMRD_Dataset *dset, const char *path, void *elem,
-        const hid_t datatype, const uint32_t index)
-{
+                 const hid_t datatype, const uint32_t index) {
     hid_t dataset, filespace, memspace;
     hsize_t *hdfdims = NULL, *offset = NULL, *count = NULL;
     herr_t h5status = 0;
@@ -840,7 +845,6 @@ int read_element(const ISMRMRD_Dataset *dset, const char *path, void *elem,
     int n;
     int ret_code = ISMRMRD_NOERROR;
 
-
     if (NULL == dset) {
         return ISMRMRD_PUSH_ERR(ISMRMRD_RUNTIMEERROR, "Dataset pointer should not be NULL.");
     }
@@ -871,17 +875,23 @@ int read_element(const ISMRMRD_Dataset *dset, const char *path, void *elem,
 
     offset[0] = index;
     count[0] = 1;
-    for (n=1; n< rank; n++) {
+    for (n = 1; n < rank; n++) {
         offset[n] = 0;
         count[n] = hdfdims[n];
     }
 
-    h5status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, count, NULL);
+    if (rank > 1) {
+        h5status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, count, NULL);
+    } else {
+        hsize_t coord = index;
+        h5status = H5Sselect_elements(filespace, H5S_SELECT_SET, 1, &coord);
+    }
+
 
     /* create space for one */
     memspace = H5Screate_simple(rank, count, NULL);
 
-    h5status = H5Dread(dataset, datatype, memspace, filespace, H5P_DEFAULT, elem);
+    h5status = H5Dread(dataset, datatype, memspace, filespace, dset->transfer_properties, elem);
     if (h5status < 0) {
         H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL);
         ret_code = ISMRMRD_PUSH_ERR(ISMRMRD_HDF5ERROR, "Failed to read from dataset.");
@@ -940,6 +950,15 @@ int ismrmrd_init_dataset(ISMRMRD_Dataset *dset, const char *filename,
     strcpy(dset->groupname, groupname);
 
     dset->fileid = 0;
+
+  
+    dset->transfer_properties = H5Pcreate(H5P_DATASET_XFER);
+
+    H5Pset_buffer(dset->transfer_properties,
+                  ISMRMRD_READ_BUFFER_SIZE,
+                  ismrmrd_conversion_buffer,
+                  ismrmrd_transfer_buffer); 
+                
     return ISMRMRD_NOERROR;
 }
 
@@ -954,7 +973,10 @@ int ismrmrd_open_dataset(ISMRMRD_Dataset *dset, const bool create_if_needed) {
 
     /* Try opening the file */
     /* Note the is_hdf5 function doesn't work well when trying to open multiple files */
-    fileid = H5Fopen(dset->filename, H5F_ACC_RDWR, H5P_DEFAULT);
+    hid_t file_access = H5Pcreate(H5P_FILE_ACCESS);
+    H5Pset_fapl_stdio(file_access);
+
+    fileid = H5Fopen(dset->filename, H5F_ACC_RDWR, file_access);
 
     if (fileid > 0) {
         dset->fileid = fileid;
@@ -1009,6 +1031,7 @@ int ismrmrd_close_dataset(ISMRMRD_Dataset *dset) {
         dset->groupname = NULL;
     }
 
+
     /* Check for a valid fileid before trying to close the file */
     if (dset->fileid > 0) {
         h5status = H5Fclose (dset->fileid);
@@ -1212,6 +1235,8 @@ int ismrmrd_read_acquisition(const ISMRMRD_Dataset *dset, uint32_t index, ISMRMR
         return ISMRMRD_PUSH_ERR(ISMRMRD_RUNTIMEERROR, "Acquisition pointer should not be NULL.");
     }
 
+    ismrmrd_cleanup_acquisition(acq);
+
     /* The path to the acquisition data */
     path = make_path(dset, "data");
 
@@ -1220,15 +1245,11 @@ int ismrmrd_read_acquisition(const ISMRMRD_Dataset *dset, uint32_t index, ISMRMR
 
     status = read_element(dset, path, &hdf5acq, datatype, index);
     memcpy(&acq->head, &hdf5acq.head, sizeof(ISMRMRD_AcquisitionHeader));
-    ismrmrd_make_consistent_acquisition(acq);
-    memcpy(acq->traj, hdf5acq.traj.p, ismrmrd_size_of_acquisition_traj(acq));
-    memcpy(acq->data, hdf5acq.data.p, ismrmrd_size_of_acquisition_data(acq));
+    acq->traj = hdf5acq.traj.p;
+    acq->data = hdf5acq.data.p;
 
     /* clean up */
     free(path);
-    free(hdf5acq.traj.p);
-    free(hdf5acq.data.p);
-
     status = H5Tclose(datatype);
     if (status < 0) {
         H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL);
diff --git a/libsrc/ismrmrd.c b/libsrc/ismrmrd.c
index a00677a..4294840 100644
--- a/libsrc/ismrmrd.c
+++ b/libsrc/ismrmrd.c
@@ -67,9 +67,14 @@ int ismrmrd_cleanup_acquisition(ISMRMRD_Acquisition *acq) {
         return ISMRMRD_PUSH_ERR(ISMRMRD_RUNTIMEERROR, "Pointer should not be NULL.");
     }
     
-    free(acq->data);
+    if (acq->data != NULL)
+        free(acq->data);
+
     acq->data = NULL;
-    free(acq->traj);
+
+    if (acq->traj != NULL)
+        free(acq->traj);
+
     acq->traj = NULL;
     return ISMRMRD_NOERROR;
 }
diff --git a/libsrc/ismrmrd.cpp b/libsrc/ismrmrd.cpp
index d35bca6..0a586de 100644
--- a/libsrc/ismrmrd.cpp
+++ b/libsrc/ismrmrd.cpp
@@ -1216,24 +1216,24 @@ template <> EXPORTISMRMRD inline ISMRMRD_DataTypes get_data_type<complex_double_
 }
 
 // Images
-template EXPORTISMRMRD class Image<uint16_t>;
-template EXPORTISMRMRD class Image<int16_t>;
-template EXPORTISMRMRD class Image<uint32_t>;
-template EXPORTISMRMRD class Image<int32_t>;
-template EXPORTISMRMRD class Image<float>;
-template EXPORTISMRMRD class Image<double>;
-template EXPORTISMRMRD class Image<complex_float_t>;
-template EXPORTISMRMRD class Image<complex_double_t>;
+template class EXPORTISMRMRD Image<uint16_t>;
+template class EXPORTISMRMRD Image<int16_t>;
+template class EXPORTISMRMRD Image<uint32_t>;
+template class EXPORTISMRMRD Image<int32_t>;
+template class EXPORTISMRMRD Image<float>;
+template class EXPORTISMRMRD Image<double>;
+template class EXPORTISMRMRD Image<complex_float_t>;
+template class EXPORTISMRMRD Image<complex_double_t>;
 
 // NDArrays
-template EXPORTISMRMRD class NDArray<uint16_t>;
-template EXPORTISMRMRD class NDArray<int16_t>;
-template EXPORTISMRMRD class NDArray<uint32_t>;
-template EXPORTISMRMRD class NDArray<int32_t>;
-template EXPORTISMRMRD class NDArray<float>;
-template EXPORTISMRMRD class NDArray<double>;
-template EXPORTISMRMRD class NDArray<complex_float_t>;
-template EXPORTISMRMRD class NDArray<complex_double_t>;
+template class EXPORTISMRMRD NDArray<uint16_t>;
+template class EXPORTISMRMRD NDArray<int16_t>;
+template class EXPORTISMRMRD NDArray<uint32_t>;
+template class EXPORTISMRMRD NDArray<int32_t>;
+template class EXPORTISMRMRD NDArray<float>;
+template class EXPORTISMRMRD NDArray<double>;
+template class EXPORTISMRMRD NDArray<complex_float_t>;
+template class EXPORTISMRMRD NDArray<complex_double_t>;
 
 
 // Helper function for generating exception message from ISMRMRD error stack
diff --git a/libsrc/meta.cpp b/libsrc/meta.cpp
index 055525b..72ef36b 100644
--- a/libsrc/meta.cpp
+++ b/libsrc/meta.cpp
@@ -5,7 +5,7 @@
 namespace ISMRMRD {
     void deserialize(const char *xml, MetaContainer &h) {
         pugi::xml_document doc;
-        pugi::xml_parse_result result = doc.load(xml);
+        pugi::xml_parse_result result = doc.load_string(xml);
 
         if (!result) {
             throw std::runtime_error("Unable to load ISMRMRD Meta XML document");
diff --git a/libsrc/pugiconfig.hpp b/libsrc/pugiconfig.hpp
deleted file mode 100644
index 56f1d22..0000000
--- a/libsrc/pugiconfig.hpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * pugixml parser - version 1.4
- * --------------------------------------------------------
- * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- * Report bugs and download new versions at http://pugixml.org/
- *
- * This library is distributed under the MIT License. See notice at the end
- * of this file.
- *
- * This work is based on the pugxml parser, which is:
- * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
- */
-
-#ifndef HEADER_PUGICONFIG_HPP
-#define HEADER_PUGICONFIG_HPP
-
-// Uncomment this to enable wchar_t mode
-// #define PUGIXML_WCHAR_MODE
-
-// Uncomment this to disable XPath
-// #define PUGIXML_NO_XPATH
-
-// Uncomment this to disable STL
-// #define PUGIXML_NO_STL
-
-// Uncomment this to disable exceptions
-// #define PUGIXML_NO_EXCEPTIONS
-
-// Set this to control attributes for public classes/functions, i.e.:
-// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
-// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
-// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
-// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
-
-// 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
-// #include "pugixml.cpp"
-
-// Uncomment this to enable long long support
-// #define PUGIXML_HAS_LONG_LONG
-
-#endif
-
-/**
- * Copyright (c) 2006-2014 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/libsrc/pugixml.cpp b/libsrc/pugixml.cpp
deleted file mode 100644
index 754f92f..0000000
--- a/libsrc/pugixml.cpp
+++ /dev/null
@@ -1,10639 +0,0 @@
-/**
- * pugixml parser - version 1.4
- * --------------------------------------------------------
- * Copyright (C) 2006-2014, 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-#ifdef PUGIXML_WCHAR_MODE
-#	include <wchar.h>
-#endif
-
-#ifndef PUGIXML_NO_XPATH
-#	include <math.h>
-#	include <float.h>
-#	ifdef PUGIXML_NO_EXCEPTIONS
-#		include <setjmp.h>
-#	endif
-#endif
-
-#ifndef PUGIXML_NO_STL
-#	include <istream>
-#	include <ostream>
-#	include <string>
-#endif
-
-// For placement new
-#include <new>
-
-#ifdef _MSC_VER
-#	pragma warning(push)
-#	pragma warning(disable: 4127) // conditional expression is constant
-#	pragma warning(disable: 4324) // structure was padded due to __declspec(align())
-#	pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
-#	pragma warning(disable: 4702) // unreachable code
-#	pragma warning(disable: 4996) // this function or variable may be unsafe
-#	pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
-#endif
-
-#ifdef __INTEL_COMPILER
-#	pragma warning(disable: 177) // function was declared but never referenced 
-#	pragma warning(disable: 279) // controlling expression is constant
-#	pragma warning(disable: 1478 1786) // function was declared "deprecated"
-#	pragma warning(disable: 1684) // conversion from pointer to same-sized integral type
-#endif
-
-#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
-#	pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away
-#endif
-
-#ifdef __BORLANDC__
-#	pragma option push
-#	pragma warn -8008 // condition is always false
-#	pragma warn -8066 // unreachable code
-#endif
-
-#ifdef __SNC__
-// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug
-#	pragma diag_suppress=178 // function was declared but never referenced
-#	pragma diag_suppress=237 // controlling expression is constant
-#endif
-
-// Inlining controls
-#if defined(_MSC_VER) && _MSC_VER >= 1300
-#	define PUGI__NO_INLINE __declspec(noinline)
-#elif defined(__GNUC__)
-#	define PUGI__NO_INLINE __attribute__((noinline))
-#else
-#	define PUGI__NO_INLINE 
-#endif
-
-// Simple static assertion
-#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
-
-// Digital Mars C++ bug workaround for passing char loaded from memory via stack
-#ifdef __DMC__
-#	define PUGI__DMC_VOLATILE volatile
-#else
-#	define PUGI__DMC_VOLATILE
-#endif
-
-// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
-#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
-using std::memcpy;
-using std::memmove;
-#endif
-
-// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
-#if defined(_MSC_VER) && !defined(__S3E__)
-#	define PUGI__MSVC_CRT_VERSION _MSC_VER
-#endif
-
-#ifdef PUGIXML_HEADER_ONLY
-#	define PUGI__NS_BEGIN namespace pugi { namespace impl {
-#	define PUGI__NS_END } }
-#	define PUGI__FN inline
-#	define PUGI__FN_NO_INLINE inline
-#else
-#	if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
-#		define PUGI__NS_BEGIN namespace pugi { namespace impl {
-#		define PUGI__NS_END } }
-#	else
-#		define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
-#		define PUGI__NS_END } } }
-#	endif
-#	define PUGI__FN
-#	define PUGI__FN_NO_INLINE PUGI__NO_INLINE
-#endif
-
-// uintptr_t
-#if !defined(_MSC_VER) || _MSC_VER >= 1600
-#	include <stdint.h>
-#else
-#	ifndef _UINTPTR_T_DEFINED
-// No native uintptr_t in MSVC6 and in some WinCE versions
-typedef size_t uintptr_t;
-#define _UINTPTR_T_DEFINED
-#	endif
-PUGI__NS_BEGIN
-	typedef unsigned __int8 uint8_t;
-	typedef unsigned __int16 uint16_t;
-	typedef unsigned __int32 uint32_t;
-PUGI__NS_END
-#endif
-
-// Memory allocation
-PUGI__NS_BEGIN
-	PUGI__FN void* default_allocate(size_t size)
-	{
-		return malloc(size);
-	}
-
-	PUGI__FN void default_deallocate(void* ptr)
-	{
-		free(ptr);
-	}
-
-	template <typename T>
-	struct xml_memory_management_function_storage
-	{
-		static allocation_function allocate;
-		static deallocation_function deallocate;
-	};
-
-	template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
-	template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
-
-	typedef xml_memory_management_function_storage<int> 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<size_t>(end - s);
-	#endif
-	}
-
-#ifdef PUGIXML_WCHAR_MODE
-	// Convert string to wide string, assuming all symbols are ASCII
-	PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
-	{
-		for (const char* i = source; *i; ++i) *dest++ = *i;
-		*dest = 0;
-	}
-#endif
-PUGI__NS_END
-
-#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
-// auto_ptr-like buffer holder for exception recovery
-PUGI__NS_BEGIN
-	struct buffer_holder
-	{
-		void* data;
-		void (*deleter)(void*);
-
-		buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
-		{
-		}
-
-		~buffer_holder()
-		{
-			if (data) deleter(data);
-		}
-
-		void* release()
-		{
-			void* result = data;
-			data = 0;
-			return result;
-		}
-	};
-PUGI__NS_END
-#endif
-
-PUGI__NS_BEGIN
-	static const size_t xml_memory_page_size =
-	#ifdef PUGIXML_MEMORY_PAGE_SIZE
-		PUGIXML_MEMORY_PAGE_SIZE
-	#else
-		32768
-	#endif
-		;
-
-	static const uintptr_t xml_memory_page_alignment = 32;
-	static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
-	static const uintptr_t xml_memory_page_name_allocated_mask = 16;
-	static const uintptr_t xml_memory_page_value_allocated_mask = 8;
-	static const uintptr_t xml_memory_page_type_mask = 7;
-
-	struct xml_allocator;
-
-	struct xml_memory_page
-	{
-		static xml_memory_page* construct(void* memory)
-		{
-			if (!memory) return 0; //$ redundant, left for performance
-
-			xml_memory_page* result = static_cast<xml_memory_page*>(memory);
-
-			result->allocator = 0;
-			result->memory = 0;
-			result->prev = 0;
-			result->next = 0;
-			result->busy_size = 0;
-			result->freed_size = 0;
-
-			return result;
-		}
-
-		xml_allocator* allocator;
-
-		void* memory;
-
-		xml_memory_page* prev;
-		xml_memory_page* next;
-
-		size_t busy_size;
-		size_t freed_size;
-
-		char data[1];
-	};
-
-	struct xml_memory_string_header
-	{
-		uint16_t page_offset; // offset from page->data
-		uint16_t full_size; // 0 if string occupies whole page
-	};
-
-	struct xml_allocator
-	{
-		xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
-		{
-		}
-
-		xml_memory_page* allocate_page(size_t data_size)
-		{
-			size_t size = offsetof(xml_memory_page, data) + data_size;
-
-			// allocate block with some alignment, leaving memory for worst-case padding
-			void* memory = xml_memory::allocate(size + xml_memory_page_alignment);
-			if (!memory) return 0;
-
-			// align upwards to page boundary
-			void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
-
-			// prepare page structure
-			xml_memory_page* page = xml_memory_page::construct(page_memory);
-			assert(page);
-
-			page->memory = memory;
-			page->allocator = _root->allocator;
-
-			return page;
-		}
-
-		static void deallocate_page(xml_memory_page* page)
-		{
-			xml_memory::deallocate(page->memory);
-		}
-
-		void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
-
-		void* allocate_memory(size_t size, xml_memory_page*& out_page)
-		{
-			if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
-
-			void* buf = _root->data + _busy_size;
-
-			_busy_size += size;
-
-			out_page = _root;
-
-			return buf;
-		}
-
-		void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
-		{
-			if (page == _root) page->busy_size = _busy_size;
-
-			assert(ptr >= page->data && ptr < page->data + page->busy_size);
-			(void)!ptr;
-
-			page->freed_size += size;
-			assert(page->freed_size <= page->busy_size);
-
-			if (page->freed_size == page->busy_size)
-			{
-				if (page->next == 0)
-				{
-					assert(_root == page);
-
-					// top page freed, just reset sizes
-					page->busy_size = page->freed_size = 0;
-					_busy_size = 0;
-				}
-				else
-				{
-					assert(_root != page);
-					assert(page->prev);
-
-					// remove from the list
-					page->prev->next = page->next;
-					page->next->prev = page->prev;
-
-					// deallocate
-					deallocate_page(page);
-				}
-			}
-		}
-
-		char_t* allocate_string(size_t length)
-		{
-			// allocate memory for string and header block
-			size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
-			
-			// round size up to pointer alignment boundary
-			size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
-
-			xml_memory_page* page;
-			xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
-
-			if (!header) return 0;
-
-			// setup header
-			ptrdiff_t page_offset = reinterpret_cast<char*>(header) - page->data;
-
-			assert(page_offset >= 0 && page_offset < (1 << 16));
-			header->page_offset = static_cast<uint16_t>(page_offset);
-
-			// full_size == 0 for large strings that occupy the whole page
-			assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
-			header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);
-
-			// round-trip through void* to avoid 'cast increases required alignment of target type' warning
-			// header is guaranteed a pointer-sized alignment, which should be enough for char_t
-			return static_cast<char_t*>(static_cast<void*>(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<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
-
-			// deallocate
-			size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;
-			xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
-
-			// if full_size == 0 then this string occupies the whole page
-			size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
-
-			deallocate_memory(header, full_size, page);
-		}
-
-		xml_memory_page* _root;
-		size_t _busy_size;
-	};
-
-	PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
-	{
-		const size_t large_allocation_threshold = xml_memory_page_size / 4;
-
-		xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
-		out_page = page;
-
-		if (!page) return 0;
-
-		if (size <= large_allocation_threshold)
-		{
-			_root->busy_size = _busy_size;
-
-			// insert page at the end of linked list
-			page->prev = _root;
-			_root->next = page;
-			_root = page;
-
-			_busy_size = size;
-		}
-		else
-		{
-			// insert page before the end of linked list, so that it is deleted as soon as possible
-			// the last page is not deleted even if it's empty (see deallocate_memory)
-			assert(_root->prev);
-
-			page->prev = _root->prev;
-			page->next = _root;
-
-			_root->prev->next = page;
-			_root->prev = page;
-		}
-
-		// allocate inside page
-		page->busy_size = size;
-
-		return page->data;
-	}
-PUGI__NS_END
-
-namespace pugi
-{
-	/// A 'name=value' XML attribute structure.
-	struct xml_attribute_struct
-	{
-		/// Default ctor
-		xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
-		{
-		}
-
-		uintptr_t header;
-
-		char_t* name;	///< Pointer to attribute name.
-		char_t*	value;	///< Pointer to attribute value.
-
-		xml_attribute_struct* prev_attribute_c;	///< Previous attribute (cyclic list)
-		xml_attribute_struct* next_attribute;	///< Next attribute
-	};
-
-	/// An XML document tree node.
-	struct xml_node_struct
-	{
-		/// Default ctor
-		/// \param type - node type
-		xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
-		{
-		}
-
-		uintptr_t header;
-
-		xml_node_struct*		parent;					///< Pointer to parent
-
-		char_t*					name;					///< Pointer to element name.
-		char_t*					value;					///< Pointer to any associated string data.
-
-		xml_node_struct*		first_child;			///< First child
-		
-		xml_node_struct*		prev_sibling_c;			///< Left brother (cyclic list)
-		xml_node_struct*		next_sibling;			///< Right brother
-		
-		xml_attribute_struct*	first_attribute;		///< First attribute
-	};
-}
-
-PUGI__NS_BEGIN
-	struct xml_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;
-	};
-
-	inline xml_allocator& get_allocator(const xml_node_struct* node)
-	{
-		assert(node);
-
-		return *reinterpret_cast<xml_memory_page*>(node->header & xml_memory_page_pointer_mask)->allocator;
-	}
-PUGI__NS_END
-
-// Low-level DOM operations
-PUGI__NS_BEGIN
-	inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
-	{
-		xml_memory_page* page;
-		void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
-
-		return new (memory) xml_attribute_struct(page);
-	}
-
-	inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)
-	{
-		xml_memory_page* page;
-		void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
-
-		return new (memory) xml_node_struct(page, type);
-	}
-
-	inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
-	{
-		uintptr_t header = a->header;
-
-		if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
-		if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
-
-		alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
-	}
-
-	inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
-	{
-		uintptr_t header = n->header;
-
-		if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
-		if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
-
-		for (xml_attribute_struct* attr = n->first_attribute; attr; )
-		{
-			xml_attribute_struct* next = attr->next_attribute;
-
-			destroy_attribute(attr, alloc);
-
-			attr = next;
-		}
-
-		for (xml_node_struct* child = n->first_child; child; )
-		{
-			xml_node_struct* next = child->next_sibling;
-
-			destroy_node(child, alloc);
-
-			child = next;
-		}
-
-		alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
-	}
-
-	PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
-	{
-		xml_node_struct* child = allocate_node(alloc, type);
-		if (!child) return 0;
-
-		child->parent = node;
-
-		xml_node_struct* first_child = node->first_child;
-			
-		if (first_child)
-		{
-			xml_node_struct* last_child = first_child->prev_sibling_c;
-
-			last_child->next_sibling = child;
-			child->prev_sibling_c = last_child;
-			first_child->prev_sibling_c = child;
-		}
-		else
-		{
-			node->first_child = child;
-			child->prev_sibling_c = child;
-		}
-			
-		return child;
-	}
-
-	PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)
-	{
-		xml_attribute_struct* a = allocate_attribute(alloc);
-		if (!a) return 0;
-
-		xml_attribute_struct* first_attribute = node->first_attribute;
-
-		if (first_attribute)
-		{
-			xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;
-
-			last_attribute->next_attribute = a;
-			a->prev_attribute_c = last_attribute;
-			first_attribute->prev_attribute_c = a;
-		}
-		else
-		{
-			node->first_attribute = a;
-			a->prev_attribute_c = a;
-		}
-			
-		return a;
-	}
-PUGI__NS_END
-
-// Helper classes for code generation
-PUGI__NS_BEGIN
-	struct opt_false
-	{
-		enum { value = 0 };
-	};
-
-	struct opt_true
-	{
-		enum { value = 1 };
-	};
-PUGI__NS_END
-
-// Unicode utilities
-PUGI__NS_BEGIN
-	inline uint16_t endian_swap(uint16_t value)
-	{
-		return static_cast<uint16_t>(((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<uint8_t>(ch);
-				return result + 1;
-			}
-			// U+0080..U+07FF
-			else if (ch < 0x800)
-			{
-				result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));
-				result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
-				return result + 2;
-			}
-			// U+0800..U+FFFF
-			else
-			{
-				result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));
-				result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
-				result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
-				return result + 3;
-			}
-		}
-
-		static value_type high(value_type result, uint32_t ch)
-		{
-			// U+10000..U+10FFFF
-			result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));
-			result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));
-			result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
-			result[3] = static_cast<uint8_t>(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<uint16_t>(ch);
-
-			return result + 1;
-		}
-
-		static value_type high(value_type result, uint32_t ch)
-		{
-			uint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;
-			uint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;
-
-			result[0] = static_cast<uint16_t>(0xD800 + msh);
-			result[1] = static_cast<uint16_t>(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<uint8_t>(ch > 255 ? '?' : ch);
-
-			return result + 1;
-		}
-
-		static value_type high(value_type result, uint32_t ch)
-		{
-			(void)ch;
-
-			*result = '?';
-
-			return result + 1;
-		}
-	};
-
-	template <size_t size> struct wchar_selector;
-
-	template <> struct wchar_selector<2>
-	{
-		typedef uint16_t type;
-		typedef utf16_counter counter;
-		typedef utf16_writer writer;
-	};
-
-	template <> struct wchar_selector<4>
-	{
-		typedef uint32_t type;
-		typedef utf32_counter counter;
-		typedef utf32_writer writer;
-	};
-
-	typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
-	typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
-
-	template <typename Traits, typename opt_swap = opt_false> struct utf_decoder
-	{
-		static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)
-		{
-			const uint8_t utf8_byte_mask = 0x3f;
-
-			while (size)
-			{
-				uint8_t lead = *data;
-
-				// 0xxxxxxx -> U+0000..U+007F
-				if (lead < 0x80)
-				{
-					result = Traits::low(result, lead);
-					data += 1;
-					size -= 1;
-
-					// process aligned single-byte (ascii) blocks
-					if ((reinterpret_cast<uintptr_t>(data) & 3) == 0)
-					{
-						// round-trip through void* to silence 'cast increases required alignment of target type' warnings
-						while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(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<unsigned int>(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<unsigned int>(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<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)
-				{
-					result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));
-					data += 4;
-					size -= 4;
-				}
-				// 10xxxxxx or 11111xxx -> invalid
-				else
-				{
-					data += 1;
-					size -= 1;
-				}
-			}
-
-			return result;
-		}
-
-		static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)
-		{
-			const uint16_t* end = data + size;
-
-			while (data < end)
-			{
-				unsigned int lead = opt_swap::value ? endian_swap(*data) : *data;
-
-				// U+0000..U+D7FF
-				if (lead < 0xD800)
-				{
-					result = Traits::low(result, lead);
-					data += 1;
-				}
-				// U+E000..U+FFFF
-				else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)
-				{
-					result = Traits::low(result, lead);
-					data += 1;
-				}
-				// surrogate pair lead
-				else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && data + 1 < end)
-				{
-					uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
-
-					if (static_cast<unsigned int>(next - 0xDC00) < 0x400)
-					{
-						result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
-						data += 2;
-					}
-					else
-					{
-						data += 1;
-					}
-				}
-				else
-				{
-					data += 1;
-				}
-			}
-
-			return result;
-		}
-
-		static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)
-		{
-			const uint32_t* end = data + size;
-
-			while (data < end)
-			{
-				uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
-
-				// U+0000..U+FFFF
-				if (lead < 0x10000)
-				{
-					result = Traits::low(result, lead);
-					data += 1;
-				}
-				// U+10000..U+10FFFF
-				else
-				{
-					result = Traits::high(result, lead);
-					data += 1;
-				}
-			}
-
-			return result;
-		}
-
-		static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)
-		{
-			for (size_t i = 0; i < size; ++i)
-			{
-				result = Traits::low(result, data[i]);
-			}
-
-			return result;
-		}
-
-		static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_utf16_block(data, size, result);
-		}
-
-		static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_utf32_block(data, size, result);
-		}
-
-		static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)
-		{
-			return decode_wchar_block_impl(reinterpret_cast<const wchar_selector<sizeof(wchar_t)>::type*>(data), size, result);
-		}
-	};
-
-	template <typename T> PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)
-	{
-		for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);
-	}
-
-#ifdef PUGIXML_WCHAR_MODE
-	PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
-	{
-		for (size_t i = 0; i < length; ++i) result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::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<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
-#else
-	#define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(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<unsigned char*>(&ui) == 1;
-	}
-
-	PUGI__FN xml_encoding get_wchar_encoding()
-	{
-		PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
-
-		if (sizeof(wchar_t) == 2)
-			return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-		else 
-			return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-	}
-
-	PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
-	{
-		// look for BOM in first few bytes
-		if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;
-		if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;
-		if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;
-		if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;
-		if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;
-
-		// look for <, <? or <?xm in various encodings
-		if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;
-		if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;
-		if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;
-		if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;
-		if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8;
-
-		// look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)
-		if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;
-		if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;
-
-		// no known BOM detected, assume utf8
-		return encoding_utf8;
-	}
-
-	PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
-	{
-		// replace wchar encoding with utf implementation
-		if (encoding == encoding_wchar) return get_wchar_encoding();
-
-		// 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;
-
-		// skip encoding autodetection if input buffer is too small
-		if (size < 4) return encoding_utf8;
-
-		// try to guess encoding (based on XML specification, Appendix F.1)
-		const uint8_t* data = static_cast<const uint8_t*>(contents);
-
-		PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];
-
-		return guess_buffer_encoding(d0, d1, d2, d3);
-	}
-
-	PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
-	{
-		size_t length = size / sizeof(char_t);
-
-		if (is_mutable)
-		{
-			out_buffer = static_cast<char_t*>(const_cast<void*>(contents));
-			out_length = length;
-		}
-		else
-		{
-			char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-			if (!buffer) return false;
-
-			memcpy(buffer, contents, length * sizeof(char_t));
-			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<const char_t*>(contents);
-		size_t length = size / sizeof(char_t);
-
-		if (is_mutable)
-		{
-			char_t* buffer = const_cast<char_t*>(data);
-
-			convert_wchar_endian_swap(buffer, data, length);
-
-			out_buffer = buffer;
-			out_length = length;
-		}
-		else
-		{
-			char_t* buffer = static_cast<char_t*>(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;
-	}
-
-	PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
-	{
-		const uint8_t* data = static_cast<const uint8_t*>(contents);
-		size_t data_length = size;
-
-		// first pass: get length in wchar_t units
-		size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, data_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-		if (!buffer) return false;
-
-		// second pass: convert utf8 input to wchar_t
-		wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
-		wchar_writer::value_type oend = utf_decoder<wchar_writer>::decode_utf8_block(data, data_length, obegin);
-
-		assert(oend == obegin + length);
-		*oend = 0;
-
-		out_buffer = buffer;
-		out_length = length + 1;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint16_t* data = static_cast<const uint16_t*>(contents);
-		size_t data_length = size / sizeof(uint16_t);
-
-		// first pass: get length in wchar_t units
-		size_t length = utf_decoder<wchar_counter, opt_swap>::decode_utf16_block(data, data_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(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<wchar_writer::value_type>(buffer);
-		wchar_writer::value_type oend = utf_decoder<wchar_writer, opt_swap>::decode_utf16_block(data, data_length, obegin);
-
-		assert(oend == obegin + length);
-		*oend = 0;
-
-		out_buffer = buffer;
-		out_length = length + 1;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint32_t* data = static_cast<const uint32_t*>(contents);
-		size_t data_length = size / sizeof(uint32_t);
-
-		// first pass: get length in wchar_t units
-		size_t length = utf_decoder<wchar_counter, opt_swap>::decode_utf32_block(data, data_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-		if (!buffer) return false;
-
-		// second pass: convert utf32 input to wchar_t
-		wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
-		wchar_writer::value_type oend = utf_decoder<wchar_writer, opt_swap>::decode_utf32_block(data, data_length, obegin);
-
-		assert(oend == obegin + length);
-		*oend = 0;
-
-		out_buffer = buffer;
-		out_length = length + 1;
-
-		return true;
-	}
-
-	PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
-	{
-		const uint8_t* data = static_cast<const uint8_t*>(contents);
-		size_t data_length = size;
-
-		// get length in wchar_t units
-		size_t length = data_length;
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-		if (!buffer) return false;
-
-		// convert latin1 input to wchar_t
-		wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
-		wchar_writer::value_type oend = utf_decoder<wchar_writer>::decode_latin1_block(data, data_length, obegin);
-
-		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_utf8(out_buffer, out_length, contents, size);
-
-		// source encoding is utf16
-		if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
-		{
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
-			return (native_encoding == encoding) ?
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
-		}
-
-		// source encoding is utf32
-		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
-		{
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
-			return (native_encoding == encoding) ?
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
-		}
-
-		// source encoding is latin1
-		if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);
-
-		assert(!"Invalid encoding");
-		return false;
-	}
-#else
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint16_t* data = static_cast<const uint16_t*>(contents);
-		size_t data_length = size / sizeof(uint16_t);
-
-		// first pass: get length in utf8 units
-		size_t length = utf_decoder<utf8_counter, opt_swap>::decode_utf16_block(data, data_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-		if (!buffer) return false;
-
-		// second pass: convert utf16 input to utf8
-		uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
-		uint8_t* oend = utf_decoder<utf8_writer, opt_swap>::decode_utf16_block(data, data_length, obegin);
-
-		assert(oend == obegin + length);
-		*oend = 0;
-
-		out_buffer = buffer;
-		out_length = length + 1;
-
-		return true;
-	}
-
-	template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
-	{
-		const uint32_t* data = static_cast<const uint32_t*>(contents);
-		size_t data_length = size / sizeof(uint32_t);
-
-		// first pass: get length in utf8 units
-		size_t length = utf_decoder<utf8_counter, opt_swap>::decode_utf32_block(data, data_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-		if (!buffer) return false;
-
-		// second pass: convert utf32 input to utf8
-		uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
-		uint8_t* oend = utf_decoder<utf8_writer, opt_swap>::decode_utf32_block(data, data_length, obegin);
-
-		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<const uint8_t*>(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 + utf_decoder<utf8_counter>::decode_latin1_block(postfix, postfix_length, 0);
-
-		// allocate buffer of suitable length
-		char_t* buffer = static_cast<char_t*>(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<uint8_t*>(buffer);
-		uint8_t* oend = utf_decoder<utf8_writer>::decode_latin1_block(postfix, postfix_length, obegin + prefix_length);
-
-		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_utf16(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
-		}
-
-		// source encoding is utf32
-		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
-		{
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
-			return (native_encoding == encoding) ?
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
-				convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
-		}
-
-		// source encoding is latin1
-		if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
-
-		assert(!"Invalid encoding");
-		return false;
-	}
-#endif
-
-	PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
-	{
-		// get length in utf8 characters
-		return utf_decoder<utf8_counter>::decode_wchar_block(str, length, 0);
-	}
-
-	PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
-	{
-		// convert to utf8
-		uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
-		uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(str, length, begin);
-	
-		assert(begin + size == end);
-		(void)!end;
-
-		// zero-terminate
-		buffer[size] = 0;
-	}
-	
-#ifndef PUGIXML_NO_STL
-	PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
-	{
-		// first pass: get length in utf8 characters
-		size_t size = as_utf8_begin(str, length);
-
-		// allocate resulting string
-		std::string result;
-		result.resize(size);
-
-		// second pass: convert to utf8
-		if (size > 0) as_utf8_end(&result[0], size, str, length);
-
-		return result;
-	}
-
-	PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
-	{
-		const uint8_t* data = reinterpret_cast<const uint8_t*>(str);
-
-		// first pass: get length in wchar_t units
-		size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
-
-		// allocate resulting string
-		std::basic_string<wchar_t> result;
-		result.resize(length);
-
-		// second pass: convert to wchar_t
-		if (length > 0)
-		{
-			wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
-			wchar_writer::value_type end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, begin);
-
-			assert(begin + length == end);
-			(void)!end;
-		}
-
-		return result;
-	}
-#endif
-
-	inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)
-	{
-		assert(target);
-		size_t target_length = strlength(target);
-
-		// always reuse document buffer memory if possible
-		if (!allocated) return target_length >= length;
-
-		// reuse heap memory if waste is not too great
-		const size_t reuse_threshold = 32;
-
-		return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
-	}
-
-	PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
-	{
-		assert(header);
-
-		size_t source_length = strlength(source);
-
-		if (source_length == 0)
-		{
-			// empty string and null pointer are equivalent, so just deallocate old memory
-			xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
-
-			if (header & header_mask) alloc->deallocate_string(dest);
-			
-			// mark the string as not allocated
-			dest = 0;
-			header &= ~header_mask;
-
-			return true;
-		}
-		else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))
-		{
-			// we can reuse old buffer, so just copy the new data (including zero terminator)
-			memcpy(dest, source, (source_length + 1) * sizeof(char_t));
-			
-			return true;
-		}
-		else
-		{
-			xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
-
-			// allocate new buffer
-			char_t* buf = alloc->allocate_string(source_length + 1);
-			if (!buf) return false;
-
-			// copy the string (including zero terminator)
-			memcpy(buf, source, (source_length + 1) * sizeof(char_t));
-
-			// deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
-			if (header & header_mask) alloc->deallocate_string(dest);
-			
-			// the string is now allocated, so set the flag
-			dest = buf;
-			header |= header_mask;
-
-			return true;
-		}
-	}
-
-	struct gap
-	{
-		char_t* end;
-		size_t size;
-			
-		gap(): end(0), size(0)
-		{
-		}
-			
-		// Push new gap, move s count bytes further (skipping the gap).
-		// Collapse previous gap.
-		void push(char_t*& s, size_t count)
-		{
-			if (end) // there was a gap already; collapse it
-			{
-				// Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
-				assert(s >= end);
-				memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(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<char*>(s) - reinterpret_cast<char*>(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<unsigned int>(ch - '0') <= 9)
-							ucsc = 16 * ucsc + (ch - '0');
-						else if (static_cast<unsigned int>((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<unsigned int>(static_cast<unsigned int>(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<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));
-			#else
-				s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));
-			#endif
-					
-				g.push(s, stre - s);
-				return stre;
-			}
-
-			case 'a':	// &a
-			{
-				++stre;
-
-				if (*stre == 'm') // &am
-				{
-					if (*++stre == 'p' && *++stre == ';') // &amp;
-					{
-						*s++ = '&';
-						++stre;
-							
-						g.push(s, stre - s);
-						return stre;
-					}
-				}
-				else if (*stre == 'p') // &ap
-				{
-					if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // &apos;
-					{
-						*s++ = '\'';
-						++stre;
-
-						g.push(s, stre - s);
-						return stre;
-					}
-				}
-				break;
-			}
-
-			case 'g': // &g
-			{
-				if (*++stre == 't' && *++stre == ';') // &gt;
-				{
-					*s++ = '>';
-					++stre;
-					
-					g.push(s, stre - s);
-					return stre;
-				}
-				break;
-			}
-
-			case 'l': // &l
-			{
-				if (*++stre == 't' && *++stre == ';') // &lt;
-				{
-					*s++ = '<';
-					++stre;
-						
-					g.push(s, stre - s);
-					return stre;
-				}
-				break;
-			}
-
-			case 'q': // &q
-			{
-				if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // &quot;
-				{
-					*s++ = '"';
-					++stre;
-					
-					g.push(s, stre - s);
-					return stre;
-				}
-				break;
-			}
-
-			default:
-				break;
-		}
-		
-		return stre;
-	}
-
-	// Utility macro for last character handling
-	#define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
-
-	PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
-	{
-		gap g;
-		
-		while (true)
-		{
-			while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;
-		
-			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
-			{
-				*s++ = '\n'; // replace first one with 0x0a
-				
-				if (*s == '\n') g.push(s, 1);
-			}
-			else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here
-			{
-				*g.flush(s) = 0;
-				
-				return s + (s[2] == '>' ? 3 : 2);
-			}
-			else if (*s == 0)
-			{
-				return 0;
-			}
-			else ++s;
-		}
-	}
-
-	PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
-	{
-		gap g;
-			
-		while (true)
-		{
-			while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;
-			
-			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
-			{
-				*s++ = '\n'; // replace first one with 0x0a
-				
-				if (*s == '\n') g.push(s, 1);
-			}
-			else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here
-			{
-				*g.flush(s) = 0;
-				
-				return s + 1;
-			}
-			else if (*s == 0)
-			{
-				return 0;
-			}
-			else ++s;
-		}
-	}
-	
-	typedef char_t* (*strconv_pcdata_t)(char_t*);
-		
-	template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
-	{
-		static char_t* parse(char_t* s)
-		{
-			gap g;
-
-			char_t* begin = s;
-
-			while (true)
-			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;
-					
-				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<opt_false, opt_false, opt_false>::parse;
-		case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;
-		case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;
-		case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;
-		case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;
-		case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;
-		case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;
-		case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;
-		default: assert(false); return 0; // should not get here
-		}
-	}
-
-	typedef char_t* (*strconv_attribute_t)(char_t*, char_t);
-	
-	template <typename opt_escape> struct strconv_attribute_impl
-	{
-		static char_t* parse_wnorm(char_t* s, char_t end_quote)
-		{
-			gap g;
-
-			// trim leading whitespaces
-			if (PUGI__IS_CHARTYPE(*s, ct_space))
-			{
-				char_t* str = s;
-				
-				do ++str;
-				while (PUGI__IS_CHARTYPE(*str, ct_space));
-				
-				g.push(s, str - s);
-			}
-
-			while (true)
-			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;
-				
-				if (*s == end_quote)
-				{
-					char_t* str = g.flush(s);
-					
-					do *str-- = 0;
-					while (PUGI__IS_CHARTYPE(*str, ct_space));
-				
-					return s + 1;
-				}
-				else if (PUGI__IS_CHARTYPE(*s, ct_space))
-				{
-					*s++ = ' ';
-		
-					if (PUGI__IS_CHARTYPE(*s, ct_space))
-					{
-						char_t* str = s + 1;
-						while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
-						
-						g.push(s, str - s);
-					}
-				}
-				else if (opt_escape::value && *s == '&')
-				{
-					s = strconv_escape(s, g);
-				}
-				else if (!*s)
-				{
-					return 0;
-				}
-				else ++s;
-			}
-		}
-
-		static char_t* parse_wconv(char_t* s, char_t end_quote)
-		{
-			gap g;
-
-			while (true)
-			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;
-				
-				if (*s == end_quote)
-				{
-					*g.flush(s) = 0;
-				
-					return s + 1;
-				}
-				else if (PUGI__IS_CHARTYPE(*s, ct_space))
-				{
-					if (*s == '\r')
-					{
-						*s++ = ' ';
-				
-						if (*s == '\n') g.push(s, 1);
-					}
-					else *s++ = ' ';
-				}
-				else if (opt_escape::value && *s == '&')
-				{
-					s = strconv_escape(s, g);
-				}
-				else if (!*s)
-				{
-					return 0;
-				}
-				else ++s;
-			}
-		}
-
-		static char_t* parse_eol(char_t* s, char_t end_quote)
-		{
-			gap g;
-
-			while (true)
-			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
-				
-				if (*s == end_quote)
-				{
-					*g.flush(s) = 0;
-				
-					return s + 1;
-				}
-				else if (*s == '\r')
-				{
-					*s++ = '\n';
-					
-					if (*s == '\n') g.push(s, 1);
-				}
-				else if (opt_escape::value && *s == '&')
-				{
-					s = strconv_escape(s, g);
-				}
-				else if (!*s)
-				{
-					return 0;
-				}
-				else ++s;
-			}
-		}
-
-		static char_t* parse_simple(char_t* s, char_t end_quote)
-		{
-			gap g;
-
-			while (true)
-			{
-				while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
-				
-				if (*s == end_quote)
-				{
-					*g.flush(s) = 0;
-				
-					return s + 1;
-				}
-				else if (opt_escape::value && *s == '&')
-				{
-					s = strconv_escape(s, g);
-				}
-				else if (!*s)
-				{
-					return 0;
-				}
-				else ++s;
-			}
-		}
-	};
-
-	PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
-	{
-		PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
-		
-		switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
-		{
-		case 0:  return strconv_attribute_impl<opt_false>::parse_simple;
-		case 1:  return strconv_attribute_impl<opt_true>::parse_simple;
-		case 2:  return strconv_attribute_impl<opt_false>::parse_eol;
-		case 3:  return strconv_attribute_impl<opt_true>::parse_eol;
-		case 4:  return strconv_attribute_impl<opt_false>::parse_wconv;
-		case 5:  return strconv_attribute_impl<opt_true>::parse_wconv;
-		case 6:  return strconv_attribute_impl<opt_false>::parse_wconv;
-		case 7:  return strconv_attribute_impl<opt_true>::parse_wconv;
-		case 8:  return strconv_attribute_impl<opt_false>::parse_wnorm;
-		case 9:  return strconv_attribute_impl<opt_true>::parse_wnorm;
-		case 10: return strconv_attribute_impl<opt_false>::parse_wnorm;
-		case 11: return strconv_attribute_impl<opt_true>::parse_wnorm;
-		case 12: return strconv_attribute_impl<opt_false>::parse_wnorm;
-		case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;
-		case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
-		case 15: return strconv_attribute_impl<opt_true>::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;
-		
-		// Parser utilities.
-		#define PUGI__SKIPWS()			{ while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
-		#define PUGI__OPTSET(OPT)			( optmsk & (OPT) )
-		#define PUGI__PUSHNODE(TYPE)		{ cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
-		#define PUGI__POPNODE()			{ cursor = cursor->parent; }
-		#define PUGI__SCANFOR(X)			{ while (*s != 0 && !(X)) ++s; }
-		#define PUGI__SCANWHILE(X)		{ while ((X)) ++s; }
-		#define PUGI__ENDSEG()			{ ch = *s; *s = 0; ++s; }
-		#define PUGI__THROW_ERROR(err, m)	return error_offset = m, error_status = err, static_cast<char_t*>(0)
-		#define PUGI__CHECK_ERROR(err, m)	{ if (*s == 0) PUGI__THROW_ERROR(err, m); }
-		
-		xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
-		{
-		}
-
-		// DOCTYPE consists of nested sections of the following possible types:
-		// <!-- ... -->, <? ... ?>, "...", '...'
-		// <![...]]>
-		// <!...>
-		// First group can not contain nested groups
-		// Second group can contain nested groups of the same type
-		// Third group can contain all other groups
-		char_t* parse_doctype_primitive(char_t* s)
-		{
-			if (*s == '"' || *s == '\'')
-			{
-				// quoted string
-				char_t ch = *s++;
-				PUGI__SCANFOR(*s == ch);
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
-				s++;
-			}
-			else if (s[0] == '<' && s[1] == '?')
-			{
-				// <? ... ?>
-				s += 2;
-				PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
-				s += 2;
-			}
-			else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
-			{
-				s += 4;
-				PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
-				if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
-
-				s += 4;
-			}
-			else PUGI__THROW_ERROR(status_bad_doctype, s);
-
-			return s;
-		}
-
-		char_t* parse_doctype_ignore(char_t* s)
-		{
-			assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
-			s++;
-
-			while (*s)
-			{
-				if (s[0] == '<' && s[1] == '!' && s[2] == '[')
-				{
-					// nested ignore section
-					s = parse_doctype_ignore(s);
-					if (!s) return s;
-				}
-				else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
-				{
-					// ignore section end
-					s += 3;
-
-					return s;
-				}
-				else s++;
-			}
-
-			PUGI__THROW_ERROR(status_bad_doctype, s);
-		}
-
-		char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
-		{
-			assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
-			s++;
-
-			while (*s)
-			{
-				if (s[0] == '<' && s[1] == '!' && s[2] != '-')
-				{
-					if (s[2] == '[')
-					{
-						// ignore
-						s = parse_doctype_ignore(s);
-						if (!s) return s;
-					}
-					else
-					{
-						// some control group
-						s = parse_doctype_group(s, endch, false);
-						if (!s) return s;
-
-						// skip >
-						assert(*s == '>');
-						s++;
-					}
-				}
-				else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
-				{
-					// unknown tag (forbidden), or some primitive group
-					s = parse_doctype_primitive(s);
-					if (!s) return s;
-				}
-				else if (*s == '>')
-				{
-					return s;
-				}
-				else s++;
-			}
-
-			if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
-
-			return s;
-		}
-
-		char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)
-		{
-			// parse node contents, starting with exclamation mark
-			++s;
-
-			if (*s == '-') // '<!-...'
-			{
-				++s;
-
-				if (*s == '-') // '<!--...'
-				{
-					++s;
-
-					if (PUGI__OPTSET(parse_comments))
-					{
-						PUGI__PUSHNODE(node_comment); // Append a new node on the tree.
-						cursor->value = s; // Save the offset.
-					}
-
-					if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
-					{
-						s = strconv_comment(s, endch);
-
-						if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
-					}
-					else
-					{
-						// Scan for terminating '-->'.
-						PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));
-						PUGI__CHECK_ERROR(status_bad_comment, s);
-
-						if (PUGI__OPTSET(parse_comments))
-							*s = 0; // Zero-terminate this segment at the first terminating '-'.
-
-						s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
-					}
-				}
-				else PUGI__THROW_ERROR(status_bad_comment, s);
-			}
-			else if (*s == '[')
-			{
-				// '<![CDATA[...'
-				if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')
-				{
-					++s;
-
-					if (PUGI__OPTSET(parse_cdata))
-					{
-						PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.
-						cursor->value = s; // Save the offset.
-
-						if (PUGI__OPTSET(parse_eol))
-						{
-							s = strconv_cdata(s, endch);
-
-							if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
-						}
-						else
-						{
-							// Scan for terminating ']]>'.
-							PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
-							PUGI__CHECK_ERROR(status_bad_cdata, s);
-
-							*s++ = 0; // Zero-terminate this segment.
-						}
-					}
-					else // Flagged for discard, but we still have to scan for the terminator.
-					{
-						// Scan for terminating ']]>'.
-						PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
-						PUGI__CHECK_ERROR(status_bad_cdata, s);
-
-						++s;
-					}
-
-					s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
-				}
-				else PUGI__THROW_ERROR(status_bad_cdata, s);
-			}
-			else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))
-			{
-				s -= 2;
-
-				if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
-
-				char_t* mark = s + 9;
-
-				s = parse_doctype_group(s, endch, true);
-				if (!s) return s;
-
-				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;
-
-					PUGI__POPNODE();
-				}
-			}
-			else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
-			else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
-			else PUGI__THROW_ERROR(status_unrecognized_tag, s);
-
-			return s;
-		}
-
-		char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)
-		{
-			// load into registers
-			xml_node_struct* cursor = ref_cursor;
-			char_t ch = 0;
-
-			// parse node contents, starting with question mark
-			++s;
-
-			// read PI target
-			char_t* target = s;
-
-			if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
-
-			PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));
-			PUGI__CHECK_ERROR(status_bad_pi, s);
-
-			// determine node type; stricmp / strcasecmp is not portable
-			bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
-
-			if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
-			{
-				if (declaration)
-				{
-					// disallow non top-level declarations
-					if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
-
-					PUGI__PUSHNODE(node_declaration);
-				}
-				else
-				{
-					PUGI__PUSHNODE(node_pi);
-				}
-
-				cursor->name = target;
-
-				PUGI__ENDSEG();
-
-				// parse value/attributes
-				if (ch == '?')
-				{
-					// empty node
-					if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
-					s += (*s == '>');
-
-					PUGI__POPNODE();
-				}
-				else if (PUGI__IS_CHARTYPE(ch, ct_space))
-				{
-					PUGI__SKIPWS();
-
-					// scan for tag end
-					char_t* value = s;
-
-					PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
-					PUGI__CHECK_ERROR(status_bad_pi, s);
-
-					if (declaration)
-					{
-						// replace ending ? with / so that 'element' terminates properly
-						*s = '/';
-
-						// we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES
-						s = value;
-					}
-					else
-					{
-						// store value and step over >
-						cursor->value = value;
-						PUGI__POPNODE();
-
-						PUGI__ENDSEG();
-
-						s += (*s == '>');
-					}
-				}
-				else PUGI__THROW_ERROR(status_bad_pi, s);
-			}
-			else
-			{
-				// scan for tag end
-				PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
-				PUGI__CHECK_ERROR(status_bad_pi, s);
-
-				s += (s[1] == '>' ? 2 : 1);
-			}
-
-			// store from registers
-			ref_cursor = cursor;
-
-			return s;
-		}
-
-		char_t* parse_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(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
-						PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
-
-						if (ch == '>')
-						{
-							// end of tag
-						}
-						else if (PUGI__IS_CHARTYPE(ch, ct_space))
-						{
-						LOC_ATTRIBUTES:
-							while (true)
-							{
-								PUGI__SKIPWS(); // Eat any whitespace.
-						
-								if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
-								{
-									xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute.
-									if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
-
-									a->name = s; // Save the offset.
-
-									PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
-									PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
-									PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
-									PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
-									if (PUGI__IS_CHARTYPE(ch, ct_space))
-									{
-										PUGI__SKIPWS(); // Eat any whitespace.
-										PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
-
-										ch = *s;
-										++s;
-									}
-									
-									if (ch == '=') // '<... #=...'
-									{
-										PUGI__SKIPWS(); // Eat any whitespace.
-
-										if (*s == '"' || *s == '\'') // '<... #="...'
-										{
-											ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
-											++s; // Step over the quote.
-											a->value = s; // Save the offset.
-
-											s = strconv_attribute(s, ch);
-										
-											if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
-
-											// After this line the loop continues from the start;
-											// Whitespaces, / and > are ok, symbols and EOF are wrong,
-											// everything else will be detected
-											if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
-										}
-										else PUGI__THROW_ERROR(status_bad_attribute, s);
-									}
-									else PUGI__THROW_ERROR(status_bad_attribute, s);
-								}
-								else if (*s == '/')
-								{
-									++s;
-									
-									if (*s == '>')
-									{
-										PUGI__POPNODE();
-										s++;
-										break;
-									}
-									else if (*s == 0 && endch == '>')
-									{
-										PUGI__POPNODE();
-										break;
-									}
-									else PUGI__THROW_ERROR(status_bad_start_element, s);
-								}
-								else if (*s == '>')
-								{
-									++s;
-
-									break;
-								}
-								else if (*s == 0 && endch == '>')
-								{
-									break;
-								}
-								else PUGI__THROW_ERROR(status_bad_start_element, s);
-							}
-
-							// !!!
-						}
-						else if (ch == '/') // '<#.../'
-						{
-							if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
-
-							PUGI__POPNODE(); // Pop.
-
-							s += (*s == '>');
-						}
-						else if (ch == 0)
-						{
-							// we stepped over null terminator, backtrack & handle closing tag
-							--s;
-							
-							if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
-						}
-						else PUGI__THROW_ERROR(status_bad_start_element, s);
-					}
-					else if (*s == '/')
-					{
-						++s;
-
-						char_t* name = cursor->name;
-						if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);
-						
-						while (PUGI__IS_CHARTYPE(*s, ct_symbol))
-						{
-							if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);
-						}
-
-						if (*name)
-						{
-							if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
-							else PUGI__THROW_ERROR(status_end_element_mismatch, s);
-						}
-							
-						PUGI__POPNODE(); // Pop.
-
-						PUGI__SKIPWS();
-
-						if (*s == 0)
-						{
-							if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
-						}
-						else
-						{
-							if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
-							++s;
-						}
-					}
-					else if (*s == '?') // '<?...'
-					{
-						s = parse_question(s, cursor, optmsk, endch);
-						if (!s) return s;
-
-						assert(cursor);
-						if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;
-					}
-					else if (*s == '!') // '<!...'
-					{
-						s = parse_exclamation(s, cursor, optmsk, endch);
-						if (!s) return s;
-					}
-					else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);
-					else PUGI__THROW_ERROR(status_unrecognized_tag, s);
-				}
-				else
-				{
-					mark = s; // Save this offset while searching for a terminator.
-
-					PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
-
-					if (*s == '<' || !*s)
-					{
-						// We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
-						assert(mark != s);
-
-						if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))
-						{
-							continue;
-						}
-						else if (PUGI__OPTSET(parse_ws_pcdata_single))
-						{
-							if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
-						}
-					}
-
-					if (!PUGI__OPTSET(parse_trim_pcdata))
-						s = mark;
-							
-					if (cursor->parent || PUGI__OPTSET(parse_fragment))
-					{
-						PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
-						cursor->value = s; // Save the offset.
-
-						s = strconv_pcdata(s);
-								
-						PUGI__POPNODE(); // Pop since this is a standalone.
-						
-						if (!*s) break;
-					}
-					else
-					{
-						PUGI__SCANFOR(*s == '<'); // '...<'
-						if (!*s) break;
-						
-						++s;
-					}
-
-					// We're after '<'
-					goto LOC_TAG;
-				}
-			}
-
-			// check that last tag is closed
-			if (cursor != 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<wchar_t>(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)
-			{
-				xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
-				if (type == 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)
-		{
-			// allocator object is a part of document object
-			xml_allocator& alloc = *static_cast<xml_allocator*>(xmldoc);
-
-			// 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;
-	
-			// create parser on stack
-			xml_parser parser(alloc);
-
-			// 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);
-
-			// update allocator state
-			alloc = parser.alloc;
-
-			xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
-			assert(result.offset >= 0 && static_cast<size_t>(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 : root->first_child;
-
-				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<size_t>(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;
-	}
-
-#ifdef PUGIXML_WCHAR_MODE
-	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
-	{
-		assert(length > 0);
-
-		// discard last character if it's the lead of a surrogate pair 
-		return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(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)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(data, length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
-
-		// convert to utf16
-		if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
-		{
-			uint16_t* dest = r_u16;
-
-			// convert to native utf16
-			uint16_t* end = utf_decoder<utf16_writer>::decode_wchar_block(data, length, dest);
-
-			// swap if necessary
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint16_t);
-		}
-
-		// convert to utf32
-		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
-		{
-			uint32_t* dest = r_u32;
-
-			// convert to native utf32
-			uint32_t* end = utf_decoder<utf32_writer>::decode_wchar_block(data, length, dest);
-
-			// swap if necessary
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint32_t);
-		}
-
-		// convert to latin1
-		if (encoding == encoding_latin1)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<latin1_writer>::decode_wchar_block(data, length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
-
-		assert(!"Invalid encoding");
-		return 0;
-	}
-#else
-	PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
-	{
-		assert(length > 4);
-
-		for (size_t i = 1; i <= 4; ++i)
-		{
-			uint8_t ch = static_cast<uint8_t>(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)
-		{
-			uint16_t* dest = r_u16;
-
-			// convert to native utf16
-			uint16_t* end = utf_decoder<utf16_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			// swap if necessary
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
-
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint16_t);
-		}
-
-		if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
-		{
-			uint32_t* dest = r_u32;
-
-			// convert to native utf32
-			uint32_t* end = utf_decoder<utf32_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			// swap if necessary
-			xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
-
-			if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
-
-			return static_cast<size_t>(end - dest) * sizeof(uint32_t);
-		}
-
-		if (encoding == encoding_latin1)
-		{
-			uint8_t* dest = r_u8;
-			uint8_t* end = utf_decoder<latin1_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
-
-			return static_cast<size_t>(end - dest);
-		}
-
-		assert(!"Invalid encoding");
-		return 0;
-	}
-#endif
-
-	class xml_buffered_writer
-	{
-		xml_buffered_writer(const xml_buffered_writer&);
-		xml_buffered_writer& operator=(const xml_buffered_writer&);
-
-	public:
-		xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
-		{
-			PUGI__STATIC_ASSERT(bufcapacity >= 8);
-		}
-
-		~xml_buffered_writer()
-		{
-			flush();
-		}
-
-		void flush()
-		{
-			flush(buffer, bufsize);
-			bufsize = 0;
-		}
-
-		void flush(const char_t* data, size_t size)
-		{
-			if (size == 0) return;
-
-			// fast path, just write data
-			if (encoding == get_write_native_encoding())
-				writer.write(data, size * sizeof(char_t));
-			else
-			{
-				// convert chunk
-				size_t result = convert_buffer_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(const char_t* data, size_t length)
-		{
-			if (bufsize + length > bufcapacity)
-			{
-				// flush the remaining buffer contents
-				flush();
-
-				// handle large chunks
-				if (length > bufcapacity)
-				{
-					if (encoding == get_write_native_encoding())
-					{
-						// fast path, can just write data chunk
-						writer.write(data, length * sizeof(char_t));
-						return;
-					}
-
-					// need to convert in suitable chunks
-					while (length > bufcapacity)
-					{
-						// get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
-						// and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
-						size_t chunk_size = get_valid_length(data, bufcapacity);
-
-						// convert chunk and write
-						flush(data, chunk_size);
-
-						// iterate
-						data += chunk_size;
-						length -= chunk_size;
-					}
-
-					// small tail is copied below
-					bufsize = 0;
-				}
-			}
-
-			memcpy(buffer + bufsize, data, length * sizeof(char_t));
-			bufsize += length;
-		}
-
-		void write(const char_t* data)
-		{
-			write(data, strlength(data));
-		}
-
-		void write(char_t d0)
-		{
-			if (bufsize + 1 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			bufsize += 1;
-		}
-
-		void write(char_t d0, char_t d1)
-		{
-			if (bufsize + 2 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			bufsize += 2;
-		}
-
-		void write(char_t d0, char_t d1, char_t d2)
-		{
-			if (bufsize + 3 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			bufsize += 3;
-		}
-
-		void write(char_t d0, char_t d1, char_t d2, char_t d3)
-		{
-			if (bufsize + 4 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			bufsize += 4;
-		}
-
-		void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
-		{
-			if (bufsize + 5 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			buffer[bufsize + 4] = d4;
-			bufsize += 5;
-		}
-
-		void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
-		{
-			if (bufsize + 6 > bufcapacity) flush();
-
-			buffer[bufsize + 0] = d0;
-			buffer[bufsize + 1] = d1;
-			buffer[bufsize + 2] = d2;
-			buffer[bufsize + 3] = d3;
-			buffer[bufsize + 4] = d4;
-			buffer[bufsize + 5] = d5;
-			bufsize += 6;
-		}
-
-		// utf8 maximum expansion: x4 (-> utf32)
-		// utf16 maximum expansion: x2 (-> utf32)
-		// utf32 maximum expansion: x1
-		enum
-		{
-			bufcapacitybytes =
-			#ifdef PUGIXML_MEMORY_OUTPUT_STACK
-				PUGIXML_MEMORY_OUTPUT_STACK
-			#else
-				10240
-			#endif
-			,
-			bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)
-		};
-
-		char_t buffer[bufcapacity];
-
-		union
-		{
-			uint8_t data_u8[4 * bufcapacity];
-			uint16_t data_u16[2 * bufcapacity];
-			uint32_t data_u32[bufcapacity];
-			char_t data_char[bufcapacity];
-		} scratch;
-
-		xml_writer& writer;
-		size_t bufsize;
-		xml_encoding encoding;
-	};
-
-	PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)
-	{
-		while (*s)
-		{
-			const char_t* prev = s;
-			
-			// While *s is a usual symbol
-			while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;
-		
-			writer.write(prev, static_cast<size_t>(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<unsigned int>(*s++);
-					assert(ch < 32);
-
-					writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
-				}
-			}
-		}
-	}
-
-	PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
-	{
-		if (flags & format_no_escapes)
-			writer.write(s);
-		else
-			text_output_escaped(writer, s, type);
-	}
-
-	PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
-	{
-		do
-		{
-			writer.write('<', '!', '[', 'C', 'D');
-			writer.write('A', 'T', 'A', '[');
-
-			const char_t* prev = s;
-
-			// look for ]]> sequence - we can't output it as is since it terminates CDATA
-			while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;
-
-			// skip ]] if we stopped at ]]>, > will go to the next CDATA section
-			if (*s) s += 2;
-
-			writer.write(prev, static_cast<size_t>(s - prev));
-
-			writer.write(']', ']', '>');
-		}
-		while (*s);
-	}
-
-	PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
-	{
-		const char_t* default_name = PUGIXML_TEXT(":anonymous");
-
-		for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
-		{
-			writer.write(' ');
-			writer.write(a.name()[0] ? a.name() : default_name);
-			writer.write('=', '"');
-
-			text_output(writer, a.value(), ctx_special_attr, flags);
-
-			writer.write('"');
-		}
-	}
-
-	PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)
-	{
-		const char_t* default_name = PUGIXML_TEXT(":anonymous");
-
-		if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
-			for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-
-		switch (node.type())
-		{
-		case node_document:
-		{
-			for (xml_node n = node.first_child(); n; n = n.next_sibling())
-				node_output(writer, n, indent, flags, depth);
-			break;
-		}
-			
-		case node_element:
-		{
-			const char_t* name = node.name()[0] ? node.name() : default_name;
-
-			writer.write('<');
-			writer.write(name);
-
-			node_output_attributes(writer, node, flags);
-
-			if (flags & format_raw)
-			{
-				if (!node.first_child())
-					writer.write(' ', '/', '>');
-				else
-				{
-					writer.write('>');
-
-					for (xml_node n = node.first_child(); n; n = n.next_sibling())
-						node_output(writer, n, indent, flags, depth + 1);
-
-					writer.write('<', '/');
-					writer.write(name);
-					writer.write('>');
-				}
-			}
-			else if (!node.first_child())
-				writer.write(' ', '/', '>', '\n');
-			else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
-			{
-				writer.write('>');
-
-				if (node.first_child().type() == node_pcdata)
-					text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
-				else
-					text_output_cdata(writer, node.first_child().value());
-
-				writer.write('<', '/');
-				writer.write(name);
-				writer.write('>', '\n');
-			}
-			else
-			{
-				writer.write('>', '\n');
-				
-				for (xml_node n = node.first_child(); n; n = n.next_sibling())
-					node_output(writer, n, indent, flags, depth + 1);
-
-				if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
-					for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
-				
-				writer.write('<', '/');
-				writer.write(name);
-				writer.write('>', '\n');
-			}
-
-			break;
-		}
-		
-		case node_pcdata:
-			text_output(writer, node.value(), ctx_special_pcdata, flags);
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_cdata:
-			text_output_cdata(writer, node.value());
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_comment:
-			writer.write('<', '!', '-', '-');
-			writer.write(node.value());
-			writer.write('-', '-', '>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_pi:
-		case node_declaration:
-			writer.write('<', '?');
-			writer.write(node.name()[0] ? node.name() : default_name);
-
-			if (node.type() == node_declaration)
-			{
-				node_output_attributes(writer, node, flags);
-			}
-			else if (node.value()[0])
-			{
-				writer.write(' ');
-				writer.write(node.value());
-			}
-
-			writer.write('?', '>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		case node_doctype:
-			writer.write('<', '!', 'D', 'O', 'C');
-			writer.write('T', 'Y', 'P', 'E');
-
-			if (node.value()[0])
-			{
-				writer.write(' ');
-				writer.write(node.value());
-			}
-
-			writer.write('>');
-			if ((flags & format_raw) == 0) writer.write('\n');
-			break;
-
-		default:
-			assert(!"Invalid node type");
-		}
-	}
-
-	inline bool has_declaration(const xml_node& node)
-	{
-		for (xml_node child = node.first_child(); child; child = child.next_sibling())
-		{
-			xml_node_type type = child.type();
-
-			if (type == node_declaration) return true;
-			if (type == node_element) return false;
-		}
-
-		return false;
-	}
-
-	inline bool allow_insert_child(xml_node_type parent, xml_node_type child)
-	{
-		if (parent != node_document && parent != node_element) return false;
-		if (child == node_document || child == node_null) return false;
-		if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;
-
-		return true;
-	}
-
-	PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)
-	{
-		assert(dest.type() == source.type());
-
-		switch (source.type())
-		{
-		case node_element:
-		{
-			dest.set_name(source.name());
-
-			for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
-				dest.append_attribute(a.name()).set_value(a.value());
-
-			for (xml_node c = source.first_child(); c; c = c.next_sibling())
-			{
-				if (c == skip) continue;
-
-				xml_node cc = dest.append_child(c.type());
-				assert(cc);
-
-				recursive_copy_skip(cc, c, skip);
-			}
-
-			break;
-		}
-
-		case node_pcdata:
-		case node_cdata:
-		case node_comment:
-		case node_doctype:
-			dest.set_value(source.value());
-			break;
-
-		case node_pi:
-			dest.set_name(source.name());
-			dest.set_value(source.value());
-			break;
-
-		case node_declaration:
-		{
-			dest.set_name(source.name());
-
-			for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
-				dest.append_attribute(a.name()).set_value(a.value());
-
-			break;
-		}
-
-		default:
-			assert(!"Invalid node type");
-		}
-	}
-
-	inline bool is_text_node(xml_node_struct* node)
-	{
-		xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
-
-		return type == node_pcdata || type == node_cdata;
-	}
-
-	// get value with conversion functions
-	PUGI__FN int get_integer_base(const char_t* value)
-	{
-		const char_t* s = value;
-
-		while (PUGI__IS_CHARTYPE(*s, ct_space))
-			s++;
-
-		if (*s == '-')
-			s++;
-
-		return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
-	}
-
-	PUGI__FN int get_value_int(const char_t* value, int def)
-	{
-		if (!value) return def;
-
-		int base = get_integer_base(value);
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return static_cast<int>(wcstol(value, 0, base));
-	#else
-		return static_cast<int>(strtol(value, 0, base));
-	#endif
-	}
-
-	PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)
-	{
-		if (!value) return def;
-
-		int base = get_integer_base(value);
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return static_cast<unsigned int>(wcstoul(value, 0, base));
-	#else
-		return static_cast<unsigned int>(strtoul(value, 0, base));
-	#endif
-	}
-
-	PUGI__FN double get_value_double(const char_t* value, double def)
-	{
-		if (!value) return def;
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return wcstod(value, 0);
-	#else
-		return strtod(value, 0);
-	#endif
-	}
-
-	PUGI__FN float get_value_float(const char_t* value, float def)
-	{
-		if (!value) return def;
-
-	#ifdef PUGIXML_WCHAR_MODE
-		return static_cast<float>(wcstod(value, 0));
-	#else
-		return static_cast<float>(strtod(value, 0));
-	#endif
-	}
-
-	PUGI__FN bool get_value_bool(const char_t* value, bool def)
-	{
-		if (!value) return def;
-
-		// only look at first char
-		char_t first = *value;
-
-		// 1*, t* (true), T* (True), y* (yes), Y* (YES)
-		return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
-	}
-
-#ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long get_value_llong(const char_t* value, long long def)
-	{
-		if (!value) return def;
-
-		int base = get_integer_base(value);
-
-	#ifdef PUGIXML_WCHAR_MODE
-		#ifdef PUGI__MSVC_CRT_VERSION
-			return _wcstoi64(value, 0, base);
-		#else
-			return wcstoll(value, 0, base);
-		#endif
-	#else
-		#ifdef PUGI__MSVC_CRT_VERSION
-			return _strtoi64(value, 0, base);
-		#else
-			return strtoll(value, 0, base);
-		#endif
-	#endif
-	}
-
-	PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def)
-	{
-		if (!value) return def;
-
-		int base = get_integer_base(value);
-
-	#ifdef PUGIXML_WCHAR_MODE
-		#ifdef PUGI__MSVC_CRT_VERSION
-			return _wcstoui64(value, 0, base);
-		#else
-			return wcstoull(value, 0, base);
-		#endif
-	#else
-		#ifdef PUGI__MSVC_CRT_VERSION
-			return _strtoui64(value, 0, base);
-		#else
-			return strtoull(value, 0, base);
-		#endif
-	#endif
-	}
-#endif
-
-	// set value with conversion functions
-	PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
-	{
-	#ifdef PUGIXML_WCHAR_MODE
-		char_t wbuf[128];
-		impl::widen_ascii(wbuf, buf);
-
-		return strcpy_insitu(dest, header, header_mask, wbuf);
-	#else
-		return strcpy_insitu(dest, header, header_mask, buf);
-	#endif
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
-	{
-		char buf[128];
-		sprintf(buf, "%d", value);
-	
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
-	{
-		char buf[128];
-		sprintf(buf, "%u", value);
-
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
-	{
-		char buf[128];
-		sprintf(buf, "%g", value);
-
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-	
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
-	{
-		return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
-	}
-
-#ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value)
-	{
-		char buf[128];
-		sprintf(buf, "%lld", value);
-	
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-
-	PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value)
-	{
-		char buf[128];
-		sprintf(buf, "%llu", value);
-	
-		return set_value_buffer(dest, header, header_mask, buf);
-	}
-#endif
-
-	// we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
-	PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
-	{
-	#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
-		// there are 64-bit versions of fseek/ftell, let's use them
-		typedef __int64 length_type;
-
-		_fseeki64(file, 0, SEEK_END);
-		length_type length = _ftelli64(file);
-		_fseeki64(file, 0, SEEK_SET);
-	#elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)
-		// there are 64-bit versions of fseek/ftell, let's use them
-		typedef off64_t length_type;
-
-		fseeko64(file, 0, SEEK_END);
-		length_type length = ftello64(file);
-		fseeko64(file, 0, SEEK_SET);
-	#else
-		// if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.
-		typedef long length_type;
-
-		fseek(file, 0, SEEK_END);
-		length_type length = ftell(file);
-		fseek(file, 0, SEEK_SET);
-	#endif
-
-		// check for I/O errors
-		if (length < 0) return status_io_error;
-		
-		// check for overflow
-		size_t result = static_cast<size_t>(length);
-
-		if (static_cast<length_type>(result) != length) return status_out_of_memory;
-
-		// finalize
-		out_result = result;
-
-		return status_ok;
-	}
-
-	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<char_t*>(buffer)[length] = 0;
-			return (length + 1) * sizeof(char_t);
-		}
-	#else
-		if (encoding == encoding_utf8)
-		{
-			static_cast<char*>(buffer)[size] = 0;
-			return size + 1;
-		}
-	#endif
-
-		return size;
-	}
-
-	PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
-	{
-		if (!file) return make_parse_result(status_file_not_found);
-
-		// get file size (can result in I/O errors)
-		size_t size = 0;
-		xml_parse_status size_status = get_file_size(file, size);
-
-		if (size_status != status_ok)
-		{
-			fclose(file);
-			return make_parse_result(size_status);
-		}
-		
-		size_t max_suffix_size = sizeof(char_t);
-
-		// allocate buffer for the whole file
-		char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
-
-		if (!contents)
-		{
-			fclose(file);
-			return make_parse_result(status_out_of_memory);
-		}
-
-		// read file in memory
-		size_t read_size = fread(contents, 1, size, file);
-		fclose(file);
-
-		if (read_size != size)
-		{
-			xml_memory::deallocate(contents);
-			return make_parse_result(status_io_error);
-		}
-
-		xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
-		
-		return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding);
-	}
-
-#ifndef PUGIXML_NO_STL
-	template <typename T> struct xml_stream_chunk
-	{
-		static xml_stream_chunk* create()
-		{
-			void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
-			
-			return new (memory) xml_stream_chunk();
-		}
-
-		static void destroy(void* ptr)
-		{
-			xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);
-
-			// free chunk chain
-			while (chunk)
-			{
-				xml_stream_chunk* next = chunk->next;
-				xml_memory::deallocate(chunk);
-				chunk = next;
-			}
-		}
-
-		xml_stream_chunk(): next(0), size(0)
-		{
-		}
-
-		xml_stream_chunk* next;
-		size_t size;
-
-		T data[xml_memory_page_size / sizeof(T)];
-	};
-
-	template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
-	{
-		buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
-
-		// read file to a chunk list
-		size_t total = 0;
-		xml_stream_chunk<T>* last = 0;
-
-		while (!stream.eof())
-		{
-			// allocate new chunk
-			xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::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<std::streamsize>(sizeof(chunk->data) / sizeof(T)));
-			chunk->size = static_cast<size_t>(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<char*>(xml_memory::allocate(total + max_suffix_size));
-		if (!buffer) return status_out_of_memory;
-
-		char* write = buffer;
-
-		for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(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 <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
-	{
-		// get length of remaining data in stream
-		typename std::basic_istream<T>::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<size_t>(length);
-
-		if (static_cast<std::streamsize>(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)
-		buffer_holder 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<T*>(buffer.data), static_cast<std::streamsize>(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<size_t>(stream.gcount());
-		assert(actual_length <= read_length);
-		
-		*out_buffer = buffer.release();
-		*out_size = actual_length * sizeof(T);
-
-		return status_ok;
-	}
-
-	template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)
-	{
-		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 doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding);
-	}
-#endif
-
-#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
-	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
-	{
-		return _wfopen(path, mode);
-	}
-#else
-	PUGI__FN char* convert_path_heap(const wchar_t* str)
-	{
-		assert(str);
-
-		// first pass: get length in utf8 characters
-		size_t length = strlength_wide(str);
-		size_t size = as_utf8_begin(str, length);
-
-		// allocate resulting string
-		char* result = static_cast<char*>(xml_memory::allocate(size + 1));
-		if (!result) return 0;
-
-		// second pass: convert to utf8
-		as_utf8_end(result, size, str, length);
-
-		return result;
-	}
-
-	PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
-	{
-		// there is no standard function to open wide paths, so our best bet is to try utf8 path
-		char* path_utf8 = convert_path_heap(path);
-		if (!path_utf8) return 0;
-
-		// convert mode to ASCII (we mirror _wfopen interface)
-		char mode_ascii[4] = {0};
-		for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);
-
-		// try to open the utf8 path
-		FILE* result = fopen(path_utf8, mode_ascii);
-
-		// free dummy buffer
-		xml_memory::deallocate(path_utf8);
-
-		return result;
-	}
-#endif
-
-	PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
-	{
-		if (!file) return false;
-
-		xml_writer_file writer(file);
-		doc.save(writer, indent, flags, encoding);
-
-		int result = ferror(file);
-
-		fclose(file);
-
-		return result == 0;
-	}
-
-	PUGI__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
-		assert(contents || size == 0);
-
-		// 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);
-
-		// 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;
-
-		// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
-		if (own || buffer != contents) *out_buffer = buffer;
-
-		return res;
-	}
-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*>(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<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
-	{
-	}
-
-	PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& 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<const char*>(data), static_cast<std::streamsize>(size));
-		}
-		else
-		{
-			assert(wide_stream);
-			assert(size % sizeof(wchar_t) == 0);
-
-			wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(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 : def;
-	}
-
-	PUGI__FN int xml_attribute::as_int(int def) const
-	{
-		return impl::get_value_int(_attr ? _attr->value : 0, def);
-	}
-
-	PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
-	{
-		return impl::get_value_uint(_attr ? _attr->value : 0, def);
-	}
-
-	PUGI__FN double xml_attribute::as_double(double def) const
-	{
-		return impl::get_value_double(_attr ? _attr->value : 0, def);
-	}
-
-	PUGI__FN float xml_attribute::as_float(float def) const
-	{
-		return impl::get_value_float(_attr ? _attr->value : 0, def);
-	}
-
-	PUGI__FN bool xml_attribute::as_bool(bool def) const
-	{
-		return impl::get_value_bool(_attr ? _attr->value : 0, def);
-	}
-
-#ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long xml_attribute::as_llong(long long def) const
-	{
-		return impl::get_value_llong(_attr ? _attr->value : 0, def);
-	}
-
-	PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
-	{
-		return impl::get_value_ullong(_attr ? _attr->value : 0, 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 : PUGIXML_TEXT("");
-	}
-
-	PUGI__FN const char_t* xml_attribute::value() const
-	{
-		return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
-	}
-
-	PUGI__FN size_t xml_attribute::hash_value() const
-	{
-		return static_cast<size_t>(reinterpret_cast<uintptr_t>(_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=(double 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);
-	}
-		
-	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);
-	}
-
-	PUGI__FN bool xml_attribute::set_value(int rhs)
-	{
-		if (!_attr) return false;
-
-		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
-	}
-
-	PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
-	{
-		if (!_attr) return false;
-
-		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
-	}
-
-	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(bool rhs)
-	{
-		if (!_attr) return false;
-
-		return impl::set_value_convert(_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_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
-	}
-
-	PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
-	{
-		if (!_attr) return false;
-
-		return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
-	}
-#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, _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, _root);
-	}
-
-	PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
-	{
-		return attribute_iterator(0, _root);
-	}
-	
-	PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const
-	{
-		return xml_object_range<xml_node_iterator>(begin(), end());
-	}
-
-	PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
-	{
-		return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
-	}
-
-	PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
-	{
-		return xml_object_range<xml_attribute_iterator>(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 : PUGIXML_TEXT("");
-	}
-
-	PUGI__FN xml_node_type xml_node::type() const
-	{
-		return _root ? static_cast<xml_node_type>((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null;
-	}
-	
-	PUGI__FN const char_t* xml_node::value() const
-	{
-		return (_root && _root->value) ? _root->value : 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
-	{
-		if (!_root) return xml_node();
-		
-		if (_root->next_sibling) return xml_node(_root->next_sibling);
-		else return 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_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
-	{
-		if (!_root) return xml_node();
-
-		impl::xml_memory_page* page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
-
-		return xml_node(static_cast<impl::xml_document_struct*>(page->allocator));
-	}
-
-	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("");
-		
-		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
-			if (i->value && impl::is_text_node(i))
-				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)
-	{
-		switch (type())
-		{
-		case node_pi:
-		case node_declaration:
-		case node_element:
-			return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
-
-		default:
-			return false;
-		}
-	}
-		
-	PUGI__FN bool xml_node::set_value(const char_t* rhs)
-	{
-		switch (type())
-		{
-		case node_pi:
-		case node_cdata:
-		case node_pcdata:
-		case node_comment:
-		case node_doctype:
-			return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
-
-		default:
-			return false;
-		}
-	}
-
-	PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
-	{
-		if (type() != node_element && type() != node_declaration) return xml_attribute();
-		
-		xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root)));
-		a.set_name(name_);
-		
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
-	{
-		if (type() != node_element && type() != node_declaration) return xml_attribute();
-		
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
-		if (!a) return xml_attribute();
-
-		a.set_name(name_);
-		
-		xml_attribute_struct* head = _root->first_attribute;
-
-		if (head)
-		{
-			a._attr->prev_attribute_c = head->prev_attribute_c;
-			head->prev_attribute_c = a._attr;
-		}
-		else
-			a._attr->prev_attribute_c = a._attr;
-		
-		a._attr->next_attribute = head;
-		_root->first_attribute = a._attr;
-				
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
-	{
-		if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
-		
-		// check that attribute belongs to *this
-		xml_attribute_struct* cur = attr._attr;
-
-		while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
-
-		if (cur != _root->first_attribute) return xml_attribute();
-
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
-		if (!a) return xml_attribute();
-
-		a.set_name(name_);
-
-		if (attr._attr->prev_attribute_c->next_attribute)
-			attr._attr->prev_attribute_c->next_attribute = a._attr;
-		else
-			_root->first_attribute = a._attr;
-		
-		a._attr->prev_attribute_c = attr._attr->prev_attribute_c;
-		a._attr->next_attribute = attr._attr;
-		attr._attr->prev_attribute_c = a._attr;
-				
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
-	{
-		if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
-		
-		// check that attribute belongs to *this
-		xml_attribute_struct* cur = attr._attr;
-
-		while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
-
-		if (cur != _root->first_attribute) return xml_attribute();
-
-		xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
-		if (!a) return xml_attribute();
-
-		a.set_name(name_);
-
-		if (attr._attr->next_attribute)
-			attr._attr->next_attribute->prev_attribute_c = a._attr;
-		else
-			_root->first_attribute->prev_attribute_c = a._attr;
-		
-		a._attr->next_attribute = attr._attr->next_attribute;
-		a._attr->prev_attribute_c = attr._attr;
-		attr._attr->next_attribute = a._attr;
-
-		return a;
-	}
-
-	PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = append_attribute(proto.name());
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = prepend_attribute(proto.name());
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = insert_attribute_after(proto.name(), attr);
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
-	{
-		if (!proto) return xml_attribute();
-
-		xml_attribute result = insert_attribute_before(proto.name(), attr);
-		result.set_value(proto.value());
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
-	{
-		if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
-		
-		xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_));
-
-		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(this->type(), type_)) return xml_node();
-		
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
-		if (!n) return xml_node();
-
-		n._root->parent = _root;
-
-		xml_node_struct* head = _root->first_child;
-
-		if (head)
-		{
-			n._root->prev_sibling_c = head->prev_sibling_c;
-			head->prev_sibling_c = n._root;
-		}
-		else
-			n._root->prev_sibling_c = n._root;
-		
-		n._root->next_sibling = head;
-		_root->first_child = n._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(this->type(), type_)) return xml_node();
-		if (!node._root || node._root->parent != _root) return xml_node();
-	
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
-		if (!n) return xml_node();
-
-		n._root->parent = _root;
-		
-		if (node._root->prev_sibling_c->next_sibling)
-			node._root->prev_sibling_c->next_sibling = n._root;
-		else
-			_root->first_child = n._root;
-		
-		n._root->prev_sibling_c = node._root->prev_sibling_c;
-		n._root->next_sibling = node._root;
-		node._root->prev_sibling_c = n._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(this->type(), type_)) return xml_node();
-		if (!node._root || node._root->parent != _root) return xml_node();
-	
-		xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
-		if (!n) return xml_node();
-
-		n._root->parent = _root;
-	
-		if (node._root->next_sibling)
-			node._root->next_sibling->prev_sibling_c = n._root;
-		else
-			_root->first_child->prev_sibling_c = n._root;
-		
-		n._root->next_sibling = node._root->next_sibling;
-		n._root->prev_sibling_c = node._root;
-		node._root->next_sibling = n._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 result = append_child(proto.type());
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
-	{
-		xml_node result = prepend_child(proto.type());
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
-	{
-		xml_node result = insert_child_after(proto.type(), node);
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
-	{
-		xml_node result = insert_child_before(proto.type(), node);
-
-		if (result) impl::recursive_copy_skip(result, proto, result);
-
-		return result;
-	}
-
-	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;
-
-		// check that attribute belongs to *this
-		xml_attribute_struct* attr = a._attr;
-
-		while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c;
-
-		if (attr != _root->first_attribute) return false;
-
-		if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c;
-		else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c;
-		
-		if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute;
-		else _root->first_attribute = a._attr->next_attribute;
-
-		impl::destroy_attribute(a._attr, impl::get_allocator(_root));
-
-		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;
-
-		if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c;
-		else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c;
-		
-		if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling;
-		else _root->first_child = n._root->next_sibling;
-		
-		impl::destroy_node(n._root, impl::get_allocator(_root));
-
-		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 = static_cast<impl::xml_document_struct*>(root()._root);
-		assert(doc);
-		
-		// 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<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page));
-		(void)page;
-
-		if (!extra) return impl::make_parse_result(status_out_of_memory);
-
-		// save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
-		char_t* rootname = _root->name;
-		_root->name = 0;
-
-		// parse
-		char_t* buffer = 0;
-		xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &buffer);
-
-		// restore name
-		_root->name = rootname;
-
-		// add extra buffer to the list
-		extra->buffer = buffer;
-		extra->next = doc->extra_buffers;
-		doc->extra_buffers = extra;
-
-		return res;
-	}
-
-	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 : 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 : PUGIXML_TEXT("")))
-					return xml_node(i);
-
-		return xml_node();
-	}
-
-#ifndef PUGIXML_NO_STL
-	PUGI__FN string_t xml_node::path(char_t delimiter) const
-	{
-		xml_node cursor = *this; // Make a copy.
-		
-		string_t result = cursor.name();
-
-		while (cursor.parent())
-		{
-			cursor = cursor.parent();
-			
-			string_t temp = cursor.name();
-			temp += delimiter;
-			temp += result;
-			result.swap(temp);
-		}
-
-		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<size_t>(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<size_t>(reinterpret_cast<uintptr_t>(_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, *this, indent, flags, depth);
-	}
-
-#ifndef PUGIXML_NO_STL
-	PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& 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<wchar_t, std::char_traits<wchar_t> >& 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
-	{
-		xml_node_struct* r = root()._root;
-
-		if (!r) return -1;
-
-		const char_t* buffer = static_cast<impl::xml_document_struct*>(r)->buffer;
-
-		if (!buffer) return -1;
-
-		switch (type())
-		{
-		case node_document:
-			return 0;
-
-		case node_element:
-		case node_declaration:
-		case node_pi:
-			return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;
-
-		case node_pcdata:
-		case node_cdata:
-		case node_comment:
-		case node_doctype:
-			return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;
-
-		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;
-
-		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 : 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 : def;
-	}
-
-	PUGI__FN int xml_text::as_int(int def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_int(d ? d->value : 0, def);
-	}
-
-	PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_uint(d ? d->value : 0, def);
-	}
-
-	PUGI__FN double xml_text::as_double(double def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_double(d ? d->value : 0, def);
-	}
-
-	PUGI__FN float xml_text::as_float(float def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_float(d ? d->value : 0, def);
-	}
-
-	PUGI__FN bool xml_text::as_bool(bool def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_bool(d ? d->value : 0, def);
-	}
-
-#ifdef PUGIXML_HAS_LONG_LONG
-	PUGI__FN long long xml_text::as_llong(long long def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_llong(d ? d->value : 0, def);
-	}
-
-	PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
-	{
-		xml_node_struct* d = _data();
-
-		return impl::get_value_ullong(d ? d->value : 0, 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) : false;
-	}
-
-	PUGI__FN bool xml_text::set(int rhs)
-	{
-		xml_node_struct* dn = _data_new();
-
-		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
-	}
-
-	PUGI__FN bool xml_text::set(unsigned int rhs)
-	{
-		xml_node_struct* dn = _data_new();
-
-		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
-	}
-
-	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_convert(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_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
-	}
-
-	PUGI__FN bool xml_text::set(unsigned long long rhs)
-	{
-		xml_node_struct* dn = _data_new();
-
-		return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
-	}
-#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=(double 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<xml_node*>(&_wrap); // BCC32 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<xml_attribute*>(&_wrap); // BCC32 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<xml_node*>(&_wrap); // BCC32 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);
-
-		// initialize sentinel page
-		PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory));
-
-		// align upwards to page boundary
-		void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1));
-
-		// prepare page structure
-		impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory);
-		assert(page);
-
-		page->busy_size = impl::xml_memory_page_size;
-
-		// allocate new root
-		_root = new (page->data) impl::xml_document_struct(page);
-		_root->prev_sibling_c = _root;
-
-		// setup sentinel page
-		page->allocator = static_cast<impl::xml_document_struct*>(_root);
-	}
-
-	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<impl::xml_document_struct*>(_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 = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
-        assert(root_page && !root_page->prev && !root_page->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;
-        }
-
-        _root = 0;
-	}
-
-#ifndef PUGIXML_NO_STL
-	PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
-	{
-		reset();
-
-		return impl::load_stream_impl(*this, stream, options, encoding);
-	}
-
-	PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
-	{
-		reset();
-
-		return impl::load_stream_impl(*this, stream, options, encoding_wchar);
-	}
-#endif
-
-	PUGI__FN xml_parse_result xml_document::load(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_file(const char* path_, unsigned int options, xml_encoding encoding)
-	{
-		reset();
-
-		FILE* file = fopen(path_, "rb");
-
-		return impl::load_file_impl(*this, file, options, encoding);
-	}
-
-	PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
-	{
-		reset();
-
-		FILE* file = impl::open_file_wide(path_, L"rb");
-
-		return impl::load_file_impl(*this, file, options, encoding);
-	}
-
-	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<impl::xml_document_struct*>(_root), _root, const_cast<void*>(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<impl::xml_document_struct*>(_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<impl::xml_document_struct*>(_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<wchar_t>(bom));
-		#else
-			buffered_writer.write('\xef', '\xbb', '\xbf');
-		#endif
-		}
-
-		if (!(flags & format_no_declaration) && !impl::has_declaration(*this))
-		{
-			buffered_writer.write(PUGIXML_TEXT("<?xml version=\"1.0\""));
-			if (encoding == encoding_latin1) buffered_writer.write(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
-			buffered_writer.write('?', '>');
-			if (!(flags & format_raw)) buffered_writer.write('\n');
-		}
-
-		impl::node_output(buffered_writer, *this, indent, flags, 0);
-	}
-
-#ifndef PUGIXML_NO_STL
-	PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& 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<wchar_t, std::char_traits<wchar_t> >& 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
-	{
-		FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
-		return impl::save_file_impl(*this, file, 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
-	{
-		FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
-		return impl::save_file_impl(*this, file, 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 ((i->header & impl::xml_memory_page_type_mask) + 1 == 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<wchar_t>& str)
-	{
-		return impl::as_utf8_impl(str.c_str(), str.size());
-	}
-	
-	PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
-	{
-		assert(str);
-
-		return impl::as_wide_impl(str, strlen(str));
-	}
-	
-	PUGI__FN std::basic_string<wchar_t> 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 <typename T> bool operator()(const T& lhs, const T& rhs) const
-		{
-			return lhs == rhs;
-		}
-	};
-
-	struct not_equal_to
-	{
-		template <typename T> bool operator()(const T& lhs, const T& rhs) const
-		{
-			return lhs != rhs;
-		}
-	};
-
-	struct less
-	{
-		template <typename T> bool operator()(const T& lhs, const T& rhs) const
-		{
-			return lhs < rhs;
-		}
-	};
-
-	struct less_equal
-	{
-		template <typename T> bool operator()(const T& lhs, const T& rhs) const
-		{
-			return lhs <= rhs;
-		}
-	};
-
-	template <typename T> void swap(T& lhs, T& rhs)
-	{
-		T temp = lhs;
-		lhs = rhs;
-		rhs = temp;
-	}
-
-	template <typename I, typename Pred> 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 <typename I> void reverse(I begin, I end)
-	{
-		while (end - begin > 1) swap(*begin++, *--end);
-	}
-
-	template <typename I> 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 <typename I> void copy_backwards(I begin, I end, I target)
-	{
-		while (begin != end) *--target = *--end;
-	}
-
-	template <typename I, typename Pred, typename T> 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 <typename I, typename Pred> 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 <typename I, typename Pred> 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 <typename I, typename Pred> 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 <typename I, typename Pred> 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
-	struct xpath_memory_block
-	{	
-		xpath_memory_block* next;
-
-		char data[
-	#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
-			PUGIXML_MEMORY_XPATH_PAGE_SIZE
-	#else
-			4096
-	#endif
-		];
-	};
-		
-	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)
-		{
-			const size_t block_capacity = sizeof(_root->data);
-
-			// align size so that we're able to store pointers in subsequent blocks
-			size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
-
-			if (_root_size + size <= block_capacity)
-			{
-				void* buf = _root->data + _root_size;
-				_root_size += size;
-				return buf;
-			}
-			else
-			{
-				size_t block_data_size = (size > block_capacity) ? size : block_capacity;
-				size_t block_size = block_data_size + offsetof(xpath_memory_block, data);
-
-				xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
-				if (!block) return 0;
-				
-				block->next = _root;
-				
-				_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)
-		{
-			// align size so that we're able to store pointers in subsequent blocks
-			old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
-			new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
-
-			// we can only reallocate the last object
-			assert(ptr == 0 || static_cast<char*>(ptr) + old_size == _root->data + _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;
-
-			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;
-
-		static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
-		{
-			char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));
-			assert(result);
-
-			memcpy(result, string, length * sizeof(char_t));
-			result[length] = 0;
-
-			return result;
-		}
-
-		static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc)
-		{
-			return duplicate_string(string, strlength(string), alloc);
-		}
-
-	public:
-		xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
-		{
-		}
-
-		explicit xpath_string(const char_t* str, xpath_allocator* alloc)
-		{
-			bool empty_ = (*str == 0);
-
-			_buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc);
-			_uses_heap = !empty_;
-		}
-
-		explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap)
-		{
-		}
-
-		xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc)
-		{
-			assert(begin <= end);
-
-			bool empty_ = (begin == end);
-
-			_buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast<size_t>(end - begin), alloc);
-			_uses_heap = !empty_;
-		}
-
-		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 = strlength(_buffer);
-				size_t source_length = strlength(o._buffer);
-				size_t result_length = target_length + source_length;
-
-				// allocate new buffer
-				char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_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;
-			}
-		}
-
-		const char_t* c_str() const
-		{
-			return _buffer;
-		}
-
-		size_t length() const
-		{
-			return strlength(_buffer);
-		}
-		
-		char_t* data(xpath_allocator* alloc)
-		{
-			// make private heap copy
-			if (!_uses_heap)
-			{
-				_buffer = duplicate_string(_buffer, alloc);
-				_uses_heap = true;
-			}
-
-			return const_cast<char_t*>(_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__FN xpath_string xpath_string_const(const char_t* str)
-	{
-		return xpath_string(str, false);
-	}
-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<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;
-	}
-
-	PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)
-	{
-		if (na.attribute())
-			return xpath_string_const(na.attribute().value());
-		else
-		{
-			const xml_node& n = na.node();
-
-			switch (n.type())
-			{
-			case node_pcdata:
-			case node_cdata:
-			case node_comment:
-			case node_pi:
-				return xpath_string_const(n.value());
-			
-			case node_document:
-			case node_element:
-			{
-				xpath_string result;
-
-				xml_node cur = n.first_child();
-				
-				while (cur && cur != n)
-				{
-					if (cur.type() == node_pcdata || cur.type() == node_cdata)
-						result.append(xpath_string_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 unsigned int node_height(xml_node n)
-	{
-		unsigned int result = 0;
-		
-		while (n)
-		{
-			++result;
-			n = n.parent();
-		}
-		
-		return result;
-	}
-	
-	PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh)
-	{
-		// normalize heights
-		for (unsigned int i = rh; i < lh; i++) ln = ln.parent();
-		for (unsigned int j = lh; j < rh; j++) rn = rn.parent();
-		
-		// one node is the ancestor of the other
-		if (ln == rn) return lh < rh;
-		
-		// find common ancestor
-		while (ln.parent() != rn.parent())
-		{
-			ln = ln.parent();
-			rn = 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
-		for (; ln; ln = ln.next_sibling())
-			if (ln == rn)
-				return true;
-				
-		return false;
-	}
-
-	PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node)
-	{
-		while (node && node != parent) node = node.parent();
-
-		return parent && node == parent;
-	}
-
-	PUGI__FN const void* document_order(const xpath_node& xnode)
-	{
-		xml_node_struct* node = xnode.node().internal_object();
-
-		if (node)
-		{
-			if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name;
-			if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value;
-			return 0;
-		}
-
-		xml_attribute_struct* attr = xnode.attribute().internal_object();
-
-		if (attr)
-		{
-			if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name;
-			if ((attr->header & xml_memory_page_value_allocated_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_order(lhs);
-			const void* ro = document_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;
-			
-			unsigned int lh = node_height(ln);
-			unsigned int rh = node_height(rn);
-			
-			return node_is_before(ln, lh, rn, rh);
-		}
-	};
-
-	struct duplicate_comparator
-	{
-		bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
-		{
-			if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
-			else return rhs.attribute() ? false : lhs.node() < rhs.node();
-		}
-	};
-	
-	PUGI__FN double gen_nan()
-	{
-	#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
-		union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1];
-		u[0].i = 0x7fc00000;
-		return u[0].f;
-	#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_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<char_t*>(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<unsigned int>(static_cast<unsigned int>(*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<unsigned int>(*mantissa - '0') <= 9);
-				*s++ = *mantissa++;
-			}
-		}
-
-		// zero-terminate
-		assert(s < result + result_size);
-		*s = 0;
-
-		return xpath_string(result, true);
-	}
-	
-	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 atof(string);
-	#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<size_t>(end - begin);
-		char_t* scratch = buffer;
-
-		if (length >= sizeof(buffer) / sizeof(buffer[0]))
-		{
-			// need to make dummy on-heap copy
-			scratch = static_cast<char_t*>(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<size_t>(pos - name) : 0;
-		}
-
-		bool operator()(const 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(const 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(const xml_attribute& attr, const 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 void 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;
-	}
-
-	PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to)
-	{
-		size_t to_length = strlength(to);
-
-		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<size_t>(pos - from) < to_length)
-				*write++ = to[pos - from]; // replace
-		}
-
-		// zero-terminate
-		*write = 0;
-	}
-
-	struct xpath_variable_boolean: xpath_variable
-	{
-		xpath_variable_boolean(): value(false)
-		{
-		}
-
-		bool value;
-		char_t name[1];
-	};
-
-	struct xpath_variable_number: xpath_variable
-	{
-		xpath_variable_number(): value(0)
-		{
-		}
-
-		double value;
-		char_t name[1];
-	};
-
-	struct xpath_variable_string: xpath_variable
-	{
-		xpath_variable_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_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<unsigned int>(*str++);
-			result += result << 10;
-			result ^= result >> 6;
-		}
-	
-		result += result << 3;
-		result ^= result >> 11;
-		result += result << 15;
-	
-		return result;
-	}
-
-	template <typename T> 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<xpath_variable_node_set>(name);
-
-		case xpath_type_number:
-			return new_xpath_variable<xpath_variable_number>(name);
-
-		case xpath_type_string:
-			return new_xpath_variable<xpath_variable_string>(name);
-
-		case xpath_type_boolean:
-			return new_xpath_variable<xpath_variable_boolean>(name);
-
-		default:
-			return 0;
-		}
-	}
-
-	template <typename T> 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<xpath_variable_node_set*>(var));
-			break;
-
-		case xpath_type_number:
-			delete_xpath_variable(static_cast<xpath_variable_number*>(var));
-			break;
-
-		case xpath_type_string:
-			delete_xpath_variable(static_cast<xpath_variable_string*>(var));
-			break;
-
-		case xpath_type_boolean:
-			delete_xpath_variable(static_cast<xpath_variable_boolean*>(var));
-			break;
-
-		default:
-			assert(!"Invalid variable type");
-		}
-	}
-
-	PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end)
-	{
-		size_t length = static_cast<size_t>(end - begin);
-		char_t* scratch = buffer;
-
-		if (length >= sizeof(buffer) / sizeof(buffer[0]))
-		{
-			// need to make dummy on-heap copy
-			scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
-			if (!scratch) return 0;
-		}
-
-		// copy string to zero-terminated buffer and perform lookup
-		memcpy(scratch, begin, length * sizeof(char_t));
-		scratch[length] = 0;
-
-		xpath_variable* result = set->get(scratch);
-
-		// free dummy buffer
-		if (scratch != buffer) xml_memory::deallocate(scratch);
-
-		return result;
-	}
-PUGI__NS_END
-
-// Internal node set class
-PUGI__NS_BEGIN
-	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)
-		{
-			sort(begin, end, document_order_comparator());
-
-			type = xpath_node_set::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(!"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<size_t>(_end - _begin);
-		}
-
-		xpath_node first() const
-		{
-			return xpath_first(_begin, _end, _type);
-		}
-
-		void push_back(const xpath_node& node, xpath_allocator* alloc)
-		{
-			if (_end == _eos)
-			{
-				size_t capacity = static_cast<size_t>(_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<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
-				assert(data);
-
-				// finalize
-				_begin = data;
-				_end = data + capacity;
-				_eos = data + new_capacity;
-			}
-
-			*_end++ = node;
-		}
-
-		void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
-		{
-			size_t size_ = static_cast<size_t>(_end - _begin);
-			size_t capacity = static_cast<size_t>(_eos - _begin);
-			size_t count = static_cast<size_t>(end_ - begin_);
-
-			if (size_ + count > capacity)
-			{
-				// reallocate the old array or allocate a new one
-				xpath_node* data = static_cast<xpath_node*>(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__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<size_t>(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_filter_posinv,				// select * from left where right; proximity position invariant
-		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
-	};
-
-	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
-	};
-
-	template <axis_t N> struct axis_to_type
-	{
-		static const axis_t axis;
-	};
-
-	template <axis_t N> const axis_t axis_to_type<N>::axis = N;
-		
-	class xpath_ast_node
-	{
-	private:
-		// node type
-		char _type;
-		char _rettype;
-
-		// for ast_step / ast_predicate
-		char _axis;
-		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;
-		} _data;
-
-		xpath_ast_node(const xpath_ast_node&);
-		xpath_ast_node& operator=(const xpath_ast_node&);
-
-		template <class Comp> 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);
-				xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
-
-				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);
-
-					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);
-
-					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(!"Wrong types");
-			return false;
-		}
-
-		template <class Comp> 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);
-				xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
-
-				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);
-
-				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);
-				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(!"Wrong types");
-				return false;
-			}
-		}
-
-		void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
-		{
-			assert(ns.size() >= first);
-
-			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->rettype() == xpath_type_number)
-				{
-					if (expr->eval_number(c, stack) == i)
-						*last++ = *it;
-				}
-				else if (expr->eval_boolean(c, stack))
-					*last++ = *it;
-			}
-			
-			ns.truncate(last);
-		}
-
-		void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack)
-		{
-			if (ns.size() == first) return;
-			
-			for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
-			{
-				apply_predicate(ns, first, pred->_left, stack);
-			}
-		}
-
-		void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc)
-		{
-			if (!a) return;
-
-			const char_t* name = a.name();
-
-			// There are no attribute nodes corresponding to attributes that declare namespaces
-			// That is, "xmlns:..." or "xmlns"
-			if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;
-			
-			switch (_test)
-			{
-			case nodetest_name:
-				if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc);
-				break;
-				
-			case nodetest_type_node:
-			case nodetest_all:
-				ns.push_back(xpath_node(a, parent), alloc);
-				break;
-				
-			case nodetest_all_in_namespace:
-				if (starts_with(name, _data.nodetest))
-					ns.push_back(xpath_node(a, parent), alloc);
-				break;
-			
-			default:
-				;
-			}
-		}
-		
-		void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc)
-		{
-			if (!n) return;
-
-			switch (_test)
-			{
-			case nodetest_name:
-				if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_type_node:
-				ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_type_comment:
-				if (n.type() == node_comment)
-					ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_type_text:
-				if (n.type() == node_pcdata || n.type() == node_cdata)
-					ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_type_pi:
-				if (n.type() == node_pi)
-					ns.push_back(n, alloc);
-				break;
-									
-			case nodetest_pi:
-				if (n.type() == node_pi && strequal(n.name(), _data.nodetest))
-					ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_all:
-				if (n.type() == node_element)
-					ns.push_back(n, alloc);
-				break;
-				
-			case nodetest_all_in_namespace:
-				if (n.type() == node_element && starts_with(n.name(), _data.nodetest))
-					ns.push_back(n, alloc);
-				break;
-
-			default:
-				assert(!"Unknown axis");
-			} 
-		}
-
-		template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T)
-		{
-			const axis_t axis = T::axis;
-
-			switch (axis)
-			{
-			case axis_attribute:
-			{
-				for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
-					step_push(ns, a, n, alloc);
-				
-				break;
-			}
-			
-			case axis_child:
-			{
-				for (xml_node c = n.first_child(); c; c = c.next_sibling())
-					step_push(ns, c, alloc);
-					
-				break;
-			}
-			
-			case axis_descendant:
-			case axis_descendant_or_self:
-			{
-				if (axis == axis_descendant_or_self)
-					step_push(ns, n, alloc);
-					
-				xml_node cur = n.first_child();
-				
-				while (cur && cur != n)
-				{
-					step_push(ns, cur, 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();
-					}
-				}
-				
-				break;
-			}
-			
-			case axis_following_sibling:
-			{
-				for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
-					step_push(ns, c, alloc);
-				
-				break;
-			}
-			
-			case axis_preceding_sibling:
-			{
-				for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
-					step_push(ns, c, alloc);
-				
-				break;
-			}
-			
-			case axis_following:
-			{
-				xml_node cur = n;
-
-				// exit from this node so that we don't include descendants
-				while (cur && !cur.next_sibling()) cur = cur.parent();
-				cur = cur.next_sibling();
-
-				for (;;)
-				{
-					step_push(ns, cur, alloc);
-
-					if (cur.first_child())
-						cur = cur.first_child();
-					else if (cur.next_sibling())
-						cur = cur.next_sibling();
-					else
-					{
-						while (cur && !cur.next_sibling()) cur = cur.parent();
-						cur = cur.next_sibling();
-
-						if (!cur) break;
-					}
-				}
-
-				break;
-			}
-
-			case axis_preceding:
-			{
-				xml_node cur = n;
-
-				while (cur && !cur.previous_sibling()) cur = cur.parent();
-				cur = cur.previous_sibling();
-
-				for (;;)
-				{
-					if (cur.last_child())
-						cur = cur.last_child();
-					else
-					{
-						// leaf node, can't be ancestor
-						step_push(ns, cur, alloc);
-
-						if (cur.previous_sibling())
-							cur = cur.previous_sibling();
-						else
-						{
-							do 
-							{
-								cur = cur.parent();
-								if (!cur) break;
-
-								if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);
-							}
-							while (!cur.previous_sibling());
-
-							cur = cur.previous_sibling();
-
-							if (!cur) break;
-						}
-					}
-				}
-
-				break;
-			}
-			
-			case axis_ancestor:
-			case axis_ancestor_or_self:
-			{
-				if (axis == axis_ancestor_or_self)
-					step_push(ns, n, alloc);
-
-				xml_node cur = n.parent();
-				
-				while (cur)
-				{
-					step_push(ns, cur, alloc);
-					
-					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(!"Unimplemented axis");
-			}
-		}
-		
-		template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, 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
-					step_push(ns, a, p, alloc);
-
-				xml_node cur = p;
-				
-				while (cur)
-				{
-					step_push(ns, cur, alloc);
-					
-					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 cur = p;
-				
-				for (;;)
-				{
-					if (cur.first_child())
-						cur = cur.first_child();
-					else if (cur.next_sibling())
-						cur = cur.next_sibling();
-					else
-					{
-						while (cur && !cur.next_sibling()) cur = cur.parent();
-						cur = cur.next_sibling();
-						
-						if (!cur) break;
-					}
-
-					step_push(ns, cur, alloc);
-				}
-
-				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, v);
-				break;
-			}
-			
-			default:
-				assert(!"Unimplemented axis");
-			}
-		}
-		
-		template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)
-		{
-			const axis_t axis = T::axis;
-			bool 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);
-
-			xpath_node_set_raw ns;
-			ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
-
-			if (_left)
-			{
-				xpath_node_set_raw s = _left->eval_node_set(c, stack);
-
-				// 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);
-					
-					if (it->node())
-						step_fill(ns, it->node(), stack.result, v);
-					else if (attributes)
-						step_fill(ns, it->attribute(), it->parent(), stack.result, v);
-						
-					apply_predicates(ns, size, stack);
-				}
-			}
-			else
-			{
-				if (c.n.node())
-					step_fill(ns, c.n.node(), stack.result, v);
-				else if (attributes)
-					step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);
-				
-				apply_predicates(ns, 0, stack);
-			}
-
-			// 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<char>(type)), _rettype(static_cast<char>(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<char>(type)), _rettype(static_cast<char>(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<char>(type)), _rettype(static_cast<char>(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<char>(type)), _rettype(static_cast<char>(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<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
-		{
-			_data.nodetest = contents;
-		}
-
-		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_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).empty();
-				}
-
-				default:
-					assert(!"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<double>(c.size);
-			
-			case ast_func_position:
-				return static_cast<double>(c.position);
-
-			case ast_func_count:
-			{
-				xpath_allocator_capture cr(stack.result);
-
-				return static_cast<double>(_left->eval_node_set(c, stack).size());
-			}
-			
-			case ast_func_string_length_0:
-			{
-				xpath_allocator_capture cr(stack.result);
-
-				return static_cast<double>(string_value(c.n, stack.result).length());
-			}
-			
-			case ast_func_string_length_1:
-			{
-				xpath_allocator_capture cr(stack.result);
-
-				return static_cast<double>(_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);
-				
-				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(!"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<xpath_string*>(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<char_t*>(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(result, true);
-		}
-
-		xpath_string eval_string(const xpath_context& c, const xpath_stack& stack)
-		{
-			switch (_type)
-			{
-			case ast_string_constant:
-				return xpath_string_const(_data.string);
-			
-			case ast_func_local_name_0:
-			{
-				xpath_node na = c.n;
-				
-				return xpath_string_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);
-				xpath_node na = ns.first();
-				
-				return xpath_string_const(local_name(na));
-			}
-
-			case ast_func_name_0:
-			{
-				xpath_node na = c.n;
-				
-				return xpath_string_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);
-				xpath_node na = ns.first();
-				
-				return xpath_string_const(qualified_name(na));
-			}
-
-			case ast_func_namespace_uri_0:
-			{
-				xpath_node na = c.n;
-				
-				return xpath_string_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);
-				xpath_node na = ns.first();
-				
-				return xpath_string_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(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* result = pos + p.length();
-
-				return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result);
-			}
-
-			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<size_t>(first);
-				assert(1 <= pos && pos <= s_length + 1);
-
-				const char_t* rbegin = s.c_str() + (pos - 1);
-				
-				return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_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<size_t>(first);
-				size_t end = last >= s_length + 1 ? s_length + 1 : static_cast<size_t>(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_const(rbegin) : xpath_string(rbegin, rend, stack.result);
-			}
-
-			case ast_func_normalize_space_0:
-			{
-				xpath_string s = string_value(c.n, stack.result);
-
-				normalize_space(s.data(stack.result));
-
-				return s;
-			}
-
-			case ast_func_normalize_space_1:
-			{
-				xpath_string s = _left->eval_string(c, stack);
-
-				normalize_space(s.data(stack.result));
-			
-				return s;
-			}
-
-			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);
-
-				translate(s.data(stack.result), from.c_str(), to.c_str());
-
-				return s;
-			}
-
-			case ast_variable:
-			{
-				assert(_rettype == _data.variable->type());
-
-				if (_rettype == xpath_type_string)
-					return xpath_string_const(_data.variable->get_string());
-
-				// fallthrough to type conversion
-			}
-
-			default:
-			{
-				switch (_rettype)
-				{
-				case xpath_type_boolean:
-					return xpath_string_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);
-					return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
-				}
-				
-				default:
-					assert(!"Wrong expression for return type string");
-					return xpath_string();
-				}
-			}
-			}
-		}
-
-		xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)
-		{
-			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);
-				xpath_node_set_raw rs = _right->eval_node_set(c, stack);
-				
-				// 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:
-			case ast_filter_posinv:
-			{
-				xpath_node_set_raw set = _left->eval_node_set(c, stack);
-
-				// either expression is a number or it contains position() call; sort by document order
-				if (_type == ast_filter) set.sort_do();
-
-				apply_predicate(set, 0, _right, stack);
-			
-				return set;
-			}
-			
-			case ast_func_id:
-				return xpath_node_set_raw();
-			
-			case ast_step:
-			{
-				switch (_axis)
-				{
-				case axis_ancestor:
-					return step_do(c, stack, axis_to_type<axis_ancestor>());
-					
-				case axis_ancestor_or_self:
-					return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());
-
-				case axis_attribute:
-					return step_do(c, stack, axis_to_type<axis_attribute>());
-
-				case axis_child:
-					return step_do(c, stack, axis_to_type<axis_child>());
-				
-				case axis_descendant:
-					return step_do(c, stack, axis_to_type<axis_descendant>());
-
-				case axis_descendant_or_self:
-					return step_do(c, stack, axis_to_type<axis_descendant_or_self>());
-
-				case axis_following:
-					return step_do(c, stack, axis_to_type<axis_following>());
-				
-				case axis_following_sibling:
-					return step_do(c, stack, axis_to_type<axis_following_sibling>());
-				
-				case axis_namespace:
-					// namespaced axis is not supported
-					return xpath_node_set_raw();
-				
-				case axis_parent:
-					return step_do(c, stack, axis_to_type<axis_parent>());
-				
-				case axis_preceding:
-					return step_do(c, stack, axis_to_type<axis_preceding>());
-
-				case axis_preceding_sibling:
-					return step_do(c, stack, axis_to_type<axis_preceding_sibling>());
-				
-				case axis_self:
-					return step_do(c, stack, axis_to_type<axis_self>());
-
-				default:
-					assert(!"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(!"Wrong expression for return type node set");
-				return xpath_node_set_raw();
-			}
-		}
-		
-		bool is_posinv()
-		{
-			switch (_type)
-			{
-			case ast_func_position:
-				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:
-			case ast_filter_posinv:
-				return true;
-
-			default:
-				if (_left && !_left->is_posinv()) return false;
-				
-				for (xpath_ast_node* n = _right; n; n = n->_next)
-					if (!n->is_posinv()) return false;
-					
-				return true;
-			}
-		}
-
-		xpath_value_type rettype() const
-		{
-			return static_cast<xpath_value_type>(_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<size_t>(value.end - value.begin);
-
-				char_t* c = static_cast<char_t*>(_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 = get_variable_scratch(_scratch, _variables, name.begin, name.end);
-
-				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");
-
-				bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv();
-
-				n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
-
-				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");
-			
-			xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name));
-			
-			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, xpath_type_node_set, expr);
-				
-				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();
-			
-			if (_lexer.current() != lex_eof)
-			{
-				// there are still unparsed tokens left, error
-				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));
-
-			return new (memory) xpath_query_impl();
-		}
-
-		static void destroy(void* ptr)
-		{
-			if (!ptr) return;
-			
-			// free all allocated pages
-			static_cast<xpath_query_impl*>(ptr)->alloc.release();
-
-			// free allocator memory (with the first page)
-			xml_memory::deallocate(ptr);
-		}
-
-		xpath_query_impl(): root(0), alloc(&block)
-		{
-			block.next = 0;
-		}
-
-		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__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_)
-	{
-		assert(begin_ <= end_);
-
-		size_t size_ = static_cast<size_t>(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_;
-		}
-		else
-		{
-			// make heap copy
-			xpath_node* storage = static_cast<xpath_node*>(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_;
-		}
-	}
-
-	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_), _begin(&_storage), _end(&_storage)
-	{
-		_assign(begin_, end_);
-	}
-
-	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(ns._type), _begin(&_storage), _end(&_storage)
-	{
-		_assign(ns._begin, ns._end);
-	}
-	
-	PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
-	{
-		if (this == &ns) return *this;
-		
-		_type = ns._type;
-		_assign(ns._begin, ns._end);
-
-		return *this;
-	}
-
-	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(): _type(xpath_type_none), _next(0)
-	{
-	}
-
-	PUGI__FN const char_t* xpath_variable::name() const
-	{
-		switch (_type)
-		{
-		case xpath_type_node_set:
-			return static_cast<const impl::xpath_variable_node_set*>(this)->name;
-
-		case xpath_type_number:
-			return static_cast<const impl::xpath_variable_number*>(this)->name;
-
-		case xpath_type_string:
-			return static_cast<const impl::xpath_variable_string*>(this)->name;
-
-		case xpath_type_boolean:
-			return static_cast<const impl::xpath_variable_boolean*>(this)->name;
-
-		default:
-			assert(!"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<const impl::xpath_variable_boolean*>(this)->value : false;
-	}
-
-	PUGI__FN double xpath_variable::get_number() const
-	{
-		return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(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<const impl::xpath_variable_string*>(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<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;
-	}
-
-	PUGI__FN bool xpath_variable::set(bool value)
-	{
-		if (_type != xpath_type_boolean) return false;
-
-		static_cast<impl::xpath_variable_boolean*>(this)->value = value;
-		return true;
-	}
-
-	PUGI__FN bool xpath_variable::set(double value)
-	{
-		if (_type != xpath_type_number) return false;
-
-		static_cast<impl::xpath_variable_number*>(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<impl::xpath_variable_string*>(this);
-
-		// duplicate string
-		size_t size = (impl::strlength(value) + 1) * sizeof(char_t);
-
-		char_t* copy = static_cast<char_t*>(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<impl::xpath_variable_node_set*>(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)
-		{
-			xpath_variable* var = _data[i];
-
-			while (var)
-			{
-				xpath_variable* next = var->_next;
-
-				impl::delete_xpath_variable(var->_type, var);
-
-				var = next;
-			}
-		}
-	}
-
-	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 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->_type = type;
-			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
-		{
-			impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);
-
-			qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
-
-			if (qimpl->root)
-			{
-				_impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());
-				_result.error = 0;
-			}
-		}
-	}
-
-	PUGI__FN xpath_query::~xpath_query()
-	{
-		impl::xpath_query_impl::destroy(_impl);
-	}
-
-	PUGI__FN xpath_value_type xpath_query::return_type() const
-	{
-		if (!_impl) return xpath_type_none;
-
-		return static_cast<impl::xpath_query_impl*>(_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::xpath_query_impl*>(_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::xpath_query_impl*>(_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;
-
-		return impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd).c_str();
-	}
-#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::xpath_query_impl*>(_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
-	{
-		if (!_impl) return xpath_node_set();
-
-		impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;
-
-		if (root->rettype() != xpath_type_node_set)
-		{
-		#ifdef PUGIXML_NO_EXCEPTIONS
-			return xpath_node_set();
-		#else
-			xpath_parse_result res;
-			res.error = "Expression does not evaluate to node set";
-
-			throw xpath_exception(res);
-		#endif
-		}
-		
-		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);
-
-		return xpath_node_set(r.begin(), r.end(), r.type());
-	}
-
-	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_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
-	{
-		xpath_node_set s = query.evaluate_node_set(*this);
-		return s.empty() ? xpath_node() : s.first();
-	}
-
-	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);
-	}
-}
-
-#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__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__IS_CHARTYPE_IMPL
-#undef PUGI__IS_CHARTYPE
-#undef PUGI__IS_CHARTYPEX
-#undef PUGI__SKIPWS
-#undef PUGI__OPTSET
-#undef PUGI__PUSHNODE
-#undef PUGI__POPNODE
-#undef PUGI__SCANFOR
-#undef PUGI__SCANWHILE
-#undef PUGI__ENDSEG
-#undef PUGI__THROW_ERROR
-#undef PUGI__CHECK_ERROR
-
-#endif
-
-/**
- * Copyright (c) 2006-2014 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/libsrc/pugixml.hpp b/libsrc/pugixml.hpp
deleted file mode 100644
index 6fb99be..0000000
--- a/libsrc/pugixml.hpp
+++ /dev/null
@@ -1,1332 +0,0 @@
-/**
- * pugixml parser - version 1.4
- * --------------------------------------------------------
- * Copyright (C) 2006-2014, 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 140
-#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 <stddef.h>
-
-// Include exception header for XPath
-#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
-#	include <exception>
-#endif
-
-// Include STL headers
-#ifndef PUGIXML_NO_STL
-#	include <iterator>
-#	include <iosfwd>
-#	include <string>
-#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 defined(__cplusplus) && __cplusplus >= 201103
-#		define PUGIXML_HAS_LONG_LONG
-#	elif defined(_MSC_VER) && _MSC_VER >= 1400
-#		define PUGIXML_HAS_LONG_LONG
-#	endif
-#endif
-
-// Character interface macros
-#ifdef PUGIXML_WCHAR_MODE
-#	define PUGIXML_TEXT(t) L ## t
-#	define PUGIXML_CHAR wchar_t
-#else
-#	define PUGIXML_TEXT(t) t
-#	define PUGIXML_CHAR char
-#endif
-
-namespace pugi
-{
-	// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
-	typedef PUGIXML_CHAR char_t;
-
-#ifndef PUGIXML_NO_STL
-	// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
-	typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > 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/>'
-		node_pcdata,		// Plain character data, i.e. 'text'
-		node_cdata,			// Character data, i.e. '<![CDATA[text]]>'
-		node_comment,		// Comment tag, i.e. '<!-- text -->'
-		node_pi,			// Processing instruction, i.e. '<?name?>'
-		node_declaration,	// Document declaration, i.e. '<?xml version="1.0"?>'
-		node_doctype		// Document type declaration, i.e. '<!DOCTYPE doc>'
-	};
-
-	// Parsing options
-
-	// Minimal parsing mode (equivalent to turning all other flags off).
-	// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
-	const unsigned int parse_minimal = 0x0000;
-
-	// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
-	const unsigned int parse_pi = 0x0001;
-
-	// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
-	const unsigned int parse_comments = 0x0002;
-
-	// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
-	const unsigned int parse_cdata = 0x0004;
-
-	// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
-	// This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
-	const unsigned int parse_ws_pcdata = 0x0008;
-
-	// This flag determines if character and entity references are expanded during parsing. This flag is on by default.
-	const unsigned int parse_escapes = 0x0010;
-
-	// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
-	const unsigned int parse_eol = 0x0020;
-	
-	// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
-	const unsigned int parse_wconv_attribute = 0x0040;
-
-	// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
-	const unsigned int parse_wnorm_attribute = 0x0080;
-	
-	// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
-	const unsigned int parse_declaration = 0x0100;
-
-	// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
-	const unsigned int parse_doctype = 0x0200;
-
-	// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
-	// of whitespace is added to the DOM tree.
-	// This flag is off by default; turning it on may result in slower parsing and more memory consumption.
-	const unsigned int parse_ws_pcdata_single = 0x0400;
-
-	// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
-	const unsigned int parse_trim_pcdata = 0x0800;
-
-	// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
-	// is a valid document. This flag is off by default.
-	const unsigned int parse_fragment = 0x1000;
-
-	// The default parsing mode.
-	// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
-	// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
-	const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
-
-	// The full parsing mode.
-	// Nodes of all types are added to the DOM tree, character/reference entities are expanded,
-	// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
-	const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
-
-	// These flags determine the encoding of input data for XML document
-	enum xml_encoding
-	{
-		encoding_auto,		// Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
-		encoding_utf8,		// UTF8 encoding
-		encoding_utf16_le,	// Little-endian UTF16
-		encoding_utf16_be,	// Big-endian UTF16
-		encoding_utf16,		// UTF16 with native endianness
-		encoding_utf32_le,	// Little-endian UTF32
-		encoding_utf32_be,	// Big-endian UTF32
-		encoding_utf32,		// UTF32 with native endianness
-		encoding_wchar,		// The same encoding wchar_t has (either UTF16 or UTF32)
-		encoding_latin1
-	};
-
-	// Formatting flags
-	
-	// Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
-	const unsigned int format_indent = 0x01;
-	
-	// Write encoding-specific BOM to the output stream. This flag is off by default.
-	const unsigned int format_write_bom = 0x02;
-
-	// Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
-	const unsigned int format_raw = 0x04;
-	
-	// Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
-	const unsigned int format_no_declaration = 0x08;
-
-	// Don't escape attribute values and PCDATA contents. This flag is off by default.
-	const unsigned int format_no_escapes = 0x10;
-
-	// Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
-	const unsigned int format_save_file_text = 0x20;
-
-	// The default set of formatting flags.
-	// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
-	const unsigned int format_default = format_indent;
-		
-	// Forward declarations
-	struct xml_attribute_struct;
-	struct xml_node_struct;
-
-	class xml_node_iterator;
-	class xml_attribute_iterator;
-	class xml_named_node_iterator;
-
-	class xml_tree_walker;
-
-	struct xml_parse_result;
-
-	class xml_node;
-
-	class xml_text;
-	
-	#ifndef PUGIXML_NO_XPATH
-	class xpath_node;
-	class xpath_node_set;
-	class xpath_query;
-	class xpath_variable_set;
-	#endif
-
-	// Range-based for loop support
-	template <typename It> class xml_object_range
-	{
-	public:
-		typedef It const_iterator;
-		typedef It iterator;
-
-		xml_object_range(It b, It e): _begin(b), _end(e)
-		{
-		}
-
-		It begin() const { return _begin; }
-		It end() const { return _end; }
-
-	private:
-		It _begin, _end;
-	};
-
-	// Writer interface for node printing (see xml_node::print)
-	class PUGIXML_CLASS xml_writer
-	{
-	public:
-		virtual ~xml_writer() {}
-
-		// Write memory chunk into stream/file/whatever
-		virtual void write(const void* data, size_t size) = 0;
-	};
-
-	// xml_writer implementation for FILE*
-	class PUGIXML_CLASS xml_writer_file: public xml_writer
-	{
-	public:
-		// Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
-		xml_writer_file(void* file);
-
-		virtual void write(const void* data, size_t size);
-
-	private:
-		void* file;
-	};
-
-	#ifndef PUGIXML_NO_STL
-	// xml_writer implementation for streams
-	class PUGIXML_CLASS xml_writer_stream: public xml_writer
-	{
-	public:
-		// Construct writer from an output stream object
-		xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
-		xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
-
-		virtual void write(const void* data, size_t size);
-
-	private:
-		std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
-		std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
-	};
-	#endif
-
-	// A light-weight handle for manipulating attributes in DOM tree
-	class PUGIXML_CLASS xml_attribute
-	{
-		friend class xml_attribute_iterator;
-		friend class xml_node;
-
-	private:
-		xml_attribute_struct* _attr;
-	
-		typedef void (*unspecified_bool_type)(xml_attribute***);
-
-	public:
-		// Default constructor. Constructs an empty attribute.
-		xml_attribute();
-		
-		// Constructs attribute from internal pointer
-		explicit xml_attribute(xml_attribute_struct* attr);
-
-		// Safe bool conversion operator
-		operator unspecified_bool_type() const;
-
-		// Borland C++ workaround
-		bool operator!() const;
-
-		// Comparison operators (compares wrapped attribute pointers)
-		bool operator==(const xml_attribute& r) const;
-		bool operator!=(const xml_attribute& r) const;
-		bool operator<(const xml_attribute& r) const;
-		bool operator>(const xml_attribute& r) const;
-		bool operator<=(const xml_attribute& r) const;
-		bool operator>=(const xml_attribute& r) const;
-
-		// Check if attribute is empty
-		bool empty() const;
-
-		// Get attribute name/value, or "" if attribute is empty
-		const char_t* name() const;
-		const char_t* value() const;
-
-		// Get attribute value, or the default value if attribute is empty
-		const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
-
-		// Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
-		int as_int(int def = 0) const;
-		unsigned int as_uint(unsigned int def = 0) const;
-		double as_double(double def = 0) const;
-		float as_float(float def = 0) const;
-
-	#ifdef PUGIXML_HAS_LONG_LONG
-		long long as_llong(long long def = 0) const;
-		unsigned long long as_ullong(unsigned long long def = 0) const;
-	#endif
-
-		// Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
-		bool as_bool(bool def = false) const;
-
-		// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
-		bool set_name(const char_t* rhs);
-		bool set_value(const char_t* rhs);
-
-		// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
-		bool set_value(int rhs);
-		bool set_value(unsigned int rhs);
-		bool set_value(double rhs);
-		bool set_value(bool rhs);
-
-	#ifdef PUGIXML_HAS_LONG_LONG
-		bool set_value(long long rhs);
-		bool set_value(unsigned long long rhs);
-	#endif
-
-		// Set attribute value (equivalent to set_value without error checking)
-		xml_attribute& operator=(const char_t* rhs);
-		xml_attribute& operator=(int rhs);
-		xml_attribute& operator=(unsigned int rhs);
-		xml_attribute& operator=(double rhs);
-		xml_attribute& operator=(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 <node>text</node> 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 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);
-
-		// 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 <typename Predicate> 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 <typename Predicate> 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 <typename Predicate> 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_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
-		xpath_node select_single_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;
-	#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<char, std::char_traits<char> >& 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<wchar_t, std::char_traits<wchar_t> >& 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<xml_node_iterator> children() const;
-		xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
-		xml_object_range<xml_attribute_iterator> attributes() const;
-
-		// Get node offset in parsed file/string (in char_t units) for debugging purposes
-		ptrdiff_t offset_debug() const;
-
-		// Get hash value (unique for handles to the same object)
-		size_t hash_value() const;
-
-		// Get internal pointer
-		xml_node_struct* internal_object() const;
-	};
-
-#ifdef __BORLANDC__
-	// Borland C++ workaround
-	bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
-	bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
-#endif
-
-	// A helper for working with text inside PCDATA nodes
-	class PUGIXML_CLASS xml_text
-	{
-		friend class xml_node;
-
-		xml_node_struct* _root;
-
-		typedef void (*unspecified_bool_type)(xml_text***);
-
-		explicit xml_text(xml_node_struct* root);
-
-		xml_node_struct* _data_new();
-		xml_node_struct* _data() const;
-
-	public:
-		// Default constructor. Constructs an empty object.
-		xml_text();
-
-		// Safe bool conversion operator
-		operator unspecified_bool_type() const;
-
-		// Borland C++ workaround
-		bool operator!() const;
-
-		// Check if text object is empty
-		bool empty() const;
-
-		// Get text, or "" if object is empty
-		const char_t* get() const;
-
-		// Get text, or the default value if object is empty
-		const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
-
-		// Get text as a number, or the default value if conversion did not succeed or object is empty
-		int as_int(int def = 0) const;
-		unsigned int as_uint(unsigned int def = 0) const;
-		double as_double(double def = 0) const;
-		float as_float(float def = 0) const;
-
-	#ifdef PUGIXML_HAS_LONG_LONG
-		long long as_llong(long long def = 0) const;
-		unsigned long long as_ullong(unsigned long long def = 0) const;
-	#endif
-
-		// Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
-		bool as_bool(bool def = false) const;
-
-		// Set text (returns false if object is empty or there is not enough memory)
-		bool set(const char_t* rhs);
-
-		// Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
-		bool set(int rhs);
-		bool set(unsigned int rhs);
-		bool set(double rhs);
-		bool set(bool rhs);
-
-	#ifdef PUGIXML_HAS_LONG_LONG
-		bool set(long long rhs);
-		bool set(unsigned long long rhs);
-	#endif
-
-		// Set text (equivalent to set without error checking)
-		xml_text& operator=(const char_t* rhs);
-		xml_text& operator=(int rhs);
-		xml_text& operator=(unsigned int rhs);
-		xml_text& operator=(double rhs);
-		xml_text& operator=(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&);
-		const 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<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
-		xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
-	#endif
-
-		// 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 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<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
-		void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& 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();
-
-		// 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];
-
-		// Non-copyable semantics
-		xpath_variable_set(const xpath_variable_set&);
-		xpath_variable_set& operator=(const xpath_variable_set&);
-
-		xpath_variable* find(const char_t* name) const;
-
-	public:
-		// Default constructor/destructor
-		xpath_variable_set();
-		~xpath_variable_set();
-
-		// 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);
-
-		// Destructor
-		~xpath_query();
-
-		// 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;
-
-		// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
-		const xpath_parse_result& result() const;
-
-		// Safe bool conversion operator
-		operator unspecified_bool_type() const;
-
-		// Borland C++ workaround
-		bool operator!() const;
-	};
-	
-	#ifndef PUGIXML_NO_EXCEPTIONS
-	// XPath exception class
-	class PUGIXML_CLASS xpath_exception: public std::exception
-	{
-	private:
-		xpath_parse_result _result;
-
-	public:
-		// Construct exception from parse result
-		explicit xpath_exception(const xpath_parse_result& result);
-
-		// Get error message
-		virtual const char* what() const throw();
-
-		// Get parse result
-		const xpath_parse_result& result() const;
-	};
-	#endif
-	
-	// XPath node class (either xml_node or xml_attribute)
-	class PUGIXML_CLASS xpath_node
-	{
-	private:
-		xml_node _node;
-		xml_attribute _attribute;
-	
-		typedef void (*unspecified_bool_type)(xpath_node***);
-
-	public:
-		// Default constructor; constructs empty XPath node
-		xpath_node();
-		
-		// Construct XPath node from XML node/attribute
-		xpath_node(const xml_node& node);
-		xpath_node(const xml_attribute& attribute, const xml_node& parent);
-
-		// Get node/attribute, if any
-		xml_node node() const;
-		xml_attribute attribute() const;
-		
-		// Get parent of contained node/attribute
-		xml_node parent() const;
-
-		// Safe bool conversion operator
-		operator unspecified_bool_type() const;
-		
-		// Borland C++ workaround
-		bool operator!() const;
-
-		// Comparison operators
-		bool operator==(const xpath_node& n) const;
-		bool operator!=(const xpath_node& n) const;
-	};
-
-#ifdef __BORLANDC__
-	// Borland C++ workaround
-	bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
-	bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
-#endif
-
-	// A fixed-size collection of XPath nodes
-	class PUGIXML_CLASS xpath_node_set
-	{
-	public:
-		// Collection type
-		enum type_t
-		{
-			type_unsorted,			// Not ordered
-			type_sorted,			// Sorted by document order (ascending)
-			type_sorted_reverse		// Sorted by document order (descending)
-		};
-		
-		// Constant iterator type
-		typedef const xpath_node* const_iterator;
-	
-		// 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);
-
-		// 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);
-	};
-#endif
-
-#ifndef PUGIXML_NO_STL
-	// Convert wide string to UTF8
-	std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
-	std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
-	
-	// Convert UTF8 to wide string
-	std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
-	std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& 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
-
-/**
- * Copyright (c) 2006-2014 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/libsrc/serialization.cpp b/libsrc/serialization.cpp
new file mode 100644
index 0000000..fc613b2
--- /dev/null
+++ b/libsrc/serialization.cpp
@@ -0,0 +1,323 @@
+#include <sstream>
+#include <string>
+
+#include "ismrmrd/serialization.h"
+#include "ismrmrd/xml.h"
+
+namespace ISMRMRD {
+
+void serialize(const Acquisition &acq, WritableStreamView &ws) {
+    AcquisitionHeader ahead = acq.getHead();
+    ws.write(reinterpret_cast<const char *>(&ahead), sizeof(AcquisitionHeader));
+    ws.write(reinterpret_cast<const char *>(acq.getTrajPtr()), ahead.trajectory_dimensions * ahead.number_of_samples * sizeof(float));
+    ws.write(reinterpret_cast<const char *>(acq.getDataPtr()), ahead.number_of_samples * ahead.active_channels * 2 * sizeof(float));
+    if (ws.bad()) {
+        throw std::runtime_error("Error writing acquisition to stream");
+    }
+}
+
+template <typename T>
+void serialize(const Image<T> &img, WritableStreamView &ws) {
+    ImageHeader ihead = img.getHead();
+    if (ismrmrd_sizeof_data_type(ihead.data_type) != sizeof(T)) {
+        throw std::runtime_error("Image data type does not match template type");
+    }
+    ws.write(reinterpret_cast<const char *>(&ihead), sizeof(ImageHeader));
+    uint64_t attr_length = img.getAttributeStringLength();
+    ws.write(reinterpret_cast<char *>(&attr_length), sizeof(uint64_t));
+    if (attr_length) {
+        ws.write(img.getAttributeString(), ihead.attribute_string_len);
+    }
+    ws.write(reinterpret_cast<const char *>(img.getDataPtr()), img.getDataSize());
+    if (ws.bad()) {
+        throw std::runtime_error("Error writing image to stream");
+    }
+}
+
+void serialize(const Waveform &wfm, WritableStreamView &ws) {
+    ws.write(reinterpret_cast<const char *>(&wfm.head), sizeof(ISMRMRD_WaveformHeader));
+    ws.write(reinterpret_cast<const char *>(wfm.begin_data()), wfm.head.number_of_samples * wfm.head.channels * sizeof(uint32_t));
+    if (ws.bad()) {
+        throw std::runtime_error("Error writing waveform to stream");
+    }
+}
+
+void serialize(const ConfigFile &cfg, WritableStreamView &ws) {
+    ws.write(cfg.config, sizeof(cfg.config));
+    if (ws.bad()) {
+        throw std::runtime_error("Error writing fixed length char array to stream");
+    }
+}
+
+void serialize(const std::string &str, WritableStreamView &ws) {
+    uint32_t len = static_cast<uint32_t>(str.length());
+    ws.write(reinterpret_cast<char *>(&len), sizeof(uint32_t));
+    ws.write(str.c_str(), len);
+    if (ws.bad()) {
+        throw std::runtime_error("Error writing string to stream");
+    }
+}
+
+void deserialize(Acquisition &acq, ReadableStreamView &rs) {
+    AcquisitionHeader ahead;
+    rs.read(reinterpret_cast<char *>(&ahead), sizeof(AcquisitionHeader));
+    acq.setHead(ahead);
+    rs.read(reinterpret_cast<char *>(acq.getTrajPtr()), ahead.trajectory_dimensions * ahead.number_of_samples * sizeof(float));
+    rs.read(reinterpret_cast<char *>(acq.getDataPtr()), ahead.number_of_samples * ahead.active_channels * 2 * sizeof(float));
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading acquisition");
+    }
+}
+
+// Helper function that deserializes attributes and pixels
+template <typename T>
+void deserialize_attr_and_pixels(Image<T> &img, ReadableStreamView &rs) {
+    uint64_t attr_length;
+    rs.read(reinterpret_cast<char *>(&attr_length), sizeof(uint64_t));
+    if (attr_length) {
+        std::vector<char> attr(attr_length + 1);
+        rs.read(&attr[0], attr_length);
+        attr[attr_length] = '\0';
+        img.setAttributeString(&attr[0]);
+    }
+    rs.read(reinterpret_cast<char *>(img.getDataPtr()), img.getDataSize());
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading image");
+    }
+}
+
+template <typename T>
+void deserialize(Image<T> &img, ReadableStreamView &rs) {
+    ImageHeader ihead;
+    rs.read(reinterpret_cast<char *>(&ihead), sizeof(ImageHeader));
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading image header");
+    }
+    if (ismrmrd_sizeof_data_type(ihead.data_type) != sizeof(T)) {
+        throw std::runtime_error("Image data type does not match template type");
+    }
+    img.setHead(ihead);
+    deserialize_attr_and_pixels(img, rs);
+}
+
+void deserialize(Waveform &wfm, ReadableStreamView &rs) {
+#if __cplusplus > 199711L
+    static_assert(std::is_same<decltype(wfm.head), ISMRMRD_WaveformHeader>::value, "Waveform header type mismatch");
+#endif
+    rs.read(reinterpret_cast<char *>(&wfm.head), sizeof(ISMRMRD_WaveformHeader));
+    ismrmrd_make_consistent_waveform(&wfm);
+    rs.read(reinterpret_cast<char *>(wfm.begin_data()), wfm.head.number_of_samples * wfm.head.channels * sizeof(uint32_t));
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading waveform");
+    }
+}
+
+void deserialize(ConfigFile &cfg, ReadableStreamView &rs) {
+    rs.read(cfg.config, sizeof(cfg.config));
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading fixed length char array");
+    }
+}
+
+void deserialize(std::string &str, ReadableStreamView &rs) {
+    uint32_t len;
+    rs.read(reinterpret_cast<char *>(&len), sizeof(uint32_t));
+    std::vector<char> buf(len);
+    rs.read(&buf[0], len);
+    if (rs.eof()) {
+        throw std::runtime_error("Error reading string");
+    }
+    // Past implementations would serialize strings with the null terminator added at the end.
+    // We remove that here.
+    if (buf[len - 1] == '\0') {
+        len--;
+    }
+    str.assign(&buf[0], len);
+}
+
+ProtocolSerializer::ProtocolSerializer(WritableStreamView &ws) : _ws(ws) {}
+
+void ProtocolSerializer::write_msg_id(uint16_t id) {
+    _ws.write(reinterpret_cast<const char *>(&id), sizeof(uint16_t));
+}
+
+void ProtocolSerializer::serialize(const ConfigFile &cf) {
+    write_msg_id(ISMRMRD_MESSAGE_CONFIG_FILE);
+    ISMRMRD::serialize(cf, _ws);
+}
+
+void ProtocolSerializer::serialize(const ConfigText &ct) {
+    write_msg_id(ISMRMRD_MESSAGE_CONFIG_TEXT);
+    ISMRMRD::serialize(ct.config_text, _ws);
+}
+
+void ProtocolSerializer::serialize(const TextMessage &tm) {
+    write_msg_id(ISMRMRD_MESSAGE_TEXT);
+    ISMRMRD::serialize(tm.message, _ws);
+}
+
+void ProtocolSerializer::serialize(const IsmrmrdHeader &hdr) {
+    std::stringstream str;
+    ISMRMRD::serialize(hdr, str);
+    auto as_str = str.str();
+    uint32_t size = static_cast<uint32_t>(as_str.size());
+    write_msg_id(ISMRMRD_MESSAGE_HEADER);
+    _ws.write(reinterpret_cast<const char *>(&size), sizeof(uint32_t));
+    _ws.write(as_str.c_str(), as_str.size());
+    if (_ws.bad()) {
+        throw std::runtime_error("Error writing header to stream");
+    }
+}
+
+void ProtocolSerializer::serialize(const Acquisition &acq) {
+    write_msg_id(ISMRMRD_MESSAGE_ACQUISITION);
+    ISMRMRD::serialize(acq, _ws);
+}
+
+template <typename T>
+void ProtocolSerializer::serialize(const Image<T> &img) {
+    write_msg_id(ISMRMRD_MESSAGE_IMAGE);
+    ISMRMRD::serialize(img, _ws);
+}
+
+void ProtocolSerializer::serialize(const Waveform &wfm) {
+    write_msg_id(ISMRMRD_MESSAGE_WAVEFORM);
+    ISMRMRD::serialize(wfm, _ws);
+}
+
+void ProtocolSerializer::close() {
+    write_msg_id(ISMRMRD_MESSAGE_CLOSE);
+}
+
+ProtocolDeserializer::ProtocolDeserializer(ReadableStreamView &rs) : _rs(rs), _peeked(ISMRMRD_MESSAGE_UNPEEKED) {}
+
+uint16_t ProtocolDeserializer::peek() {
+    if (_peeked == ISMRMRD_MESSAGE_UNPEEKED) {
+        _rs.read(reinterpret_cast<char *>(&_peeked), sizeof(uint16_t));
+        if (_peeked == ISMRMRD_MESSAGE_IMAGE) {
+            _rs.read(reinterpret_cast<char *>(&_peeked_image_header), sizeof(ImageHeader));
+        }
+        if (_rs.eof()) {
+            throw std::runtime_error("Error reading message ID");
+        }
+    }
+    return _peeked;
+}
+
+int ProtocolDeserializer::peek_image_data_type() {
+    if (_peeked == ISMRMRD_MESSAGE_IMAGE) {
+        return _peeked_image_header.data_type;
+    } else {
+        throw std::runtime_error("Cannot peak image data type if not peeking an image");
+    }
+}
+
+void ProtocolDeserializer::deserialize(ConfigFile &cf) {
+    if (peek() != ISMRMRD_MESSAGE_CONFIG_FILE) {
+        throw std::runtime_error("Expected config file message");
+    }
+    ISMRMRD::deserialize(cf, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+void ProtocolDeserializer::deserialize(ConfigText &ct) {
+    if (peek() != ISMRMRD_MESSAGE_CONFIG_TEXT) {
+        throw std::runtime_error("Expected config text message");
+    }
+    ISMRMRD::deserialize(ct.config_text, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+void ProtocolDeserializer::deserialize(TextMessage &tm) {
+    if (peek() != ISMRMRD_MESSAGE_TEXT) {
+        throw std::runtime_error("Expected text message");
+    }
+    ISMRMRD::deserialize(tm.message, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+void ProtocolDeserializer::deserialize(IsmrmrdHeader &hdr) {
+    if (peek() == ISMRMRD_MESSAGE_CLOSE) {
+        throw ProtocolStreamClosed();
+    }
+    if (peek() != ISMRMRD_MESSAGE_HEADER) {
+        throw std::runtime_error("Expected ISMRMRD_MESSAGE_HEADER");
+    }
+    uint32_t size;
+    _rs.read(reinterpret_cast<char *>(&size), sizeof(uint32_t));
+    std::string str(size, '\0');
+    _rs.read(&str[0], size);
+    ISMRMRD::deserialize(str.c_str(), hdr);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+void ProtocolDeserializer::deserialize(Acquisition &acq) {
+    if (peek() == ISMRMRD_MESSAGE_CLOSE) {
+        throw ProtocolStreamClosed();
+    }
+    if (peek() != ISMRMRD_MESSAGE_ACQUISITION) {
+        throw std::runtime_error("Expected ISMRMRD_MESSAGE_ACQUISITION");
+    }
+    ISMRMRD::deserialize(acq, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+template <typename T>
+void ProtocolDeserializer::deserialize(Image<T> &img) {
+    if (peek() == ISMRMRD_MESSAGE_CLOSE) {
+        throw ProtocolStreamClosed();
+    }
+    if (peek() != ISMRMRD_MESSAGE_IMAGE) {
+        throw std::runtime_error("Expected ISMRMRD_MESSAGE_IMAGE");
+    }
+    img.setHead(_peeked_image_header);
+    deserialize_attr_and_pixels(img, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+void ProtocolDeserializer::deserialize(Waveform &wfm) {
+    if (peek() == ISMRMRD_MESSAGE_CLOSE) {
+        throw ProtocolStreamClosed();
+    }
+    if (peek() != ISMRMRD_MESSAGE_WAVEFORM) {
+        throw std::runtime_error("Expected ISMRMRD_MESSAGE_WAVEFORM");
+    }
+    ISMRMRD::deserialize(wfm, _rs);
+    _peeked = ISMRMRD_MESSAGE_UNPEEKED;
+}
+
+// template instantiations
+template EXPORTISMRMRD void serialize(const Image<unsigned short> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<unsigned int> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<short> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<int> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<float> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<double> &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<std::complex<float> > &img, WritableStreamView &ws);
+template EXPORTISMRMRD void serialize(const Image<std::complex<double> > &img, WritableStreamView &ws);
+template EXPORTISMRMRD void deserialize(Image<unsigned short> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<unsigned int> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<short> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<int> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<float> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<double> &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<std::complex<float> > &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void deserialize(Image<std::complex<double> > &img, ReadableStreamView &rs);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<unsigned short> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<unsigned int> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<short> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<int> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<float> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<double> &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<std::complex<float> > &img);
+template EXPORTISMRMRD void ProtocolSerializer::serialize(const Image<std::complex<double> > &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<unsigned short> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<unsigned int> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<short> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<int> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<float> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<double> &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<std::complex<float> > &img);
+template EXPORTISMRMRD void ProtocolDeserializer::deserialize(Image<std::complex<double> > &img);
+
+} // namespace ISMRMRD
diff --git a/libsrc/xml.cpp b/libsrc/xml.cpp
index 9476e47..917f453 100644
--- a/libsrc/xml.cpp
+++ b/libsrc/xml.cpp
@@ -2,6 +2,7 @@
 #include "ismrmrd/version.h"
 #include "pugixml.hpp"
 #include <cstdlib>
+#include <tuple>
 
 namespace ISMRMRD
 {
@@ -16,9 +17,9 @@ namespace ISMRMRD
     if (!matrixSize) {
       throw std::runtime_error("matrixSize not found in encodingSpace");
     } else {
-      e.matrixSize.x = std::atoi(matrixSize.child_value("x"));
-      e.matrixSize.y = std::atoi(matrixSize.child_value("y"));
-      e.matrixSize.z = std::atoi(matrixSize.child_value("z"));
+      e.matrixSize.x = std::stoi(matrixSize.child_value("x"));
+      e.matrixSize.y = std::stoi(matrixSize.child_value("y"));
+      e.matrixSize.z = std::stoi(matrixSize.child_value("z"));
     }
 
     if (!fieldOfView_mm) {
@@ -39,9 +40,9 @@ namespace ISMRMRD
     
     if (nc) {
       Limit l;
-      l.minimum = std::atoi(nc.child_value("minimum"));
-      l.maximum = std::atoi(nc.child_value("maximum"));
-      l.center = std::atoi(nc.child_value("center"));
+      l.minimum = std::stoi(nc.child_value("minimum"));
+      l.maximum = std::stoi(nc.child_value("maximum"));
+      l.center = std::stoi(nc.child_value("center"));
       o = l;
     }
 
@@ -73,25 +74,36 @@ namespace ISMRMRD
     return r;
   }
 
-  Optional<long> parse_optional_long(pugi::xml_node& n, const char* child) {
-    Optional<long> r;
+  Optional<std::int64_t> parse_optional_long(pugi::xml_node& n, const char* child) {
+    Optional<std::int64_t> r;
     pugi::xml_node nc = n.child(child);
     if (nc) {
-      r = std::atol(nc.child_value());
+      r = std::stoll(nc.child_value());
     }
     return r;
   }
 
-  Optional<unsigned short> parse_optional_ushort(pugi::xml_node& n, const char* child) {
-    Optional<unsigned short> r;
+  Optional<std::uint16_t> parse_optional_ushort(pugi::xml_node& n, const char* child) {
+    Optional<std::uint16_t> r;
     pugi::xml_node nc = n.child(child);
     if (nc) {
-      r = static_cast<unsigned short>(std::atoi(nc.child_value()));
+      r = static_cast<std::uint16_t>(std::stoi(nc.child_value()));
     }
     return r;
   }
 
-  std::vector<float> parse_vector_float(pugi::xml_node& n, const char* child) 
+  float parse_float(pugi::xml_node& n, const char* child){
+        try {
+            return std::stof(n.child_value(child), nullptr);
+        } catch (const std::invalid_argument& ){
+            throw std::runtime_error("Illegal value encountered in node " + std::string(n.name()) + ". Value is not a float:" + std::string(n.child_value(child)));
+
+        } catch (const std::out_of_range &  ){
+            throw std::runtime_error("Value out of float range in node " + std::string(n.name()) + ". Value:" + std::string(n.child_value(child)));
+        }
+  }
+
+  std::vector<float> parse_vector_float(pugi::xml_node& n, const char* child)
   {
     std::vector<float> r;
     
@@ -148,7 +160,7 @@ namespace ISMRMRD
       }
 
       v.name = std::string(name.child_value());
-      v.value = std::atoi(value.child_value());
+      v.value = std::stoi(value.child_value());
 
       r.push_back(v);
 
@@ -206,8 +218,19 @@ namespace ISMRMRD
    
     return r;
   }
+  MultibandCalibrationType parse_multiband_type(const std::string& multibandString) {
+      if (multibandString == "separable2D")
+          return MultibandCalibrationType::SEPARABLE2D;
+      if (multibandString == "full3D")
+          return MultibandCalibrationType::FULL3D;
+      if (multibandString == "other")
+          return MultibandCalibrationType::OTHER;
+
+      throw std::runtime_error("Invalid multiband calibration type in xml header: " + multibandString);
 
-  TrajectoryType parse_trajectory_type(const std::string trajectoryString) {
+  }
+
+  TrajectoryType parse_trajectory_type(const std::string& trajectoryString) {
       if (trajectoryString == "cartesian")
           return TrajectoryType::CARTESIAN;
       if (trajectoryString == "epi")
@@ -224,7 +247,7 @@ namespace ISMRMRD
       throw std::runtime_error("Invalid trajectory type in xml header");
   }
 
-  WaveformType parse_waveform_type(const std::string waveformString) {
+  WaveformType parse_waveform_type(const std::string& waveformString) {
       if (waveformString == "ecg")
           return WaveformType::ECG;
       if ( waveformString == "pulse")
@@ -242,12 +265,57 @@ namespace ISMRMRD
   }
 
 
+    DiffusionDimension parse_diffusiondimension(const std::string& diffusiondimension) {
+        if (diffusiondimension == "average") return DiffusionDimension::AVERAGE;
+        if (diffusiondimension == "contrast") return DiffusionDimension::CONTRAST;
+        if (diffusiondimension == "phase") return DiffusionDimension::PHASE;
+        if (diffusiondimension == "repetition") return DiffusionDimension::REPETITION;
+        if (diffusiondimension == "set") return DiffusionDimension::SET;
+        if (diffusiondimension == "segment") return DiffusionDimension::SEGMENT;
+        if (diffusiondimension == "user_0") return DiffusionDimension::USER_0;
+        if (diffusiondimension == "user_1") return DiffusionDimension::USER_1;
+        if (diffusiondimension == "user_2") return DiffusionDimension::USER_2;
+        if (diffusiondimension == "user_3") return DiffusionDimension::USER_3;
+        if (diffusiondimension == "user_4") return DiffusionDimension::USER_4;
+        if (diffusiondimension == "user_5") return DiffusionDimension::USER_5;
+        if (diffusiondimension == "user_6") return DiffusionDimension::USER_6;
+        if (diffusiondimension == "user_7") return DiffusionDimension::USER_7;
+        throw std::runtime_error("Invalid diffusion dimension in xml header");
+    }
+
+  static Diffusion parse_diffusion(pugi::xml_node& node) {
+      Diffusion diff{};
+      diff.bvalue = std::stof(node.child_value("bvalue"));
+
+      auto grad_node = node.child("gradientDirection");
+
+      diff.gradientDirection.rl = std::stof(grad_node.child_value("rl"));
+      diff.gradientDirection.ap = std::stof(grad_node.child_value("ap"));
+      diff.gradientDirection.fh = std::stof(grad_node.child_value("fh"));
+      return diff;
+  }
+
+  static Optional<std::vector<Diffusion>> parse_diffusion_vector(pugi::xml_node& node){
+
+      auto diffusion_node = node.child("diffusion");
+      if (!diffusion_node) return {};
+
+      std::vector<Diffusion> diffusions;
+      while(diffusion_node){
+          diffusions.push_back(parse_diffusion(diffusion_node));
+          diffusion_node = diffusion_node.next_sibling("diffusion");
+      }
+      return diffusions;
+
+
+  }
+
   //End of utility functions for deserializing header
 
   void deserialize(const char* xml, IsmrmrdHeader& h) 
   {
     pugi::xml_document doc;
-    pugi::xml_parse_result result = doc.load(xml);
+    pugi::xml_parse_result result = doc.load_string(xml);
     
     if (!result) {
       throw std::runtime_error("Unable to load ISMRMRD XML header");
@@ -308,6 +376,11 @@ namespace ISMRMRD
 	    e.encodingLimits.repetition             = parse_encoding_limit(encodingLimits,"repetition");
 	    e.encodingLimits.set                    = parse_encoding_limit(encodingLimits,"set");
 	    e.encodingLimits.segment                = parse_encoding_limit(encodingLimits,"segment");
+      for (size_t k = 0; k < ISMRMRD_USER_INTS; k++){
+        auto name = std::string("user_") + std::to_string(k);
+        e.encodingLimits.user[k]              = parse_encoding_limit(encodingLimits,name.c_str()); 
+      }
+
 	  }
 	  
 	  pugi::xml_node trajectory = encoding.child("trajectory");
@@ -327,6 +400,9 @@ namespace ISMRMRD
 		parse_user_parameter_long(trajectoryDescription, "userParameterLong");
 	      traj.userParameterDouble = 
 		parse_user_parameter_double(trajectoryDescription, "userParameterDouble");
+	      traj.userParameterString = 
+		parse_user_parameter_string(trajectoryDescription, "userParameterString");
+
 	      traj.comment = parse_optional_string(trajectoryDescription, "comment");
 	      e.trajectoryDescription = traj;
 	    } catch (std::runtime_error& e) {
@@ -344,15 +420,33 @@ namespace ISMRMRD
 	    if (!accelerationFactor) {
 	      throw std::runtime_error("Unable to accelerationFactor section in parallelImaging");
 	    } else {
-	      info.accelerationFactor.kspace_encoding_step_1 = static_cast<unsigned short>(std::atoi(accelerationFactor.child_value("kspace_encoding_step_1")));
-	      info.accelerationFactor.kspace_encoding_step_2 = static_cast<unsigned short>(std::atoi(accelerationFactor.child_value("kspace_encoding_step_2")));
+	      info.accelerationFactor.kspace_encoding_step_1 = static_cast<std::uint16_t>(std::stoi(accelerationFactor.child_value("kspace_encoding_step_1")));
+	      info.accelerationFactor.kspace_encoding_step_2 = static_cast<std::uint16_t>(std::stoi(accelerationFactor.child_value("kspace_encoding_step_2")));
 	    }
 	    
 	    info.calibrationMode = parse_optional_string(parallelImaging,"calibrationMode");
 	    info.interleavingDimension = parse_optional_string(parallelImaging,"interleavingDimension");
-	    e.parallelImaging = info;
+
+     pugi::xml_node multiband = parallelImaging.child("multiband");
+        if (multiband) {
+            Multiband mb;
+            mb.deltaKz = parse_float(multiband, "deltaKz");
+            mb.multiband_factor =  static_cast<std::uint32_t>(std::stoi(multiband.child_value("multiband_factor")));
+
+            auto spacing_node = multiband.child("spacing");
+            do {
+                mb.spacing.push_back(MultibandSpacing{parse_vector_float(spacing_node, "dZ")});
+                spacing_node = spacing_node.next_sibling("spacing");
+            } while (spacing_node);
+
+            mb.calibration = parse_multiband_type(multiband.child_value("calibration"));
+            mb.calibration_encoding = std::stoul(multiband.child_value("calibration_encoding"));
+            info.multiband = mb;
+        }
+        e.parallelImaging = info;
 	  }
 
+
 	  e.echoTrainLength = parse_optional_long(encoding, "echoTrainLength");
 
 	  h.encoding.push_back(e);
@@ -365,6 +459,7 @@ namespace ISMRMRD
 	SubjectInformation info;
 	info.patientName = parse_optional_string(subjectInformation, "patientName");
 	info.patientWeight_kg = parse_optional_float(subjectInformation, "patientWeight_kg");
+	info.patientHeight_m = parse_optional_float(subjectInformation, "patientHeight_m");
 	info.patientID = parse_optional_string(subjectInformation, "patientID");
 	info.patientBirthdate = parse_optional_string(subjectInformation, "patientBirthdate");
 	info.patientGender = parse_optional_string(subjectInformation, "patientGender");
@@ -380,6 +475,7 @@ namespace ISMRMRD
 	info.referringPhysicianName = parse_optional_string(studyInformation,"referringPhysicianName");
 	info.studyDescription = parse_optional_string(studyInformation,"studyDescription");
 	info.studyInstanceUID = parse_optional_string(studyInformation,"studyInstanceUID");
+	info.bodyPartExamined = parse_optional_string(studyInformation,"bodyPartExamined");
 	h.studyInformation = info;
       }
 
@@ -392,8 +488,8 @@ namespace ISMRMRD
 	info.relativeTablePosition = parse_optional_threeDimensionalFloat(measurementInformation, "relativeTablePosition");
 	info.initialSeriesNumber = parse_optional_long(measurementInformation, "initialSeriesNumber");
 	info.protocolName = parse_optional_string(measurementInformation, "protocolName");
+	info.sequenceName = parse_optional_string(measurementInformation, "sequenceName");
 	info.seriesDescription = parse_optional_string(measurementInformation, "seriesDescription");
-	
 	pugi::xml_node measurementDependency = measurementInformation.child("measurementDependency");
 	while (measurementDependency) {
 	  try {
@@ -436,14 +532,15 @@ namespace ISMRMRD
 	pugi::xml_node coilLabel = acquisitionSystemInformation.child("coilLabel");
 	while (coilLabel) {
 	  CoilLabel l;
-	  l.coilNumber = std::atoi(coilLabel.child_value("coilNumber"));
+	  l.coilNumber = std::stoi(coilLabel.child_value("coilNumber"));
 	  l.coilName = parse_string(coilLabel, "coilName");
 	  info.coilLabel.push_back(l);
 	  coilLabel = coilLabel.next_sibling("coilLabel");
 	}
 	info.institutionName = parse_optional_string(acquisitionSystemInformation, "institutionName");
 	info.stationName = parse_optional_string(acquisitionSystemInformation, "stationName");
-    info.deviceID = parse_optional_string(acquisitionSystemInformation,"deviceID");
+  info.deviceID = parse_optional_string(acquisitionSystemInformation,"deviceID");
+  info.deviceSerialNumber = parse_optional_string(acquisitionSystemInformation,"deviceSerialNumber");
 	h.acquisitionSystemInformation = info;
       }
 
@@ -463,6 +560,17 @@ namespace ISMRMRD
     r = parse_vector_float(sequenceParameters, "flipAngle_deg");
     if (!r.empty()) p.flipAngle_deg = r;
 
+
+    auto diffusiondimension = std::string(sequenceParameters.child_value("diffusionDimension"));
+    if (!diffusiondimension.empty()) p.diffusionDimension = parse_diffusiondimension(diffusiondimension);
+
+    
+    p.diffusion = parse_diffusion_vector(sequenceParameters);
+
+    p.diffusionScheme = parse_optional_string(sequenceParameters, "diffusionScheme");
+
+
+
     p.sequence_type = parse_optional_string(sequenceParameters, "sequence_type");
 
     r = parse_vector_float(sequenceParameters, "echo_spacing");
@@ -503,96 +611,101 @@ namespace ISMRMRD
 
   }
 
+    using std::to_string;
+    std::string to_string(const std::string& s){return s;}
 
-  //Utility functions for serialization
-  void to_string_val(const std::string& v, std::string& o)
-  {
-    o = v;
-  }
-
-  void to_string_val(const float& v, std::string& o)
-  {
-    char buffer[256];
-    sprintf(buffer,"%f",v);
-    o = std::string(buffer);
-  }
-
-  void to_string_val(const double& v, std::string& o)
-  {
-    char buffer[256];
-    sprintf(buffer,"%f",v);
-    o = std::string(buffer);
-  }
-
-  void to_string_val(const unsigned short& v, std::string& o)
-  {
-    char buffer[256];
-    sprintf(buffer,"%d",v);
-    o = std::string(buffer);
-  }
-
-  void to_string_val(const long& v, std::string& o)
-  {
-    char buffer[256];
-    sprintf(buffer,"%ld",v);
-    o = std::string(buffer);
-  }
-
-  void to_string_val(const TrajectoryType& v, std::string& o)
+  std::string to_string(TrajectoryType v)
   {
       switch (v){
           case TrajectoryType::CARTESIAN:
-              o = "cartesian";
-              break;
+              return "cartesian";
           case TrajectoryType::EPI:
-              o = "epi";
-              break;
+              return "epi";
           case TrajectoryType::RADIAL:
-              o = "radial";
-              break;
+              return  "radial";
           case TrajectoryType::GOLDENANGLE:
-              o = "goldenangle";
-              break;
+              return "goldenangle";
           case TrajectoryType::SPIRAL:
-              o = "spiral";
-              break;
+              return  "spiral";
           case TrajectoryType::OTHER:
-              o = "other";
-              break;
+              return "other";
       }
+      throw std::runtime_error("Illegal enum class value");
   }
 
-  void to_string_val(const WaveformType& v, std::string& o)
+  std::string to_string(const WaveformType& v)
   {
       switch (v){
           case WaveformType::ECG:
-              o = "ecg";
-              break;
+              return "ecg";
           case WaveformType::PULSE:
-              o = "pulse";
-              break;
+              return "pulse";
           case WaveformType::RESPIRATORY:
-              o = "respiratory";
-              break;
+              return "respiratory";
           case WaveformType::TRIGGER:
-              o = "trigger";
-              break;
+              return "trigger";
           case WaveformType::GRADIENTWAVEFORM:
-              o = "gradientwaveform";
-              break;
+              return "gradientwaveform";
           case WaveformType::OTHER:
-              o = "other";
-              break;
+              return "other";
+      }
 
+      throw std::runtime_error("Illegal enum class value");
+  }
+
+  std::string to_string(const  DiffusionDimension& d){
+      switch (d){
+      case DiffusionDimension::AVERAGE:
+          return "average";
+      case DiffusionDimension::CONTRAST:
+          return "contrast";
+      case DiffusionDimension::PHASE:
+          return "phase";
+      case DiffusionDimension::REPETITION:
+          return "repetition";
+      case DiffusionDimension::SET:
+          return "set";
+      case DiffusionDimension::SEGMENT:
+          return "segment";
+      case DiffusionDimension::USER_0:
+          return "user_0";
+      case DiffusionDimension::USER_1:
+          return "user_1";
+      case DiffusionDimension::USER_2:
+          return "user_2";
+      case DiffusionDimension::USER_3:
+          return "user_3";
+      case DiffusionDimension::USER_4:
+          return "user_4";
+      case DiffusionDimension::USER_5:
+          return "user_5";
+      case DiffusionDimension::USER_6:
+          return "user_6";
+      case DiffusionDimension::USER_7:
+          return "user_7";
       }
+      throw std::runtime_error("Illegal enum class value");
   }
 
-  template <class T> void append_optional_node(pugi::xml_node& n, const char* child, const Optional<T>& v) 
+std::string to_string(const MultibandCalibrationType& v)
+{
+    switch (v){
+        case MultibandCalibrationType::FULL3D:
+            return "full3D";
+        case MultibandCalibrationType::SEPARABLE2D:
+            return "separable2D";
+        case MultibandCalibrationType::OTHER:
+            return "other";
+    }
+
+    throw std::runtime_error("Illegal enum class value");
+}
+
+template <class T> void append_optional_node(pugi::xml_node& n, const char* child, const Optional<T>& v)
   {
     if (v) {
       pugi::xml_node n2 = n.append_child(child);
-      std::string v_as_string;
-      to_string_val(*v, v_as_string);
+      std::string v_as_string = to_string(*v);
       n2.append_child(pugi::node_pcdata).set_value(v_as_string.c_str());
     }
   } 
@@ -600,8 +713,7 @@ namespace ISMRMRD
   template <class T> void append_node(pugi::xml_node& n, const char* child, const T& v) 
   {
     pugi::xml_node n2 = n.append_child(child);
-    std::string v_as_string;
-    to_string_val(v, v_as_string);
+    auto v_as_string = to_string(v);
     n2.append_child(pugi::node_pcdata).set_value(v_as_string.c_str());
   } 
 
@@ -697,6 +809,7 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
       n1.set_name("subjectInformation");
       append_optional_node(n1,"patientName",h.subjectInformation->patientName);
       append_optional_node(n1,"patientWeight_kg",h.subjectInformation->patientWeight_kg);
+      append_optional_node(n1,"patientHeight_m",h.subjectInformation->patientHeight_m);
       append_optional_node(n1,"patientID",h.subjectInformation->patientID);
       append_optional_node(n1,"patientBirthdate",h.subjectInformation->patientBirthdate);
       append_optional_node(n1,"patientGender",h.subjectInformation->patientGender);
@@ -712,6 +825,7 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
       append_optional_node(n1,"referringPhysicianName",h.studyInformation->referringPhysicianName);
       append_optional_node(n1,"studyDescription",h.studyInformation->studyDescription);
       append_optional_node(n1,"studyInstanceUID",h.studyInformation->studyInstanceUID);
+      append_optional_node(n1,"bodyPartExamined",h.studyInformation->bodyPartExamined);
     }
 
     if (h.measurementInformation) {
@@ -724,6 +838,7 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
       append_optional_three_dimensional_float(n1,"relativeTablePosition",h.measurementInformation->relativeTablePosition);
       append_optional_node(n1,"initialSeriesNumber",h.measurementInformation->initialSeriesNumber);
       append_optional_node(n1,"protocolName",h.measurementInformation->protocolName);
+      append_optional_node(n1,"sequenceName",h.measurementInformation->sequenceName);
       append_optional_node(n1,"seriesDescription",h.measurementInformation->seriesDescription);
 
       for (size_t i = 0; i < h.measurementInformation->measurementDependency.size(); i++) {
@@ -764,6 +879,7 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
       append_optional_node(n1,"institutionName",h.acquisitionSystemInformation->institutionName);
       append_optional_node(n1,"stationName",h.acquisitionSystemInformation->stationName);
       append_optional_node(n1,"deviceID",h.acquisitionSystemInformation->deviceID);
+      append_optional_node(n1,"deviceSerialNumber",h.acquisitionSystemInformation->deviceSerialNumber);
     }
 
     n1 = root.append_child();
@@ -789,6 +905,12 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
       append_encoding_limit(n2,"repetition",h.encoding[i].encodingLimits.repetition);
       append_encoding_limit(n2,"set",h.encoding[i].encodingLimits.set);
       append_encoding_limit(n2,"segment",h.encoding[i].encodingLimits.segment);
+
+      for (size_t k = 0; k < ISMRMRD_USER_INTS; k++){
+        auto name = std::string("user_") + std::to_string(k);
+        append_encoding_limit(n2,name.c_str(),h.encoding[i].encodingLimits.user[k]);
+      }
+
       append_node(n1,"trajectory",h.encoding[i].trajectory);
       
       if (h.encoding[i].trajectoryDescription) {
@@ -796,19 +918,38 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
 	append_node(n2,"identifier",h.encoding[i].trajectoryDescription->identifier);
 	append_user_parameter(n2,"userParameterLong",h.encoding[i].trajectoryDescription->userParameterLong); 
 	append_user_parameter(n2,"userParameterDouble",h.encoding[i].trajectoryDescription->userParameterDouble); 
+	append_user_parameter(n2,"userParameterString",h.encoding[i].trajectoryDescription->userParameterString); 
 	append_optional_node(n2,"comment",h.encoding[i].trajectoryDescription->comment);
       }
 
       if (h.encoding[i].parallelImaging) {
+        const auto& parallelImaging = *h.encoding[i].parallelImaging;
 	n2 = n1.append_child("parallelImaging");
 	n3 = n2.append_child("accelerationFactor");
-	append_node(n3,"kspace_encoding_step_1",h.encoding[i].parallelImaging->accelerationFactor.kspace_encoding_step_1);
-	append_node(n3,"kspace_encoding_step_2",h.encoding[i].parallelImaging->accelerationFactor.kspace_encoding_step_2);
-	append_optional_node(n2, "calibrationMode", h.encoding[i].parallelImaging->calibrationMode);
-	append_optional_node(n2, "interleavingDimension", h.encoding[i].parallelImaging->interleavingDimension);
+	append_node(n3,"kspace_encoding_step_1",parallelImaging.accelerationFactor.kspace_encoding_step_1);
+	append_node(n3,"kspace_encoding_step_2",parallelImaging.accelerationFactor.kspace_encoding_step_2);
+	append_optional_node(n2, "calibrationMode", parallelImaging.calibrationMode);
+	append_optional_node(n2, "interleavingDimension",parallelImaging.interleavingDimension);
+
+
+      if (parallelImaging.multiband){
+          auto& multiband = *parallelImaging.multiband;
+        auto n4 = n2.append_child("multiband");
+        for (const auto& mb : multiband.spacing){
+            auto n5 = n4.append_child("spacing");
+            for (const auto dZ : mb.dZ){
+              append_node(n5,"dZ",dZ);
+            }
+        }
+        append_node(n4, "deltaKz", multiband.deltaKz);
+        append_node(n4, "multiband_factor", multiband.multiband_factor);
+        append_node(n4,"calibration",multiband.calibration);
+        append_node(n4,"calibration_encoding",multiband.calibration_encoding);
+      }
+
       }
 
-      append_optional_node(n1, "echoTrainLength", h.encoding[i].echoTrainLength);
+      append_optional_node(n1,"echoTrainLength",h.encoding[i].echoTrainLength);
 
     }
 
@@ -851,6 +992,19 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
               append_node(n1, "echo_spacing", h.sequenceParameters->echo_spacing->operator[](i));
           }
       }
+
+      append_optional_node(n1,"diffusionDimension", h.sequenceParameters->diffusionDimension);
+      if (h.sequenceParameters->diffusion){
+          for (const auto& diff : h.sequenceParameters->diffusion.get()){
+              auto diff_node = n1.append_child("diffusion");
+              append_node(diff_node,"bvalue",diff.bvalue);
+              auto grad_node = diff_node.append_child("gradientDirection");
+              append_node(grad_node,"rl",diff.gradientDirection.rl);
+              append_node(grad_node,"ap",diff.gradientDirection.ap);
+              append_node(grad_node,"fh",diff.gradientDirection.fh);
+          }
+      }
+      append_optional_node(n1, "diffusionScheme", h.sequenceParameters->diffusionScheme);
     }
 
     if (h.userParameters) {
@@ -870,4 +1024,196 @@ void append_optional_three_dimensional_float(pugi::xml_node& n, const char* chil
   }
 
 
-}
+  std::ostream& operator<<(std::ostream& stream, const IsmrmrdHeader& header){
+    serialize(header, stream);
+    return stream;
+  }
+
+  bool operator==(const IsmrmrdHeader &lhs, const IsmrmrdHeader &rhs) {
+      return std::tie(lhs.version, lhs.subjectInformation, lhs.studyInformation, lhs.measurementInformation, lhs.acquisitionSystemInformation, lhs.experimentalConditions, lhs.encoding, lhs.sequenceParameters, lhs.userParameters, lhs.waveformInformation) == std::tie(rhs.version, rhs.subjectInformation, rhs.studyInformation, rhs.measurementInformation, rhs.acquisitionSystemInformation, rhs.experimentalConditions, rhs.encoding, rhs.sequenceParameters, rhs.userParameters, rhs.waveformInformation);
+  }
+  bool operator!=(const IsmrmrdHeader &lhs, const IsmrmrdHeader &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const SubjectInformation &lhs, const SubjectInformation &rhs) {
+      return std::tie(lhs.patientName, lhs.patientWeight_kg, lhs.patientID, lhs.patientBirthdate, lhs.patientGender) == std::tie(rhs.patientName, rhs.patientWeight_kg, rhs.patientID, rhs.patientBirthdate, rhs.patientGender);
+  }
+  bool operator!=(const SubjectInformation &lhs, const SubjectInformation &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const StudyInformation &lhs, const StudyInformation &rhs) {
+      return std::tie(lhs.studyDate, lhs.studyTime, lhs.studyID, lhs.accessionNumber, lhs.referringPhysicianName, lhs.studyDescription, lhs.studyInstanceUID, lhs.bodyPartExamined) == std::tie(rhs.studyDate, rhs.studyTime, rhs.studyID, rhs.accessionNumber, rhs.referringPhysicianName, rhs.studyDescription, rhs.studyInstanceUID,lhs.bodyPartExamined);
+  }
+  bool operator!=(const StudyInformation &lhs, const StudyInformation &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator<(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return std::tie(lhs.dependencyType, lhs.measurementID) < std::tie(rhs.dependencyType, rhs.measurementID);
+  }
+  bool operator>(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return rhs < lhs;
+  }
+  bool operator<=(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return !(rhs < lhs);
+  }
+  bool operator>=(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return !(lhs < rhs);
+  }
+  bool operator==(const ReferencedImageSequence &lhs, const ReferencedImageSequence &rhs) {
+      return lhs.referencedSOPInstanceUID == rhs.referencedSOPInstanceUID;
+  }
+  bool operator!=(const ReferencedImageSequence &lhs, const ReferencedImageSequence &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const MeasurementInformation &lhs, const MeasurementInformation &rhs) {
+      return std::tie(lhs.measurementID, lhs.seriesDate, lhs.seriesTime, lhs.patientPosition, lhs.relativeTablePosition, lhs.initialSeriesNumber, lhs.protocolName, lhs.seriesDescription, lhs.measurementDependency, lhs.seriesInstanceUIDRoot, lhs.frameOfReferenceUID, lhs.referencedImageSequence,lhs.sequenceName) == std::tie(rhs.measurementID, rhs.seriesDate, rhs.seriesTime, rhs.patientPosition, rhs.relativeTablePosition, rhs.initialSeriesNumber, rhs.protocolName, rhs.seriesDescription, rhs.measurementDependency, rhs.seriesInstanceUIDRoot, rhs.frameOfReferenceUID, rhs.referencedImageSequence,lhs.sequenceName);
+  }
+  bool operator!=(const MeasurementInformation &lhs, const MeasurementInformation &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const CoilLabel &lhs, const CoilLabel &rhs) {
+      return std::tie(lhs.coilNumber, lhs.coilName) == std::tie(rhs.coilNumber, rhs.coilName);
+  }
+  bool operator!=(const CoilLabel &lhs, const CoilLabel &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const AcquisitionSystemInformation &lhs, const AcquisitionSystemInformation &rhs) {
+      return std::tie(lhs.systemVendor, lhs.systemModel, lhs.systemFieldStrength_T, lhs.relativeReceiverNoiseBandwidth, lhs.receiverChannels, lhs.coilLabel, lhs.institutionName, lhs.stationName, lhs.deviceID, lhs.deviceSerialNumber) == std::tie(rhs.systemVendor, rhs.systemModel, rhs.systemFieldStrength_T, rhs.relativeReceiverNoiseBandwidth, rhs.receiverChannels, rhs.coilLabel, rhs.institutionName, rhs.stationName, rhs.deviceID,lhs.deviceSerialNumber);
+  }
+  bool operator!=(const AcquisitionSystemInformation &lhs, const AcquisitionSystemInformation &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const ExperimentalConditions &lhs, const ExperimentalConditions &rhs) {
+      return lhs.H1resonanceFrequency_Hz == rhs.H1resonanceFrequency_Hz;
+  }
+  bool operator!=(const ExperimentalConditions &lhs, const ExperimentalConditions &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const MatrixSize &lhs, const MatrixSize &rhs) {
+      return std::tie(lhs.x, lhs.y, lhs.z) == std::tie(rhs.x, rhs.y, rhs.z);
+  }
+  bool operator!=(const MatrixSize &lhs, const MatrixSize &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const FieldOfView_mm &lhs, const FieldOfView_mm &rhs) {
+      return std::tie(lhs.x, lhs.y, lhs.z) == std::tie(rhs.x, rhs.y, rhs.z);
+  }
+  bool operator!=(const FieldOfView_mm &lhs, const FieldOfView_mm &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const EncodingSpace &lhs, const EncodingSpace &rhs) {
+      return std::tie(lhs.matrixSize, lhs.fieldOfView_mm) == std::tie(rhs.matrixSize, rhs.fieldOfView_mm);
+  }
+  bool operator!=(const EncodingSpace &lhs, const EncodingSpace &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const Limit &lhs, const Limit &rhs) {
+      return std::tie(lhs.minimum, lhs.maximum, lhs.center) == std::tie(rhs.minimum, rhs.maximum, rhs.center);
+  }
+  bool operator!=(const Limit &lhs, const Limit &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const EncodingLimits &lhs, const EncodingLimits &rhs) {
+      return std::tie(lhs.kspace_encoding_step_0, lhs.kspace_encoding_step_1, lhs.kspace_encoding_step_2, lhs.average, lhs.slice, lhs.contrast, lhs.phase, lhs.repetition, lhs.set, lhs.segment) == std::tie(rhs.kspace_encoding_step_0, rhs.kspace_encoding_step_1, rhs.kspace_encoding_step_2, rhs.average, rhs.slice, rhs.contrast, rhs.phase, rhs.repetition, rhs.set, rhs.segment);
+  }
+  bool operator!=(const EncodingLimits &lhs, const EncodingLimits &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const UserParameterLong &lhs, const UserParameterLong &rhs) {
+      return std::tie(lhs.name, lhs.value) == std::tie(rhs.name, rhs.value);
+  }
+  bool operator!=(const UserParameterLong &lhs, const UserParameterLong &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const UserParameterDouble &lhs, const UserParameterDouble &rhs) {
+      return std::tie(lhs.name, lhs.value) == std::tie(rhs.name, rhs.value);
+  }
+  bool operator!=(const UserParameterDouble &lhs, const UserParameterDouble &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const UserParameterString &lhs, const UserParameterString &rhs) {
+      return std::tie(lhs.name, lhs.value) == std::tie(rhs.name, rhs.value);
+  }
+  bool operator!=(const UserParameterString &lhs, const UserParameterString &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const UserParameters &lhs, const UserParameters &rhs) {
+      return std::tie(lhs.userParameterLong, lhs.userParameterDouble, lhs.userParameterString, lhs.userParameterBase64) == std::tie(rhs.userParameterLong, rhs.userParameterDouble, rhs.userParameterString, rhs.userParameterBase64);
+  }
+  bool operator!=(const UserParameters &lhs, const UserParameters &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const TrajectoryDescription &lhs, const TrajectoryDescription &rhs) {
+      return std::tie(lhs.identifier, lhs.userParameterLong, lhs.userParameterDouble, lhs.comment) == std::tie(rhs.identifier, rhs.userParameterLong, rhs.userParameterDouble, rhs.comment);
+  }
+  bool operator!=(const TrajectoryDescription &lhs, const TrajectoryDescription &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const AccelerationFactor &lhs, const AccelerationFactor &rhs) {
+      return std::tie(lhs.kspace_encoding_step_1, lhs.kspace_encoding_step_2) == std::tie(rhs.kspace_encoding_step_1, rhs.kspace_encoding_step_2);
+  }
+  bool operator!=(const AccelerationFactor &lhs, const AccelerationFactor &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const ParallelImaging &lhs, const ParallelImaging &rhs) {
+      return std::tie(lhs.accelerationFactor, lhs.calibrationMode, lhs.interleavingDimension, lhs.multiband) == std::tie(rhs.accelerationFactor, rhs.calibrationMode, rhs.interleavingDimension,rhs.multiband);
+  }
+  bool operator!=(const ParallelImaging &lhs, const ParallelImaging &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const Multiband &lhs, const Multiband &rhs) {
+      return std::tie(lhs.spacing, lhs.deltaKz,lhs.multiband_factor,lhs.calibration,lhs.calibration_encoding) == std::tie(rhs.spacing, rhs.deltaKz,rhs.multiband_factor,rhs.calibration,rhs.calibration_encoding);
+  }
+  bool operator!=(const Multiband &lhs, const Multiband &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const Encoding &lhs, const Encoding &rhs) {
+      return std::tie(lhs.encodedSpace, lhs.reconSpace, lhs.encodingLimits, lhs.trajectory, lhs.trajectoryDescription, lhs.parallelImaging, lhs.echoTrainLength) == std::tie(rhs.encodedSpace, rhs.reconSpace, rhs.encodingLimits, rhs.trajectory, rhs.trajectoryDescription, rhs.parallelImaging, rhs.echoTrainLength);
+  }
+  bool operator!=(const Encoding &lhs, const Encoding &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const SequenceParameters &lhs, const SequenceParameters &rhs) {
+      return std::tie(lhs.TR, lhs.TE, lhs.TI, lhs.flipAngle_deg, lhs.sequence_type, lhs.echo_spacing, lhs.diffusion, lhs.diffusionDimension, lhs.diffusionScheme) == std::tie(rhs.TR, rhs.TE, rhs.TI, rhs.flipAngle_deg, rhs.sequence_type, rhs.echo_spacing, lhs.diffusion, lhs.diffusionDimension, lhs.diffusionScheme);
+  }
+  bool operator!=(const SequenceParameters &lhs, const SequenceParameters &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const WaveformInformation &lhs, const WaveformInformation &rhs) {
+      return std::tie(lhs.waveformName, lhs.waveformType, lhs.userParameters) == std::tie(rhs.waveformName, rhs.waveformType, rhs.userParameters);
+  }
+  bool operator!=(const WaveformInformation &lhs, const WaveformInformation &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const threeDimensionalFloat &lhs, const threeDimensionalFloat &rhs) {
+      return std::tie(lhs.x, lhs.y, lhs.z) == std::tie(rhs.x, rhs.y, rhs.z);
+  }
+  bool operator!=(const threeDimensionalFloat &lhs, const threeDimensionalFloat &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return std::tie(lhs.dependencyType, lhs.measurementID) == std::tie(rhs.dependencyType, rhs.measurementID);
+  }
+  bool operator!=(const MeasurementDependency &lhs, const MeasurementDependency &rhs) {
+      return !(rhs == lhs);
+  }
+   bool operator==(const MultibandSpacing &lhs, const MultibandSpacing &rhs) {
+      return lhs.dZ == rhs.dZ;
+  }
+  bool operator!=(const MultibandSpacing &lhs, const MultibandSpacing &rhs) {
+      return !(rhs == lhs);
+  }
+
+ bool operator==(const Diffusion &lhs, const Diffusion &rhs) {
+      return std::tie(lhs.bvalue, lhs.gradientDirection) == std::tie(rhs.bvalue, rhs.gradientDirection);
+  }
+  bool operator!=(const Diffusion &lhs, const Diffusion &rhs) {
+      return !(rhs == lhs);
+  }
+  bool operator==(const GradientDirection &lhs, const GradientDirection &rhs) {
+      return std::tie(lhs.rl, lhs.ap, lhs.fh) == std::tie(rhs.rl, rhs.ap, rhs.fh);
+  }
+  bool operator!=(const GradientDirection &lhs, const GradientDirection &rhs) {
+      return !(rhs == lhs);
+  }
+  }
diff --git a/matlab/+ismrmrd/+xml/deserialize.m b/matlab/+ismrmrd/+xml/deserialize.m
index 4d23316..3bb2304 100644
--- a/matlab/+ismrmrd/+xml/deserialize.m
+++ b/matlab/+ismrmrd/+xml/deserialize.m
@@ -192,6 +192,7 @@ function status = isNumericalType(name)
     headerNumericalTypes = { ...
       'version', ...
       'patientWeight_kg', ...
+      'patientHeight_m', ...
       'accessionNumber', ...
       'initialSeriesNumber', ...
       'systemFieldStrength_T', ...
diff --git a/matlab/+ismrmrd/+xml/serialize.m b/matlab/+ismrmrd/+xml/serialize.m
index 45794f4..b51debe 100644
--- a/matlab/+ismrmrd/+xml/serialize.m
+++ b/matlab/+ismrmrd/+xml/serialize.m
@@ -16,6 +16,7 @@ if isfield(header,'subjectInformation')
     subjectInformationNode = docNode.createElement('subjectInformation');
     append_optional(docNode,subjectInformationNode,subjectInformation,'patientName');
     append_optional(docNode,subjectInformationNode,subjectInformation,'patientWeight_kg',@num2str);
+    append_optional(docNode,subjectInformationNode,subjectInformation,'patientHeight_m',@num2str);
     append_optional(docNode,subjectInformationNode,subjectInformation,'patientID');
     append_optional(docNode,subjectInformationNode,subjectInformation,'patientBirthdate');
     append_optional(docNode,subjectInformationNode,subjectInformation,'patientGender');
@@ -126,6 +127,15 @@ for enc = header.encoding(:)'
     append_encoding_limits(docNode,n2,'repetition',enc.encodingLimits);
     append_encoding_limits(docNode,n2,'set',enc.encodingLimits);
     append_encoding_limits(docNode,n2,'segment',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_0',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_1',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_2',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_3',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_4',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_5',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_6',enc.encodingLimits);
+    append_encoding_limits(docNode,n2,'user_7',enc.encodingLimits);
+
     node.appendChild(n2);
 
     append_node(docNode,node,enc,'trajectory');
diff --git a/matlab/+ismrmrd/AcquisitionHeader.m b/matlab/+ismrmrd/AcquisitionHeader.m
index ffc4cd9..8114883 100644
--- a/matlab/+ismrmrd/AcquisitionHeader.m
+++ b/matlab/+ismrmrd/AcquisitionHeader.m
@@ -70,6 +70,10 @@ classdef AcquisitionHeader < handle
             'ACQ_IS_DUMMYSCAN_DATA',                   27, ...
             'ACQ_IS_RTFEEDBACK_DATA',                  28, ...
             'ACQ_IS_SURFACECOILCORRECTIONSCAN_DATA',   29, ...
+            'ACQ_COMPRESSION1',                        53, ...
+            'ACQ_COMPRESSION2',                        54, ...
+            'ACQ_COMPRESSION3',                        55, ...
+            'ACQ_COMPRESSION4',                        56, ...
             'ACQ_USER1',                               57, ...
             'ACQ_USER2',                               58, ...
             'ACQ_USER3',                               59, ...
diff --git a/matlab/+ismrmrd/Dataset.m b/matlab/+ismrmrd/Dataset.m
index 95fa5b1..0b9dee1 100644
--- a/matlab/+ismrmrd/Dataset.m
+++ b/matlab/+ismrmrd/Dataset.m
@@ -123,6 +123,7 @@ classdef Dataset
 
             % Close the XML
             H5D.close(xml_id);
+            H5T.close(xml_dtype);
         end
 
         function nacq = getNumberOfAcquisitions(obj)
@@ -139,7 +140,7 @@ classdef Dataset
             nacq = dims(1);
             H5S.close(space);
             H5D.close(dset);
-
+            H5P.close(lapl_id);
         end
 
         function block = readAcquisition(obj, start, stop)
@@ -157,6 +158,7 @@ classdef Dataset
             if (H5L.exists(obj.fid, obj.datapath, lapl) == 0)
                 error([obj.datapath ' does not exist in the HDF5 dataset.']);
             end
+            H5P.close(lapl);
 
             % Open the data
             dset = H5D.open(obj.fid, obj.datapath);
@@ -284,7 +286,7 @@ classdef Dataset
             nacq = dims(1);
             H5S.close(space);
             H5D.close(dset);
-
+            H5P.close(lapl_id);
         end
 
         function block = readWaveform(obj, start, stop)
@@ -302,6 +304,7 @@ classdef Dataset
             if (H5L.exists(obj.fid, obj.waveformpath, lapl) == 0)
                 error([obj.waveformpath ' does not exist in the HDF5 dataset.']);
             end
+            H5P.close(lapl);
 
             % Open the data
             dset = H5D.open(obj.fid, obj.waveformpath);
diff --git a/matlab/+ismrmrd/ImageHeader.m b/matlab/+ismrmrd/ImageHeader.m
index d245a17..7853621 100644
--- a/matlab/+ismrmrd/ImageHeader.m
+++ b/matlab/+ismrmrd/ImageHeader.m
@@ -33,15 +33,27 @@ classdef ImageHeader < handle
 
     properties(Constant)
         FLAGS = struct( ...
-            'IMAGE_IS_NAVIGATION_DATA',  1, ...
-            'IMAGE_USER1',              57, ...
-            'IMAGE_USER2',              58, ...
-            'IMAGE_USER3',              59, ...
-            'IMAGE_USER4',              60, ...
-            'IMAGE_USER5',              61, ...
-            'IMAGE_USER6',              62, ...
-            'IMAGE_USER7',              63, ...
-            'IMAGE_USER8',              64);
+            'IMAGE_IS_NAVIGATION_DATA',   1, ...
+            'IMAGE_FIRST_IN_AVERAGE',     5, ...
+            'IMAGE_LAST_IN_AVERAGE',      6, ...
+            'IMAGE_FIRST_IN_SLICE',       7, ...
+            'IMAGE_LAST_IN_SLICE',        8, ...
+            'IMAGE_FIRST_IN_CONTRAST',    9, ...
+            'IMAGE_LAST_IN_CONTRAST',    10, ...
+            'IMAGE_FIRST_IN_PHASE',      11, ...
+            'IMAGE_LAST_IN_PHASE',       12, ...
+            'IMAGE_FIRST_IN_REPETITION', 13, ...
+            'IMAGE_LAST_IN_REPETITION',  14, ...
+            'IMAGE_FIRST_IN_SET',        15, ...
+            'IMAGE_LAST_IN_SET',         16, ...
+            'IMAGE_USER1',               57, ...
+            'IMAGE_USER2',               58, ...
+            'IMAGE_USER3',               59, ...
+            'IMAGE_USER4',               60, ...
+            'IMAGE_USER5',               61, ...
+            'IMAGE_USER6',               62, ...
+            'IMAGE_USER7',               63, ...
+            'IMAGE_USER8',               64);
         
         DATA_TYPE = struct( ...
             'USHORT',   uint16(1), ...
diff --git a/matlab/test/xml/ismrmrd_example.xml b/matlab/test/xml/ismrmrd_example.xml
index d394f42..884ff32 100644
--- a/matlab/test/xml/ismrmrd_example.xml
+++ b/matlab/test/xml/ismrmrd_example.xml
@@ -3,6 +3,7 @@
   <subjectInformation>
     <patientName>phantom</patientName>
     <patientWeight_kg>70.3068</patientWeight_kg>
+    <patientHeight_m>1.80</patientHeight_m>
   </subjectInformation>
   <acquisitionSystemInformation>
     <systemVendor>SIEMENS</systemVendor>
@@ -15,6 +16,7 @@
     <H1resonanceFrequency_Hz>63642459</H1resonanceFrequency_Hz>
   </experimentalConditions>
   <encoding>
+    <trajectory>cartesian</trajectory>
     <encodedSpace>
       <matrixSize>
         <x>256</x>
@@ -61,7 +63,6 @@
         <center>0</center>
       </set>
     </encodingLimits>
-    <trajectory>cartesian</trajectory>
     <parallelImaging>
       <accelerationFactor>
         <kspace_encoding_step_1>1</kspace_encoding_step_1>
@@ -69,11 +70,11 @@
       </accelerationFactor>
       <calibrationMode>other</calibrationMode>
     </parallelImaging>
+
   </encoding>
   <sequenceParameters>
     <TR>4.6</TR>
     <TE>2.35</TE>
     <TI>300</TI>
   </sequenceParameters>
-</ismrmrdHeader>
-
+</ismrmrdHeader>
\ No newline at end of file
diff --git a/matlab/test/xml/ismrmrd_example_extended.xml b/matlab/test/xml/ismrmrd_example_extended.xml
index 0f86302..7eb68f2 100644
--- a/matlab/test/xml/ismrmrd_example_extended.xml
+++ b/matlab/test/xml/ismrmrd_example_extended.xml
@@ -3,6 +3,7 @@
   <subjectInformation>
     <patientName>Anonymous Patient</patientName>
     <patientWeight_kg>70.3068</patientWeight_kg>
+    <patientHeight_m>1.80</patientHeight_m>
     <patientID>BHHG8866789</patientID>
     <patientBirthdate>1980-06-12</patientBirthdate>
     <patientGender>M</patientGender>
@@ -15,14 +16,21 @@
     <referringPhysicianName>John Doe, MD</referringPhysicianName>
     <studyDescription>This is an example study with all headers set</studyDescription>
     <studyInstanceUID>1.34.896.2789852.787873.9999</studyInstanceUID>
+    <bodyPartExamined>WHOLEBODY</bodyPartExamined>
   </studyInformation>
   <measurementInformation>
     <measurementID>LLJGHH888986</measurementID>
     <seriesDate>2012-08-13</seriesDate>
     <seriesTime>09:10:12</seriesTime>
     <patientPosition>HFS</patientPosition>
+    <relativeTablePosition>
+      <x>0</x>
+      <y>0</y>
+      <z>10</z>
+    </relativeTablePosition>
     <initialSeriesNumber>1</initialSeriesNumber>
     <protocolName>ExampleProt</protocolName>
+    <sequenceName>ep_b0_1000</sequenceName>
     <seriesDescription>MRIStudy1</seriesDescription>
     <measurementDependency>
       <dependencyType>Noise</dependencyType>
@@ -178,6 +186,7 @@
     <institutionName>Some University</institutionName>
     <stationName>MRI Scanner 1</stationName>
     <deviceID>20434</deviceID>
+    <deviceSerialNumber>27182818284590452</deviceSerialNumber>
   </acquisitionSystemInformation>
   <experimentalConditions>
     <H1resonanceFrequency_Hz>63642459</H1resonanceFrequency_Hz>
@@ -228,8 +237,14 @@
         <maximum>0</maximum>
         <center>0</center>
       </set>
+      <user_1>
+        <minimum>0</minimum>
+        <maximum>0</maximum>
+        <center>0</center>
+      </user_1>
     </encodingLimits>
     <trajectory>cartesian</trajectory>
+
   </encoding>
   <encoding>
     <encodedSpace>
@@ -270,7 +285,7 @@
       <average>
         <minimum>0</minimum>
         <maximum>16</maximum>
-        <center>0</center>	
+        <center>0</center>
       </average>
       <slice>
         <minimum>0</minimum>
@@ -280,17 +295,17 @@
       <contrast>
         <minimum>2</minimum>
         <maximum>4</maximum>
-        <center>3</center>	
+        <center>3</center>
       </contrast>
       <phase>
         <minimum>0</minimum>
         <maximum>0</maximum>
-        <center>0</center>	
+        <center>0</center>
       </phase>
       <repetition>
-	<minimum>0</minimum>
+        <minimum>0</minimum>
         <maximum>16</maximum>
-        <center>8</center>	
+        <center>8</center>
       </repetition>
       <set>
         <minimum>0</minimum>
@@ -300,40 +315,55 @@
       <segment>
         <minimum>0</minimum>
         <maximum>1</maximum>
-        <center>0</center>	
+        <center>0</center>
       </segment>
     </encodingLimits>
     <trajectory>other</trajectory>
     <trajectoryDescription>
       <identifier>Custom Trajector</identifier>
       <userParameterLong>
-	<name>sample_rate_ns</name>
-	<value>100</value>
+        <name>sample_rate_ns</name>
+        <value>100</value>
       </userParameterLong>
       <userParameterLong>
-	<name>constant_1</name>
-	<value>300</value>
+        <name>constant_1</name>
+        <value>300</value>
       </userParameterLong>
       <userParameterDouble>
-	<name>coefficient_1</name>
-	<value>2.72386</value>
+        <name>coefficient_1</name>
+        <value>2.72386</value>
       </userParameterDouble>
       <userParameterDouble>
-	<name>coefficient_2</name>
-	<value>2233.72</value>
+        <name>coefficient_2</name>
+        <value>2233.72</value>
       </userParameterDouble>
       <userParameterDouble>
-	<name>coefficient_3</name>
-	<value>112.86</value>
+        <name>coefficient_3</name>
+        <value>112.86</value>
       </userParameterDouble>
+      <userParameterString><name>Trajectory monitoring system vendor</name><value>Purview</value></userParameterString>
       <comment>This is an example description of a custom trajectory</comment>
     </trajectoryDescription>
     <parallelImaging>
       <accelerationFactor>
-	<kspace_encoding_step_1>1</kspace_encoding_step_1>
-	<kspace_encoding_step_2>2</kspace_encoding_step_2>
+        <kspace_encoding_step_1>1</kspace_encoding_step_1>
+        <kspace_encoding_step_2>2</kspace_encoding_step_2>
       </accelerationFactor>
       <calibrationMode>other</calibrationMode>
+      <multiband>
+        <spacing>
+          <dZ>0.0</dZ>
+          <dZ>2.0</dZ>
+        </spacing>
+        <spacing>
+          <dZ>0.0</dZ>
+          <dZ>2.5</dZ>
+        </spacing>
+        <deltaKz>1.0</deltaKz>
+        <multiband_factor>2</multiband_factor>
+        <calibration>full3D</calibration>
+        <calibration_encoding>0</calibration_encoding>
+      </multiband>
     </parallelImaging>
     <echoTrainLength>12</echoTrainLength>
   </encoding>
@@ -373,5 +403,4 @@
       <value>VGhpcyBpcyBhIGpvdXJuZXkgaW50byBzb3VuZA==</value>
     </userParameterBase64>
   </userParameters>
-</ismrmrdHeader>
-
+</ismrmrdHeader>
\ No newline at end of file
diff --git a/schema/ismrmrd.xsd b/schema/ismrmrd.xsd
index 77e7537..78bf187 100644
--- a/schema/ismrmrd.xsd
+++ b/schema/ismrmrd.xsd
@@ -22,6 +22,7 @@
     <xs:all>
       <xs:element minOccurs="0" name="patientName" type="xs:string" />
       <xs:element minOccurs="0" name="patientWeight_kg" type="xs:float" />
+      <xs:element minOccurs="0" name="patientHeight_m" type="xs:float" />
       <xs:element minOccurs="0" name="patientID" type="xs:string" />
       <xs:element minOccurs="0" name="patientBirthdate" type="xs:date" />
       <xs:element minOccurs="0" name="patientGender">
@@ -43,6 +44,7 @@
       <xs:element minOccurs="0" maxOccurs="1" name="referringPhysicianName" type="xs:string" />
       <xs:element minOccurs="0" maxOccurs="1" name="studyDescription" type="xs:string" />
       <xs:element minOccurs="0" maxOccurs="1" name="studyInstanceUID" type="xs:string" />
+      <xs:element minOccurs="0" maxOccurs="1" name="bodyPartExamined" type="xs:string" />
     </xs:all>
   </xs:complexType>
 
@@ -68,6 +70,7 @@
       <xs:element minOccurs="0" name="relativeTablePosition" type="threeDimensionalFloat" />
       <xs:element minOccurs="0" name="initialSeriesNumber" type="xs:long" />
       <xs:element minOccurs="0" name="protocolName" type="xs:string" />
+      <xs:element minOccurs="0" name="sequenceName" type="xs:string" />
       <xs:element minOccurs="0" name="seriesDescription" type="xs:string" />
       <xs:element maxOccurs="unbounded" minOccurs="0" name="measurementDependency" type="measurementDependencyType" />
       <xs:element minOccurs="0" name="seriesInstanceUIDRoot" type="xs:string" />
@@ -101,6 +104,7 @@
       <xs:element minOccurs="0" maxOccurs="1" name="institutionName" type="xs:string" />
       <xs:element minOccurs="0" maxOccurs="1" name="stationName" type="xs:string" />
       <xs:element minOccurs="0" maxOccurs="1" name="deviceID" type="xs:string" />
+      <xs:element minOccurs="0" maxOccurs="1" name="deviceSerialNumber" type="xs:string" />
     </xs:sequence>
   </xs:complexType>
 
@@ -174,6 +178,14 @@
       <xs:element maxOccurs="1" minOccurs="0" name="repetition" type="limitType" />
       <xs:element maxOccurs="1" minOccurs="0" name="set" type="limitType" />
       <xs:element maxOccurs="1" minOccurs="0" name="segment" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_0" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_1" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_2" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_3" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_4" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_5" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_6" type="limitType" />
+      <xs:element maxOccurs="1" minOccurs="0" name="user_7" type="limitType" />
     </xs:all>
   </xs:complexType>
 
@@ -193,6 +205,7 @@
       <xs:element maxOccurs="1" minOccurs="1" name="identifier" type="xs:string" />
       <xs:element maxOccurs="unbounded" minOccurs="0" name="userParameterLong" type="userParameterLongType" />
       <xs:element maxOccurs="unbounded" minOccurs="0" name="userParameterDouble" type="userParameterDoubleType" />
+      <xs:element maxOccurs="unbounded" minOccurs="0" name="userParameterString" type="userParameterStringType" />
       <xs:element maxOccurs="1" minOccurs="0" name="comment" type="xs:string" />
     </xs:sequence>
   </xs:complexType>
@@ -205,10 +218,47 @@
       <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="flipAngle_deg" />
       <xs:element minOccurs="0" maxOccurs="1" type="xs:string" name="sequence_type" />
       <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="echo_spacing" />
+      <xs:element minOccurs="0" maxOccurs="1" type="diffusionDimensionType" name="diffusionDimension" />
+      <xs:element minOccurs="0" maxOccurs="unbounded" type="diffusionType" name="diffusion" />
+      <xs:element minOccurs="0" maxOccurs="1" type="xs:string" name="diffusionScheme" />
+    </xs:sequence>
+  </xs:complexType>
 
+  <xs:complexType name="diffusionType">
+    <xs:sequence>
+      <xs:element type="gradientDirectionType" name="gradientDirection" />
+      <xs:element type="xs:float" name="bvalue" />
     </xs:sequence>
   </xs:complexType>
 
+
+  <xs:simpleType name="diffusionDimensionType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="average" />
+      <xs:enumeration value="contrast" />
+      <xs:enumeration value="phase" />
+      <xs:enumeration value="repetition" />
+      <xs:enumeration value="set" />
+      <xs:enumeration value="segment" />
+      <xs:enumeration value="user_0" />
+      <xs:enumeration value="user_1" />
+      <xs:enumeration value="user_2" />
+      <xs:enumeration value="user_3" />
+      <xs:enumeration value="user_4" />
+      <xs:enumeration value="user_5" />
+      <xs:enumeration value="user_6" />
+      <xs:enumeration value="user_7" />
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:complexType name="gradientDirectionType">
+    <xs:all>
+      <xs:element type="xs:float" name="rl" />
+      <xs:element type="xs:float" name="ap" />
+      <xs:element type="xs:float" name="fh" />
+    </xs:all>
+  </xs:complexType>
+
   <xs:complexType name="userParameterLongType">
     <xs:all>
       <xs:element name="name" type="xs:string" />
@@ -252,6 +302,32 @@
     </xs:sequence>
   </xs:complexType>
 
+
+  <xs:complexType name="multibandType">
+    <xs:sequence>
+      <xs:element maxOccurs="unbounded" minOccurs="1" name="spacing" type="multibandSpacingType" />
+      <xs:element name="deltaKz" minOccurs="1" maxOccurs="1" type="xs:float" />
+      <xs:element name="multiband_factor" minOccurs="1" maxOccurs="1" type="xs:unsignedInt" />
+      <xs:element name="calibration" minOccurs="1" maxOccurs="1" type="multibandCalibrationType" />
+      <xs:element name="calibration_encoding" minOccurs="1" maxOccurs="1" type="xs:unsignedLong" />
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:complexType name="multibandSpacingType">
+    <xs:sequence>
+      <xs:element maxOccurs="unbounded" minOccurs="1" name="dZ" type="xs:float" />
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:simpleType name="multibandCalibrationType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="separable2D" />
+      <xs:enumeration value="full3D" />
+      <xs:enumeration value="other" />
+    </xs:restriction>
+  </xs:simpleType>
+
+
   <xs:complexType name="accelerationFactorType">
     <xs:all>
       <xs:element name="kspace_encoding_step_1" type="xs:unsignedShort" />
@@ -284,6 +360,7 @@
       <xs:element type="accelerationFactorType" name="accelerationFactor" />
       <xs:element maxOccurs="1" minOccurs="0" type="calibrationModeType" name="calibrationMode" />
       <xs:element maxOccurs="1" minOccurs="0" type="interleavingDimensionType" name="interleavingDimension" />
+      <xs:element maxOccurs="1" minOccurs="0" type="multibandType" name="multiband" />
     </xs:sequence>
   </xs:complexType>
 
@@ -306,4 +383,4 @@
       <xs:element name="userParameters" type="userParametersType" />
     </xs:sequence>
   </xs:complexType>
-</xs:schema>
+</xs:schema>
\ No newline at end of file
diff --git a/schema/ismrmrd_example.xml b/schema/ismrmrd_example.xml
index 3e5c157..884ff32 100644
--- a/schema/ismrmrd_example.xml
+++ b/schema/ismrmrd_example.xml
@@ -3,13 +3,14 @@
   <subjectInformation>
     <patientName>phantom</patientName>
     <patientWeight_kg>70.3068</patientWeight_kg>
+    <patientHeight_m>1.80</patientHeight_m>
   </subjectInformation>
   <acquisitionSystemInformation>
     <systemVendor>SIEMENS</systemVendor>
     <systemModel>Avanto</systemModel>
     <systemFieldStrength_T>1.494</systemFieldStrength_T>
-    <receiverChannels>32</receiverChannels>
     <relativeReceiverNoiseBandwidth>0.79</relativeReceiverNoiseBandwidth>
+    <receiverChannels>32</receiverChannels>
   </acquisitionSystemInformation>
   <experimentalConditions>
     <H1resonanceFrequency_Hz>63642459</H1resonanceFrequency_Hz>
@@ -62,18 +63,18 @@
         <center>0</center>
       </set>
     </encodingLimits>
+    <parallelImaging>
+      <accelerationFactor>
+        <kspace_encoding_step_1>1</kspace_encoding_step_1>
+        <kspace_encoding_step_2>1</kspace_encoding_step_2>
+      </accelerationFactor>
+      <calibrationMode>other</calibrationMode>
+    </parallelImaging>
+
   </encoding>
-  <parallelImaging>
-    <accelerationFactor>
-      <kspace_encoding_step_1>1</kspace_encoding_step_1>
-      <kspace_encoding_step_2>1</kspace_encoding_step_2>
-    </accelerationFactor>
-    <calibrationMode>other</calibrationMode>
-  </parallelImaging>
   <sequenceParameters>
     <TR>4.6</TR>
     <TE>2.35</TE>
     <TI>300</TI>
   </sequenceParameters>
-</ismrmrdHeader>
-
+</ismrmrdHeader>
\ No newline at end of file
diff --git a/schema/ismrmrd_example_extended.xml b/schema/ismrmrd_example_extended.xml
index e34342f..6895fb5 100644
--- a/schema/ismrmrd_example_extended.xml
+++ b/schema/ismrmrd_example_extended.xml
@@ -3,6 +3,7 @@
   <subjectInformation>
     <patientName>Anonymous Patient</patientName>
     <patientWeight_kg>70.3068</patientWeight_kg>
+    <patientHeight_m>1.80</patientHeight_m>
     <patientID>BHHG8866789</patientID>
     <patientBirthdate>1980-06-12</patientBirthdate>
     <patientGender>M</patientGender>
@@ -15,6 +16,7 @@
     <referringPhysicianName>John Doe, MD</referringPhysicianName>
     <studyDescription>This is an example study with all headers set</studyDescription>
     <studyInstanceUID>1.34.896.2789852.787873.9999</studyInstanceUID>
+    <bodyPartExamined>WHOLEBODY</bodyPartExamined>
   </studyInformation>
   <measurementInformation>
     <measurementID>LLJGHH888986</measurementID>
@@ -28,6 +30,7 @@
     </relativeTablePosition>
     <initialSeriesNumber>1</initialSeriesNumber>
     <protocolName>ExampleProt</protocolName>
+    <sequenceName>ep_b0_1000</sequenceName>
     <seriesDescription>MRIStudy1</seriesDescription>
     <measurementDependency>
       <dependencyType>Noise</dependencyType>
@@ -183,6 +186,7 @@
     <institutionName>Some University</institutionName>
     <stationName>MRI Scanner 1</stationName>
     <deviceID>20434</deviceID>
+    <deviceSerialNumber>27182818284590452</deviceSerialNumber>
   </acquisitionSystemInformation>
   <experimentalConditions>
     <H1resonanceFrequency_Hz>63642459</H1resonanceFrequency_Hz>
@@ -233,8 +237,14 @@
         <maximum>0</maximum>
         <center>0</center>
       </set>
+      <user_1>
+        <minimum>0</minimum>
+        <maximum>0</maximum>
+        <center>0</center>
+      </user_1>
     </encodingLimits>
     <trajectory>cartesian</trajectory>
+
   </encoding>
   <encoding>
     <encodedSpace>
@@ -275,7 +285,7 @@
       <average>
         <minimum>0</minimum>
         <maximum>16</maximum>
-        <center>0</center>	
+        <center>0</center>
       </average>
       <slice>
         <minimum>0</minimum>
@@ -285,17 +295,17 @@
       <contrast>
         <minimum>2</minimum>
         <maximum>4</maximum>
-        <center>3</center>	
+        <center>3</center>
       </contrast>
       <phase>
         <minimum>0</minimum>
         <maximum>0</maximum>
-        <center>0</center>	
+        <center>0</center>
       </phase>
       <repetition>
-	<minimum>0</minimum>
+        <minimum>0</minimum>
         <maximum>16</maximum>
-        <center>8</center>	
+        <center>8</center>
       </repetition>
       <set>
         <minimum>0</minimum>
@@ -305,40 +315,58 @@
       <segment>
         <minimum>0</minimum>
         <maximum>1</maximum>
-        <center>0</center>	
+        <center>0</center>
       </segment>
     </encodingLimits>
     <trajectory>other</trajectory>
     <trajectoryDescription>
       <identifier>Custom Trajector</identifier>
       <userParameterLong>
-	<name>sample_rate_ns</name>
-	<value>100</value>
+        <name>sample_rate_ns</name>
+        <value>100</value>
       </userParameterLong>
       <userParameterLong>
-	<name>constant_1</name>
-	<value>300</value>
+        <name>constant_1</name>
+        <value>300</value>
       </userParameterLong>
       <userParameterDouble>
-	<name>coefficient_1</name>
-	<value>2.72386</value>
+        <name>coefficient_1</name>
+        <value>2.72386</value>
       </userParameterDouble>
       <userParameterDouble>
-	<name>coefficient_2</name>
-	<value>2233.72</value>
+        <name>coefficient_2</name>
+        <value>2233.72</value>
       </userParameterDouble>
       <userParameterDouble>
-	<name>coefficient_3</name>
-	<value>112.86</value>
+        <name>coefficient_3</name>
+        <value>112.86</value>
       </userParameterDouble>
+      <userParameterString>
+        <name>Trajectory monitoring system vendor</name>
+        <value>Purview</value>
+      </userParameterString>
       <comment>This is an example description of a custom trajectory</comment>
     </trajectoryDescription>
     <parallelImaging>
       <accelerationFactor>
-	<kspace_encoding_step_1>1</kspace_encoding_step_1>
-	<kspace_encoding_step_2>2</kspace_encoding_step_2>
+        <kspace_encoding_step_1>1</kspace_encoding_step_1>
+        <kspace_encoding_step_2>2</kspace_encoding_step_2>
       </accelerationFactor>
       <calibrationMode>other</calibrationMode>
+      <multiband>
+        <spacing>
+          <dZ>0.0</dZ>
+          <dZ>2.0</dZ>
+        </spacing>
+        <spacing>
+          <dZ>0.0</dZ>
+          <dZ>2.5</dZ>
+        </spacing>
+        <deltaKz>1.0</deltaKz>
+        <multiband_factor>2</multiband_factor>
+        <calibration>full3D</calibration>
+        <calibration_encoding>0</calibration_encoding>
+      </multiband>
     </parallelImaging>
     <echoTrainLength>12</echoTrainLength>
   </encoding>
@@ -351,6 +379,32 @@
     <TI>300.08</TI>
     <flipAngle_deg>90.0</flipAngle_deg>
     <flipAngle_deg>120.0</flipAngle_deg>
+    <diffusionDimension>segment</diffusionDimension>
+    <diffusion>
+      <gradientDirection>
+        <ap>1</ap>
+        <fh>0</fh>
+        <rl>0</rl>
+      </gradientDirection>
+      <bvalue>2.0</bvalue>
+    </diffusion>
+    <diffusion>
+      <gradientDirection>
+        <ap>0</ap>
+        <fh>1</fh>
+        <rl>0</rl>
+      </gradientDirection>
+      <bvalue>1.0</bvalue>
+    </diffusion>
+    <diffusion>
+      <gradientDirection>
+        <ap>0</ap>
+        <fh>0</fh>
+        <rl>1</rl>
+      </gradientDirection>
+      <bvalue>4.0</bvalue>
+    </diffusion>
+    <diffusionScheme>bipolar</diffusionScheme>
   </sequenceParameters>
   <userParameters>
     <userParameterLong>
@@ -378,5 +432,4 @@
       <value>VGhpcyBpcyBhIGpvdXJuZXkgaW50byBzb3VuZA==</value>
     </userParameterBase64>
   </userParameters>
-</ismrmrdHeader>
-
+</ismrmrdHeader>
\ No newline at end of file
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b29471b..59464e5 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Boost 1.43 COMPONENTS unit_test_framework)
+find_package(Boost 1.43 COMPONENTS unit_test_framework filesystem)
 
 if (NOT Boost_UNIT_TEST_FRAMEWORK_FOUND)
     message("Boost Unit Test Framework not found. Not compiling tests")
@@ -9,7 +9,17 @@ if (NOT Boost_USE_STATIC_LIBS)
     add_definitions(-DBOOST_TEST_DYN_LINK)
 endif()
 
-include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${Boost_INCLUDE_DIR})
+file(READ ${CMAKE_SOURCE_DIR}/schema/ismrmrd_example.xml ismrmrd_example)
+string(REPLACE "\"" "\\\"" ismrmrd_example ${ismrmrd_example})
+string(REPLACE "\n" "\\\n" ismrmrd_example ${ismrmrd_example})
+
+file(READ ${CMAKE_SOURCE_DIR}/schema/ismrmrd_example_extended.xml ismrmrd_example_extended)
+string(REPLACE "\"" "\\\"" ismrmrd_example_extended ${ismrmrd_example_extended})
+string(REPLACE "\n" "\\\n" ismrmrd_example_extended ${ismrmrd_example_extended})
+
+configure_file(embedded_xml.h.in embedded_xml.h)
+
+include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
 
 add_executable(test_ismrmrd
     test_main.cpp
@@ -18,8 +28,16 @@ add_executable(test_ismrmrd
     test_ndarray.cpp
     test_flags.cpp
     test_channels.cpp
-    test_quaternions.cpp)
+    test_xml_header.cpp
+    test_quaternions.cpp
+    test_serialization.cpp 
+    test_dataset.cpp)
 
 target_link_libraries(test_ismrmrd ismrmrd ${Boost_LIBRARIES})
 
-add_custom_target(check COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_ismrmrd DEPENDS test_ismrmrd)
+add_test(NAME check COMMAND test_ismrmrd )
+
+
+add_executable(benchmark_dataset
+    benchmark_dataset.cpp)
+    target_link_libraries(benchmark_dataset ismrmrd ${Boost_LIBRARIES})
\ No newline at end of file
diff --git a/tests/benchmark_dataset.cpp b/tests/benchmark_dataset.cpp
new file mode 100644
index 0000000..e086d71
--- /dev/null
+++ b/tests/benchmark_dataset.cpp
@@ -0,0 +1,52 @@
+#include <boost/filesystem.hpp>
+#include <boost/random.hpp>
+#include <chrono>
+#include <iostream>
+#include <ismrmrd/dataset.h>
+#include <ismrmrd/ismrmrd.h>
+
+using namespace ISMRMRD;
+
+static boost::random::mt19937 rng;
+static boost::random::uniform_real_distribution<float> dist = boost::random::uniform_real_distribution<float>();
+
+float create_random_float() {
+    return dist(rng);
+}
+
+int main(int argc, char **argv) {
+
+    boost::filesystem::path temp = boost::filesystem::unique_path();
+
+    Acquisition acq = Acquisition(256, 32, 2);
+
+    std::vector<Acquisition> acqs(10240, acq);
+    for (size_t i = 0; i < acqs.size(); i++) {
+        std::generate((float *)acqs[i].data_begin(), (float *)acqs[i].data_end(), create_random_float);
+        std::generate((float *)acqs[i].traj_begin(), (float *)acqs[i].traj_end(), create_random_float);
+    }
+
+    {
+
+        auto start = std::chrono::high_resolution_clock::now();
+        Dataset dataset = Dataset(temp.string().c_str(), "/test", true);
+
+        std::for_each(acqs.begin(),acqs.end(),[&dataset](Acquisition& acq){ dataset.appendAcquisition(acq);});
+
+        auto duration = std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start);
+        std::cout << "Write duration: " << duration.count() << "s" << std::endl;
+    }
+
+    {
+        auto start = std::chrono::high_resolution_clock::now();
+        Dataset dataset = Dataset(temp.string().c_str(), "/test", false);
+        for (size_t i = 0; i < acqs.size(); i++) {
+            Acquisition acq;
+            dataset.readAcquisition(uint32_t(i), acq);
+        }
+        auto duration = std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start);
+        std::cout << "Read duration: " << duration.count() << "s" << std::endl;
+    }
+
+    boost::filesystem::remove(temp);
+}
\ No newline at end of file
diff --git a/tests/embedded_xml.h.in b/tests/embedded_xml.h.in
new file mode 100644
index 0000000..adfc367
--- /dev/null
+++ b/tests/embedded_xml.h.in
@@ -0,0 +1,5 @@
+#pragma once
+#include <string>
+
+const std::string basic_xml = "${ismrmrd_example}";
+const std::string extended_xml = "${ismrmrd_example_extended}";
\ No newline at end of file
diff --git a/tests/test_dataset.cpp b/tests/test_dataset.cpp
new file mode 100644
index 0000000..a72f6ee
--- /dev/null
+++ b/tests/test_dataset.cpp
@@ -0,0 +1,56 @@
+#include "ismrmrd/dataset.h"
+#include "ismrmrd/ismrmrd.h"
+#include "ismrmrd/version.h"
+#include <boost/filesystem.hpp>
+#include <boost/random.hpp>
+#include <boost/test/unit_test.hpp>
+
+using namespace ISMRMRD;
+
+static boost::random::mt19937 rng;
+static boost::random::uniform_real_distribution<float> dist = boost::random::uniform_real_distribution<float>();
+
+float create_random_float() {
+    return dist(rng);
+}
+
+
+BOOST_AUTO_TEST_SUITE(DatasetTest)
+
+BOOST_AUTO_TEST_CASE(test_read_write) {
+
+    boost::filesystem::path temp = boost::filesystem::unique_path();
+
+    Acquisition acq = Acquisition(32, 4, 2);
+
+    std::vector<Acquisition> acqs(10, acq);
+    for (size_t i = 0; i < acqs.size(); i++) {
+        std::generate((float *)acqs[i].data_begin(), (float *)acqs[i].data_end(), create_random_float);
+        std::generate((float *)acqs[i].traj_begin(), (float *)acqs[i].traj_end(), create_random_float);
+    }
+
+    {
+        Dataset dataset = Dataset(temp.string().c_str(), "/test", true);
+        for (size_t i = 0; i < acqs.size(); i++)
+            dataset.appendAcquisition(acqs[i]);
+    }
+
+    {
+        Dataset dataset = Dataset(temp.string().c_str(), "/test", false);
+        for (size_t i = 0; i < acqs.size(); i++) {
+            Acquisition acq;
+            dataset.readAcquisition(uint32_t(i), acq);
+
+            const Acquisition &acq_ref = acqs[i];
+
+            BOOST_REQUIRE(acq.getHead() == acq_ref.getHead());
+
+            BOOST_CHECK(std::equal(acq.data_begin(), acq.data_end(), acq_ref.data_begin()));
+            BOOST_CHECK(std::equal(acq.traj_begin(), acq.traj_end(), acq_ref.traj_begin()));
+        }
+    }
+
+    boost::filesystem::remove(temp);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/test_serialization.cpp b/tests/test_serialization.cpp
new file mode 100644
index 0000000..bb8309c
--- /dev/null
+++ b/tests/test_serialization.cpp
@@ -0,0 +1,481 @@
+#include <boost/test/unit_test.hpp>
+#include <fstream>
+#include <sstream>
+
+#include "ismrmrd/dataset.h"
+#include "ismrmrd/serialization.h"
+#include "ismrmrd/serialization_iostream.h"
+
+using namespace ISMRMRD;
+
+BOOST_AUTO_TEST_SUITE(SerialiationTest)
+
+// Some helpers
+template <typename T>
+T value_from_size_t(size_t i);
+template <>
+uint16_t value_from_size_t<uint16_t>(size_t i) { return (uint16_t)i; }
+template <>
+uint32_t value_from_size_t<uint32_t>(size_t i) { return (uint32_t)i; }
+template <>
+int16_t value_from_size_t<int16_t>(size_t i) { return (int16_t)i; }
+template <>
+int32_t value_from_size_t<int32_t>(size_t i) { return (int32_t)i; }
+template <>
+float value_from_size_t<float>(size_t i) { return (float)(i * 1.0f); }
+template <>
+double value_from_size_t<double>(size_t i) { return (double)(i * 1.0f); }
+template <>
+std::complex<float> value_from_size_t<std::complex<float> >(size_t i) { return std::complex<float>(1.0f * i, 1.0f * i); }
+template <>
+std::complex<double> value_from_size_t<std::complex<double> >(size_t i) { return std::complex<double>(1.0f * i, 1.0f * i); }
+
+// Test the serialization of a single acquisition
+BOOST_AUTO_TEST_CASE(test_acquisition_serialization) {
+    Acquisition acq;
+    acq.resize(256, 16, 3);
+
+    // Fill in some data
+    for (size_t i = 0; i < acq.getNumberOfDataElements(); i++) {
+        acq.getDataPtr()[i] = value_from_size_t<std::complex<float> >(i);
+    }
+
+    // Fill trajectory
+    for (size_t i = 0; i < acq.getNumberOfTrajElements(); i++) {
+        acq.getTrajPtr()[i] = value_from_size_t<float>(i);
+    }
+
+    // Test serialization and deserialization
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+    ISMRMRD::serialize(acq, ws);
+    Acquisition acq2;
+    ISMRMRD::deserialize(acq2, rs);
+
+    // Check that the data is the same
+    BOOST_ASSERT(acq.getHead() == acq2.getHead());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq.getDataPtr(), acq.getDataPtr() + acq.getNumberOfDataElements(),
+                                  acq2.getDataPtr(), acq2.getDataPtr() + acq2.getNumberOfDataElements());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq.getTrajPtr(), acq.getTrajPtr() + acq.getNumberOfTrajElements(),
+                                  acq2.getTrajPtr(), acq2.getTrajPtr() + acq2.getNumberOfTrajElements());
+}
+
+typedef std::tuple<unsigned short, short, unsigned int, int, float, double, std::complex<float>, std::complex<double> > image_types_w_tuples;
+
+// Test the serialization of a single image
+BOOST_AUTO_TEST_CASE_TEMPLATE(test_image_serialization, T, image_types_w_tuples) {
+    Image<T> img;
+    img.resize(64, 64, 8, 4);
+
+    //  just one header field, we will validate it and
+    //  we are also validating matrix sizes, assume rest
+    //  is working. Image does not have an == operator
+    img.setImageSeriesIndex(10);
+
+    // Fill in some data
+    for (size_t i = 0; i < img.getNumberOfDataElements(); i++) {
+        img.getDataPtr()[i] = value_from_size_t<T>(i);
+    }
+
+    // set some meta data
+    img.setAttributeString("This is my attribute string");
+
+    // Test serialization and deserialization
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+    ISMRMRD::serialize(img, ws);
+
+    Image<T> img2;
+    ISMRMRD::deserialize(img2, rs);
+
+    // Check that the data is the same
+    BOOST_CHECK_EQUAL(img.getImageSeriesIndex(), img2.getImageSeriesIndex());
+    BOOST_CHECK_EQUAL(img.getAttributeString(), img2.getAttributeString());
+    BOOST_CHECK_EQUAL_COLLECTIONS(img.getDataPtr(), img.getDataPtr() + img.getNumberOfDataElements(),
+                                  img2.getDataPtr(), img2.getDataPtr() + img2.getNumberOfDataElements());
+}
+
+// Test the serialization of a single waveform
+BOOST_AUTO_TEST_CASE(test_waveform_serialization) {
+    Waveform wf(256, 16);
+    wf.head.time_stamp = 1234567890;
+
+    // Fill in some data
+    std::fill(wf.begin_data(), wf.end_data(), 1);
+
+    // Test serialization and deserialization
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+    ISMRMRD::serialize(wf, ws);
+    Waveform wf2;
+    ISMRMRD::deserialize(wf2, rs);
+
+    // Check that the data is the same
+    BOOST_ASSERT(wf.head.time_stamp == wf2.head.time_stamp);
+    BOOST_CHECK_EQUAL_COLLECTIONS(wf.begin_data(), wf.end_data(),
+                                  wf2.begin_data(), wf2.end_data());
+}
+
+// Test serrialization of const length arrays
+BOOST_AUTO_TEST_CASE(test_config_serialization) {
+    ConfigFile cfg;
+
+    strcpy(cfg.config, "default.xml");
+
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+
+    ISMRMRD::serialize(cfg, ws);
+
+    ConfigFile cfg2;
+    ISMRMRD::deserialize(cfg2, rs);
+    std::string str2(cfg2.config);
+    BOOST_CHECK_EQUAL("default.xml", str2);
+}
+
+// Test string serialization
+BOOST_AUTO_TEST_CASE(test_string_serialization) {
+    std::string str("This can really be any string, including <XML> configuration and unicode characters: 你好");
+
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+
+    ISMRMRD::serialize(str, ws);
+
+    std::string str2;
+    ISMRMRD::deserialize(str2, rs);
+    BOOST_CHECK_EQUAL(str, str2);
+}
+
+// Some early iterations used explicit null terminations in strings,
+// We are testing here that it doesn't trip us up
+BOOST_AUTO_TEST_CASE(test_string_desiralization_with_null_terminators) {
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+
+    std::vector<char> buffer = { 'h', 'e', 'l', 'l', 'o', '\0' };
+
+    // Write a config text manually
+    uint32_t len = static_cast<uint32_t>(buffer.size());
+    BOOST_CHECK_EQUAL(len, 6); // 6 characters with the null terminator
+    ss.write(reinterpret_cast<char *>(&len), sizeof(uint32_t));
+    ss.write(&buffer[0], len);
+
+    IStreamView rs(ss);
+
+    ConfigText cf;
+    ISMRMRD::deserialize(cf.config_text, rs);
+
+    BOOST_CHECK_EQUAL(cf.config_text.size(), 5); // String is 1 shorter
+    BOOST_CHECK_EQUAL(cf.config_text, "hello");
+}
+
+BOOST_AUTO_TEST_CASE(test_of_control_plane_protocol_serialization) {
+    ConfigFile cfg;
+    std::string config_name("test_config.xml");
+    strcpy(cfg.config, config_name.c_str());
+
+    ConfigText cfg_txt;
+    cfg_txt.config_text = "{{ \"test\": \"test\" }}";
+
+    TextMessage txt_msg;
+    txt_msg.message = "This is a test";
+
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+
+    ProtocolSerializer serializer(ws);
+    ProtocolDeserializer deserializer(rs);
+
+    serializer.serialize(cfg);
+    serializer.serialize(cfg_txt);
+    serializer.serialize(txt_msg);
+
+    ConfigFile cfg2;
+    deserializer.deserialize(cfg2);
+    BOOST_CHECK_EQUAL(cfg2.config, config_name.c_str());
+
+    ConfigText cfg_txt2;
+    deserializer.deserialize(cfg_txt2);
+    BOOST_CHECK_EQUAL(cfg_txt2.config_text, cfg_txt.config_text);
+
+    TextMessage txt_msg2;
+    deserializer.deserialize(txt_msg2);
+    BOOST_CHECK_EQUAL(txt_msg2.message, txt_msg.message);
+}
+
+BOOST_AUTO_TEST_CASE(test_of_protocol_serialization) {
+
+    // A header
+    ISMRMRD::IsmrmrdHeader h;
+    h.experimentalConditions.H1resonanceFrequency_Hz = 63500000;
+    h.encoding.push_back(ISMRMRD::Encoding());
+    h.encoding[0].encodedSpace.matrixSize.x = 256;
+    h.encoding[0].encodedSpace.matrixSize.y = 256;
+    h.encoding[0].encodedSpace.matrixSize.z = 1;
+    h.encoding[0].reconSpace.matrixSize.x = 256;
+    h.encoding[0].reconSpace.matrixSize.y = 256;
+    h.encoding[0].reconSpace.matrixSize.z = 1;
+    h.encoding[0].trajectory = ISMRMRD::TrajectoryType::CARTESIAN;
+
+    // A few Acquisitions
+    Acquisition acq1, acq2;
+    acq1.resize(256, 16, 3);
+    acq2.resize(256, 16, 3);
+
+    // Fill in some data
+    for (size_t i = 0; i < acq1.getNumberOfDataElements(); i++) {
+        acq1.getDataPtr()[i] = value_from_size_t<std::complex<float> >(i);
+        acq2.getDataPtr()[i] = value_from_size_t<std::complex<float> >(2 * i);
+    }
+
+    // Fill trajectory
+    for (size_t i = 0; i < acq1.getNumberOfTrajElements(); i++) {
+        acq1.getTrajPtr()[i] = value_from_size_t<float>(i);
+        acq2.getTrajPtr()[i] = value_from_size_t<float>(2 * i);
+    }
+
+    // A waveform
+    Waveform wf(256, 16);
+    wf.head.time_stamp = 1234567890;
+
+    // An image
+    Image<float> img;
+    img.resize(64, 64, 8, 4);
+
+    //  just one header field, we will validate it and
+    //  we are also validating matrix sizes, assume rest
+    //  is working. Image does not have an == operator
+    img.setImageSeriesIndex(10);
+
+    // Fill in some data
+    for (size_t i = 0; i < img.getNumberOfDataElements(); i++) {
+        img.getDataPtr()[i] = value_from_size_t<float>(i);
+    }
+
+    // set some meta data
+    img.setAttributeString("This is my attribute string");
+
+    std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
+
+    OStreamView ws(ss);
+    IStreamView rs(ss);
+
+    ProtocolSerializer serializer(ws);
+    ProtocolDeserializer deserializer(rs);
+
+    serializer.serialize(h);
+    serializer.serialize(acq1);
+    serializer.serialize(img);
+    serializer.serialize(acq2);
+    serializer.serialize(wf);
+
+    // Test deserialization
+    ISMRMRD::IsmrmrdHeader h2;
+    Acquisition acq3, acq4;
+    Waveform wf2;
+    Image<float> img2;
+    deserializer.deserialize(h2);
+    deserializer.deserialize(acq3);
+    deserializer.deserialize(img2);
+    deserializer.deserialize(acq4);
+    deserializer.deserialize(wf2);
+
+    // Check that the header is the same
+    BOOST_CHECK_EQUAL(h, h2);
+
+    // Check that the data are the same
+    BOOST_ASSERT(acq1.getHead() == acq3.getHead());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq1.getDataPtr(), acq1.getDataPtr() + acq1.getNumberOfDataElements(),
+                                  acq3.getDataPtr(), acq3.getDataPtr() + acq3.getNumberOfDataElements());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq1.getTrajPtr(), acq1.getTrajPtr() + acq1.getNumberOfTrajElements(),
+                                  acq3.getTrajPtr(), acq3.getTrajPtr() + acq3.getNumberOfTrajElements());
+
+    BOOST_ASSERT(acq2.getHead() == acq4.getHead());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq2.getDataPtr(), acq2.getDataPtr() + acq2.getNumberOfDataElements(),
+                                  acq4.getDataPtr(), acq4.getDataPtr() + acq4.getNumberOfDataElements());
+    BOOST_CHECK_EQUAL_COLLECTIONS(acq2.getTrajPtr(), acq2.getTrajPtr() + acq2.getNumberOfTrajElements(),
+                                  acq4.getTrajPtr(), acq4.getTrajPtr() + acq4.getNumberOfTrajElements());
+
+    BOOST_ASSERT(wf.head.time_stamp == wf2.head.time_stamp);
+    BOOST_CHECK_EQUAL_COLLECTIONS(wf.begin_data(), wf.end_data(),
+                                  wf2.begin_data(), wf2.end_data());
+
+    // Check that the image is the same
+    BOOST_CHECK_EQUAL(img.getImageSeriesIndex(), img2.getImageSeriesIndex());
+    BOOST_CHECK_EQUAL(img.getAttributeString(), img2.getAttributeString());
+    BOOST_CHECK_EQUAL_COLLECTIONS(img.getDataPtr(), img.getDataPtr() + img.getNumberOfDataElements(),
+                                  img2.getDataPtr(), img2.getDataPtr() + img2.getNumberOfDataElements());
+}
+
+std::string executable_name(std::string exename) {
+#ifdef _MSC_VER /* MS compiler */
+    exename += ".exe";
+#endif
+    return exename;
+}
+
+std::string path_separator() {
+#ifdef _MSC_VER /* MS compiler */
+    return "\\";
+#else
+    return "/";
+#endif
+}
+
+std::string util_path() {
+    // current directory
+    std::string path = "." + path_separator();
+    int level = 0;
+    while (level < 3) {
+        std::string testpath(path + executable_name("utilities" + path_separator() + "ismrmrd_generate_cartesian_shepp_logan"));
+        std::ifstream f(testpath.c_str());
+        if (f.good()) {
+            return path;
+        } else {
+            path += ".." + path_separator();
+            level++;
+        }
+    }
+    throw std::runtime_error("Could not find utilities directory");
+    return path;
+}
+
+std::string random_file_name(std::string prefix) {
+    std::stringstream ss;
+    ss << prefix << "_" << std::rand();
+    return ss.str();
+}
+
+void test_end_to_end_streaming_reconstruction(bool use_binary_files) {
+    std::string tmp_raw_data = random_file_name("." + path_separator() + "testdata") + ".h5";
+    std::string tmp_raw_data_stream1 = random_file_name("." + path_separator() + "stream_testdata1") + ".bin";
+    std::string tmp_raw_data_stream2 = random_file_name("." + path_separator() + "stream_testdata2") + ".bin";
+    std::string tmp_recon_data_stream1 = random_file_name("." + path_separator() + "stream_recon1") + ".bin";
+    std::string tmp_recon_data_stream2 = random_file_name("." + path_separator() + "stream_recon2") + ".bin";
+    std::string tmp_raw_data_copy = random_file_name("." + path_separator() + "testdata_copy") + ".h5";
+    std::string tmp_stream_recon_data = random_file_name("." + path_separator() + "stream_recon") + ".h5";
+    std::string tmp_stream_recon_data_copy = random_file_name("." + path_separator() + "stream_recon_copy") + ".h5";
+
+    std::string simulator_path = util_path() + executable_name("utilities" + path_separator() + "ismrmrd_generate_cartesian_shepp_logan");
+    BOOST_CHECK_EQUAL(std::system((simulator_path + " -o " + tmp_raw_data).c_str()), 0);
+
+    std::string recon_path = util_path() + executable_name("utilities" + path_separator() + "ismrmrd_recon_cartesian_2d");
+    std::string stream_recon_path = util_path() + executable_name("utilities" + path_separator() + "ismrmrd_stream_recon_cartesian_2d");
+    std::string stoh_path = util_path() + executable_name("utilities" + path_separator() + "ismrmrd_stream_to_hdf5");
+    std::string htos_path = util_path() + executable_name("utilities" + path_separator() + "ismrmrd_hdf5_to_stream");
+
+    // Use a scope here to make sure we close the files (which will be needed later)
+    {
+        // Stream raw data to another raw data file
+        if (use_binary_files) {
+            std::string cmd = htos_path + " -i " + tmp_raw_data + " -o " + tmp_raw_data_stream1;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+            cmd = stoh_path + " -i " + tmp_raw_data_stream1 + " -o " + tmp_raw_data_copy;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+        } else {
+            std::string cmd = htos_path + " -i " + tmp_raw_data + " --use-stdout | " + stoh_path + " --use-stdin -o " + tmp_raw_data_copy;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+        }
+        // Open dataset
+        Dataset d(tmp_raw_data.c_str(), "dataset", false);
+        Dataset d_copy(tmp_raw_data_copy.c_str(), "dataset", false);
+
+        std::string header_str;
+        IsmrmrdHeader h;
+        d.readHeader(header_str);
+        deserialize(header_str.c_str(), h);
+
+        std::string header_str_copy;
+        IsmrmrdHeader h_copy;
+        d_copy.readHeader(header_str_copy);
+        deserialize(header_str_copy.c_str(), h_copy);
+
+        BOOST_CHECK_EQUAL(h, h_copy);
+        BOOST_CHECK_EQUAL(d.getNumberOfAcquisitions(), d_copy.getNumberOfAcquisitions());
+
+        for (uint32_t i = 0; i < d.getNumberOfAcquisitions(); i++) {
+            Acquisition acq;
+            Acquisition acq_copy;
+            d.readAcquisition(i, acq);
+            d_copy.readAcquisition(i, acq_copy);
+
+            BOOST_ASSERT(acq.getHead() == acq_copy.getHead());
+            BOOST_CHECK_EQUAL_COLLECTIONS(acq.getDataPtr(), acq.getDataPtr() + acq.getNumberOfDataElements(),
+                                          acq_copy.getDataPtr(), acq_copy.getDataPtr() + acq_copy.getNumberOfDataElements());
+            BOOST_CHECK_EQUAL_COLLECTIONS(acq.getTrajPtr(), acq.getTrajPtr() + acq.getNumberOfTrajElements(),
+                                          acq_copy.getTrajPtr(), acq_copy.getTrajPtr() + acq_copy.getNumberOfTrajElements());
+        }
+    }
+
+    { // Scope to make sure files get closed.
+        if (use_binary_files) {
+            std::string cmd = htos_path + " -i " + tmp_raw_data + " -o " + tmp_raw_data_stream2;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+            cmd = stream_recon_path + " -i " + tmp_raw_data_stream2 + " -o " + tmp_recon_data_stream1;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+            cmd = stoh_path + " -i " + tmp_recon_data_stream1 + " -o " + tmp_stream_recon_data;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+        } else {
+            std::string stream_recon_cmd = htos_path + " -i " + tmp_raw_data + " --use-stdout | " + stream_recon_path + " --use-stdin --use-stdout | " + stoh_path + " --use-stdin -o " + tmp_stream_recon_data;
+            BOOST_CHECK_EQUAL(std::system(stream_recon_cmd.c_str()), 0);
+        }
+
+        std::string recon_cmd = recon_path + " " + tmp_raw_data;
+        BOOST_CHECK_EQUAL(std::system(recon_cmd.c_str()), 0);
+
+        if (use_binary_files) {
+            std::string cmd = htos_path + " --image-series image_0  -i " + tmp_stream_recon_data + " -o " + tmp_recon_data_stream2;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+            cmd = stoh_path + " -i " + tmp_recon_data_stream2 + " -o " + tmp_stream_recon_data_copy;
+            BOOST_CHECK_EQUAL(std::system(cmd.c_str()), 0);
+
+        } else {
+            std::string recon_copy_cmd = htos_path + " -i " + tmp_stream_recon_data + " --image-series image_0 --use-stdout | " + stoh_path + " --use-stdin -o " + tmp_stream_recon_data_copy;
+            BOOST_CHECK_EQUAL(std::system(recon_copy_cmd.c_str()), 0);
+        }
+
+        Dataset d(tmp_raw_data.c_str(), "dataset", false);
+        Dataset d_recon(tmp_stream_recon_data.c_str(), "dataset", false);
+        Dataset d_recon_copy(tmp_stream_recon_data_copy.c_str(), "dataset", false);
+
+        Image<float> cpp_recon;
+        d.readImage("cpp", 0, cpp_recon);
+
+        Image<float> cpp_stream_recon;
+        d_recon.readImage("image_0", 0, cpp_stream_recon);
+
+        Image<float> cpp_stream_recon_copy;
+        d_recon_copy.readImage("image_0", 0, cpp_stream_recon_copy);
+
+        // The 3 images should be the same
+        BOOST_CHECK_EQUAL_COLLECTIONS(cpp_recon.getDataPtr(), cpp_recon.getDataPtr() + cpp_recon.getNumberOfDataElements(),
+                                      cpp_stream_recon.getDataPtr(), cpp_stream_recon.getDataPtr() + cpp_stream_recon.getNumberOfDataElements());
+        BOOST_CHECK_EQUAL_COLLECTIONS(cpp_recon.getDataPtr(), cpp_recon.getDataPtr() + cpp_recon.getNumberOfDataElements(),
+                                      cpp_stream_recon_copy.getDataPtr(), cpp_stream_recon_copy.getDataPtr() + cpp_stream_recon_copy.getNumberOfDataElements());
+    }
+
+    // delete temporary files
+    if (use_binary_files) {
+        BOOST_CHECK_EQUAL(std::remove(tmp_raw_data_stream1.c_str()), 0);
+    }
+    BOOST_CHECK_EQUAL(std::remove(tmp_stream_recon_data.c_str()), 0);
+    BOOST_CHECK_EQUAL(std::remove(tmp_stream_recon_data_copy.c_str()), 0);
+    BOOST_CHECK_EQUAL(std::remove(tmp_raw_data.c_str()), 0);
+    BOOST_CHECK_EQUAL(std::remove(tmp_raw_data_copy.c_str()), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_end_to_end_streaming_reconstruction_pipes) {
+    test_end_to_end_streaming_reconstruction(false);
+}
+
+BOOST_AUTO_TEST_CASE(test_end_to_end_streaming_reconstruction_files) {
+    test_end_to_end_streaming_reconstruction(true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/test_xml_header.cpp b/tests/test_xml_header.cpp
new file mode 100644
index 0000000..b163e10
--- /dev/null
+++ b/tests/test_xml_header.cpp
@@ -0,0 +1,49 @@
+#include "ismrmrd/xml.h"
+#include "ismrmrd/version.h"
+#include "embedded_xml.h"
+#include <boost/test/unit_test.hpp>
+#include <sstream>
+
+using namespace ISMRMRD;
+
+BOOST_AUTO_TEST_SUITE(HeaderTest)
+
+BOOST_AUTO_TEST_CASE(test_basic_xml_header)
+{
+
+    IsmrmrdHeader header;
+    deserialize(basic_xml.c_str(),header);
+    std::stringstream stream;
+    serialize(header,stream);
+
+    IsmrmrdHeader header2;
+    deserialize(stream.str().c_str(),header2);
+
+
+    BOOST_CHECK_EQUAL(header,header2);
+}
+
+BOOST_AUTO_TEST_CASE(test_extended_xml_header)
+{
+
+    IsmrmrdHeader header;
+    deserialize(extended_xml.c_str(),header);
+    std::stringstream stream;
+    serialize(header,stream);
+
+    BOOST_CHECK(header.encoding.at(1).parallelImaging);
+    BOOST_CHECK(header.encoding.at(1).parallelImaging->multiband);
+    BOOST_CHECK_EQUAL(header.encoding.at(1).parallelImaging.get().multiband.get().deltaKz,1.0f);
+    BOOST_CHECK_EQUAL(header.encoding.at(1).parallelImaging.get().multiband.get().multiband_factor,2);
+    BOOST_CHECK(header.sequenceParameters.get().diffusion);
+    BOOST_CHECK_EQUAL(header.sequenceParameters.get().diffusion.get().size(),3 );
+
+    IsmrmrdHeader header2;
+    deserialize(stream.str().c_str(),header2);
+
+    BOOST_CHECK_EQUAL(header,header2);
+
+
+
+}
+BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt
index 48f7f48..af16c44 100644
--- a/utilities/CMakeLists.txt
+++ b/utilities/CMakeLists.txt
@@ -10,16 +10,26 @@ install(TARGETS ismrmrd_info DESTINATION bin)
 
 if (NOT WIN32)
   add_executable(ismrmrd_test_xml
-    ismrmrd_test_xml.cpp
-    ${CMAKE_SOURCE_DIR}/libsrc/pugixml.cpp )
+    ismrmrd_test_xml.cpp)
   target_link_libraries(ismrmrd_test_xml ismrmrd)
   install(TARGETS ismrmrd_test_xml DESTINATION bin)
 endif()
 
 if (HDF5_FOUND)
     add_executable(ismrmrd_read_timing_test read_timing_test.cpp)
-    target_link_libraries(ismrmrd_read_timing_test ismrmrd)
-    install(TARGETS ismrmrd_read_timing_test DESTINATION bin)
+    target_link_libraries(ismrmrd_read_timing_test ismrmrd ${HDF5_C_LIBRARIES})
+
+
+    if (WIN32 AND (CMAKE_VERSION VERSION_GREATER_EQUAL 3.21))
+     install(TARGETS ismrmrd_read_timing_test DESTINATION bin 
+            RUNTIME_DEPENDENCIES
+            PRE_EXCLUDE_REGEXES "api-ms-" "ex-ms"
+            POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
+            DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/.."
+            )
+    else ()
+        install(TARGETS ismrmrd_read_timing_test DESTINATION bin)
+        endif()
 
     find_package(Boost 1.43 COMPONENTS program_options random)
     if (WIN32)
@@ -48,7 +58,7 @@ if (HDF5_FOUND)
             ismrmrd
             ${Boost_PROGRAM_OPTIONS_LIBRARY}
             ${FFTW3_LIBRARIES})
-        
+
         install(TARGETS ismrmrd_generate_cartesian_shepp_logan DESTINATION bin)
 
         # Shepp-Logan phantom
@@ -61,6 +71,30 @@ if (HDF5_FOUND)
 
         install(TARGETS ismrmrd_recon_cartesian_2d DESTINATION bin)
 
+        add_executable(ismrmrd_hdf5_to_stream ismrmrd_hdf5_to_stream.cpp)
+        target_link_libraries(ismrmrd_hdf5_to_stream ismrmrd ${Boost_PROGRAM_OPTIONS_LIBRARY})
+        install(TARGETS ismrmrd_hdf5_to_stream DESTINATION bin)
+
+        add_executable(ismrmrd_stream_to_hdf5 ismrmrd_stream_to_hdf5.cpp)
+        target_link_libraries(ismrmrd_stream_to_hdf5 ismrmrd ${Boost_PROGRAM_OPTIONS_LIBRARY})
+        install(TARGETS ismrmrd_stream_to_hdf5 DESTINATION bin)
+
+        add_executable(ismrmrd_stream_recon_cartesian_2d stream_recon_cartesian_2d.cpp)
+        target_link_libraries(ismrmrd_stream_recon_cartesian_2d ismrmrd ${FFTW3_LIBRARIES} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+        
+
+        if (WIN32)       
+            install(TARGETS ismrmrd_stream_recon_cartesian_2d DESTINATION bin 
+            RUNTIME_DEPENDENCIES
+            PRE_EXCLUDE_REGEXES "api-ms-" "ex-ms"
+            POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
+            DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/.."
+            )
+            else()
+            install(TARGETS ismrmrd_stream_recon_cartesian_2d DESTINATION bin)
+        endif()
+
+
     else()
         message("FFTW3 or Boost NOT Found, cannot build utilities")
     endif()
diff --git a/utilities/generate_cartesian_shepp_logan.cpp b/utilities/generate_cartesian_shepp_logan.cpp
index 51d1d5b..ab18e9b 100644
--- a/utilities/generate_cartesian_shepp_logan.cpp
+++ b/utilities/generate_cartesian_shepp_logan.cpp
@@ -8,6 +8,9 @@
  */
 
 #include <iostream>
+#include <boost/uuid/uuid.hpp>            
+#include <boost/uuid/uuid_generators.hpp> 
+#include <boost/uuid/uuid_io.hpp>         
 #include "ismrmrd/ismrmrd.h"
 #include "ismrmrd/xml.h"
 #include "ismrmrd/dataset.h"
@@ -80,7 +83,7 @@ int main(int argc, char** argv)
 	dims.push_back(ncoils);
 
 	NDArray<complex_float_t> coil_images(dims);
-    std::fill(coil_images.begin(), coil_images.end(), complex_float_t(0.0f, 0.0f));
+    	std::fill(coil_images.begin(), coil_images.end(), complex_float_t(0.0f, 0.0f));
 
 	for (unsigned int c = 0; c < ncoils; c++) {
             for (unsigned int y = 0; y < matrix_size; y++) {
@@ -93,19 +96,19 @@ int main(int argc, char** argv)
 
         //Let's append the data to the file
         //Create if needed
-	    Dataset d(outfile.c_str(),dataset.c_str(), true);
-	    Acquisition acq;
+        Dataset d(outfile.c_str(),dataset.c_str(), true);
+        Acquisition acq;
         uint16_t readout = static_cast<uint16_t>(matrix_size*ros);
         
-	    if (noise_calibration)
-            {
-                acq.resize(readout, ncoils);
-                memset((void *)acq.getDataPtr(), 0, acq.getDataSize());
-                acq.setFlag(ISMRMRD_ACQ_IS_NOISE_MEASUREMENT);
-                add_noise(acq,noise_level);
-                acq.sample_time_us() = 5.0;
-                d.appendAcquisition(acq);
-	    }
+        if (noise_calibration)
+        {
+            acq.resize(readout, ncoils);
+            memset((void *)acq.getDataPtr(), 0, acq.getDataSize());
+            acq.setFlag(ISMRMRD_ACQ_IS_NOISE_MEASUREMENT);
+            add_noise(acq,noise_level);
+            acq.sample_time_us() = 5.0;
+            d.appendAcquisition(acq);
+        }
         
         if (store_coordinates) {
             acq.resize(readout, ncoils, 2);
@@ -116,11 +119,11 @@ int main(int argc, char** argv)
         memset((void*)acq.getDataPtr(), 0, acq.getDataSize());
         
         acq.available_channels() = ncoils;
-	    acq.center_sample() = (readout>>1);
-
-	    int hw = cal_width/2;
-	    int from = matrix_size / 2 - hw;
-	    int till = matrix_size / 2 + hw - 1;
+        acq.center_sample() = (readout>>1);
+	
+        int hw = cal_width/2;
+        int from = matrix_size / 2 - hw;
+        int till = matrix_size / 2 + hw - 1;
 
         for (unsigned int r = 0; r < repetitions; r++) {
             for (unsigned int a = 0; a < acc_factor; a++) {
@@ -148,7 +151,7 @@ int main(int argc, char** argv)
                             acq.setFlag(ISMRMRD_ACQ_IS_PARALLEL_CALIBRATION_AND_IMAGING);
                     }
 
-					acq.idx().kspace_encode_step_1 = static_cast<uint16_t>(i);
+                    acq.idx().kspace_encode_step_1 = static_cast<uint16_t>(i);
                     acq.idx().repetition = r*acc_factor + a;
                     acq.sample_time_us() = 5.0;
                     for (uint16_t c = 0; c < ncoils; c++) {
@@ -180,6 +183,16 @@ int main(int argc, char** argv)
 	sys.receiverChannels = ncoils;
 	h.acquisitionSystemInformation = sys;
 
+	SubjectInformation subject;
+	subject.patientID = "ISMRMRDSheppLoganPhantom";
+	h.subjectInformation = subject;
+
+	MeasurementInformation meas;
+	boost::uuids::random_generator uuidGenerator;
+	meas.measurementID = boost::uuids::to_string(uuidGenerator());
+	meas.patientPosition = "HFS";
+	h.measurementInformation = meas;
+    
 	//Create an encoding section
         Encoding e;
         e.encodedSpace.matrixSize.x = readout;
@@ -220,7 +233,7 @@ int main(int argc, char** argv)
         
 	//Write the header to the data file.
 	d.writeHeader(xml_header);
-
+    
         //Write out some arrays for convenience
         d.appendNDArray("phantom", *phantom);
         d.appendNDArray("csm", *coils);
diff --git a/utilities/ismrmrd_hdf5_to_stream.cpp b/utilities/ismrmrd_hdf5_to_stream.cpp
new file mode 100644
index 0000000..cb1aa0b
--- /dev/null
+++ b/utilities/ismrmrd_hdf5_to_stream.cpp
@@ -0,0 +1,195 @@
+#include "ismrmrd/dataset.h"
+#include "ismrmrd/serialization.h"
+#include "ismrmrd/serialization_iostream.h"
+#include "ismrmrd_io_utils.h"
+
+#include <boost/program_options.hpp>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+namespace po = boost::program_options;
+
+void serialize_to_stream(const std::string &input_file, const std::string &groupname, const std::vector<std::string> &image_series, std::ostream &os, std::string config_file, std::string config_text) {
+    ISMRMRD::Dataset d(input_file.c_str(), groupname.c_str(), false);
+    ISMRMRD::OStreamView ws(os);
+    ISMRMRD::ProtocolSerializer serializer(ws);
+
+    if (config_file.size()) {
+        ISMRMRD::ConfigFile cfg;
+        strcpy(cfg.config, config_file.c_str());
+        serializer.serialize(cfg);
+    }
+
+    if (config_text.size()) {
+        ISMRMRD::ConfigText cfg;
+        cfg.config_text = config_text;
+        serializer.serialize(cfg);
+    }
+
+    try {
+        std::string xml;
+        d.readHeader(xml);
+        ISMRMRD::IsmrmrdHeader hdr;
+        ISMRMRD::deserialize(xml.c_str(), hdr);
+        serializer.serialize(hdr);
+    } catch (std::exception &) {
+        if (image_series.empty()) {
+            // rethrow exception if image series not specified
+            throw;
+        }
+    }
+
+    // If user has asked for image series, we will only send images,
+    // otherwise we will send acquisitions and waveforms.
+    if (!image_series.empty()) {
+        for (size_t s = 0; s < image_series.size(); s++) {
+            unsigned int number_of_images = d.getNumberOfImages(image_series[s].c_str());
+            for (unsigned int i = 0; i < number_of_images; i++) {
+                // Danger: Here we will take advantage of the fact that the
+                // C++ wrapper calls ismrmrd_read_image (C API) without checking
+                // that the type is correct. This is a bug in the C++ wrapper.
+                ISMRMRD::Image<complex_double_t> img;
+                d.readImage(image_series[s].c_str(), i, img);
+
+                if (img.getHead().data_type == ISMRMRD::ISMRMRD_USHORT) {
+                    ISMRMRD::Image<unsigned short> *img_ushort = reinterpret_cast<ISMRMRD::Image<unsigned short> *>(&img);
+                    serializer.serialize(*img_ushort);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_SHORT) {
+                    ISMRMRD::Image<short> *img_short = reinterpret_cast<ISMRMRD::Image<short> *>(&img);
+                    serializer.serialize(*img_short);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_UINT) {
+                    ISMRMRD::Image<unsigned int> *img_uint = reinterpret_cast<ISMRMRD::Image<unsigned int> *>(&img);
+                    serializer.serialize(*img_uint);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_INT) {
+                    ISMRMRD::Image<int> *img_int = reinterpret_cast<ISMRMRD::Image<int> *>(&img);
+                    serializer.serialize(*img_int);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_FLOAT) {
+                    ISMRMRD::Image<float> *img_float = reinterpret_cast<ISMRMRD::Image<float> *>(&img);
+                    serializer.serialize(*img_float);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_DOUBLE) {
+                    ISMRMRD::Image<double> *img_double = reinterpret_cast<ISMRMRD::Image<double> *>(&img);
+                    serializer.serialize(*img_double);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_CXFLOAT) {
+                    ISMRMRD::Image<complex_float_t> *img_cxfloat = reinterpret_cast<ISMRMRD::Image<complex_float_t> *>(&img);
+                    serializer.serialize(*img_cxfloat);
+                } else if (img.getHead().data_type == ISMRMRD::ISMRMRD_CXDOUBLE) {
+                    serializer.serialize(img);
+                } else {
+                    throw std::runtime_error("Unknown data type");
+                }
+            }
+        }
+    } else {
+        // Here we implement the pattern of merge sorting acquisitions and waveforms based on timestamp.
+        // For each dataset of waveforms and acquisitions, we will assume that they are sorted by timestamp.
+        unsigned int number_of_acquisitions = d.getNumberOfAcquisitions();
+        unsigned int number_of_waveforms = d.getNumberOfWaveforms();
+        unsigned int a = 0, a_fetched = number_of_acquisitions, w = 0, w_fetched = number_of_waveforms;
+        ISMRMRD::Acquisition acq;
+        ISMRMRD::Waveform wfm;
+        while (a < number_of_acquisitions || w < number_of_waveforms) {
+            if (a < number_of_acquisitions && a_fetched != a) {
+                d.readAcquisition(a, acq);
+                a_fetched = a;
+            }
+            if (w < number_of_waveforms && w_fetched != w) {
+                d.readWaveform(w, wfm);
+                w_fetched = w;
+            }
+
+            // If we have both acquisitions and waveforms, advance the one with the
+            // smaller timestamp, otherwise advance the one that exists
+            if (a < number_of_acquisitions && w < number_of_waveforms) {
+                if (acq.getHead().acquisition_time_stamp < wfm.head.time_stamp) {
+                    serializer.serialize(acq);
+                    a++;
+                } else {
+                    serializer.serialize(wfm);
+                    w++;
+                }
+            } else if (a < number_of_acquisitions) {
+                serializer.serialize(acq);
+                a++;
+            } else {
+                serializer.serialize(wfm);
+                w++;
+            }
+        }
+    }
+    serializer.close();
+}
+
+int main(int argc, char **argv) {
+
+    // Parse arguments using boost program options
+    po::options_description desc("Allowed options");
+
+    // Arguments
+    std::string config_file = "";
+    std::string local_config_file = "";
+    std::string config_text = "";
+    std::string input_file;
+    std::string output_file = "";
+    bool use_stdout = false;
+    std::vector<std::string> image_series;
+    std::string groupname;
+
+    // clang-format off
+    desc.add_options()
+        ("help,h", "produce help message")
+        ("input,i", po::value<std::string>(&input_file)->required(),"input ISMRMRD HDF5 file")
+        ("output,o", po::value<std::string>(&output_file),"Binary output file")
+        ("use-stdout", po::bool_switch(&use_stdout), "Use stdout for output")
+        ("group,g", po::value<std::string>(&groupname)->default_value("dataset"), "group name")
+        ("image-series,s", po::value<std::vector<std::string>>(&image_series)->multitoken(), "image series to extract")
+        ("config-file,c", po::value<std::string>(&config_file), "Configuration name (aka config file)")
+        ("local-config-file,C", po::value<std::string>(&local_config_file), "Configuration text file");
+    // clang-format on
+
+    po::variables_map vm;
+    try {
+        po::store(po::parse_command_line(argc, argv, desc), vm);
+        po::notify(vm);
+    } catch (boost::wrapexcept<po::required_option> &e) {
+        std::cerr << "Error: " << e.what() << std::endl;
+        std::cerr << desc << std::endl;
+        return 1;
+    }
+
+    if (vm.count("help")) {
+        std::cerr << desc << "\n";
+        return 1;
+    }
+
+    if (vm.count("output") && use_stdout) {
+        std::cerr << "Error: Cannot specify both output file and use-stdout" << std::endl;
+        return 1;
+    }
+
+    if (vm.count("config-file") && vm.count("local-config-file")) {
+        std::cerr << "Error: Cannot specify both config-name and config-text-file" << std::endl;
+        return 1;
+    }
+
+    // Read config text file into string
+    if (vm.count("local-config-file")) {
+        std::ifstream f(local_config_file);
+        std::stringstream buffer;
+        buffer << f.rdbuf();
+        config_text = buffer.str();
+    }
+
+    if (use_stdout) {
+        ISMRMRD::set_binary_io();
+        serialize_to_stream(input_file, groupname, image_series, std::cout, config_file, config_text);
+    } else if (output_file != "") {
+        std::ofstream out(output_file, std::ios::out | std::ios::binary);
+        serialize_to_stream(input_file, groupname, image_series, out, config_file, config_text);
+    } else {
+        std::cerr << "Error: Must specify either output file or use-stdout" << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/utilities/ismrmrd_io_utils.h b/utilities/ismrmrd_io_utils.h
new file mode 100644
index 0000000..64e3d07
--- /dev/null
+++ b/utilities/ismrmrd_io_utils.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifdef _MSC_VER
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+namespace ISMRMRD {
+void set_binary_io() {
+#ifdef _MSC_VER
+    _setmode(_fileno(stdin), _O_BINARY);
+    _setmode(_fileno(stdout), _O_BINARY);
+#endif
+}
+} // namespace ISMRMRD
diff --git a/utilities/ismrmrd_stream_to_hdf5.cpp b/utilities/ismrmrd_stream_to_hdf5.cpp
new file mode 100644
index 0000000..f795ef2
--- /dev/null
+++ b/utilities/ismrmrd_stream_to_hdf5.cpp
@@ -0,0 +1,151 @@
+#include "ismrmrd/dataset.h"
+#include "ismrmrd/serialization_iostream.h"
+#include "ismrmrd_io_utils.h"
+#include <boost/program_options.hpp>
+#include <fstream>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+template <typename T>
+std::string create_image_series_name(const ISMRMRD::Image<T> &img) {
+    std::stringstream ss;
+    ss << "image_" << img.getHead().image_series_index;
+    return ss.str();
+}
+
+void convert_stream_to_hdf5(std::string output_file, std::string groupname, std::istream &is) {
+    ISMRMRD::Dataset d(output_file.c_str(), groupname.c_str(), true);
+
+    ISMRMRD::IStreamView rs(is);
+    ISMRMRD::ProtocolDeserializer deserializer(rs);
+
+    // Some reconstructions return the header but it is not required.
+    if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_HEADER) {
+        ISMRMRD::IsmrmrdHeader hdr;
+        deserializer.deserialize(hdr);
+
+        // We will convert the XML header to a string and write it to the HDF5 file
+        std::stringstream xmlstream;
+        ISMRMRD::serialize(hdr, xmlstream);
+        d.writeHeader(xmlstream.str());
+    }
+
+    while (deserializer.peek() != ISMRMRD::ISMRMRD_MESSAGE_CLOSE) {
+        if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_ACQUISITION) {
+            ISMRMRD::Acquisition acq;
+            deserializer.deserialize(acq);
+            d.appendAcquisition(acq);
+        } else if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_IMAGE) {
+            if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_USHORT) {
+                ISMRMRD::Image<unsigned short> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_SHORT) {
+                ISMRMRD::Image<short> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_UINT) {
+                ISMRMRD::Image<unsigned int> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_INT) {
+                ISMRMRD::Image<int> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_FLOAT) {
+                ISMRMRD::Image<float> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_DOUBLE) {
+                ISMRMRD::Image<double> img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_CXFLOAT) {
+                ISMRMRD::Image<std::complex<float> > img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else if (deserializer.peek_image_data_type() == ISMRMRD::ISMRMRD_CXDOUBLE) {
+                ISMRMRD::Image<std::complex<double> > img;
+                deserializer.deserialize(img);
+                d.appendImage(create_image_series_name(img), img);
+            } else {
+                throw std::runtime_error("Unknown image type");
+            }
+        } else if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_WAVEFORM) {
+            ISMRMRD::Waveform wfm;
+            deserializer.deserialize(wfm);
+            d.appendWaveform(wfm);
+        } else if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_TEXT) {
+            ISMRMRD::TextMessage txt;
+            deserializer.deserialize(txt);
+            std::cerr << "TEXT MESSAGE: " << txt.message << std::endl;
+        } else {
+            std::stringstream ss;
+            ss << "Unknown message type " << deserializer.peek();
+            throw std::runtime_error(ss.str());
+        }
+    }
+
+    // If we can read any more at this point, it is an error
+    if (is.get() != EOF) {
+        throw std::runtime_error("Extra data after ISMRMRD_CLOSE");
+    }
+}
+
+int main(int argc, char **argv) {
+    // Arguments
+    std::string input_file = "";
+    std::string output_file;
+    std::string groupname;
+    bool use_stdin = false;
+
+    // Parse arguments using boost program options
+    po::options_description desc("Allowed options");
+
+    // clang-format off
+    desc.add_options()
+        ("help,h", "produce help message")
+        ("input,i", po::value<std::string>(&input_file),"Binary input file")
+        ("output,o", po::value<std::string>(&output_file)->required(),"ISMRMRD HDF5 output file")
+        ("use-stdin", po::bool_switch(&use_stdin), "Use stdout for output")
+        ("group,g", po::value<std::string>(&groupname)->default_value("dataset"), "group name");
+    // clang-format on
+
+    po::variables_map vm;
+    try {
+        po::store(po::parse_command_line(argc, argv, desc), vm);
+        po::notify(vm);
+    } catch (boost::wrapexcept<po::required_option> &e) {
+        std::cerr << "Error: " << e.what() << std::endl;
+        std::cerr << desc << std::endl;
+        return 1;
+    }
+
+    if (vm.count("input") && use_stdin) {
+        std::cerr << "Error: Cannot specify both output file and use-stdout" << std::endl;
+        return 1;
+    }
+
+    if (vm.count("help")) {
+        std::cerr << desc << "\n";
+        return 1;
+    }
+
+    if (vm.count("input")) {
+        std::ifstream is(input_file.c_str(), std::ios::binary);
+        if (!is) {
+            std::cerr << "Error: Could not open input file " << input_file << std::endl;
+            return 1;
+        }
+        convert_stream_to_hdf5(output_file, groupname, is);
+    } else if (use_stdin) {
+        ISMRMRD::set_binary_io();
+        convert_stream_to_hdf5(output_file, groupname, std::cin);
+    } else {
+        std::cerr << "Error: Must specify either input file or use-stdin" << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/utilities/ismrmrd_test_xml.cpp b/utilities/ismrmrd_test_xml.cpp
index 47c1541..c5e97fc 100644
--- a/utilities/ismrmrd_test_xml.cpp
+++ b/utilities/ismrmrd_test_xml.cpp
@@ -24,7 +24,7 @@ int main(int argc, char** argv)
   deserialize(xml.c_str(),h);
 
   pugi::xml_document doc;
-  pugi::xml_parse_result result = doc.load(xml.c_str());
+  pugi::xml_parse_result result = doc.load_string(xml.c_str());
   if (!result) {
     std::cout << "Unable to load XML document using pugixml parser" << std::endl;
   }
diff --git a/utilities/stream_recon_cartesian_2d.cpp b/utilities/stream_recon_cartesian_2d.cpp
new file mode 100644
index 0000000..69f4baf
--- /dev/null
+++ b/utilities/stream_recon_cartesian_2d.cpp
@@ -0,0 +1,178 @@
+#include "fftw3.h"
+#include "ismrmrd/serialization_iostream.h"
+#include "ismrmrd_io_utils.h"
+#include <boost/program_options.hpp>
+#include <fstream>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+// Helper function for the FFTW library
+void circshift(complex_float_t *out, const complex_float_t *in, int xdim, int ydim, int xshift, int yshift) {
+    for (int i = 0; i < ydim; i++) {
+        int ii = (i + yshift) % ydim;
+        for (int j = 0; j < xdim; j++) {
+            int jj = (j + xshift) % xdim;
+            out[ii * xdim + jj] = in[i * xdim + j];
+        }
+    }
+}
+
+#define fftshift(out, in, x, y) circshift(out, in, x, y, (x / 2), (y / 2))
+
+void reconstruct(std::istream &in, std::ostream &out) {
+    ISMRMRD::IStreamView rs(in);
+    ISMRMRD::OStreamView ws(out);
+
+    ISMRMRD::ProtocolDeserializer deserializer(rs);
+    ISMRMRD::ProtocolSerializer serializer(ws);
+
+    if (deserializer.peek() == ISMRMRD::ISMRMRD_MESSAGE_CONFIG_FILE) {
+        ISMRMRD::ConfigFile cfg;
+        deserializer.deserialize(cfg);
+        std::string config_name(cfg.config);
+        std::cerr << "Reconstruction received config file: " << config_name << std::endl;
+        std::cerr << "Configuration file is ignore in this sample reconstruction" << std::endl;
+    }
+
+    ISMRMRD::IsmrmrdHeader hdr;
+    deserializer.deserialize(hdr);
+
+    if (hdr.encoding.size() != 1) {
+        throw std::runtime_error("This simple reconstruction application only supports one encoding space");
+    }
+
+    ISMRMRD::EncodingSpace e_space = hdr.encoding[0].encodedSpace;
+    ISMRMRD::EncodingSpace r_space = hdr.encoding[0].reconSpace;
+
+    if (e_space.matrixSize.z != 1) {
+        throw std::runtime_error("This simple reconstruction application only supports 2D encoding spaces");
+    }
+
+    uint16_t nX = e_space.matrixSize.x;
+    uint16_t nY = e_space.matrixSize.y;
+    uint16_t nCoils = 0;
+    ISMRMRD::NDArray<complex_float_t> buffer;
+    while (std::cin) {
+        ISMRMRD::Acquisition acq;
+        try {
+            deserializer.deserialize(acq);
+        } catch (ISMRMRD::ProtocolStreamClosed &) {
+            break;
+        }
+
+        if (!nCoils) {
+            nCoils = acq.active_channels();
+
+            // Allocate a buffer for the data
+            std::vector<size_t> dims;
+            dims.push_back(nX);
+            dims.push_back(nY);
+            dims.push_back(nCoils);
+            buffer = ISMRMRD::NDArray<complex_float_t>(dims);
+            std::fill(buffer.begin(), buffer.end(), complex_float_t(0.0f, 0.0f));
+        }
+
+        for (uint16_t c = 0; c < nCoils; c++) {
+            memcpy(&buffer(0, acq.idx().kspace_encode_step_1, c), &acq.data(0, c), sizeof(complex_float_t) * nX);
+        }
+    }
+
+    for (uint16_t c = 0; c < nCoils; c++) {
+        fftwf_complex *tmp = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * (nX * nY));
+        if (!tmp) {
+            throw std::runtime_error("Error allocating temporary storage for FFTW");
+        }
+        fftwf_plan p = fftwf_plan_dft_2d(nY, nX, tmp, tmp, FFTW_BACKWARD, FFTW_ESTIMATE);
+        fftshift(reinterpret_cast<complex_float_t *>(tmp), &buffer(0, 0, c), nX, nY);
+        fftwf_execute(p);
+        fftshift(&buffer(0, 0, c), reinterpret_cast<std::complex<float> *>(tmp), nX, nY);
+        fftwf_destroy_plan(p);
+        fftwf_free(tmp);
+    }
+
+    // Allocate an image
+    ISMRMRD::Image<float> img_out(r_space.matrixSize.x, r_space.matrixSize.y, 1, 1);
+    std::fill(img_out.begin(), img_out.end(), 0.0f);
+
+    // if there is oversampling in the readout direction remove it
+    uint16_t offset = ((e_space.matrixSize.x - r_space.matrixSize.x) >> 1);
+
+    // Take the sqrt of the sum of squares
+    for (uint16_t y = 0; y < r_space.matrixSize.y; y++) {
+        for (uint16_t x = 0; x < r_space.matrixSize.x; x++) {
+            for (uint16_t c = 0; c < nCoils; c++) {
+                img_out(x, y) += (std::abs(buffer(x + offset, y, c))) * (std::abs(buffer(x + offset, y, c)));
+            }
+            img_out(x, y) = std::sqrt(img_out(x, y));
+        }
+    }
+
+    img_out.setImageType(ISMRMRD::ISMRMRD_IMTYPE_MAGNITUDE);
+    img_out.setSlice(0);
+    img_out.setFieldOfView(r_space.fieldOfView_mm.x, r_space.fieldOfView_mm.y, r_space.fieldOfView_mm.z);
+
+    serializer.serialize(hdr);
+    serializer.serialize(img_out);
+    serializer.close();
+}
+
+int main(int argc, char **argv) {
+    // Parse arguments using boost program options
+    po::options_description desc("Allowed options");
+
+    // Arguments
+    std::string input_file = "";
+    std::string output_file = "";
+    bool use_stdin = false;
+    bool use_stdout = false;
+    // clang-format off
+    desc.add_options()
+        ("help,h", "produce help message")
+        ("input,i", po::value<std::string>(&input_file),"input ISMRMRD HDF5 file")
+        ("output,o", po::value<std::string>(&output_file),"Binary output file")
+        ("use-stdout", po::bool_switch(&use_stdout), "Use stdout for output")
+        ("use-stdin", po::bool_switch(&use_stdin), "Use stdout for output");
+    // clang-format on
+
+    po::variables_map vm;
+    po::store(po::parse_command_line(argc, argv, desc), vm);
+    po::notify(vm);
+
+    if (vm.count("help")) {
+        std::cerr << desc << "\n";
+        return 1;
+    }
+
+    if (vm.count("input") && use_stdin) {
+        std::cerr << "Error: Cannot specify both input file and use-stdin" << std::endl;
+        return 1;
+    }
+
+    if (vm.count("output") && use_stdout) {
+        std::cerr << "Error: Cannot specify both output file and use-stdout" << std::endl;
+        return 1;
+    }
+
+    if (use_stdin && use_stdout) {
+        ISMRMRD::set_binary_io();
+        reconstruct(std::cin, std::cout);
+    } else if (input_file.size() && output_file.size()) {
+        std::ifstream input(input_file.c_str(), std::ios::in | std::ios::binary);
+        std::ofstream output(output_file.c_str(), std::ios::out | std::ios::binary);
+        reconstruct(input, output);
+    } else if (input_file.size() && use_stdout) {
+        ISMRMRD::set_binary_io();
+        std::ifstream input(input_file.c_str(), std::ios::in | std::ios::binary);
+        reconstruct(input, std::cout);
+    } else if (output_file.size() && use_stdin) {
+        ISMRMRD::set_binary_io();
+        std::ofstream output(output_file.c_str(), std::ios::out | std::ios::binary);
+        reconstruct(std::cin, output);
+    } else {
+        std::cerr << "Error: Must specify either input file and output file or use-stdin and use-stdout" << std::endl;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/vcpkg.json b/vcpkg.json
new file mode 100644
index 0000000..27c062d
--- /dev/null
+++ b/vcpkg.json
@@ -0,0 +1,18 @@
+{
+  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
+  "name": "ismrmrd",
+  "version": "1.13.3",
+  "dependencies": [
+    {
+      "name": "hdf5",
+      "features": ["threadsafe"]
+    },
+    "fftw3",
+    "pugixml",
+    "boost-test",
+    "boost-random",
+    "boost-program-options",
+    "boost-uuid",
+    "boost-filesystem"
+  ]
+}

More details

Full run details

Historical runs