[mapnik] 01/08: Imported Upstream version 3.0.10+ds

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Feb 26 22:40:40 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository mapnik.

commit ff80a3bb6d8bf798d1b941e425643e5f165ed459
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Feb 26 19:48:18 2016 +0100

    Imported Upstream version 3.0.10+ds
---
 .gitmodules                                        |    5 +-
 .travis.yml                                        |   96 +-
 CHANGELOG.md                                       |   43 +-
 INSTALL.md                                         |    8 +-
 Makefile                                           |   20 +-
 README.md                                          |    4 +-
 SConstruct                                         |   15 +-
 appveyor.yml                                       |    4 +-
 benchmark/bench_framework.hpp                      |  198 +-
 benchmark/test_array_allocation.cpp                |   98 +-
 benchmark/test_numeric_cast_vs_static_cast.cpp     |   16 +-
 benchmark/test_png_encoding1.cpp                   |    2 +-
 benchmark/test_png_encoding2.cpp                   |    2 +-
 benchmark/test_polygon_clipping_rendering.cpp      |   32 +-
 benchmark/test_proj_transform1.cpp                 |   38 +-
 bootstrap.sh                                       |   51 +-
 demo/python/rundemo.py                             |    2 +-
 demo/viewer/about_dialog.cpp                       |    2 +-
 demo/viewer/about_dialog.hpp                       |    2 +-
 demo/viewer/info_dialog.cpp                        |    2 +-
 demo/viewer/info_dialog.hpp                        |    2 +-
 demo/viewer/layer_info_dialog.cpp                  |    2 +-
 demo/viewer/layer_info_dialog.hpp                  |    2 +-
 demo/viewer/layerdelegate.cpp                      |    2 +-
 demo/viewer/layerdelegate.hpp                      |    2 +-
 demo/viewer/layerlistmodel.cpp                     |    2 +-
 demo/viewer/layerlistmodel.hpp                     |    2 +-
 demo/viewer/layerwidget.cpp                        |    2 +-
 demo/viewer/layerwidget.hpp                        |    2 +-
 demo/viewer/main.cpp                               |    2 +-
 demo/viewer/mainwindow.cpp                         |    2 +-
 demo/viewer/mainwindow.hpp                         |    2 +-
 demo/viewer/mapwidget.cpp                          |    2 +-
 demo/viewer/mapwidget.hpp                          |    2 +-
 demo/viewer/styles_model.cpp                       |    2 +-
 demo/viewer/styles_model.hpp                       |    2 +-
 deps/agg/include/agg_array.h                       |   78 +-
 deps/agg/include/agg_conv_clip_polygon.h           |    4 +-
 deps/agg/include/agg_conv_clip_polyline.h          |    4 +-
 deps/agg/include/agg_rasterizer_cells_aa.h         |    0
 deps/agg/include/agg_rasterizer_compound_aa.h      |    0
 deps/agg/include/agg_vcgen_dash.h                  |    7 +-
 deps/mapnik/build.py                               |   11 +-
 include/mapnik/attribute.hpp                       |    4 +-
 include/mapnik/color.hpp                           |   39 +-
 include/mapnik/csv/csv_grammar.hpp                 |   50 +-
 include/mapnik/enumeration.hpp                     |   83 +-
 include/mapnik/expression_evaluator.hpp            |   18 +-
 include/mapnik/expression_grammar.hpp              |   41 +-
 include/mapnik/expression_grammar_impl.hpp         |   37 +-
 include/mapnik/feature.hpp                         |    8 +-
 include/mapnik/font_engine_freetype.hpp            |   12 +-
 include/mapnik/geometry.hpp                        |   19 +-
 include/mapnik/geometry_envelope_impl.hpp          |   23 +-
 include/mapnik/geometry_reprojection_impl.hpp      |   43 +-
 include/mapnik/geometry_unique.hpp                 |   82 -
 include/mapnik/gradient.hpp                        |   15 +-
 include/mapnik/group/group_layout_manager.hpp      |   40 +-
 include/mapnik/image.hpp                           |    3 -
 include/mapnik/image_any.hpp                       |    5 +-
 include/mapnik/image_filter.hpp                    |   16 +-
 include/mapnik/image_filter_grammar.hpp            |   50 +-
 include/mapnik/image_filter_grammar_impl.hpp       |  143 +-
 include/mapnik/image_impl.hpp                      |    8 -
 include/mapnik/image_null.hpp                      |    4 +-
 include/mapnik/image_scaling_traits.hpp            |    4 +-
 include/mapnik/image_util_jpeg.hpp                 |    2 +
 include/mapnik/image_util_png.hpp                  |    2 +
 include/mapnik/image_view.hpp                      |    4 -
 include/mapnik/image_view_any.hpp                  |    5 +-
 include/mapnik/image_view_impl.hpp                 |   19 -
 include/mapnik/image_view_null.hpp                 |    2 +-
 include/mapnik/json/geometry_grammar.hpp           |    2 +
 include/mapnik/json/geometry_grammar_impl.hpp      |   39 +-
 include/mapnik/json/positions_grammar.hpp          |    4 +-
 include/mapnik/label_collision_detector.hpp        |   28 +-
 include/mapnik/mapped_memory_cache.hpp             |    4 +-
 include/mapnik/marker.hpp                          |   48 +-
 include/mapnik/marker_helpers.hpp                  |  107 +-
 include/mapnik/markers_placements/point.hpp        |   21 +-
 include/mapnik/memory_datasource.hpp               |    1 +
 include/mapnik/miniz_png.hpp                       |   95 -
 include/mapnik/offset_converter.hpp                |    2 -
 include/mapnik/params.hpp                          |    3 +-
 include/mapnik/png_io.hpp                          |   35 +-
 include/mapnik/pool.hpp                            |    1 -
 include/mapnik/quad_tree.hpp                       |    7 +-
 include/mapnik/renderer_common.hpp                 |   16 +-
 .../renderer_common/apply_vertex_converter.hpp     |    2 +-
 .../renderer_common/process_group_symbolizer.hpp   |  403 --
 .../renderer_common/render_group_symbolizer.hpp    |   66 +
 .../renderer_common/render_markers_symbolizer.hpp  |   76 +
 include/mapnik/renderer_common/render_thunk.hpp    |  115 +
 .../renderer_common/render_thunk_extractor.hpp     |   97 +
 include/mapnik/simplify_converter.hpp              |   12 +-
 .../{span_image_filter.h => span_image_filter.hpp} |    2 +
 include/mapnik/svg/svg_converter.hpp               |   10 +-
 include/mapnik/svg/svg_path_adapter.hpp            |    2 +-
 include/mapnik/svg/svg_path_attributes.hpp         |   19 +-
 include/mapnik/svg/svg_path_grammar.hpp            |   35 +-
 include/mapnik/svg/svg_renderer_agg.hpp            |  159 +-
 include/mapnik/symbolizer.hpp                      |    6 +-
 include/mapnik/symbolizer_base.hpp                 |   18 +-
 include/mapnik/symbolizer_default_values.hpp       |    5 +
 include/mapnik/symbolizer_utils.hpp                |    3 +-
 include/mapnik/text/face.hpp                       |    3 +-
 include/mapnik/text/glyph_positions.hpp            |   50 +-
 include/mapnik/text/renderer.hpp                   |   27 +
 .../mapnik/transform_expression_grammar_impl.hpp   |    4 +-
 include/mapnik/transform_path_adapter.hpp          |   12 +-
 include/mapnik/transform_processor.hpp             |   14 +-
 include/mapnik/util/dasharray_parser.hpp           |   23 +-
 include/mapnik/util/noncopyable.hpp                |   11 +
 include/mapnik/util/recursive_wrapper.hpp          |  150 -
 include/mapnik/util/spatial_index.hpp              |   19 +-
 include/mapnik/util/variant.hpp                    |  866 +---
 include/mapnik/util/variant_io.hpp                 |    2 +-
 include/mapnik/value.hpp                           |  667 ++-
 include/mapnik/value_hash.hpp                      |    2 +-
 include/mapnik/value_types.hpp                     |   27 +
 include/mapnik/version.hpp                         |    2 +-
 include/mapnik/vertex.hpp                          |   17 -
 include/mapnik/vertex_processor.hpp                |   20 +-
 include/mapnik/view_transform.hpp                  |   18 +-
 include/mapnik/warning_ignore.hpp                  |    5 +-
 include/mapnik/wkb.hpp                             |    1 +
 localize.sh                                        |    1 +
 plugins/input/csv/csv_datasource.cpp               |   37 +-
 plugins/input/csv/csv_inline_featureset.cpp        |    4 +-
 plugins/input/csv/csv_utils.hpp                    |  128 +-
 plugins/input/geojson/geojson_datasource.cpp       |   78 +-
 plugins/input/geojson/geojson_datasource.hpp       |    2 +
 plugins/input/ogr/ogr_converter.cpp                |    3 +
 plugins/input/pgraster/build.py                    |    3 +-
 plugins/input/shape/shape_datasource.cpp           |    8 +-
 plugins/input/shape/shape_index_featureset.cpp     |   25 +-
 plugins/input/shape/shape_index_featureset.hpp     |   19 +-
 plugins/input/shape/shape_io.cpp                   |   79 +
 plugins/input/shape/shape_io.hpp                   |   20 +-
 plugins/input/shape/shapefile.hpp                  |    5 +
 scripts/appveyor-system-info.ps1                   |   84 +
 scripts/build-appveyor.bat                         |   23 +-
 scripts/build-local.bat                            |   31 +-
 scripts/msbuild-force-mp-and-buildinparallel.props |    9 +
 scripts/parse-appveyor-yml.ps1                     |    7 +
 scripts/time-header                                |    9 +
 scripts/travis-common.sh                           |  109 +
 src/agg/agg_renderer.cpp                           |   12 +-
 src/agg/process_debug_symbolizer.cpp               |   10 +-
 src/agg/process_group_symbolizer.cpp               |   73 +-
 src/agg/process_line_pattern_symbolizer.cpp        |    8 +-
 src/agg/process_markers_symbolizer.cpp             |  131 +-
 src/agg/process_point_symbolizer.cpp               |    1 -
 src/agg/process_polygon_pattern_symbolizer.cpp     |    6 +-
 src/build.py                                       |    6 +-
 src/cairo/cairo_renderer.cpp                       |    6 +-
 src/cairo/process_group_symbolizer.cpp             |   60 +-
 src/cairo/process_markers_symbolizer.cpp           |   97 +-
 src/color.cpp                                      |   86 +-
 src/dasharray_parser.cpp                           |   33 +-
 src/datasource_cache.cpp                           |    4 +-
 src/expression_node.cpp                            |   21 +-
 src/font_engine_freetype.cpp                       |    9 +-
 src/geometry_envelope.cpp                          |    2 +
 src/gradient.cpp                                   |   18 -
 src/grid/process_group_symbolizer.cpp              |   79 +-
 src/grid/process_markers_symbolizer.cpp            |  149 +-
 src/grid/process_point_symbolizer.cpp              |    1 -
 src/group/group_layout_manager.cpp                 |   19 +-
 src/image_compositing.cpp                          |    8 +-
 src/image_copy.cpp                                 |    8 +-
 .../image_filter_grammar.cpp                       |   21 +-
 src/image_filter_types.cpp                         |    1 -
 src/image_util.cpp                                 |   14 +-
 src/image_util_png.cpp                             |    4 +-
 src/mapped_memory_cache.cpp                        |    5 +-
 src/marker_cache.cpp                               |    6 +-
 src/memory_datasource.cpp                          |   28 +-
 src/miniz.c                                        | 4834 --------------------
 src/miniz_png.cpp                                  |  375 --
 src/renderer_common.cpp                            |   24 +-
 src/renderer_common/process_group_symbolizer.cpp   |  233 -
 src/renderer_common/render_group_symbolizer.cpp    |  194 +
 .../renderer_common/render_markers_symbolizer.cpp  |   90 +-
 src/renderer_common/render_thunk_extractor.cpp     |  155 +
 src/save_map.cpp                                   |    5 +-
 src/svg/output/svg_renderer.cpp                    |    1 -
 src/svg/svg_parser.cpp                             |  135 +-
 src/svg/svg_path_parser.cpp                        |    8 +-
 src/text/glyph_positions.cpp                       |    2 +-
 src/text/renderer.cpp                              |    4 +-
 src/tiff_reader.cpp                                |    8 +-
 src/warp.cpp                                       |    4 +-
 test/catch.hpp                                     | 3326 ++++++++------
 test/catch_ext.hpp                                 |   22 +
 test/unit/color/css_color.cpp                      |   24 +
 test/unit/core/box2d_test.cpp                      |   59 +
 test/unit/core/expressions_test.cpp                |  183 +
 test/unit/core/value_test.cpp                      |  109 +
 test/unit/datasource/csv.cpp                       |   10 +-
 test/unit/datasource/ds_test_util.hpp              |   45 +-
 test/unit/datasource/geojson.cpp                   |   46 +-
 test/unit/datasource/postgis.cpp                   |  279 +-
 test/unit/datasource/shapeindex.cpp                |  114 +-
 test/unit/geometry/geometry.cpp                    |   13 +
 test/unit/geometry/geometry_equal.hpp              |   20 +-
 test/unit/geometry/geometry_is_simple.cpp          |   32 +
 test/unit/geometry/geometry_is_valid.cpp           |   77 +-
 test/unit/imaging/image_filter.cpp                 |  138 +-
 test/unit/imaging/image_io_test.cpp                |   58 +-
 test/unit/numerics/enumeration.cpp                 |   16 +
 test/unit/run.cpp                                  |   60 +-
 test/unit/svg/svg_parser_test.cpp                  |  255 +-
 test/unit/svg/svg_path_parser_test.cpp             |  164 +
 .../image_util_jpeg.hpp => test/unit/svg/util.hpp  |   32 +-
 test/visual/runner.cpp                             |   14 +-
 utils/mapnik-config/build.py                       |    2 +-
 utils/mapnik-index/mapnik-index.cpp                |    2 +-
 utils/mapnik-index/process_csv_file.cpp            |   27 +-
 utils/mapnik-index/process_geojson_file.cpp        |    5 +-
 utils/mapnik-index/process_geojson_file.hpp        |    2 +-
 utils/mapnik-render/mapnik-render.cpp              |    2 +-
 utils/shapeindex/shapeindex.cpp                    |  127 +-
 utils/svg2png/svg2png.cpp                          |   11 +-
 224 files changed, 6903 insertions(+), 10993 deletions(-)

diff --git a/.gitmodules b/.gitmodules
index 4cca9a4..dfcbea5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,4 +5,7 @@
 [submodule "test/data-visual"]
 	path = test/data-visual
 	url = https://github.com/mapnik/test-data-visual.git
-	branch = master
\ No newline at end of file
+	branch = master
+[submodule "deps/mapbox/variant"]
+	path = deps/mapbox/variant
+	url = https://github.com/mapbox/variant.git
diff --git a/.travis.yml b/.travis.yml
index 656ac25..bce7b05 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,10 @@
-language: cpp
+language: c
 
 sudo: false
 
 git:
   depth: 10
-  submodules: true
+  submodules: false
 
 env:
   global:
@@ -27,65 +27,67 @@ matrix:
   include:
     - os: linux
       compiler: clang
-      env: JOBS=8 MASON_PUBLISH=true
-    - os: linux
-      compiler: gcc
-      env: JOBS=6
+      env: JOBS=8 CXX="ccache clang++-3.5 -Qunused-arguments" CC="clang-3.5" MASON_PUBLISH=true BENCH=True
+      addons:
+        apt:
+          sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
+          packages: [ 'clang-3.5' ]
+    # OOM killer knocking out build on render_markers_symbolizer.cpp
+    #
+    #- os: linux
+    #  compiler: gcc
+    #  env: JOBS=6 CXX="ccache g++-4.9" CC="gcc-4.9"
+    #  addons:
+    #    apt:
+    #      sources: [ 'ubuntu-toolchain-r-test' ]
+    #      packages: [ 'gcc-4.9', 'g++-4.9', 'libstdc++-4.9-dev', 'libstdc++6' ]
     - os: osx
       compiler: clang
-      env: JOBS=8 MASON_PUBLISH=true
+      # https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions
+      osx_image: xcode7.2 # upgrades clang from 6 -> 7
+      env: JOBS=8 MASON_PUBLISH=true HEAVY_JOBS=3
     - os: osx
       compiler: clang
-      env: JOBS=8 COVERAGE=true
+      osx_image: xcode7.2 # upgrades clang from 6 -> 7
+      env: JOBS=8 COVERAGE=true HEAVY_JOBS=3
 
 before_install:
+ - source scripts/travis-common.sh
+ - export PYTHONUSERBASE=$(pwd)/mason_packages/.link
+ - export PATH=${PYTHONUSERBASE}/bin:${PATH}
  - export COVERAGE=${COVERAGE:-false}
  - export MASON_PUBLISH=${MASON_PUBLISH:-false}
+ - export BENCH=${BENCH:-false}
  - if [[ ${TRAVIS_BRANCH} != 'master' ]]; then export MASON_PUBLISH=false; fi
  - if [[ ${TRAVIS_PULL_REQUEST} != 'false' ]]; then export MASON_PUBLISH=false; fi
+ - git_submodule_update --init --depth=10
 
 install:
- - if [[ $(uname -s) == 'Linux' ]]; then
-     export CXX="ccache clang++-3.5 -Qunused-arguments";
-     export CC="ccache clang-3.5 -Qunused-arguments";
-     export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python2.7/site-packages;
-   else
-     brew rm postgis --force;
-     brew install postgis --force;
-     pg_ctl -w start -l postgres.log --pgdata /usr/local/var/postgres;
-     createuser -s postgres;
-     export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python/site-packages;
-   fi
- - psql -c 'create database template_postgis;' -U postgres;
- - psql -c 'create extension postgis;' -d template_postgis -U postgres;
- - if [[ ${COVERAGE} == true ]]; then
-     PYTHONUSERBASE=$(pwd)/mason_packages/.link pip install --user cpp-coveralls;
-   fi
+ - on 'linux' export PYTHONPATH=${PYTHONUSERBASE}/lib/python2.7/site-packages
+ - on 'osx' export PYTHONPATH=${PYTHONUSERBASE}/lib/python/site-packages
+ - on 'osx' brew rm postgis --force
+ - on 'osx' brew install postgis --force
+ - on 'osx' pg_ctl -w start -l postgres.log --pgdata /usr/local/var/postgres
+ - on 'osx' createuser -s postgres
+ - psql -c 'create database template_postgis;' -U postgres
+ - psql -c 'create extension postgis;' -d template_postgis -U postgres
+ - enabled ${COVERAGE} pip install --user cpp-coveralls
 
-script:
+before_script:
  - source bootstrap.sh
- - if [[ ${COVERAGE} == true ]]; then
-     ./configure CUSTOM_LDFLAGS='--coverage' CUSTOM_CXXFLAGS='--coverage' CUSTOM_CFLAGS='--coverage' DEBUG=True;
-   elif [[ ${MASON_PUBLISH} == true ]]; then
-     export MASON_NAME=mapnik;
-     export MASON_VERSION=latest;
-     export MASON_LIB_FILE=lib/libmapnik-wkt.a;
-     source ./.mason/mason.sh;
-     ./configure PREFIX=${MASON_PREFIX} PATH_REPLACE='' MAPNIK_BUNDLED_SHARE_DIRECTORY=True RUNTIME_LINK='static';
-   else
-     ./configure;
-   fi
+ - commit_message_parse
+
+script:
+ - export SCONSFLAGS='--debug=time'
+ - configure BENCHMARK=${BENCH}
  - make
- - make test || TEST_RESULT=$?
- - if [[ ${COVERAGE} == true ]]; then
-     ./mason_packages/.link/bin/cpp-coveralls --build-root . --gcov-options '\-lp' --exclude mason_packages --exclude .sconf_temp --exclude benchmark --exclude deps --exclude scons --exclude test --exclude demo --exclude docs --exclude fonts --exclude utils > /dev/null; 
-   fi
- - if [[ ${COVERAGE} != true ]]; then
-     make bench;
-   fi
- - if [[ ${TEST_RESULT} != 0 ]]; then exit $TEST_RESULT ; fi;
- - if [[ ${MASON_PUBLISH} == true ]]; then
-     ./mason_latest.sh build;
-     ./mason_latest.sh link;
+ - make test
+ - enabled ${COVERAGE} coverage
+ - enabled ${BENCH} make bench
+
+after_success:
+ - if enabled ${MASON_PUBLISH}; then
+     ./mason_latest.sh build &&
+     ./mason_latest.sh link &&
      ./mason_latest.sh publish;
    fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6114f8..f7c6078 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,37 @@ Developers: Please commit along with changes.
 
 For a complete change history, see the git log.
 
+## 3.0.10
+
+Released: February 25, 2016
+
+(Packaged from 5c0d496)
+
+#### Summary
+
+ - The `shapeindex` command now has a `--index-parts` option. When used the index will be bigger
+   but will allow the Shapefile datasource to only parse polygon parts within the query bounds.
+ - WARNING: index files generated with this newer Mapnik are invalid for older versions of Mapnik.
+ - Any `.index` files accompanying a `.shp` must now be regenerated otherwise
+   it will be skipped. To avoid this problem you can delete the existing `.index` files, or ideally run `shapeindex` to recreate the `.index`. (https://github.com/mapnik/mapnik/pull/3300)
+   The trigger for this change was an optimization that required a new binary format for the shapefile indexes (https://github.com/mapnik/mapnik/pull/3217).
+ - Shapeindex - another fix for skipping `null` shapes (#3288)
+ - Fixed support for filter expressions starting with `not` (https://github.com/mapnik/mapnik/issues/3017)
+ - Ensure `mapped_memory_cache` acts as singleton across shared objects (#3306)
+ - Removed miniz support in PNG encoder (#3281)
+ - Added `-fvisibility=hidden -fvisibility-inlines-hidden` to default compiler flags
+ - Fixed parsing of SVG `PathElement` (https://github.com/mapnik/mapnik/issues/3225)
+ - JSON parsing now supports arbitrary (nested) attributes in `geometry`
+ - Support for rendering `dash-array` in SVGs
+ - SVG parser is now stricter (fails is all input is not parsable) (#3251)
+ - SVG parser now correctly handles optional separator `(,)` between multiple command parts
+ - Optimized parsing of `png` format string
+ - The `memory_datasource` now dynamically reports correct datasource type (vector or raster)
+ - Upgraded `mapbox::variant v1.1.0`
+ - Compare: https://github.com/mapnik/mapnik/compare/v3.0.9...v3.0.10
+
+
+
 ## 3.0.9
 
 Released: November 26, 2015
@@ -18,12 +49,12 @@ Released: November 26, 2015
  - Fixed mapnik.util.variant issue when compiling with gcc-5.x and SSO enabled by default (https://github.com/mapnik/mapnik/issues/3103) (via @nkovacs)
  - Fixed issue with complex scripts where some character sequences weren't rendered correctly (https://github.com/mapnik/mapnik/issues/3050) (via @jkroll20)
  - Revived postgis.input tests
- - JSON: geometry grammar has been refactored and optimized to have expectation points
+ - JSON: geometry grammar has been re-factored and optimized to have expectation points
  - Filled missing specializations for value_bool in `mapnik::value` comparison operators
  - `mapnik.Image` - fixed copy semantics implementation for internal buffer
  - JSON parsing: unified error_handler across all grammars
  - Improved unit test coverage
- - Raster scaling: fixed nodata handling, acurracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147)
+ - Raster scaling: fixed nodata handling, accuracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147)
  - Added [`code of conduct`](http://contributor-covenant.org)
  - GeoJSON plug-in is updated to skip feature with empty geometries
  - GeoJSON plug-in : ensure original order of features is preserved (fixed) (https://github.com/mapnik/mapnik/issues/3182)
@@ -41,9 +72,9 @@ Released: October 23, 2015
 
  - Renamed `SHAPE_MEMORY_MAPPED_FILE` define to `MAPNIK_MEMORY_MAPPED_FILE`. Pass `./configure MEMORY_MAPPED_FILE=True|False` to request
    support for memory mapped files across Mapnik plugins (currently shape, csv, and geojson).
- - Unified `mapnik-index` utility supporing GeoJSON and CSV formats
+ - Unified `mapnik-index` utility supporting GeoJSON and CSV formats
  - Increased unit test coverage for GeoJSON and CSV plugins
- - shape.input - refactor to support *.shx and improve handling various bogus shapefiles
+ - shape.input - re-factor to support *.shx and improve handling various bogus shapefiles
  - geojson.input - make JSON parser stricter + support single Feature/Geometry as well as FeatureCollection
  - maintain 'FT_LOAD_NO_HINTING' + support >= harfbuzz 1.0.5
  - geojson.input - implement on-disk-index support
@@ -109,7 +140,7 @@ Released: August 26, 2015
 
 #### Summary
 
-- CSV.input: plug-in has been refactored to minimise memory usage and to improve handling of larger input.
+- CSV.input: plug-in has been re-factored to minimise memory usage and to improve handling of larger input.
   (NOTE: [large_csv](https://github.com/mapnik/mapnik/tree/large_csv) branch adds experimental trunsduction parser with deferred string initialisation)
 - CSV.input: added internal spatial index (boost::geometry::index::tree) for fast `bounding box` queries (https://github.com/mapnik/mapnik/pull/3010)
 - Fixed deadlock in recursive datasource registration via @zerebubuth (https://github.com/mapnik/mapnik/pull/3038)
@@ -1200,7 +1231,7 @@ Released April 1, 2009
 
 - Plugins: Use memory mapped files for reading shape file (r628)
 
-- Core: Use streams to write images (i/o refactor) (r628) (#15)
+- Core: Use streams to write images (i/o re-factor) (r628) (#15)
 
 # Mapnik 0.5.1
 
diff --git a/INSTALL.md b/INSTALL.md
index 0776a6f..d27004d 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -2,6 +2,13 @@
 
 Mapnik runs on Linux, OS X, Windows, and BSD systems.
 
+First clone mapnik from github and initialize submodules
+
+```bash
+git clone https://github.com/mapnik/mapnik.git
+git submodule update --init
+```
+
 To configure and build Mapnik do:
 
 ```bash
@@ -35,7 +42,6 @@ NOTE: the above will not work on windows, rather see https://github.com/mapnik/m
 
 Then to run the tests locally (without needing to install):
 
-    git submodule update --init
     make test
 
 Install like:
diff --git a/Makefile b/Makefile
index 3ff06a5..777303d 100755
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,10 @@ ifeq ($(JOBS),)
 	JOBS:=1
 endif
 
+ifeq ($(HEAVY_JOBS),)
+	HEAVY_JOBS:=1
+endif
+
 all: mapnik
 
 install:
@@ -37,22 +41,18 @@ python:
 	python bindings/python/test/visual.py -q
 
 src/json/libmapnik-json.a:
-	# we first build memory intensive files with -j1
-	$(PYTHON) scons/scons.py -j1 \
+	# we first build memory intensive files with -j$(HEAVY_JOBS)
+	$(PYTHON) scons/scons.py -j$(HEAVY_JOBS) \
 		--config=cache --implicit-cache --max-drift=1 \
-		src/renderer_common/process_group_symbolizer.os \
+		src/renderer_common/render_group_symbolizer.os \
+		src/renderer_common/render_markers_symbolizer.os \
+		src/renderer_common/render_thunk_extractor.os \
 		src/json/libmapnik-json.a \
 		src/wkt/libmapnik-wkt.a \
 		src/css_color_grammar.os \
 		src/expression_grammar.os \
 		src/transform_expression_grammar.os \
-		src/image_filter_types.os \
-		src/agg/process_markers_symbolizer.os \
-		src/agg/process_group_symbolizer.os \
-		src/grid/process_markers_symbolizer.os \
-		src/grid/process_group_symbolizer.os \
-		src/cairo/process_markers_symbolizer.os \
-		src/cairo/process_group_symbolizer.os \
+		src/image_filter_grammar.os \
 
 mapnik: src/json/libmapnik-json.a
 	# then install the rest with -j$(JOBS)
diff --git a/README.md b/README.md
index 18268de..b5ebf6a 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,8 @@ _/      _/    _/_/_/  _/_/_/    _/    _/  _/  _/    _/
                     _/
 ```
 
-[![Build Status Linux](https://secure.travis-ci.org/mapnik/mapnik.png)](http://travis-ci.org/mapnik/mapnik)
-[![Build status Windows](https://ci.appveyor.com/api/projects/status/hc9l7okdjtucfqqn?svg=true)](https://ci.appveyor.com/project/Mapbox/mapnik)
+[![Build Status Linux](https://api.travis-ci.org/mapnik/mapnik.svg?branch=master)](http://travis-ci.org/mapnik/mapnik)
+[![Build Status Windows](https://ci.appveyor.com/api/projects/status/hc9l7okdjtucfqqn?branch=master&svg=true)](https://ci.appveyor.com/project/Mapbox/mapnik)
 [![Coverage Status](https://coveralls.io/repos/mapnik/mapnik/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapnik/mapnik?branch=master)
 
 Mapnik is an open source toolkit for developing mapping applications. At the core is a C++ shared library providing algorithms and patterns for spatial data access and visualization.
diff --git a/SConstruct b/SConstruct
index f2b328e..2b851e4 100644
--- a/SConstruct
+++ b/SConstruct
@@ -294,6 +294,7 @@ opts.AddVariables(
     # Note: setting DEBUG=True will override any custom OPTIMIZATION level
     BoolVariable('DEBUG', 'Compile a debug version of Mapnik', 'False'),
     BoolVariable('DEBUG_UNDEFINED', 'Compile a version of Mapnik using clang/llvm undefined behavior asserts', 'False'),
+    BoolVariable('DEBUG_SANITIZE', 'Compile a version of Mapnik using clang/llvm address sanitation', 'False'),
     ListVariable('INPUT_PLUGINS','Input drivers to include',DEFAULT_PLUGINS,PLUGINS.keys()),
     ('WARNING_CXXFLAGS', 'Compiler flags you can set to reduce warning levels which are placed after -Wall.', ''),
 
@@ -1629,7 +1630,7 @@ if not preconfigured:
                 env["CAIRO_ALL_LIBS"] = ['cairo']
                 if env['RUNTIME_LINK'] == 'static':
                     env["CAIRO_ALL_LIBS"].extend(
-                        ['pixman-1','expat']
+                        ['pixman-1']
                     )
                 # todo - run actual checkLib?
                 env['HAS_CAIRO'] = True
@@ -1785,17 +1786,23 @@ if not preconfigured:
 
         # Common flags for g++/clang++ CXX compiler.
         # TODO: clean up code more to make -Wextra -Wsign-compare -Wsign-conversion -Wconversion viable
-        common_cxx_flags = '-Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
+        common_cxx_flags = '-fvisibility=hidden -fvisibility-inlines-hidden -Wall %s %s -ftemplate-depth-300 -Wsign-compare -Wshadow ' % (env['WARNING_CXXFLAGS'], pthread)
+
+        if 'clang++' in env['CXX']:
+            common_cxx_flags += ' -Wno-unsequenced '
 
         if env['DEBUG']:
             env.Append(CXXFLAGS = common_cxx_flags + '-O0')
         else:
-            # TODO - add back -fvisibility-inlines-hidden
-            # https://github.com/mapnik/mapnik/issues/1863
             env.Append(CXXFLAGS = common_cxx_flags + '-O%s' % (env['OPTIMIZATION']))
         if env['DEBUG_UNDEFINED']:
             env.Append(CXXFLAGS = '-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv -fwrapv')
 
+        if env['DEBUG_SANITIZE']:
+            env.Append(CXXFLAGS = ['-fsanitize=address'])
+            env.Append(LINKFLAGS = ['-fsanitize=address'])
+
+
         # if requested, sort LIBPATH and CPPPATH one last time before saving...
         if env['PRIORITIZE_LINKING']:
             conf.prioritize_paths(silent=True)
diff --git a/appveyor.yml b/appveyor.yml
index 6566a9a..ea7af41 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,7 +8,9 @@ environment:
 
 os: Visual Studio 2015
 
-shallow_clone: true
+#shallow_clone: true
+# limit clone to latest 5 commits
+clone_depth: 5
 
 install:
   - scripts\build-appveyor.bat
diff --git a/benchmark/bench_framework.hpp b/benchmark/bench_framework.hpp
index 52088e4..34c63a8 100644
--- a/benchmark/bench_framework.hpp
+++ b/benchmark/bench_framework.hpp
@@ -2,6 +2,7 @@
 #define __MAPNIK_BENCH_FRAMEWORK_HPP__
 
 // mapnik
+#include <mapnik/debug.hpp>
 #include <mapnik/params.hpp>
 #include <mapnik/value_types.hpp>
 #include <mapnik/safe_cast.hpp>
@@ -9,7 +10,7 @@
 
 // stl
 #include <chrono>
-#include <iomanip>
+#include <cstdio> // snprintf
 #include <iostream>
 #include <set>
 #include <sstream>
@@ -38,26 +39,79 @@ public:
     {
         return iterations_;
     }
+    mapnik::parameters const& params() const
+    {
+        return params_;
+    }
     virtual bool validate() const = 0;
     virtual bool operator()() const = 0;
     virtual ~test_case() {}
 };
 
-void handle_args(int argc, char** argv, mapnik::parameters & params)
+// gathers --long-option values in 'params';
+// returns the index of the first non-option argument,
+// or negated index of an ill-formed option argument
+inline int parse_args(int argc, char** argv, mapnik::parameters & params)
 {
-    if (argc > 0) {
-        for (int i=1;i<argc;++i) {
-            std::string opt(argv[i]);
-            // parse --foo bar
-            if (!opt.empty() && (opt.find("--") != 0)) {
-                std::string key = std::string(argv[i-1]);
-                if (!key.empty() && (key.find("--") == 0)) {
-                    key = key.substr(key.find_first_not_of("-"));
-                    params[key] = opt;
-                }
-            }
+    for (int i = 1; i < argc; ++i) {
+        const char* opt = argv[i];
+        if (opt[0] != '-') {
+            // non-option argument, return its index
+            return i;
+        }
+        if (opt[1] != '-') {
+            // we only accept --long-options, but instead of throwing,
+            // just issue a warning and let the caller decide what to do
+            std::clog << argv[0] << ": invalid option '" << opt << "'\n";
+            return -i; // negative means ill-formed option #i
+        }
+        if (opt[2] == '\0') {
+            // option-list terminator '--'
+            return i + 1;
+        }
+
+        // take option name without the leading '--'
+        std::string key(opt + 2);
+        size_t eq = key.find('=');
+        if (eq != std::string::npos) {
+            // one-argument form '--foo=bar'
+            params[key.substr(0, eq)] = key.substr(eq + 1);
+        }
+        else if (i + 1 < argc) {
+            // two-argument form '--foo' 'bar'
+            params[key] = std::string(argv[++i]);
+        }
+        else {
+            // missing second argument
+            std::clog << argv[0] << ": missing option '" << opt << "' value\n";
+            return -i; // negative means ill-formed option #i
         }
     }
+    return argc; // there were no non-option arguments
+}
+
+inline void handle_common_args(mapnik::parameters const& params)
+{
+    if (auto severity = params.get<std::string>("log-severity")) {
+        if (*severity == "debug")
+            mapnik::logger::set_severity(mapnik::logger::debug);
+        else if (*severity == "warn")
+            mapnik::logger::set_severity(mapnik::logger::warn);
+        else if (*severity == "error")
+            mapnik::logger::set_severity(mapnik::logger::error);
+        else if (*severity == "none")
+            mapnik::logger::set_severity(mapnik::logger::none);
+        else
+            std::clog << "ignoring option --log-severity='" << *severity
+                      << "' (allowed values are: debug, warn, error, none)\n";
+    }
+}
+
+inline int handle_args(int argc, char** argv, mapnik::parameters & params)
+{
+    int res = parse_args(argc, argv, params);
+    handle_common_args(params);
+    return res;
 }
 
 #define BENCHMARK(test_class,name)                      \
@@ -88,52 +142,112 @@ int run(T const& test_runner, std::string const& name)
         if (!test_runner.validate())
         {
             std::clog << "test did not validate: " << name << "\n";
-            return -1;
+            return 1;
         }
         // run test once before timing
         // if it returns false then we'll abort timing
-        if (test_runner())
+        if (!test_runner())
         {
-            std::chrono::high_resolution_clock::time_point start;
-            std::chrono::high_resolution_clock::duration elapsed;
-            std::stringstream s;
-            s << name << ":"
-                << std::setw(45 - (int)s.tellp()) << std::right
-                << " t:" << test_runner.threads()
-                << " i:" << test_runner.iterations();
-            if (test_runner.threads() > 0)
+            return 2;
+        }
+
+        std::chrono::high_resolution_clock::time_point start;
+        std::chrono::high_resolution_clock::duration elapsed;
+        auto opt_min_duration = test_runner.params().template get<double>("min-duration", 0.0);
+        std::chrono::duration<double> min_seconds(*opt_min_duration);
+        auto min_duration = std::chrono::duration_cast<decltype(elapsed)>(min_seconds);
+        std::size_t loops = 0;
+
+        if (test_runner.threads() > 0)
+        {
+            using thread_group = std::vector<std::unique_ptr<std::thread> >;
+            using value_type = thread_group::value_type;
+            thread_group tg;
+            for (std::size_t i=0;i<test_runner.threads();++i)
             {
-                using thread_group = std::vector<std::unique_ptr<std::thread> >;
-                using value_type = thread_group::value_type;
-                thread_group tg;
-                for (std::size_t i=0;i<test_runner.threads();++i)
-                {
-                    tg.emplace_back(new std::thread(test_runner));
-                }
-                start = std::chrono::high_resolution_clock::now();
-                std::for_each(tg.begin(), tg.end(), [](value_type & t) {if (t->joinable()) t->join();});
-                elapsed = std::chrono::high_resolution_clock::now() - start;
+                tg.emplace_back(new std::thread(test_runner));
             }
-            else
-            {
-                start = std::chrono::high_resolution_clock::now();
+            start = std::chrono::high_resolution_clock::now();
+            std::for_each(tg.begin(), tg.end(), [](value_type & t) {if (t->joinable()) t->join();});
+            elapsed = std::chrono::high_resolution_clock::now() - start;
+            loops = 1;
+        }
+        else
+        {
+            start = std::chrono::high_resolution_clock::now();
+            do {
                 test_runner();
                 elapsed = std::chrono::high_resolution_clock::now() - start;
-            }
-            s << std::setw(65 - (int)s.tellp()) << std::right
-                << std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count() << " milliseconds\n";
-            std::clog << s.str();
+                ++loops;
+            } while (elapsed < min_duration);
+        }
+
+        double iters = loops * test_runner.iterations();
+        double dur_total = std::chrono::duration<double, std::milli>(elapsed).count();
+        double dur_avg = dur_total / iters;
+        char iters_unit = ' ';
+        char msg[200];
+
+        if (iters >= 1e7) iters *= 1e-6, iters_unit = 'M';
+        else if (iters >= 1e4) iters *= 1e-3, iters_unit = 'k';
+
+        std::snprintf(msg, sizeof(msg),
+                "%-43s %3zu threads %4.0f%c iters %6.0f milliseconds",
+                name.c_str(),
+                test_runner.threads(),
+                iters, iters_unit,
+                dur_total);
+        std::clog << msg;
+
+        // log average time per iteration, currently only for non-threaded runs
+        if (test_runner.threads() == 0)
+        {
+            char unit = 'm';
+            if (dur_avg < 1e-5) dur_avg *= 1e+9, unit = 'p';
+            else if (dur_avg < 1e-2) dur_avg *= 1e+6, unit = 'n';
+            else if (dur_avg < 1e+1) dur_avg *= 1e+3, unit = 'u';
+            std::snprintf(msg, sizeof(msg), " %4.0f%cs/iter", dur_avg, unit);
+            std::clog << msg;
         }
+
+        std::clog << "\n";
         return 0;
     }
     catch (std::exception const& ex)
     {
         std::clog << "test runner did not complete: " << ex.what() << "\n";
-        return -1;
+        return 4;
     }
-    return 0;
 }
 
+struct sequencer
+{
+    sequencer(int argc, char** argv)
+      : exit_code_(0)
+    {
+        benchmark::handle_args(argc, argv, params_);
+    }
+
+    int done() const
+    {
+        return exit_code_;
+    }
+
+    template <typename Test, typename... Args>
+    sequencer & run(std::string const& name, Args && ...args)
+    {
+        // Test instance lifetime is confined to this function
+        Test test_runner(params_, std::forward<Args>(args)...);
+        // any failing test run will make exit code non-zero
+        exit_code_ |= benchmark::run(test_runner, name);
+        return *this; // allow chaining calls
+    }
+
+protected:
+    mapnik::parameters params_;
+    int exit_code_;
+};
+
 }
 
 #endif // __MAPNIK_BENCH_FRAMEWORK_HPP__
diff --git a/benchmark/test_array_allocation.cpp b/benchmark/test_array_allocation.cpp
index fda9bb6..186c162 100644
--- a/benchmark/test_array_allocation.cpp
+++ b/benchmark/test_array_allocation.cpp
@@ -231,33 +231,6 @@ public:
     }
 };
 
-class test3d : public benchmark::test_case
-{
-public:
-    uint32_t size_;
-    std::vector<uint8_t> array_;
-    test3d(mapnik::parameters const& params)
-     : test_case(params),
-       size_(*params.get<mapnik::value_integer>("size",256*256)),
-       array_(size_,0) { }
-    bool validate() const
-    {
-        return true;
-    }
-    bool operator()() const
-    {
-         for (std::size_t i=0;i<iterations_;++i) {
-             std::deque<uint8_t> data(size_);
-             for (std::size_t i=0;i<size_;++i) {
-                 if (data[i] != 0) {
-                     throw std::runtime_error("found non zero value");
-                 }
-             }
-         }
-         return true;
-    }
-};
-
 class test4 : public benchmark::test_case
 {
 public:
@@ -386,62 +359,21 @@ public:
 
 int main(int argc, char** argv)
 {
-    int return_value = 0;
-    mapnik::parameters params;
-    benchmark::handle_args(argc,argv,params);
-    {
-        test4 test_runner4(params);
-        return_value = return_value | run(test_runner4,"calloc");
-    }
-    {
-        test1 test_runner(params);
-        return_value = return_value | run(test_runner,"malloc/memcpy");
-    }
-    {
-        test1b test_runner(params);
-        return_value = return_value | run(test_runner,"malloc/memset");
-    }
-    {
-        test1c test_runner(params);
-        return_value = return_value | run(test_runner,"operator new/std::fill");
-    }
-    {
-        test2 test_runner(params);
-        return_value = return_value | run(test_runner,"operator new/memcpy");
-    }
-    {
-        test3 test_runner(params);
-        return_value = return_value | run(test_runner,"vector(N)");
-    }
-    {
-        test3b test_runner(params);
-        return_value = return_value | run(test_runner,"vector/resize");
-    }
-    {
-        test3c test_runner(params);
-        return_value = return_value | run(test_runner,"vector/assign");
-    }
-    {
-        test3d test_runner(params);
-        return_value = return_value | run(test_runner,"deque(N)");
-    }
-    {
-        test5 test_runner(params);
-        return_value = return_value | run(test_runner,"std::string range");
-    }
-    {
-        test5b test_runner(params);
-        return_value = return_value | run(test_runner,"std::string &[0]");
-    }
-    {
-        test6 test_runner(params);
-        return_value = return_value | run(test_runner,"valarray");
-    }
+    return benchmark::sequencer(argc, argv)
+        .run<test4>("calloc")
+        .run<test1>("malloc/memcpy")
+        .run<test1b>("malloc/memset")
+        .run<test1c>("operator new/std::fill")
+        .run<test2>("operator new/memcpy")
+        .run<test3>("vector(N)")
+        .run<test3b>("vector/resize")
+        .run<test3c>("vector/assign")
+        .run<test3d>("deque(N)")
+        .run<test5>("std::string range")
+        .run<test5b>("std::string &[0]")
+        .run<test6>("valarray")
 #if BOOST_VERSION >= 105400
-    {
-        test7 test_runner(params);
-        return_value = return_value | run(test_runner,"static_vector");
-    }
+        .run<test7>("static_vector")
 #endif
-    return return_value;
+        .done();
 }
diff --git a/benchmark/test_numeric_cast_vs_static_cast.cpp b/benchmark/test_numeric_cast_vs_static_cast.cpp
index 8ea6e6b..9e14bf3 100644
--- a/benchmark/test_numeric_cast_vs_static_cast.cpp
+++ b/benchmark/test_numeric_cast_vs_static_cast.cpp
@@ -73,16 +73,8 @@ public:
 
 int main(int argc, char** argv)
 {
-    mapnik::parameters params;
-    benchmark::handle_args(argc,argv,params);
-    int return_value = 0;
-    {
-        test_static test_runner(params);
-        return_value = return_value | run(test_runner,"static_cast");
-    }
-    {
-        test_numeric test_runner(params);
-        return_value = return_value | run(test_runner,"numeric_cast");
-    }
-    return return_value;
+    return benchmark::sequencer(argc, argv)
+        .run<test_static>("static_cast")
+        .run<test_numeric>("numeric_cast")
+        .done();
 }
diff --git a/benchmark/test_png_encoding1.cpp b/benchmark/test_png_encoding1.cpp
index 55d3f18..ec98f28 100644
--- a/benchmark/test_png_encoding1.cpp
+++ b/benchmark/test_png_encoding1.cpp
@@ -19,8 +19,8 @@ public:
             out.clear();
             out = mapnik::save_to_string(im_,"png8:m=h:z=1");
         }
+        return true;
     }
-    return true;
 };
 
 BENCHMARK(test,"encoding blank png")
diff --git a/benchmark/test_png_encoding2.cpp b/benchmark/test_png_encoding2.cpp
index 640050c..910a9ac 100644
--- a/benchmark/test_png_encoding2.cpp
+++ b/benchmark/test_png_encoding2.cpp
@@ -30,8 +30,8 @@ public:
             out.clear();
             out = mapnik::save_to_string(*im_,"png8:m=h:z=1");
         }
+        return true;
     }
-    return true;
 };
 
 BENCHMARK(test,"encoding multicolor png")
diff --git a/benchmark/test_polygon_clipping_rendering.cpp b/benchmark/test_polygon_clipping_rendering.cpp
index ab23459..758505d 100644
--- a/benchmark/test_polygon_clipping_rendering.cpp
+++ b/benchmark/test_polygon_clipping_rendering.cpp
@@ -51,30 +51,10 @@ int main(int argc, char** argv)
     mapnik::box2d<double> z1(-20037508.3428,-8317435.0606,20037508.3428,18399242.7298);
     // bbox for 16/10491/22911.png
     mapnik::box2d<double> z16(-13622912.929097254,6026906.8062295765,-13621689.93664469,6028129.79868214);
-    int return_value = 0;
-    {
-        test test_runner(params,
-                          "benchmark/data/polygon_rendering_clip.xml",
-                          z1);
-        return_value = return_value | run(test_runner,"polygon clip render z1");
-    }
-    {
-        test test_runner(params,
-                          "benchmark/data/polygon_rendering_no_clip.xml",
-                          z1);
-        return_value = return_value | run(test_runner,"polygon noclip render z1");
-    }
-    {
-        test test_runner(params,
-                          "benchmark/data/polygon_rendering_clip.xml",
-                          z16);
-        return_value = return_value | run(test_runner,"polygon clip render z16");
-    }
-    {
-        test test_runner(params,
-                          "benchmark/data/polygon_rendering_no_clip.xml",
-                          z16);
-        return_value = return_value | run(test_runner,"polygon noclip render z16");
-    }
-    return return_value;
+    return benchmark::sequencer(argc, argv)
+        .run<test>("polygon clip render z1", "benchmark/data/polygon_rendering_clip.xml", z1)
+        .run<test>("polygon noclip render z1", "benchmark/data/polygon_rendering_no_clip.xml", z1)
+        .run<test>("polygon clip render z16", "benchmark/data/polygon_rendering_clip.xml", z16)
+        .run<test>("polygon noclip render z16", "benchmark/data/polygon_rendering_no_clip.xml", z16)
+        .done();
 }
diff --git a/benchmark/test_proj_transform1.cpp b/benchmark/test_proj_transform1.cpp
index 451c51a..37f4b0c 100644
--- a/benchmark/test_proj_transform1.cpp
+++ b/benchmark/test_proj_transform1.cpp
@@ -59,42 +59,16 @@ public:
 // echo -180 -60 | cs2cs -f "%.10f" +init=epsg:4326 +to +init=epsg:3857
 int main(int argc, char** argv)
 {
-    mapnik::parameters params;
-    benchmark::handle_args(argc,argv,params);
     mapnik::box2d<double> from(-180,-80,180,80);
     mapnik::box2d<double> to(-20037508.3427892476,-15538711.0963092316,20037508.3427892476,15538711.0963092316);
     std::string from_str("+init=epsg:4326");
     std::string to_str("+init=epsg:3857");
     std::string from_str2("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
     std::string to_str2("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over");
-    int return_value = 0;
-    test test_runner(params,
-                     from_str,
-                     to_str,
-                     from,
-                     to,
-                     true);
-    return_value = return_value | run(test_runner,"lonlat->merc epsg");
-    test test_runner2(params,
-                     from_str2,
-                     to_str2,
-                     from,
-                     to,
-                     true);
-    return_value = return_value | run(test_runner2,"lonlat->merc literal");
-    test test_runner3(params,
-                     to_str,
-                     from_str,
-                     to,
-                     from,
-                     true);
-    return_value = return_value | run(test_runner3,"merc->lonlat epsg");
-    test test_runner4(params,
-                     to_str2,
-                     from_str2,
-                     to,
-                     from,
-                     true);
-    return_value = return_value | run(test_runner4,"merc->lonlat literal");
-    return return_value;
+    return benchmark::sequencer(argc, argv)
+        .run<test>("lonlat->merc epsg", from_str, to_str, from, to, true)
+        .run<test>("lonlat->merc literal", from_str2, to_str2, from, to, true)
+        .run<test>("merc->lonlat epsg", to_str, from_str, to, from, true)
+        .run<test>("merc->lonlat literal", to_str2, from_str2, to, from, true)
+        .done();
 }
diff --git a/bootstrap.sh b/bootstrap.sh
old mode 100755
new mode 100644
index b148139..f565d01
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -23,15 +23,6 @@ function setup_mason() {
     export CC=${CC:-clang}
 }
 
-if [[ $(uname -s) == 'Darwin' ]]; then
-    FIND_PATTERN="\/Users\/travis\/build\/mapbox\/mason"
-else
-    FIND_PATTERN="\/home\/travis\/build\/mapbox\/mason"
-fi
-
-REPLACE="$(pwd)"
-REPLACE=${REPLACE////\\/}
-
 function install() {
     MASON_PLATFORM_ID=$(mason env MASON_PLATFORM_ID)
     if [[ ! -d ./mason_packages/${MASON_PLATFORM_ID}/${1}/${2} ]]; then
@@ -40,7 +31,7 @@ function install() {
         if [[ $3 ]]; then
             LA_FILE=$(${MASON_DIR:-~/.mason}/mason prefix $1 $2)/lib/$3.la
             if [[ -f ${LA_FILE} ]]; then
-               perl -i -p -e "s/${FIND_PATTERN}/${REPLACE}/g;" ${LA_FILE}
+               perl -i -p -e 's:\Q$ENV{HOME}/build/mapbox/mason\E:$ENV{PWD}:g' ${LA_FILE}
             else
                 echo "$LA_FILE not found"
             fi
@@ -51,26 +42,28 @@ function install() {
 ICU_VERSION="55.1"
 
 function install_mason_deps() {
-    install gdal 1.11.2 libgdal &
-    install boost 1.59.0 &
-    install boost_liball 1.59.0 &
-    install freetype 2.6 libfreetype &
-    install harfbuzz 0.9.40 libharfbuzz &
     install jpeg_turbo 1.4.0 libjpeg &
-    install libpng 1.6.17 libpng &
-    install webp 0.4.2 libwebp &
-    install icu ${ICU_VERSION} &
-    install proj 4.8.0 libproj &
+    install libpng 1.6.20 libpng &
     install libtiff 4.0.4beta libtiff &
-    install libpq 9.4.0 &
-    install sqlite 3.8.8.1 libsqlite3 &
+    install libpq 9.4.1 &
+    install sqlite 3.8.8.3 libsqlite3 &
     install expat 2.1.0 libexpat &
+    wait
+    install icu ${ICU_VERSION} &
+    install proj 4.8.0 libproj &
     install pixman 0.32.6 libpixman-1 &
     install cairo 1.14.2 libcairo &
     install protobuf 2.6.1 &
-    wait
     # technically protobuf is not a mapnik core dep, but installing
     # here by default helps make mapnik-vector-tile builds easier
+    wait
+    install webp 0.4.2 libwebp &
+    install gdal 1.11.2 libgdal &
+    install boost 1.59.0 &
+    install boost_liball 1.59.0 &
+    install freetype 2.6 libfreetype &
+    install harfbuzz 0.9.41 libharfbuzz &
+    wait
 }
 
 MASON_LINKED_ABS=$(pwd)/mason_packages/.link
@@ -80,22 +73,15 @@ export CPLUS_INCLUDE_PATH="${MASON_LINKED_ABS}/include"
 export LIBRARY_PATH="${MASON_LINKED_ABS}/lib"
 
 function make_config() {
-    if [[ $(uname -s) == 'Darwin' ]]; then
-        local PATH_REPLACE="/Users/travis/build/mapbox/mason/mason_packages:./mason_packages"
-    else
-        local PATH_REPLACE="/home/travis/build/mapbox/mason/mason_packages:./mason_packages"
-    fi
-
     echo "
 CXX = '$CXX'
 CC = '$CC'
-CUSTOM_CXXFLAGS = '-fvisibility=hidden -fvisibility-inlines-hidden -DU_CHARSET_IS_UTF8=1'
 RUNTIME_LINK = 'static'
 INPUT_PLUGINS = 'all'
 PATH = '${MASON_LINKED_REL}/bin'
 PKG_CONFIG_PATH = '${MASON_LINKED_REL}/lib/pkgconfig'
 PATH_REMOVE = '/usr:/usr/local'
-PATH_REPLACE = '${PATH_REPLACE}'
+PATH_REPLACE = '$HOME/build/mapbox/mason/mason_packages:./mason_packages'
 BOOST_INCLUDES = '${MASON_LINKED_REL}/include'
 BOOST_LIBS = '${MASON_LINKED_REL}/lib'
 ICU_INCLUDES = '${MASON_LINKED_REL}/include'
@@ -126,8 +112,7 @@ CPP_TESTS = True
 PGSQL2SQLITE = True
 XMLPARSER = 'ptree'
 SVG2PNG = True
-SAMPLE_INPUT_PLUGINS = True
-" > ./config.py
+"
 }
 
 # NOTE: the `mapnik-settings.env` is used by test/run (which is run by `make test`)
@@ -141,7 +126,7 @@ function setup_runtime_settings() {
 function main() {
     setup_mason
     install_mason_deps
-    make_config
+    make_config > ./config.py
     setup_runtime_settings
     echo "Ready, now run:"
     echo ""
diff --git a/demo/python/rundemo.py b/demo/python/rundemo.py
index 7e6c42c..910d846 100755
--- a/demo/python/rundemo.py
+++ b/demo/python/rundemo.py
@@ -18,7 +18,7 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 from __future__ import print_function
 import sys
 from os import path
diff --git a/demo/viewer/about_dialog.cpp b/demo/viewer/about_dialog.cpp
index 06f0a96..2a37349 100644
--- a/demo/viewer/about_dialog.cpp
+++ b/demo/viewer/about_dialog.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/about_dialog.hpp b/demo/viewer/about_dialog.hpp
index 6705671..160dcc8 100644
--- a/demo/viewer/about_dialog.hpp
+++ b/demo/viewer/about_dialog.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/info_dialog.cpp b/demo/viewer/info_dialog.cpp
index 304abf8..b250020 100644
--- a/demo/viewer/info_dialog.cpp
+++ b/demo/viewer/info_dialog.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/info_dialog.hpp b/demo/viewer/info_dialog.hpp
index dde4c66..c4b887f 100644
--- a/demo/viewer/info_dialog.hpp
+++ b/demo/viewer/info_dialog.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layer_info_dialog.cpp b/demo/viewer/layer_info_dialog.cpp
index 23de7e8..f7e983d 100644
--- a/demo/viewer/layer_info_dialog.cpp
+++ b/demo/viewer/layer_info_dialog.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layer_info_dialog.hpp b/demo/viewer/layer_info_dialog.hpp
index efac782..370551e 100644
--- a/demo/viewer/layer_info_dialog.hpp
+++ b/demo/viewer/layer_info_dialog.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layerdelegate.cpp b/demo/viewer/layerdelegate.cpp
index 13a1135..5e43317 100644
--- a/demo/viewer/layerdelegate.cpp
+++ b/demo/viewer/layerdelegate.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layerdelegate.hpp b/demo/viewer/layerdelegate.hpp
index 941621d..92a57a7 100644
--- a/demo/viewer/layerdelegate.hpp
+++ b/demo/viewer/layerdelegate.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 #ifndef LAYER_DELEGATE_HPP
diff --git a/demo/viewer/layerlistmodel.cpp b/demo/viewer/layerlistmodel.cpp
index 6a4b0bb..f2ad085 100644
--- a/demo/viewer/layerlistmodel.cpp
+++ b/demo/viewer/layerlistmodel.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layerlistmodel.hpp b/demo/viewer/layerlistmodel.hpp
index ab838c7..d691333 100644
--- a/demo/viewer/layerlistmodel.hpp
+++ b/demo/viewer/layerlistmodel.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layerwidget.cpp b/demo/viewer/layerwidget.cpp
index 58b88a2..d392150 100644
--- a/demo/viewer/layerwidget.cpp
+++ b/demo/viewer/layerwidget.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/layerwidget.hpp b/demo/viewer/layerwidget.hpp
index f458ee9..e75ee7d 100644
--- a/demo/viewer/layerwidget.hpp
+++ b/demo/viewer/layerwidget.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/main.cpp b/demo/viewer/main.cpp
index e806197..d8f06d2 100644
--- a/demo/viewer/main.cpp
+++ b/demo/viewer/main.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/mainwindow.cpp b/demo/viewer/mainwindow.cpp
index 9e9a34d..939028a 100644
--- a/demo/viewer/mainwindow.cpp
+++ b/demo/viewer/mainwindow.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/mainwindow.hpp b/demo/viewer/mainwindow.hpp
index 260968c..ebfc2c5 100644
--- a/demo/viewer/mainwindow.hpp
+++ b/demo/viewer/mainwindow.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp
index 04ce9c7..9b2c527 100644
--- a/demo/viewer/mapwidget.cpp
+++ b/demo/viewer/mapwidget.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/mapwidget.hpp b/demo/viewer/mapwidget.hpp
index cc7964b..e65908f 100644
--- a/demo/viewer/mapwidget.hpp
+++ b/demo/viewer/mapwidget.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/styles_model.cpp b/demo/viewer/styles_model.cpp
index d2b2f70..d7b68ba 100644
--- a/demo/viewer/styles_model.cpp
+++ b/demo/viewer/styles_model.cpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/demo/viewer/styles_model.hpp b/demo/viewer/styles_model.hpp
index b6ead4b..a218859 100644
--- a/demo/viewer/styles_model.hpp
+++ b/demo/viewer/styles_model.hpp
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
diff --git a/deps/agg/include/agg_array.h b/deps/agg/include/agg_array.h
index 80e02c2..d07e46b 100644
--- a/deps/agg/include/agg_array.h
+++ b/deps/agg/include/agg_array.h
@@ -27,8 +27,8 @@ namespace agg
     {
     public:
         typedef T value_type;
-        pod_array_adaptor(T* array, unsigned size) : 
-            m_array(array), m_size(size) {}
+        pod_array_adaptor(T* array, unsigned _size) : 
+            m_array(array), m_size(_size) {}
 
         unsigned size() const { return m_size; }
         const T& operator [] (unsigned i) const { return m_array[i]; }
@@ -87,7 +87,7 @@ namespace agg
         void clear()                 { m_size = 0; }
         void add(const T& v)         { m_array[m_size++] = v; }
         void push_back(const T& v)   { m_array[m_size++] = v; }
-        void inc_size(unsigned size) { m_size += size; }
+        void inc_size(unsigned _size) { m_size += _size; }
         
         unsigned size() const { return m_size; }
         const T& operator [] (unsigned i) const { return m_array[i]; }
@@ -112,9 +112,9 @@ namespace agg
         ~pod_array() { pod_allocator<T>::deallocate(m_array, m_size); }
         pod_array() : m_array(0), m_size(0) {}
 
-        pod_array(unsigned size) : 
-            m_array(pod_allocator<T>::allocate(size)), 
-            m_size(size) 
+        pod_array(unsigned _size) : 
+            m_array(pod_allocator<T>::allocate(_size)), 
+            m_size(_size) 
         {}
 
         pod_array(const self_type& v) : 
@@ -124,12 +124,12 @@ namespace agg
             memcpy(m_array, v.m_array, sizeof(T) * m_size);
         }
 
-        void resize(unsigned size)
+        void resize(unsigned _size)
         {
-            if(size != m_size)
+            if(_size != m_size)
             {
                 pod_allocator<T>::deallocate(m_array, m_size);
-                m_array = pod_allocator<T>::allocate(m_size = size);
+                m_array = pod_allocator<T>::allocate(m_size = _size);
             }
         }
         const self_type& operator = (const self_type& v)
@@ -191,7 +191,7 @@ namespace agg
         void add(const T& v)         { m_array[m_size++] = v; }
         void push_back(const T& v)   { m_array[m_size++] = v; }
         void insert_at(unsigned pos, const T& val);
-        void inc_size(unsigned size) { m_size += size; }
+        void inc_size(unsigned _size) { m_size += _size; }
         unsigned size()      const   { return m_size; }
         unsigned byte_size() const   { return m_size * sizeof(T); }
         void serialize(int8u* ptr) const;
@@ -230,10 +230,10 @@ namespace agg
 
     //------------------------------------------------------------------------
     template<class T> 
-    void pod_vector<T>::allocate(unsigned size, unsigned extra_tail)
+    void pod_vector<T>::allocate(unsigned _size, unsigned extra_tail)
     {
-        capacity(size, extra_tail);
-        m_size = size;
+        capacity(_size, extra_tail);
+        m_size = _size;
     }
 
 
@@ -245,10 +245,10 @@ namespace agg
         {
             if(new_size > m_capacity)
             {
-                T* data = pod_allocator<T>::allocate(new_size);
-                memcpy(data, m_array, m_size * sizeof(T));
+                T* _data = pod_allocator<T>::allocate(new_size);
+                memcpy(_data, m_array, m_size * sizeof(T));
                 pod_allocator<T>::deallocate(m_array, m_capacity);
-                m_array = data;
+                m_array = _data;
             }
         }
         else
@@ -289,11 +289,11 @@ namespace agg
 
     //------------------------------------------------------------------------
     template<class T> 
-    void pod_vector<T>::deserialize(const int8u* data, unsigned byte_size)
+    void pod_vector<T>::deserialize(const int8u* _data, unsigned _byte_size)
     {
-        byte_size /= sizeof(T);
-        allocate(byte_size);
-        if(byte_size) memcpy(m_array, data, byte_size * sizeof(T));
+        _byte_size /= sizeof(T);
+        allocate(_byte_size);
+        if(_byte_size) memcpy(m_array, _data, _byte_size * sizeof(T));
     }
 
     //------------------------------------------------------------------------
@@ -371,9 +371,9 @@ namespace agg
             }
         }
 
-        void cut_at(unsigned size)
+        void cut_at(unsigned _size)
         {
-            if(size < m_size) m_size = size;
+            if(_size < m_size) m_size = _size;
         }
 
         unsigned size() const { return m_size; }
@@ -529,11 +529,11 @@ namespace agg
 
     //------------------------------------------------------------------------
     template<class T, unsigned S> 
-    void pod_bvector<T, S>::free_tail(unsigned size)
+    void pod_bvector<T, S>::free_tail(unsigned _size)
     {
-        if(size < m_size)
+        if(_size < m_size)
         {
-            unsigned nb = (size + block_mask) >> block_shift;
+            unsigned nb = (_size + block_mask) >> block_shift;
             while(m_num_blocks > nb)
             {
                 pod_allocator<T>::deallocate(m_blocks[--m_num_blocks], block_size);
@@ -544,7 +544,7 @@ namespace agg
                 m_blocks = 0;
                 m_max_blocks = 0;
             }
-            m_size = size;
+            m_size = _size;
         }
     }
 
@@ -728,16 +728,16 @@ namespace agg
 
     //------------------------------------------------------------------------
     template<class T, unsigned S> 
-    void pod_bvector<T, S>::deserialize(const int8u* data, unsigned byte_size)
+    void pod_bvector<T, S>::deserialize(const int8u* _data, unsigned _byte_size)
     {
         remove_all();
-        byte_size /= sizeof(T);
-        for(unsigned i = 0; i < byte_size; ++i)
+        _byte_size /= sizeof(T);
+        for(unsigned i = 0; i < _byte_size; ++i)
         {
             T* ptr = data_ptr();
-            memcpy(ptr, data, sizeof(T));
+            memcpy(ptr, _data, sizeof(T));
             ++m_size;
-            data += sizeof(T);
+            _data += sizeof(T);
         }
     }
 
@@ -746,27 +746,27 @@ namespace agg
     //------------------------------------------------------------------------
     template<class T, unsigned S> 
     void pod_bvector<T, S>::deserialize(unsigned start, const T& empty_val, 
-                                        const int8u* data, unsigned byte_size)
+                                        const int8u* _data, unsigned _byte_size)
     {
         while(m_size < start)
         {
             add(empty_val);
         }
 
-        byte_size /= sizeof(T);
-        for(unsigned i = 0; i < byte_size; ++i)
+        _byte_size /= sizeof(T);
+        for(unsigned i = 0; i < _byte_size; ++i)
         {
             if(start + i < m_size)
             {
-                memcpy(&((*this)[start + i]), data, sizeof(T));
+                memcpy(&((*this)[start + i]), _data, sizeof(T));
             }
             else
             {
                 T* ptr = data_ptr();
-                memcpy(ptr, data, sizeof(T));
+                memcpy(ptr, _data, sizeof(T));
                 ++m_size;
             }
-            data += sizeof(T);
+            _data += sizeof(T);
         }
     }
 
@@ -1087,8 +1087,8 @@ namespace agg
     public:
         typedef typename Array::value_type value_type;
 
-        range_adaptor(Array& array, unsigned start, unsigned size) :
-            m_array(array), m_start(start), m_size(size)
+        range_adaptor(Array& array, unsigned start, unsigned _size) :
+            m_array(array), m_start(start), m_size(_size)
         {}
 
         unsigned size() const { return m_size; }
diff --git a/deps/agg/include/agg_conv_clip_polygon.h b/deps/agg/include/agg_conv_clip_polygon.h
index 79aceed..a7e28db 100644
--- a/deps/agg/include/agg_conv_clip_polygon.h
+++ b/deps/agg/include/agg_conv_clip_polygon.h
@@ -42,9 +42,9 @@ namespace agg
         conv_clip_polygon(VertexSource& vs) : 
             conv_adaptor_vpgen<VertexSource, vpgen_clip_polygon>(vs) {}
 
-        void clip_box(double x1, double y1, double x2, double y2)
+        void clip_box(double _x1, double _y1, double _x2, double _y2)
         {
-            base_type::vpgen().clip_box(x1, y1, x2, y2);
+            base_type::vpgen().clip_box(_x1, _y1, _x2, _y2);
         }
 
         double x1() const { return base_type::vpgen().x1(); }
diff --git a/deps/agg/include/agg_conv_clip_polyline.h b/deps/agg/include/agg_conv_clip_polyline.h
index 8bad596..8aa34c9 100644
--- a/deps/agg/include/agg_conv_clip_polyline.h
+++ b/deps/agg/include/agg_conv_clip_polyline.h
@@ -42,9 +42,9 @@ namespace agg
         conv_clip_polyline(VertexSource& vs) : 
             conv_adaptor_vpgen<VertexSource, vpgen_clip_polyline>(vs) {}
 
-        void clip_box(double x1, double y1, double x2, double y2)
+        void clip_box(double _x1, double _y1, double _x2, double _y2)
         {
-            base_type::vpgen().clip_box(x1, y1, x2, y2);
+            base_type::vpgen().clip_box(_x1, _y1, _x2, _y2);
         }
 
         double x1() const { return base_type::vpgen().x1(); }
diff --git a/deps/agg/include/agg_rasterizer_cells_aa.h b/deps/agg/include/agg_rasterizer_cells_aa.h
old mode 100755
new mode 100644
diff --git a/deps/agg/include/agg_rasterizer_compound_aa.h b/deps/agg/include/agg_rasterizer_compound_aa.h
old mode 100755
new mode 100644
diff --git a/deps/agg/include/agg_vcgen_dash.h b/deps/agg/include/agg_vcgen_dash.h
index c87dce4..f531013 100644
--- a/deps/agg/include/agg_vcgen_dash.h
+++ b/deps/agg/include/agg_vcgen_dash.h
@@ -2,8 +2,8 @@
 // Anti-Grain Geometry - Version 2.4
 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
 //
-// Permission to copy, use, modify, sell and distribute this software 
-// is granted provided this copyright notice appears in all copies. 
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
 // This software is provided "as is" without express or implied
 // warranty, and with no claim as to its suitability for any purpose.
 //
@@ -21,6 +21,7 @@
 
 #include "agg_basics.h"
 #include "agg_vertex_sequence.h"
+#include <mapnik/config.hpp>
 
 namespace agg
 {
@@ -29,7 +30,7 @@ namespace agg
     //
     // See Implementation agg_vcgen_dash.cpp
     //
-    class vcgen_dash
+    class MAPNIK_DECL vcgen_dash
     {
         enum max_dashes_e
         {
diff --git a/deps/mapnik/build.py b/deps/mapnik/build.py
index 32afe11..0d6dc84 100644
--- a/deps/mapnik/build.py
+++ b/deps/mapnik/build.py
@@ -4,14 +4,15 @@ from glob import glob
 Import('env')
 
 subdirs =  {
-  'sparsehash':'sparsehash',
-  'sparsehash/internal':'sparsehash/internal',
-  '../agg/include':'agg',
+  './sparsehash':{'dir':'sparsehash','glob':'*'},
+  './sparsehash/internal':{'dir':'sparsehash/internal','glob':'*'},
+  '../agg/include':{'dir':'agg','glob':'agg*'},
+  '../mapbox':{'dir':'mapbox/variant','glob':'*/*.hpp'}
 }
 
 if 'install' in COMMAND_LINE_TARGETS:
     for k,v in subdirs.items():
-        pathdir = os.path.join(k,'*')
+        pathdir = os.path.join(k,v['glob'])
         includes = glob(pathdir)
-        inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/'+v)
+        inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/'+v['dir'])
         env.Alias(target='install', source=env.Install(inc_target, includes))
diff --git a/include/mapnik/attribute.hpp b/include/mapnik/attribute.hpp
index b0cf683..edfaebe 100644
--- a/include/mapnik/attribute.hpp
+++ b/include/mapnik/attribute.hpp
@@ -36,8 +36,8 @@ namespace mapnik {
 struct attribute
 {
     std::string name_;
-    explicit attribute(std::string const& name)
-        : name_(name) {}
+    explicit attribute(std::string const& _name)
+        : name_(_name) {}
 
     template <typename V ,typename F>
     V const& value(F const& f) const
diff --git a/include/mapnik/color.hpp b/include/mapnik/color.hpp
index b22a998..690803a 100644
--- a/include/mapnik/color.hpp
+++ b/include/mapnik/color.hpp
@@ -55,19 +55,19 @@ public:
         premultiplied_(false)
         {}
 
-    color(std::uint8_t red, std::uint8_t green, std::uint8_t blue, std::uint8_t alpha = 0xff, bool premultiplied = false)
-      : red_(red),
-        green_(green),
-        blue_(blue),
-        alpha_(alpha),
+    color(std::uint8_t _red, std::uint8_t _green, std::uint8_t _blue, std::uint8_t _alpha = 0xff, bool premultiplied = false)
+      : red_(_red),
+        green_(_green),
+        blue_(_blue),
+        alpha_(_alpha),
         premultiplied_(premultiplied)
         {}
 
-    color(std::uint32_t rgba, bool premultiplied = false)
-      : red_(rgba & 0xff),
-        green_((rgba >> 8) & 0xff),
-        blue_((rgba >> 16) & 0xff),
-        alpha_((rgba >> 24) & 0xff),
+    color(std::uint32_t _rgba, bool premultiplied = false)
+      : red_(_rgba & 0xff),
+        green_((_rgba >> 8) & 0xff),
+        blue_((_rgba >> 16) & 0xff),
+        alpha_((_rgba >> 24) & 0xff),
         premultiplied_(premultiplied) {}
 
     // copy ctor
@@ -128,23 +128,23 @@ public:
         return alpha_;
     }
 
-    inline void set_red(std::uint8_t red)
+    inline void set_red(std::uint8_t _red)
     {
-        red_ = red;
+        red_ = _red;
     }
 
-    inline void set_green(std::uint8_t green)
+    inline void set_green(std::uint8_t _green)
     {
-        green_ = green;
+        green_ = _green;
     }
 
-    inline void set_blue(std::uint8_t blue)
+    inline void set_blue(std::uint8_t _blue)
     {
-        blue_ = blue;
+        blue_ = _blue;
     }
-    inline void set_alpha(std::uint8_t alpha)
+    inline void set_alpha(std::uint8_t _alpha)
     {
-        alpha_ = alpha;
+        alpha_ = _alpha;
     }
     inline bool get_premultiplied() const
     {
@@ -173,8 +173,7 @@ template <typename charT, typename traits>
 std::basic_ostream<charT, traits> &
 operator << ( std::basic_ostream<charT, traits> & s, mapnik::color const& c )
 {
-    std::string hex_string( c.to_string() );
-    s << hex_string;
+    s << c.to_string();
     return s;
 }
 
diff --git a/include/mapnik/csv/csv_grammar.hpp b/include/mapnik/csv/csv_grammar.hpp
index f665dae..6edecad 100644
--- a/include/mapnik/csv/csv_grammar.hpp
+++ b/include/mapnik/csv/csv_grammar.hpp
@@ -35,34 +35,48 @@ using csv_value  = std::string;
 using csv_line = std::vector<csv_value>;
 using csv_data = std::vector<csv_line>;
 
-template <typename Iterator>
-struct csv_white_space_skipper : qi::grammar<Iterator>
+struct csv_white_space_skipper : qi::primitive_parser<csv_white_space_skipper>
 {
-    csv_white_space_skipper()
-        : csv_white_space_skipper::base_type(skip)
+    template <typename Context, typename Iterator>
+    struct attribute
     {
-        using namespace qi;
-        qi::lit_type lit;
-        skip = +lit(' ')
-            ;
+        typedef qi::unused_type type;
+    };
+
+    template <typename Iterator, typename Context
+      , typename Skipper, typename Attribute>
+    bool parse(Iterator& first, Iterator const& last
+      , Context& /*context*/, Skipper const& skipper
+      , Attribute& /*attr*/) const
+    {
+        qi::skip_over(first, last, skipper);
+        if (first != last && *first == ' ')
+        {
+            while (++first != last && *first == ' ')
+                ;
+            return true;
+        }
+        return false;
+    }
+
+    template <typename Context>
+    qi::info what(Context& /*context*/) const
+    {
+        return qi::info("csv_white_space_skipper");
     }
-    qi::rule<Iterator> skip;
 };
 
-template <typename Iterator, typename Skipper = csv_white_space_skipper<Iterator> >
+
+template <typename Iterator, typename Skipper = csv_white_space_skipper>
 struct csv_line_grammar : qi::grammar<Iterator, csv_line(char, char), Skipper>
 {
     csv_line_grammar()
         : csv_line_grammar::base_type(line)
     {
-        using namespace qi;
-        qi::_a_type _a;
         qi::_r1_type _r1;
         qi::_r2_type _r2;
         qi::lit_type lit;
-        qi::_1_type _1;
         qi::char_type char_;
-        qi::omit_type omit;
         unesc_char.add
             ("\\a", '\a')
             ("\\b", '\b')
@@ -76,11 +90,11 @@ struct csv_line_grammar : qi::grammar<Iterator, csv_line(char, char), Skipper>
             ("\\\"", '\"')
             ("\"\"", '\"') // double quote
             ;
-        line = -omit[char_("\n\r")] >> column(_r1, _r2) % lit(_r1)
+        line = -lit("\n\r") >> column(_r1, _r2) % lit(_r1)
             ;
-        column = quoted(_r2) | *(char_ - (lit(_r1)))
+        column = quoted(_r2) | *(char_ - lit(_r1))
             ;
-        quoted = omit[char_(_r1)[_a = _1]] > text(_a) > -lit(_a) // support unmatched quotes or not (??)
+        quoted = lit(_r1) > text(_r1) > lit(_r1) // support unmatched quotes or not (??)
             ;
         text = *(unesc_char | (char_ - lit(_r1)))
             ;
@@ -90,7 +104,7 @@ private:
     qi::rule<Iterator, csv_line(char, char),  Skipper> line;
     qi::rule<Iterator, csv_value(char, char)> column; // no-skip
     qi::rule<Iterator, csv_value(char)> text; // no-skip
-    qi::rule<Iterator, qi::locals<char>, csv_value(char)> quoted; //no-skip
+    qi::rule<Iterator, csv_value(char)> quoted; // no-skip
     qi::symbols<char const, char const> unesc_char;
 };
 
diff --git a/include/mapnik/enumeration.hpp b/include/mapnik/enumeration.hpp
index c0cfe1e..9064c2d 100644
--- a/include/mapnik/enumeration.hpp
+++ b/include/mapnik/enumeration.hpp
@@ -41,8 +41,8 @@ public:
     illegal_enum_value():
         what_() {}
 
-    illegal_enum_value( std::string const& what ) :
-        what_( what )
+    illegal_enum_value( std::string const& _what ) :
+        what_( _what )
     {
     }
     virtual ~illegal_enum_value() throw() {}
@@ -59,9 +59,9 @@ protected:
 
 /** Slim wrapper for enumerations. It creates a new type from a native enum and
  * a char pointer array. It almost exactly behaves like a native enumeration
- * type. It supports string conversion through stream operators. This is usefull
+ * type. It supports string conversion through stream operators. This is useful
  * for debugging, serialization/deserialization and also helps with implementing
- * language bindings. The two convinient macros DEFINE_ENUM() and IMPLEMENT_ENUM()
+ * language bindings. The two convenient macros DEFINE_ENUM() and IMPLEMENT_ENUM()
  * are provided to help with instanciation.
  *
  * @par Limitations:
@@ -203,57 +203,12 @@ public:
                                  str + "' for enum " + our_name_);
     }
 
-    /** Parses the input stream @p is for a word consisting of characters and
-     * digits (<i>a-z, A-Z, 0-9</i>) and underscores (<i>_</i>).
-     * The failbit of the stream is set if the word is not a valid identifier.
-     */
-    std::istream & parse(std::istream & is)
-    {
-        std::string word;
-        char c;
-
-        while ( is.peek() != std::char_traits< char >::eof())
-        {
-            is >> c;
-            if ( isspace(c) && word.empty() )
-            {
-                continue;
-            }
-            if ( isalnum(c) || (c == '_') || c == '-' )
-            {
-                word += c;
-            }
-            else
-            {
-                is.unget();
-                break;
-            }
-        }
-
-        try
-        {
-            from_string( word );
-        }
-        catch (illegal_enum_value const&)
-        {
-            is.setstate(std::ios::failbit);
-        }
-
-        return is;
-    }
-
     /** Returns the current value as a string identifier. */
     std::string as_string() const
     {
         return our_strings_[value_];
     }
 
-    /** Prints the string identifier to the output stream @p os. */
-    std::ostream & print(std::ostream & os = std::cerr) const
-    {
-        return os << our_strings_[value_];
-    }
-
     /** Static helper function to iterate over valid identifiers. */
     static const char * get_string(unsigned i)
     {
@@ -284,22 +239,6 @@ public:
         return true;
     }
 
-    static std::string const& get_full_qualified_name()
-    {
-        return our_name_;
-    }
-
-    static std::string get_name()
-    {
-        std::string::size_type idx = our_name_.find_last_of(":");
-        if ( idx == std::string::npos )
-        {
-            return our_name_;
-        } else {
-            return our_name_.substr( idx + 1 );
-        }
-    }
-
 private:
     ENUM value_;
     static const char ** our_strings_ ;
@@ -314,19 +253,7 @@ template <class ENUM, int THE_MAX>
 std::ostream &
 operator<<(std::ostream & os, const mapnik::enumeration<ENUM, THE_MAX> & e)
 {
-    e.print( os );
-    return os;
-}
-
-/** istream operator for enumeration
- * @relates mapnik::enumeration
- */
-template <class ENUM, int THE_MAX>
-std::istream &
-operator>>(std::istream & is, mapnik::enumeration<ENUM, THE_MAX> & e)
-{
-    e.parse( is );
-    return is;
+    return os << e.as_string();
 }
 
 } // end of namespace
diff --git a/include/mapnik/expression_evaluator.hpp b/include/mapnik/expression_evaluator.hpp
index 6ac0451..58b3448 100644
--- a/include/mapnik/expression_evaluator.hpp
+++ b/include/mapnik/expression_evaluator.hpp
@@ -45,27 +45,27 @@ struct evaluate
         : feature_(f),
           vars_(v) {}
 
-    value_integer operator() (value_integer val) const
+    value_type operator() (value_integer val) const
     {
         return val;
     }
 
-    value_double operator() (value_double val) const
+    value_type operator() (value_double val) const
     {
         return val;
     }
 
-    value_bool operator() (value_bool val) const
+    value_type operator() (value_bool val) const
     {
         return val;
     }
 
-    value_null operator() (value_null val) const
+    value_type operator() (value_null val) const
     {
         return val;
     }
 
-    value_unicode_string const& operator() (value_unicode_string const& str) const
+    value_type operator() (value_unicode_string const& str) const
     {
         return str;
     }
@@ -90,16 +90,16 @@ struct evaluate
         return geom.value<value_type,feature_type>(feature_);
     }
 
-    value_type operator() (binary_node<tags::logical_and> const & x) const
+    value_type operator() (binary_node<tags::logical_and> const& x) const
     {
         return (util::apply_visitor(*this, x.left).to_bool())
             && (util::apply_visitor(*this, x.right).to_bool());
     }
 
-    value_type operator() (binary_node<tags::logical_or> const & x) const
+    value_type operator() (binary_node<tags::logical_or> const& x) const
     {
-        return (util::apply_visitor(*this,x.left).to_bool())
-            || (util::apply_visitor(*this,x.right).to_bool());
+        return (util::apply_visitor(*this, x.left).to_bool())
+            || (util::apply_visitor(*this, x.right).to_bool());
     }
 
     template <typename Tag>
diff --git a/include/mapnik/expression_grammar.hpp b/include/mapnik/expression_grammar.hpp
index ff9423e..ea65425 100644
--- a/include/mapnik/expression_grammar.hpp
+++ b/include/mapnik/expression_grammar.hpp
@@ -107,43 +107,6 @@ struct regex_replace_impl
     mapnik::transcoder const& tr_;
 };
 
-struct geometry_types : qi::symbols<char, mapnik::value_integer>
-{
-    geometry_types()
-    {
-        add
-            ("point", 1)
-            ("linestring", 2)
-            ("polygon",3)
-            ("collection",4)
-            ;
-    }
-};
-
-struct boolean_constants :  qi::symbols<char,mapnik::value_bool>
-{
-    boolean_constants()
-    {
-        add
-            ("true", true)
-            ("false", false)
-            ;
-    }
-};
-
-struct floating_point_constants :  qi::symbols<char,mapnik::value_double>
-{
-    floating_point_constants()
-    {
-        add
-            ("pi", 3.1415926535897932384626433832795)
-            ("deg_to_rad",0.017453292519943295769236907684886)
-            ("rad_to_deg",57.295779513082320876798154814105)
-            ;
-    }
-};
-
-
 template <typename T>
 struct integer_parser
 {
@@ -200,9 +163,7 @@ struct expression_grammar : qi::grammar<Iterator, expr_node(), space_type>
 
     qi::symbols<char const, char const> unesc_char;
     qi::rule<Iterator, char() > quote_char;
-    geometry_types geom_type;
-    boolean_constants bool_const;
-    floating_point_constants float_const;
+    qi::symbols<char, expr_node> constant;
     unary_function_types unary_func_type;
     binary_function_types binary_func_type;
 
diff --git a/include/mapnik/expression_grammar_impl.hpp b/include/mapnik/expression_grammar_impl.hpp
index 9c82cb8..8202fca 100644
--- a/include/mapnik/expression_grammar_impl.hpp
+++ b/include/mapnik/expression_grammar_impl.hpp
@@ -103,8 +103,24 @@ expression_grammar<Iterator>::expression_grammar(std::string const& encoding)
     standard_wide::char_type char_;
     standard_wide::no_case_type no_case;
     using boost::phoenix::construct;
+    using boost::phoenix::if_else;
 
-    expr = logical_expr.alias();
+    constant.add
+        ("null",        mapnik::value_null())
+        ("false",       mapnik::value_bool(false))
+        ("true",        mapnik::value_bool(true))
+        ("point",       mapnik::value_integer(1))
+        ("linestring",  mapnik::value_integer(2))
+        ("polygon",     mapnik::value_integer(3))
+        ("collection",  mapnik::value_integer(4))
+        ("pi",          mapnik::value_double(3.1415926535897932384626433832795))
+        ("deg_to_rad",  mapnik::value_double(0.017453292519943295769236907684886))
+        ("rad_to_deg",  mapnik::value_double(57.295779513082320876798154814105))
+        ;
+
+    expr = logical_expr [_val = _1]
+        | ustring [_val = unicode_(_1)]
+        ;
 
     logical_expr = not_expr [_val = _1]
         >>
@@ -167,10 +183,11 @@ expression_grammar<Iterator>::expression_grammar(std::string const& encoding)
             )
         ;
 
-    unary_function_expr = unary_func_type > lit('(') > expr > lit(')')
+    unary_function_expr = unary_func_type >> '(' > logical_expr > ')'
         ;
 
-    binary_function_expr = binary_func_type > lit('(') > expr > lit(',') > expr > lit(')')
+    binary_function_expr = binary_func_type >> '(' > logical_expr > ','
+                                                   > logical_expr > ')'
         ;
 
     unary_expr = primary_expr [_val = _1]
@@ -180,19 +197,15 @@ expression_grammar<Iterator>::expression_grammar(std::string const& encoding)
 
     primary_expr = strict_double [_val = _1]
         | int__[_val = _1]
-        | no_case[bool_const][_val = _1]
-        | no_case[lit("null")] [_val = value_null() ]
-        | no_case[geom_type][_val = _1 ]
-        | no_case[float_const] [_val = _1 ]
+        | no_case[constant] [_val = _1]
         | quoted_ustring [_val = unicode_(_1)]
-        | lit("[mapnik::geometry_type]")[_val = construct<mapnik::geometry_type_attribute>()]
-        | attr [_val = construct<mapnik::attribute>( _1 ) ]
+        | attr [if_else(_1 == "mapnik::geometry_type",
+                        _val = construct<mapnik::geometry_type_attribute>(),
+                        _val = construct<mapnik::attribute>(_1))]
         | global_attr [_val = construct<mapnik::global_attribute>( _1 )]
-        | lit("not") >> expr [_val =  !_1]
         | unary_function_expr [_val = _1]
         | binary_function_expr [_val = _1]
-        | '(' >> expr [_val = _1 ] >> ')'
-        | ustring[_val = unicode_(_1)] // if we get here then try parsing as unquoted string
+        | '(' > logical_expr [_val = _1 ] > ')'
         ;
 
     unesc_char.add("\\a", '\a')("\\b", '\b')("\\f", '\f')("\\n", '\n')
diff --git a/include/mapnik/feature.hpp b/include/mapnik/feature.hpp
index 769ca05..f340c83 100644
--- a/include/mapnik/feature.hpp
+++ b/include/mapnik/feature.hpp
@@ -90,7 +90,7 @@ private:
 using context_type = context<std::map<std::string,std::size_t> >;
 using context_ptr = std::shared_ptr<context_type>;
 
-static const value default_feature_value;
+static const value default_feature_value{};
 
 class MAPNIK_DECL feature_impl : private util::noncopyable
 {
@@ -101,15 +101,15 @@ public:
     using cont_type = std::vector<value_type>;
     using iterator = feature_kv_iterator;
 
-    feature_impl(context_ptr const& ctx, mapnik::value_integer id)
-        : id_(id),
+    feature_impl(context_ptr const& ctx, mapnik::value_integer _id)
+        : id_(_id),
         ctx_(ctx),
         data_(ctx_->mapping_.size()),
         geom_(geometry::geometry_empty()),
         raster_() {}
 
     inline mapnik::value_integer id() const { return id_;}
-    inline void set_id(mapnik::value_integer id) { id_ = id;}
+    inline void set_id(mapnik::value_integer _id) { id_ = _id;}
     template <typename T>
     inline void put(context_type::key_type const& key, T const& val)
     {
diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp
index 33ac450..73227cd 100644
--- a/include/mapnik/font_engine_freetype.hpp
+++ b/include/mapnik/font_engine_freetype.hpp
@@ -100,10 +100,8 @@ private:
     static font_memory_cache_type global_memory_fonts_;
 };
 
-class MAPNIK_DECL face_manager : private util::noncopyable
+class MAPNIK_DECL face_manager
 {
-    using face_ptr_cache_type = std::map<std::string, face_ptr>;
-
 public:
     face_manager(font_library & library,
                  freetype_engine::font_file_mapping_type const& font_file_mapping,
@@ -112,9 +110,13 @@ public:
     face_set_ptr get_face_set(std::string const& name);
     face_set_ptr get_face_set(font_set const& fset);
     face_set_ptr get_face_set(std::string const& name, boost::optional<font_set> fset);
-    inline stroker_ptr get_stroker() { return stroker_; }
+    stroker_ptr get_stroker() const { return stroker_; }
+
 private:
-    face_ptr_cache_type face_ptr_cache_;
+    using face_cache = std::map<std::string, face_ptr>;
+    using face_cache_ptr = std::shared_ptr<face_cache>;
+
+    face_cache_ptr face_cache_;
     font_library & library_;
     freetype_engine::font_file_mapping_type const& font_file_mapping_;
     freetype_engine::font_memory_cache_type const& font_memory_cache_;
diff --git a/include/mapnik/geometry.hpp b/include/mapnik/geometry.hpp
index 3766ebf..7063685 100644
--- a/include/mapnik/geometry.hpp
+++ b/include/mapnik/geometry.hpp
@@ -45,9 +45,6 @@ struct point
     point(mapnik::coord<double, 2> const& c)
         : x(c.x), y(c.y) {}
 
-    point(point const& other) = default;
-    point(point && other) noexcept = default;
-    point & operator=(point const& other) = default;
     friend inline bool operator== (point<T> const& a, point<T> const& b)
     {
         return a.x == b.x && a.y == b.y;
@@ -65,12 +62,8 @@ template <typename T>
 struct line_string : std::vector<point<T> >
 {
     line_string() = default;
-    line_string (std::size_t size)
+    explicit line_string(std::size_t size)
         : std::vector<point<T> >(size) {}
-    line_string (line_string && other) = default ;
-    line_string& operator=(line_string &&) = default;
-    line_string (line_string const& ) = default;
-    line_string& operator=(line_string const&) = default;
     inline std::size_t num_points() const { return std::vector<point<T>>::size(); }
     inline void add_coord(T x, T y) { std::vector<point<T>>::template emplace_back(x,y);}
 };
@@ -79,17 +72,12 @@ template <typename T>
 struct linear_ring : line_string<T>
 {
     linear_ring() = default;
-    linear_ring(std::size_t size)
+    explicit linear_ring(std::size_t size)
         : line_string<T>(size) {}
-    linear_ring (linear_ring && other) = default ;
-    linear_ring& operator=(linear_ring &&) = default;
     linear_ring(line_string<T> && other)
-        : line_string<T>(other) {}
-    linear_ring (linear_ring const& ) = default;
+        : line_string<T>(std::move(other)) {}
     linear_ring(line_string<T> const& other)
         : line_string<T>(other) {}
-    linear_ring& operator=(linear_ring const&) = default;
-
 };
 
 template <typename T>
@@ -102,7 +90,6 @@ struct polygon
     using rings_container = InteriorRings<T>;
     rings_container interior_rings;
 
-    polygon() = default;
     inline void set_exterior_ring(linear_ring<T> && ring)
     {
         exterior_ring = std::move(ring);
diff --git a/include/mapnik/geometry_envelope_impl.hpp b/include/mapnik/geometry_envelope_impl.hpp
index dbed6aa..5c39bc3 100644
--- a/include/mapnik/geometry_envelope_impl.hpp
+++ b/include/mapnik/geometry_envelope_impl.hpp
@@ -41,7 +41,7 @@ struct geometry_envelope
     {
         return mapnik::util::apply_visitor(*this, geom);
     }
-    
+
     void operator() (mapnik::geometry::geometry_empty const&) const {}
 
     template <typename T>
@@ -60,12 +60,12 @@ struct geometry_envelope
         bool first = true;
         for (auto const& pt : line)
         {
-            if (first && !bbox.valid()) 
+            if (first && !bbox.valid())
             {
                 bbox.init(pt.x, pt.y, pt.x, pt.y);
                 first = false;
             }
-            else 
+            else
             {
                 bbox.expand_to_include(pt.x, pt.y);
             }
@@ -73,17 +73,23 @@ struct geometry_envelope
     }
 
     template <typename T>
+    void operator() (mapnik::geometry::linear_ring<T> const& ring) const
+    {
+        (*this)(static_cast<mapnik::geometry::line_string<T> const&>(ring));
+    }
+
+    template <typename T>
     void operator() (mapnik::geometry::polygon<T> const& poly) const
     {
         bool first = true;
         for (auto const& pt : poly.exterior_ring)
         {
-            if (first && !bbox.valid()) 
+            if (first && !bbox.valid())
             {
                 bbox.init(pt.x, pt.y, pt.x, pt.y);
                 first = false;
             }
-            else 
+            else
             {
                 bbox.expand_to_include(pt.x, pt.y);
             }
@@ -96,12 +102,12 @@ struct geometry_envelope
         bool first = true;
         for (auto const& pt : multi_point)
         {
-            if (first && !bbox.valid()) 
+            if (first && !bbox.valid())
             {
                 bbox.init(pt.x, pt.y, pt.x, pt.y);
                 first = false;
             }
-            else 
+            else
             {
                 bbox.expand_to_include(pt.x, pt.y);
             }
@@ -140,7 +146,7 @@ struct geometry_envelope
 
 template <typename T>
 mapnik::box2d<double> envelope(T const& geom)
-{   
+{
     box2d<double> bbox;
     detail::geometry_envelope op(bbox);
     op(geom);
@@ -149,4 +155,3 @@ mapnik::box2d<double> envelope(T const& geom)
 
 } // end ns geometry
 } // end ns mapnik
-
diff --git a/include/mapnik/geometry_reprojection_impl.hpp b/include/mapnik/geometry_reprojection_impl.hpp
index 7e78635..ff01bd4 100644
--- a/include/mapnik/geometry_reprojection_impl.hpp
+++ b/include/mapnik/geometry_reprojection_impl.hpp
@@ -36,7 +36,7 @@ geometry_empty reproject_internal(geometry_empty const&, proj_transform const&,
 }
 
 template <typename T>
-point<T> reproject_internal(point<T> const & p, proj_transform const& proj_trans, unsigned int & n_err)
+point<T> reproject_internal(point<T> const& p, proj_transform const& proj_trans, unsigned int & n_err)
 {
     point<T> new_p(p);
     if (!proj_trans.forward(new_p))
@@ -47,7 +47,7 @@ point<T> reproject_internal(point<T> const & p, proj_transform const& proj_trans
 }
 
 template <typename T>
-line_string<T> reproject_internal(line_string<T> const & ls, proj_transform const& proj_trans, unsigned int & n_err)
+line_string<T> reproject_internal(line_string<T> const& ls, proj_transform const& proj_trans, unsigned int & n_err)
 {
     line_string<T> new_ls(ls);
     unsigned int err = proj_trans.forward(new_ls);
@@ -59,7 +59,7 @@ line_string<T> reproject_internal(line_string<T> const & ls, proj_transform cons
 }
 
 template <typename T>
-polygon<T> reproject_internal(polygon<T> const & poly, proj_transform const& proj_trans, unsigned int & n_err)
+polygon<T> reproject_internal(polygon<T> const& poly, proj_transform const& proj_trans, unsigned int & n_err)
 {
     polygon<T> new_poly;
     linear_ring<T> new_ext(poly.exterior_ring);
@@ -171,18 +171,19 @@ geometry_collection<T> reproject_internal(geometry_collection<T> const & c, proj
 }
 
 template <typename T>
-struct geom_reproj_copy_visitor {
+struct geom_reproj_copy_visitor
+{
 
     geom_reproj_copy_visitor(proj_transform const & proj_trans, unsigned int & n_err)
         : proj_trans_(proj_trans),
           n_err_(n_err) {}
 
-    geometry<T> operator() (geometry_empty const&)
+    geometry<T> operator() (geometry_empty const&) const
     {
         return geometry_empty();
     }
 
-    geometry<T> operator() (point<T> const& p)
+    geometry<T> operator() (point<T> const& p) const
     {
         geometry<T> geom; // default empty
         unsigned int intial_err = n_err_;
@@ -192,7 +193,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (line_string<T> const& ls)
+    geometry<T> operator() (line_string<T> const& ls) const
     {
         geometry<T> geom; // default empty
         unsigned int intial_err = n_err_;
@@ -202,7 +203,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (polygon<T> const& poly)
+    geometry<T> operator() (polygon<T> const& poly) const
     {
         geometry<T> geom; // default empty
         polygon<T> new_poly = reproject_internal(poly, proj_trans_, n_err_);
@@ -211,7 +212,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (multi_point<T> const& mp)
+    geometry<T> operator() (multi_point<T> const& mp) const
     {
         geometry<T> geom; // default empty
         multi_point<T> new_mp = reproject_internal(mp, proj_trans_, n_err_);
@@ -220,7 +221,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (multi_line_string<T> const& mls)
+    geometry<T> operator() (multi_line_string<T> const& mls) const
     {
         geometry<T> geom; // default empty
         multi_line_string<T> new_mls = reproject_internal(mls, proj_trans_, n_err_);
@@ -229,7 +230,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (multi_polygon<T> const& mpoly)
+    geometry<T> operator() (multi_polygon<T> const& mpoly) const
     {
         geometry<T> geom; // default empty
         multi_polygon<T> new_mpoly = reproject_internal(mpoly, proj_trans_, n_err_);
@@ -238,7 +239,7 @@ struct geom_reproj_copy_visitor {
         return geom;
     }
 
-    geometry<T> operator() (geometry_collection<T> const& c)
+    geometry<T> operator() (geometry_collection<T> const& c) const
     {
         geometry<T> geom; // default empty
         geometry_collection<T> new_c = reproject_internal(c, proj_trans_, n_err_);
@@ -283,15 +284,15 @@ struct geom_reproj_visitor {
         : proj_trans_(proj_trans) {}
 
     template <typename T>
-    bool operator() (geometry<T> & geom)
+    bool operator() (geometry<T> & geom) const
     {
         return mapnik::util::apply_visitor((*this), geom);
     }
 
-    bool operator() (geometry_empty &) { return true; }
+    bool operator() (geometry_empty &) const { return true; }
 
     template <typename T>
-    bool operator() (point<T> & p)
+    bool operator() (point<T> & p) const
     {
         if (!proj_trans_.forward(p))
         {
@@ -301,7 +302,7 @@ struct geom_reproj_visitor {
     }
 
     template <typename T>
-    bool operator() (line_string<T> & ls)
+    bool operator() (line_string<T> & ls) const
     {
         if (proj_trans_.forward(ls) > 0)
         {
@@ -311,7 +312,7 @@ struct geom_reproj_visitor {
     }
 
     template <typename T>
-    bool operator() (polygon<T> & poly)
+    bool operator() (polygon<T> & poly) const
     {
         if (proj_trans_.forward(poly.exterior_ring) > 0)
         {
@@ -329,13 +330,13 @@ struct geom_reproj_visitor {
     }
 
     template <typename T>
-    bool operator() (multi_point<T> & mp)
+    bool operator() (multi_point<T> & mp) const
     {
         return (*this) (static_cast<line_string<T> &>(mp));
     }
 
     template <typename T>
-    bool operator() (multi_line_string<T> & mls)
+    bool operator() (multi_line_string<T> & mls) const
     {
         for (auto & ls : mls)
         {
@@ -348,7 +349,7 @@ struct geom_reproj_visitor {
     }
 
     template <typename T>
-    bool operator() (multi_polygon<T> & mpoly)
+    bool operator() (multi_polygon<T> & mpoly) const
     {
         for (auto & poly : mpoly)
         {
@@ -361,7 +362,7 @@ struct geom_reproj_visitor {
     }
 
     template <typename T>
-    bool operator() (geometry_collection<T> & c)
+    bool operator() (geometry_collection<T> & c) const
     {
         for (auto & g : c)
         {
diff --git a/include/mapnik/geometry_unique.hpp b/include/mapnik/geometry_unique.hpp
deleted file mode 100644
index 1678228..0000000
--- a/include/mapnik/geometry_unique.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-#ifndef MAPNIK_GEOMETRY_UNIQUE_HPP
-#define MAPNIK_GEOMETRY_UNIQUE_HPP
-
-#include <mapnik/geometry.hpp>
-#include <mapnik/geometry_adapters.hpp>
-#include <boost/geometry/algorithms/unique.hpp>
-
-#include <type_traits>
-
-namespace mapnik { namespace geometry {
-
-namespace detail {
-
-struct geometry_unique
-{
-    using result_type = void;
-
-    result_type operator() (geometry & geom) const
-    {
-        mapnik::util::apply_visitor(*this, geom);
-    }
-
-    result_type operator() (geometry_collection & collection) const
-    {
-        for (auto & geom : collection)
-        {
-            (*this)(geom);
-        }
-    }
-
-    result_type operator() (line_string & line) const
-    {
-        boost::geometry::unique(line);
-    }
-
-    result_type operator() (polygon & poly) const
-    {
-        boost::geometry::unique(poly);
-    }
-
-    template <typename T>
-    result_type operator() (T & geom) const
-    {
-        // no-op
-    }
-
-};
-
-}
-
-template <typename GeomType>
-inline void unique(GeomType & geom)
-{
-    static_assert(!std::is_const<GeomType>::value,"mapnik::geometry::unique on const& is invalid");
-    detail::geometry_unique()(geom);
-}
-
-}}
-
-#endif // MAPNIK_GEOMETRY_UNIQUE_HPP
diff --git a/include/mapnik/gradient.hpp b/include/mapnik/gradient.hpp
index f8eff6e..f151ebe 100644
--- a/include/mapnik/gradient.hpp
+++ b/include/mapnik/gradient.hpp
@@ -28,7 +28,6 @@
 
 // mapnik
 #include <mapnik/color.hpp>
-#include <mapnik/enumeration.hpp>
 
 // stl
 #include <vector>
@@ -39,26 +38,20 @@ namespace mapnik
 using stop_pair = std::pair<double, mapnik::color>;
 using stop_array = std::vector<stop_pair >;
 
-enum gradient_enum
+enum gradient_e
 {
     NO_GRADIENT,
     LINEAR,
-    RADIAL,
-    gradient_enum_MAX
+    RADIAL
 };
 
-DEFINE_ENUM( gradient_e, gradient_enum );
-
-enum gradient_unit_enum
+enum gradient_unit_e
 {
     USER_SPACE_ON_USE,
     USER_SPACE_ON_USE_BOUNDING_BOX, // used to indicate % age values were specified. This are % of the svg image extent.
-    OBJECT_BOUNDING_BOX, //  (i.e., the abstract coordinate system where (0,0) is at the top/left of the object bounding box and (1,1) is at the bottom/right of the object bounding box)
-    gradient_unit_enum_MAX
+    OBJECT_BOUNDING_BOX //  (i.e., the abstract coordinate system where (0,0) is at the top/left of the object bounding box and (1,1) is at the bottom/right of the object bounding box)
 };
 
-DEFINE_ENUM( gradient_unit_e, gradient_unit_enum );
-
 class MAPNIK_DECL gradient
 {
     // transform
diff --git a/include/mapnik/group/group_layout_manager.hpp b/include/mapnik/group/group_layout_manager.hpp
index 38ee30b..548e37b 100644
--- a/include/mapnik/group/group_layout_manager.hpp
+++ b/include/mapnik/group/group_layout_manager.hpp
@@ -31,43 +31,51 @@
 // stl
 #include <vector>
 
-using std::vector;
-
 namespace mapnik
 {
 
-using bound_box = box2d<double>;
-
 struct group_layout_manager
 {
-    group_layout_manager(group_layout const& layout)
+    using bound_box = box2d<double>;
+
+    group_layout_manager()
+        : update_layout_(false)
+    {}
+
+    explicit group_layout_manager(group_layout const& layout)
         : layout_(layout),
-          input_origin_(0, 0),
-          member_boxes_(vector<bound_box>()),
-          member_offsets_(vector<pixel_position>()),
-          update_layout_(true)
+          update_layout_(false)
     {
     }
 
     group_layout_manager(group_layout const& layout, pixel_position const& input_origin)
         : layout_(layout),
           input_origin_(input_origin),
-          member_boxes_(vector<bound_box>()),
-          member_offsets_(vector<pixel_position>()),
-          update_layout_(true)
+          update_layout_(false)
     {
     }
 
     group_layout_manager(group_layout const& layout, pixel_position const& input_origin,
-                         vector<bound_box> const& item_boxes)
+                         std::vector<bound_box> const& item_boxes)
         : layout_(layout),
           input_origin_(input_origin),
           member_boxes_(item_boxes),
-          member_offsets_(vector<pixel_position>()),
           update_layout_(true)
     {
     }
 
+    void set_input_origin(double x, double y)
+    {
+        input_origin_.set(x, y);
+        update_layout_ = true;
+    }
+
+    void set_input_origin(pixel_position const& input_origin)
+    {
+        input_origin_ = input_origin;
+        update_layout_ = true;
+    }
+
     inline void set_layout(group_layout const& layout)
     {
         layout_ = layout;
@@ -94,8 +102,8 @@ private:
 
     group_layout layout_;
     pixel_position input_origin_;
-    vector<bound_box> member_boxes_;
-    vector<pixel_position> member_offsets_;
+    std::vector<bound_box> member_boxes_;
+    std::vector<pixel_position> member_offsets_;
     bool update_layout_;
 };
 
diff --git a/include/mapnik/image.hpp b/include/mapnik/image.hpp
index 9ebdfc3..13b21c8 100644
--- a/include/mapnik/image.hpp
+++ b/include/mapnik/image.hpp
@@ -54,9 +54,6 @@ template <std::size_t max_size>
 struct image_dimensions
 {
     image_dimensions(int width, int height);
-    image_dimensions(image_dimensions const& other) = default;
-    image_dimensions(image_dimensions && other) = default;
-    image_dimensions& operator= (image_dimensions rhs);
     std::size_t width() const;
     std::size_t height() const;
 private:
diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp
index 735ad82..8aedf30 100644
--- a/include/mapnik/image_any.hpp
+++ b/include/mapnik/image_any.hpp
@@ -55,8 +55,9 @@ struct MAPNIK_DECL image_any : image_base
               bool painted = false);
 
     template <typename T>
-        image_any(T && _data) noexcept
-        : image_base(std::move(_data)) {}
+        image_any(T && _data)
+        noexcept(std::is_nothrow_constructible<image_base, T && >::value)
+        : image_base(std::forward<T>(_data)) {}
 
     unsigned char const* bytes() const;
     unsigned char* bytes();
diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp
index 62a3f3b..9b0b754 100644
--- a/include/mapnik/image_filter.hpp
+++ b/include/mapnik/image_filter.hpp
@@ -683,7 +683,7 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op)
     using namespace boost::gil;
     rgba8_view_t src_view = rgba8_view(src);
     bool premultiplied = src.get_premultiplied();
-    
+
     for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
     {
         rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
@@ -738,7 +738,7 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op)
             X = deviate_x * Y / deviate_y;
             Z = (1.0 - (deviate_x + deviate_y)) * Y / deviate_y;
             // Neutral grey calculated from luminance (in D65)
-            double neutral_X = 0.312713 * Y / 0.329016; 
+            double neutral_X = 0.312713 * Y / 0.329016;
             double neutral_Z = 0.358271 * Y / 0.329016;
             // Difference between simulated color and neutral grey
             double diff_X = neutral_X - X;
@@ -761,12 +761,12 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op)
             // Convert to RGB color space
             dr = X * 3.24071 + Y * -1.53726 + Z * -0.498571; // XYZ->RGB (sRGB:D65)
             dg = X * -0.969258 + Y * 1.87599 + Z * 0.0415557;
-            db = X * 0.0556352 + Y * -0.203996 + Z * 1.05707;            
+            db = X * 0.0556352 + Y * -0.203996 + Z * 1.05707;
             // Compensate simulated color towards a neutral fit in RGB space
             double fit_r = ((dr < 0.0 ? 0.0 : 1.0) - dr) / diff_r;
             double fit_g = ((dg < 0.0 ? 0.0 : 1.0) - dg) / diff_g;
             double fit_b = ((db < 0.0 ? 0.0 : 1.0) - db) / diff_b;
-            double adjust = std::max( (fit_r > 1.0 || fit_r < 0.0) ? 0.0 : fit_r, 
+            double adjust = std::max( (fit_r > 1.0 || fit_r < 0.0) ? 0.0 : fit_r,
                                       (fit_g > 1.0 || fit_g < 0.0) ? 0.0 : fit_g
                                     );
             adjust = std::max((fit_b > 1.0 || fit_b < 0.0) ? 0.0 : fit_b, adjust);
@@ -777,7 +777,7 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op)
             // Apply gamma correction
             dr = std::pow(dr, 1.0 / 2.2);
             dg = std::pow(dg, 1.0 / 2.2);
-            db = std::pow(db, 1.0 / 2.2);            
+            db = std::pow(db, 1.0 / 2.2);
             // premultiply
             dr *= da;
             dg *= da;
@@ -917,7 +917,7 @@ struct filter_visitor
     : src_(src) {}
 
     template <typename T>
-    void operator () (T const& filter)
+    void operator () (T const& filter) const
     {
         apply_filter(src_, filter);
     }
@@ -931,9 +931,9 @@ struct filter_radius_visitor
     filter_radius_visitor(int & radius)
         : radius_(radius) {}
     template <typename T>
-    void operator () (T const& /*filter*/) {}
+    void operator () (T const& /*filter*/)  const {}
 
-    void operator () (agg_stack_blur const& op)
+    void operator () (agg_stack_blur const& op) const
     {
         if (static_cast<int>(op.rx) > radius_) radius_ = static_cast<int>(op.rx);
         if (static_cast<int>(op.ry) > radius_) radius_ = static_cast<int>(op.ry);
diff --git a/include/mapnik/image_filter_grammar.hpp b/include/mapnik/image_filter_grammar.hpp
index 04ced60..7ab2d12 100644
--- a/include/mapnik/image_filter_grammar.hpp
+++ b/include/mapnik/image_filter_grammar.hpp
@@ -20,19 +20,19 @@
  *
  *****************************************************************************/
 
-#ifndef MAPNIK_IMAGE_FILITER_GRAMMAR_HPP
-#define MAPNIK_IMAGE_FILITER_GRAMMAR_HPP
+#ifndef MAPNIK_IMAGE_FILTER_GRAMMAR_HPP
+#define MAPNIK_IMAGE_FILTER_GRAMMAR_HPP
+
+// mapnik
+#include <mapnik/config.hpp>
+#include <mapnik/css_color_grammar.hpp>
+#include <mapnik/color.hpp>
 
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/spirit/include/qi.hpp>
-#include <boost/fusion/include/adapt_struct.hpp>
 #pragma GCC diagnostic pop
 
-// mapnik
-#include <mapnik/css_color_grammar.hpp>
-#include <mapnik/color.hpp>
-
 // stl
 #include <cmath>
 
@@ -43,16 +43,6 @@ struct color_stop;
 struct colorize_alpha;
 }
 
-}
-
-BOOST_FUSION_ADAPT_STRUCT(
-    mapnik::filter::color_stop,
-    (mapnik::color, color )
-    (double, offset)
-)
-
-namespace mapnik {
-
 namespace qi = boost::spirit::qi;
 
 struct percent_offset_impl
@@ -76,21 +66,29 @@ template <typename Iterator, typename ContType>
 struct image_filter_grammar :
         qi::grammar<Iterator, ContType(), qi::ascii::space_type>
 {
+    using alternative_type = qi::rule<Iterator, ContType(), qi::ascii::space_type>;
+
     image_filter_grammar();
+
     qi::rule<Iterator, ContType(), qi::ascii::space_type> start;
-    qi::rule<Iterator, ContType(), qi::ascii::space_type> filter;
-    qi::rule<Iterator, qi::locals<int,int>, void(ContType&), qi::ascii::space_type> agg_blur_filter;
-    qi::rule<Iterator, qi::locals<double,double,double,double,double,double,double,double>,
-             void(ContType&), qi::ascii::space_type> scale_hsla_filter;
-    qi::rule<Iterator, qi::locals<mapnik::filter::colorize_alpha, mapnik::filter::color_stop>, void(ContType&), qi::ascii::space_type> colorize_alpha_filter;
+    qi::rule<Iterator, ContType(), qi::ascii::space_type,
+                                   qi::locals<alternative_type*>> filter;
     qi::rule<Iterator, qi::ascii::space_type> no_args;
+    qi::symbols<char, alternative_type*> alternatives;
     qi::uint_parser< unsigned, 10, 1, 3 > radius_;
     css_color_grammar<Iterator> css_color_;
-    qi::rule<Iterator,void(mapnik::filter::color_stop &),qi::ascii::space_type> color_stop_offset;
+    qi::rule<Iterator, filter::color_stop(), qi::ascii::space_type> color_stop_;
+    qi::rule<Iterator, double(), qi::ascii::space_type> color_stop_offset;
     phoenix::function<percent_offset_impl> percent_offset;
-    qi::rule<Iterator, qi::locals<color>, void(ContType&), qi::ascii::space_type> color_to_alpha_filter;
+
+private:
+    alternative_type & add(std::string const& symbol);
+
+    static constexpr unsigned max_alternatives = 16;
+    unsigned num_alternatives = 0;
+    alternative_type alternative_storage[max_alternatives];
 };
 
-}
+} // namespace mapnik
 
-#endif // MAPNIK_IMAGE_FILITER_PARSER_HPP
+#endif // MAPNIK_IMAGE_FILTER_GRAMMAR_HPP
diff --git a/include/mapnik/image_filter_grammar_impl.hpp b/include/mapnik/image_filter_grammar_impl.hpp
index 4dfa540..98de711 100644
--- a/include/mapnik/image_filter_grammar_impl.hpp
+++ b/include/mapnik/image_filter_grammar_impl.hpp
@@ -29,6 +29,14 @@
 #include <boost/spirit/include/phoenix.hpp>
 #pragma GCC diagnostic pop
 
+namespace { // internal
+
+    BOOST_PHOENIX_ADAPT_FUNCTION(
+        typename std::remove_reference<A1>::type, ovo, // = optional_value_or
+        boost::get_optional_value_or, 2)
+
+} // namespace internal
+
 namespace mapnik {
 
 namespace qi = boost::spirit::qi;
@@ -41,97 +49,90 @@ image_filter_grammar<Iterator,ContType>::image_filter_grammar()
     qi::lit_type lit;
     qi::_val_type _val;
     qi::_1_type _1;
+    qi::_2_type _2;
+    qi::_3_type _3;
+    qi::_4_type _4;
+    qi::_5_type _5;
+    qi::_6_type _6;
+    qi::_7_type _7;
+    qi::_8_type _8;
     qi::_a_type _a;
-    qi::_b_type _b;
-    qi::_c_type _c;
-    qi::_d_type _d;
-    qi::_e_type _e;
-    qi::_f_type _f;
-    qi::_g_type _g;
-    qi::_h_type _h;
-    qi::_r1_type _r1;
-    qi::eps_type eps;
-    qi::char_type char_;
+    qi::attr_type attr;
     qi::double_type double_;
-    qi::no_skip_type no_skip;
+    qi::hold_type hold;
+    qi::omit_type omit;
     using phoenix::push_back;
     using phoenix::construct;
-    using phoenix::at_c;
-    start = -(filter % no_skip[*char_(", ")])
+
+    start = -(filter % *lit(','))
         ;
 
-    filter =
-        lit("emboss") >> no_args [push_back(_val,construct<mapnik::filter::emboss>())]
-        |
-        lit("blur") >> no_args [push_back(_val,construct<mapnik::filter::blur>())]
-        |
-        lit("gray") >> no_args [push_back(_val,construct<mapnik::filter::gray>())]
-        |
-        lit("edge-detect") >> no_args [push_back(_val,construct<mapnik::filter::edge_detect>())]
-        |
-        lit("sobel") >> no_args [push_back(_val,construct<mapnik::filter::sobel>())]
-        |
-        lit("sharpen") >> no_args [push_back(_val,construct<mapnik::filter::sharpen>())]
-        |
-        lit("x-gradient") >> no_args [push_back(_val,construct<mapnik::filter::x_gradient>())]
-        |
-        lit("y-gradient") >> no_args [push_back(_val,construct<mapnik::filter::y_gradient>())]
-        |
-        lit("invert") >> no_args [push_back(_val,construct<mapnik::filter::invert>())]
-        |
-        lit("color-blind-protanope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_protanope>())]
-        |
-        lit("color-blind-deuteranope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_deuteranope>())]
-        |
-        lit("color-blind-tritanope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_tritanope>())]
-        |
-        agg_blur_filter(_val)
-        |
-        scale_hsla_filter(_val)
-        |
-        colorize_alpha_filter(_val)
+    filter = omit[alternatives[_a = _1]] >> qi::lazy(*_a)
+        ;
+
+    add("emboss") = no_args >> attr(construct<mapnik::filter::emboss>());
+    add("blur") = no_args >> attr(construct<mapnik::filter::blur>());
+    add("gray") = no_args >> attr(construct<mapnik::filter::gray>());
+    add("edge-detect") = no_args >> attr(construct<mapnik::filter::edge_detect>());
+    add("sobel") = no_args >> attr(construct<mapnik::filter::sobel>());
+    add("sharpen") = no_args >> attr(construct<mapnik::filter::sharpen>());
+    add("x-gradient") = no_args >> attr(construct<mapnik::filter::x_gradient>());
+    add("y-gradient") = no_args >> attr(construct<mapnik::filter::y_gradient>());
+    add("invert") = no_args >> attr(construct<mapnik::filter::invert>());
+    add("color-blind-protanope") = no_args >> attr(construct<mapnik::filter::color_blind_protanope>());
+    add("color-blind-deuteranope") = no_args >> attr(construct<mapnik::filter::color_blind_deuteranope>());
+    add("color-blind-tritanope") = no_args >> attr(construct<mapnik::filter::color_blind_tritanope>());
+
+    add("agg-stack-blur") =
+        (lit('(') >> radius_ >> -( lit(',') >> radius_ ) >> lit(')'))
+        [push_back(_val, construct<filter::agg_stack_blur>(_1, ovo(_2, _1)))]
         |
-        color_to_alpha_filter(_val)
+        no_args
+        [push_back(_val, construct<filter::agg_stack_blur>(1, 1))]
         ;
 
-    agg_blur_filter = lit("agg-stack-blur")[_a = 1, _b = 1]
-        >> -( lit('(') >> -( radius_[_a = _1][_b = _1]
-                             >> -(lit(',') >> radius_[_b = _1]))
-              >> lit(')'))
-        [push_back(_r1,construct<mapnik::filter::agg_stack_blur>(_a,_b))]
+    add("scale-hsla") =
+        (lit('(')
+          >> double_ >> lit(',') >> double_ >> lit(',')
+          >> double_ >> lit(',') >> double_ >> lit(',')
+          >> double_ >> lit(',') >> double_ >> lit(',')
+          >> double_ >> lit(',') >> double_ >> lit(')'))
+        [push_back(_val, construct<filter::scale_hsla>(_1,_2,_3,_4,_5,_6,_7,_8))]
         ;
 
-    scale_hsla_filter = lit("scale-hsla")
-        >> lit('(')
-        >> double_[_a = _1] >> lit(',') >> double_[_b = _1] >> lit(',')
-        >> double_[_c = _1] >> lit(',') >> double_[_d = _1] >> lit(',')
-        >> double_[_e = _1] >> lit(',') >> double_[_f = _1] >> lit(',')
-        >> double_[_g = _1] >> lit(',') >> double_[_h = _1] >> lit(')')
-        [push_back(_r1, construct<mapnik::filter::scale_hsla>(_a,_b,_c,_d,_e,_f,_g,_h))]
+    add("colorize-alpha") = qi::as<filter::colorize_alpha>()
+        [lit('(') >> color_stop_ % lit(',') >> lit(')')]
+        [push_back(_val, _1)]
         ;
 
-    colorize_alpha_filter = lit("colorize-alpha")[_a = construct<mapnik::filter::colorize_alpha>()]
-        >> lit('(')
-        >> (css_color_[at_c<0>(_b) = _1, at_c<1>(_b) = 0]
-            >> -color_stop_offset(_b)) [push_back(_a,_b)]
-        >> -(+(lit(',') >> css_color_[at_c<0>(_b) =_1,at_c<1>(_b) = 0]
-             >> -color_stop_offset(_b))[push_back(_a,_b)])
-        >> lit(')') [push_back(_r1,_a)]
+    color_stop_ = (css_color_ >> -color_stop_offset)
+        [_val = construct<filter::color_stop>(_1, ovo(_2, 0.0))]
         ;
 
-    color_stop_offset = (double_ >> lit('%'))[at_c<1>(_r1) = percent_offset(_1)]
-        |
-        double_[at_c<1>(_r1) = _1]
+    color_stop_offset = double_[_val = _1]
+        >> -lit('%')[_val = percent_offset(_val)]
         ;
 
-    color_to_alpha_filter = lit("color-to-alpha")
-        >> lit('(')
-        >> css_color_[_a = _1]
-        >> lit(')')
-        [push_back(_r1,construct<mapnik::filter::color_to_alpha>(_a))]
+    add("color-to-alpha") =
+        hold[lit('(') >> css_color_ >> lit(')')]
+        [push_back(_val, construct<filter::color_to_alpha>(_1))]
         ;
 
     no_args = -(lit('(') >> lit(')'));
 }
 
+template <typename Iterator, typename ContType>
+auto image_filter_grammar<Iterator, ContType>::add(std::string const& symbol)
+    -> alternative_type &
+{
+    if (num_alternatives >= max_alternatives)
+    {
+        throw std::length_error("too many alternatives in image_filter_grammar");
+    }
+
+    alternative_storage[num_alternatives].name(symbol);
+    alternatives.add(symbol, &alternative_storage[num_alternatives]);
+    return alternative_storage[num_alternatives++];
 }
+
+} // namespace mapnik
diff --git a/include/mapnik/image_impl.hpp b/include/mapnik/image_impl.hpp
index 732e7eb..c756547 100644
--- a/include/mapnik/image_impl.hpp
+++ b/include/mapnik/image_impl.hpp
@@ -44,14 +44,6 @@ image_dimensions<max_size>::image_dimensions(int width, int height)
 }
 
 template <std::size_t max_size>
-image_dimensions<max_size>& image_dimensions<max_size>::operator= (image_dimensions rhs)
-{
-    std::swap(width_, rhs.width_);
-    std::swap(height_, rhs.height_);
-    return *this;
-}
-
-template <std::size_t max_size>
 std::size_t image_dimensions<max_size>::width() const
 {
     return width_;
diff --git a/include/mapnik/image_null.hpp b/include/mapnik/image_null.hpp
index 67dbbd3..5a698bb 100644
--- a/include/mapnik/image_null.hpp
+++ b/include/mapnik/image_null.hpp
@@ -25,6 +25,7 @@
 
 // mapnik
 #include <mapnik/config.hpp>
+#include <mapnik/image.hpp>
 #include <mapnik/pixel_types.hpp>
 
 //stl
@@ -47,9 +48,6 @@ public:
           bool /*initialize*/ = true,
           bool /*premultiplied*/ = false,
           bool /*painted*/ = false) {}
-    image(image<null_t> const&) {}
-    image(image<null_t> &&) noexcept {}
-    image<null_t>& operator=(image<null_t>) { return *this; }
     bool operator==(image<null_t> const&) const { return true; }
     bool operator<(image<null_t> const&) const { return false; }
 
diff --git a/include/mapnik/image_scaling_traits.hpp b/include/mapnik/image_scaling_traits.hpp
index 69eec5a..81ddf54 100644
--- a/include/mapnik/image_scaling_traits.hpp
+++ b/include/mapnik/image_scaling_traits.hpp
@@ -24,7 +24,9 @@
 #define MAPNIK_IMAGE_SCALING_TRAITS_HPP
 
 // mapnik
-#include <mapnik/span_image_filter.h>
+#include <mapnik/image.hpp>
+#include <mapnik/image_scaling.hpp>
+#include <mapnik/span_image_filter.hpp>
 
 // agg
 #include "agg_image_accessors.h"
diff --git a/include/mapnik/image_util_jpeg.hpp b/include/mapnik/image_util_jpeg.hpp
index b8b213a..feb8105 100644
--- a/include/mapnik/image_util_jpeg.hpp
+++ b/include/mapnik/image_util_jpeg.hpp
@@ -23,6 +23,8 @@
 #ifndef MAPNIK_IMAGE_UTIL_JPEG_HPP
 #define MAPNIK_IMAGE_UTIL_JPEG_HPP
 
+#include <mapnik/config.hpp>
+
 // stl
 #include <string>
 #include <iostream>
diff --git a/include/mapnik/image_util_png.hpp b/include/mapnik/image_util_png.hpp
index e02cb44..8592c4f 100644
--- a/include/mapnik/image_util_png.hpp
+++ b/include/mapnik/image_util_png.hpp
@@ -23,6 +23,8 @@
 #ifndef MAPNIK_IMAGE_UTIL_PNG_HPP
 #define MAPNIK_IMAGE_UTIL_PNG_HPP
 
+#include <mapnik/palette.hpp>
+
 // stl
 #include <string>
 #include <iostream>
diff --git a/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp
index c2f023a..846da66 100644
--- a/include/mapnik/image_view.hpp
+++ b/include/mapnik/image_view.hpp
@@ -37,11 +37,7 @@ public:
     static constexpr std::size_t pixel_size = sizeof(pixel_type);
 
     image_view(std::size_t x, std::size_t y, std::size_t width, std::size_t height, T const& data);
-    ~image_view();
 
-    image_view(image_view<T> const& rhs);
-    image_view(image_view<T> && other) noexcept;
-    image_view<T>& operator=(image_view<T> rhs) = delete;
     bool operator==(image_view<T> const& rhs) const;
     bool operator<(image_view<T> const& rhs) const;
 
diff --git a/include/mapnik/image_view_any.hpp b/include/mapnik/image_view_any.hpp
index f61e33d..23c9779 100644
--- a/include/mapnik/image_view_any.hpp
+++ b/include/mapnik/image_view_any.hpp
@@ -47,8 +47,9 @@ struct MAPNIK_DECL image_view_any : image_view_base
     image_view_any() = default;
 
     template <typename T>
-    image_view_any(T && data) noexcept
-        : image_view_base(std::move(data)) {}
+    image_view_any(T && data)
+        noexcept(std::is_nothrow_constructible<image_view_base, T && >::value)
+        : image_view_base(std::forward<T>(data)) {}
 
     std::size_t width() const;
     std::size_t height() const;
diff --git a/include/mapnik/image_view_impl.hpp b/include/mapnik/image_view_impl.hpp
index 89ae151..1403204 100644
--- a/include/mapnik/image_view_impl.hpp
+++ b/include/mapnik/image_view_impl.hpp
@@ -43,25 +43,6 @@ image_view<T>::image_view(std::size_t x, std::size_t y, std::size_t width, std::
 }
 
 template <typename T>
-image_view<T>::~image_view() {}
-
-template <typename T>
-image_view<T>::image_view(image_view<T> const& rhs)
-    : x_(rhs.x_),
-      y_(rhs.y_),
-      width_(rhs.width_),
-      height_(rhs.height_),
-      data_(rhs.data_) {}
-
-template <typename T>
-image_view<T>::image_view(image_view<T> && other) noexcept
-    : x_(std::move(other.x_)),
-    y_(std::move(other.y_)),
-    width_(std::move(other.width_)),
-    height_(std::move(other.height_)),
-    data_(std::move(other.data_)) {}
-
-template <typename T>
 bool image_view<T>::operator==(image_view<T> const& rhs) const
 {
     return rhs.data_.bytes() == data_.bytes();
diff --git a/include/mapnik/image_view_null.hpp b/include/mapnik/image_view_null.hpp
index 5672c05..76ebc5f 100644
--- a/include/mapnik/image_view_null.hpp
+++ b/include/mapnik/image_view_null.hpp
@@ -23,7 +23,7 @@
 #ifndef MAPNIK_IMAGE_VIEW_NULL_HPP
 #define MAPNIK_IMAGE_VIEW_NULL_HPP
 
-#include <mapnik/image.hpp>
+#include <mapnik/image_view.hpp>
 
 //stl
 #include <stdexcept>
diff --git a/include/mapnik/json/geometry_grammar.hpp b/include/mapnik/json/geometry_grammar.hpp
index 7d768a7..800a48b 100644
--- a/include/mapnik/json/geometry_grammar.hpp
+++ b/include/mapnik/json/geometry_grammar.hpp
@@ -49,6 +49,8 @@ struct geometry_grammar :
     qi::symbols<char, int> geometry_type_dispatch;
     positions_grammar<Iterator> coordinates;
     boost::phoenix::function<create_geometry_impl> create_geometry;
+    // generic JSON
+    generic_json<Iterator> json_;
     // error handler
     ErrorHandler error_handler;
 };
diff --git a/include/mapnik/json/geometry_grammar_impl.hpp b/include/mapnik/json/geometry_grammar_impl.hpp
index 30fad88..90f09bf 100644
--- a/include/mapnik/json/geometry_grammar_impl.hpp
+++ b/include/mapnik/json/geometry_grammar_impl.hpp
@@ -30,8 +30,6 @@
 #include <boost/spirit/include/phoenix_object.hpp>
 #include <boost/spirit/include/phoenix_stl.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
-#include <iostream>                     // for clog, endl, etc
-#include <string>                       // for string
 
 namespace mapnik { namespace json {
 
@@ -51,18 +49,45 @@ geometry_grammar<Iterator, ErrorHandler>::geometry_grammar()
     qi::_a_type _a;
     qi::_b_type _b;
     qi::eps_type eps;
+    qi::omit_type omit;
     using qi::fail;
     using qi::on_error;
     using phoenix::push_back;
 
     start = geometry.alias() | lit("null");
 
+    // generic json types
+    json_.value =  json_.object | json_.array | json_.string_ | json_.number
+        ;
+
+    json_.pairs = json_.key_value % lit(',')
+        ;
+
+    json_.key_value = (json_.string_ > lit(':') > json_.value)
+        ;
+
+    json_.object = lit('{')
+        > *json_.pairs
+        > lit('}')
+        ;
+    json_.array = lit('[')
+        > json_.value > *(lit(',') > json_.value)
+        > lit(']')
+        ;
+    json_.number = json_.strict_double
+        | json_.int__
+        | lit("true")
+        | lit ("false")
+        | lit("null")
+        ;
     geometry = lit('{')[_a = 0]
-        > (-lit(',') >> (lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1])
-           ^
-           (-lit(',') >> (lit("\"coordinates\"") > lit(':') > coordinates[_b = _1]))
-           ^
-           (-lit(',') >> (lit("\"geometries\"") > lit(':') > lit('[') > geometry_collection[_val = _1] > lit(']'))))[create_geometry(_val,_a,_b)]
+        > (((lit("\"type\"") > lit(':') > geometry_type_dispatch[_a = _1])
+            |
+            (lit("\"coordinates\"") > lit(':') > coordinates[_b = _1])
+            |
+            (lit("\"geometries\"") > lit(':') > lit('[') > geometry_collection[_val = _1] > lit(']'))
+            |
+            omit[json_.key_value]) % lit(',')) [create_geometry(_val,_a,_b)]
         > lit('}')
         ;
 
diff --git a/include/mapnik/json/positions_grammar.hpp b/include/mapnik/json/positions_grammar.hpp
index a652e8f..1535422 100644
--- a/include/mapnik/json/positions_grammar.hpp
+++ b/include/mapnik/json/positions_grammar.hpp
@@ -63,10 +63,10 @@ struct set_position_impl
 struct push_position_impl
 {
     using result_type = void;
-    template <typename T0,typename T1>
+    template <typename T0, typename T1>
     result_type operator() (T0 & coords, T1 const& pos) const
     {
-        if (pos) coords.push_back(*pos);
+        if (pos) coords.emplace_back(*pos);
     }
 };
 
diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp
index 74a76be..6846180 100644
--- a/include/mapnik/label_collision_detector.hpp
+++ b/include/mapnik/label_collision_detector.hpp
@@ -145,17 +145,17 @@ private:
 public:
     using query_iterator = tree_t::query_iterator;
 
-    explicit label_collision_detector4(box2d<double> const& extent)
-        : tree_(extent) {}
+    explicit label_collision_detector4(box2d<double> const& _extent)
+        : tree_(_extent) {}
 
     bool has_placement(box2d<double> const& box)
     {
-        tree_t::query_iterator itr = tree_.query_in_box(box);
-        tree_t::query_iterator end = tree_.query_end();
+        tree_t::query_iterator tree_itr = tree_.query_in_box(box);
+        tree_t::query_iterator tree_end = tree_.query_end();
 
-        for ( ;itr != end; ++itr)
+        for ( ;tree_itr != tree_end; ++tree_itr)
         {
-            if (itr->get().box.intersects(box)) return false;
+            if (tree_itr->get().box.intersects(box)) return false;
         }
 
         return true;
@@ -168,12 +168,12 @@ public:
                                                                box.maxx() + margin, box.maxy() + margin)
                                                : box);
 
-        tree_t::query_iterator itr = tree_.query_in_box(margin_box);
-        tree_t::query_iterator end = tree_.query_end();
+        tree_t::query_iterator tree_itr = tree_.query_in_box(margin_box);
+        tree_t::query_iterator tree_end = tree_.query_end();
 
-        for (;itr != end; ++itr)
+        for (;tree_itr != tree_end; ++tree_itr)
         {
-            if (itr->get().box.intersects(margin_box))
+            if (tree_itr->get().box.intersects(margin_box))
             {
                 return false;
             }
@@ -196,12 +196,12 @@ public:
                                                                box.maxx() + margin, box.maxy() + margin)
                                                : box);
 
-        tree_t::query_iterator itr = tree_.query_in_box(repeat_box);
-        tree_t::query_iterator end = tree_.query_end();
+        tree_t::query_iterator tree_itr = tree_.query_in_box(repeat_box);
+        tree_t::query_iterator tree_end = tree_.query_end();
 
-        for ( ;itr != end; ++itr)
+        for ( ;tree_itr != tree_end; ++tree_itr)
         {
-            if (itr->get().box.intersects(margin_box) || (text == itr->get().text && itr->get().box.intersects(repeat_box)))
+            if (tree_itr->get().box.intersects(margin_box) || (text == tree_itr->get().text && tree_itr->get().box.intersects(repeat_box)))
             {
                 return false;
             }
diff --git a/include/mapnik/mapped_memory_cache.hpp b/include/mapnik/mapped_memory_cache.hpp
index f41c2e8..c67d63a 100644
--- a/include/mapnik/mapped_memory_cache.hpp
+++ b/include/mapnik/mapped_memory_cache.hpp
@@ -28,8 +28,8 @@
 #include <mapnik/util/singleton.hpp>
 #include <mapnik/util/noncopyable.hpp>
 
-// boost
 #include <memory>
+#include <string>
 #include <unordered_map>
 #include <boost/optional.hpp>
 
@@ -52,6 +52,8 @@ public:
     void clear();
 };
 
+extern template class MAPNIK_DECL singleton<mapped_memory_cache, CreateStatic>;
+
 }
 
 #endif // MAPNIK_MAPPED_MEMORY_CACHE_HPP
diff --git a/include/mapnik/marker.hpp b/include/mapnik/marker.hpp
index 6f7cbf0..8ba117e 100644
--- a/include/mapnik/marker.hpp
+++ b/include/mapnik/marker.hpp
@@ -41,8 +41,10 @@ namespace mapnik
 struct image_any;
 namespace svg { struct path_attributes; }
 
-using attr_storage = agg::pod_bvector<mapnik::svg::path_attributes>;
-using svg_storage_type = mapnik::svg::svg_storage<mapnik::svg::svg_path_storage,attr_storage>;
+using svg::svg_path_adapter;
+
+using svg_attribute_type = agg::pod_bvector<svg::path_attributes>;
+using svg_storage_type = svg::svg_storage<svg::svg_path_storage, svg_attribute_type>;
 using svg_path_ptr = std::shared_ptr<svg_storage_type>;
 using image_ptr = std::shared_ptr<image_any>;
 
@@ -56,23 +58,17 @@ public:
         bitmap_data_.set(0xff000000);
     }
 
-    marker_rgba8(image_rgba8 const & data)
+    explicit marker_rgba8(image_rgba8 const& data)
         : bitmap_data_(data) {}
 
-    marker_rgba8(image_rgba8 && data)
+    explicit marker_rgba8(image_rgba8 && data) noexcept
         : bitmap_data_(std::move(data)) {}
 
-    marker_rgba8(marker_rgba8 const& rhs)
-        : bitmap_data_(rhs.bitmap_data_) {}
-
-    marker_rgba8(marker_rgba8 && rhs) noexcept
-        : bitmap_data_(std::move(rhs.bitmap_data_)) {}
-
     box2d<double> bounding_box() const
     {
-        std::size_t width = bitmap_data_.width();
-        std::size_t height = bitmap_data_.height();
-        return box2d<double>(static_cast<double>(0), static_cast<double>(0), static_cast<double>(width), static_cast<double>(height));
+        std::size_t _width = bitmap_data_.width();
+        std::size_t _height = bitmap_data_.height();
+        return box2d<double>(static_cast<double>(0), static_cast<double>(0), static_cast<double>(_width), static_cast<double>(_height));
     }
 
     inline double width() const
@@ -97,18 +93,12 @@ private:
 struct marker_svg
 {
 public:
-    marker_svg() { }
+    marker_svg() = default;
 
-    marker_svg(mapnik::svg_path_ptr data)
+    explicit marker_svg(mapnik::svg_path_ptr data) noexcept
         : vector_data_(data) {}
 
-    marker_svg(marker_svg const& rhs)
-        : vector_data_(rhs.vector_data_) {}
-
-    marker_svg(marker_svg && rhs) noexcept
-        : vector_data_(rhs.vector_data_) {}
-
-    box2d<double> bounding_box() const
+    inline box2d<double> bounding_box() const
     {
         return vector_data_->bounding_box();
     }
@@ -122,11 +112,15 @@ public:
         return vector_data_->bounding_box().height();
     }
 
-    mapnik::svg_path_ptr get_data() const
+    inline mapnik::svg_path_ptr get_data() const
     {
         return vector_data_;
     }
 
+    inline std::tuple<double,double> dimensions() const
+    {
+        return std::make_tuple(vector_data_->width(), vector_data_->height());
+    }
 private:
     mapnik::svg_path_ptr vector_data_;
 
@@ -134,9 +128,8 @@ private:
 
 struct marker_null
 {
-    marker_null() = default;
 public:
-    box2d<double> bounding_box() const
+    inline box2d<double> bounding_box() const
     {
         return box2d<double>();
     }
@@ -189,8 +182,9 @@ struct marker : marker_base
     marker() = default;
 
     template <typename T>
-    marker(T && data) noexcept
-        : marker_base(std::move(data)) {}
+    marker(T && _data)
+        noexcept(std::is_nothrow_constructible<marker_base, T && >::value)
+        : marker_base(std::forward<T>(_data)) {}
 
     double width() const
     {
diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp
index 9183d6a..49c011a 100644
--- a/include/mapnik/marker_helpers.hpp
+++ b/include/mapnik/marker_helpers.hpp
@@ -35,6 +35,7 @@
 #include <mapnik/box2d.hpp>
 #include <mapnik/vertex_processor.hpp>
 #include <mapnik/renderer_common/apply_vertex_converter.hpp>
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
 
 // agg
 #include "agg_trans_affine.h"
@@ -52,60 +53,53 @@ template <typename Detector>
 struct vector_markers_dispatch : util::noncopyable
 {
     vector_markers_dispatch(svg_path_ptr const& src,
+                            svg_path_adapter & path,
+                            svg_attribute_type const& attrs,
                             agg::trans_affine const& marker_trans,
                             symbolizer_base const& sym,
                             Detector & detector,
                             double scale_factor,
                             feature_impl const& feature,
-                            attributes const& vars)
-        : src_(src),
-          marker_trans_(marker_trans),
-          sym_(sym),
-          detector_(detector),
-          feature_(feature),
-          vars_(vars),
-          scale_factor_(scale_factor)
+                            attributes const& vars,
+                            bool snap_to_pixels,
+                            markers_renderer_context & renderer_context)
+        : params_(src->bounding_box(), recenter(src) * marker_trans,
+                  sym, feature, vars, scale_factor, snap_to_pixels)
+        , renderer_context_(renderer_context)
+        , src_(src)
+        , path_(path)
+        , attrs_(attrs)
+        , detector_(detector)
     {}
 
-    virtual ~vector_markers_dispatch() {}
-
     template <typename T>
     void add_path(T & path)
     {
-        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
-        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
-        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
-        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
-        value_double opacity = get<value_double,keys::opacity>(sym_, feature_, vars_);
-        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
-        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
-        coord2d center = src_->bounding_box().center();
-        agg::trans_affine_translation recenter(-center.x, -center.y);
-        agg::trans_affine tr = recenter * marker_trans_;
-        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
-        markers_placement_params params { src_->bounding_box(), tr, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
         markers_placement_finder<T, Detector> placement_finder(
-            placement_method, path, detector_, params);
+            params_.placement_method, path, detector_, params_.placement_params);
         double x, y, angle = .0;
-        while (placement_finder.get_point(x, y, angle, ignore_placement))
+        while (placement_finder.get_point(x, y, angle, params_.ignore_placement))
         {
-            agg::trans_affine matrix = tr;
+            agg::trans_affine matrix = params_.placement_params.tr;
             matrix.rotate(angle);
             matrix.translate(x, y);
-            render_marker(matrix, opacity);
+            renderer_context_.render_marker(src_, path_, attrs_, params_, matrix);
         }
     }
 
-    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;
-
 protected:
+    static agg::trans_affine recenter(svg_path_ptr const& src)
+    {
+        coord2d center = src->bounding_box().center();
+        return agg::trans_affine_translation(-center.x, -center.y);
+    }
+
+    markers_dispatch_params params_;
+    markers_renderer_context & renderer_context_;
     svg_path_ptr const& src_;
-    agg::trans_affine const& marker_trans_;
-    symbolizer_base const& sym_;
+    svg_path_adapter & path_;
+    svg_attribute_type const& attrs_;
     Detector & detector_;
-    feature_impl const& feature_;
-    attributes const& vars_;
-    double scale_factor_;
 };
 
 template <typename Detector>
@@ -117,53 +111,35 @@ struct raster_markers_dispatch : util::noncopyable
                             Detector & detector,
                             double scale_factor,
                             feature_impl const& feature,
-                            attributes const& vars)
-    : src_(src),
-        marker_trans_(marker_trans),
-        sym_(sym),
-        detector_(detector),
-        feature_(feature),
-        vars_(vars),
-        scale_factor_(scale_factor)
+                            attributes const& vars,
+                            markers_renderer_context & renderer_context)
+        : params_(box2d<double>(0, 0, src.width(), src.height()),
+                  marker_trans, sym, feature, vars, scale_factor)
+        , renderer_context_(renderer_context)
+        , src_(src)
+        , detector_(detector)
     {}
 
-    virtual ~raster_markers_dispatch() {}
-
     template <typename T>
     void add_path(T & path)
     {
-        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
-        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
-        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
-        value_double opacity = get<value_double, keys::opacity>(sym_, feature_, vars_);
-        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
-        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
-        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
-        box2d<double> bbox(0,0, src_.width(),src_.height());
-        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
-        markers_placement_params params { bbox, marker_trans_, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
         markers_placement_finder<T, Detector> placement_finder(
-            placement_method, path, detector_, params);
+            params_.placement_method, path, detector_, params_.placement_params);
         double x, y, angle = .0;
-        while (placement_finder.get_point(x, y, angle, ignore_placement))
+        while (placement_finder.get_point(x, y, angle, params_.ignore_placement))
         {
-            agg::trans_affine matrix = marker_trans_;
+            agg::trans_affine matrix = params_.placement_params.tr;
             matrix.rotate(angle);
             matrix.translate(x, y);
-            render_marker(matrix, opacity);
+            renderer_context_.render_marker(src_, params_, matrix);
         }
     }
 
-    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;
-
 protected:
+    markers_dispatch_params params_;
+    markers_renderer_context & renderer_context_;
     image_rgba8 const& src_;
-    agg::trans_affine const& marker_trans_;
-    symbolizer_base const& sym_;
     Detector & detector_;
-    feature_impl const& feature_;
-    attributes const& vars_;
-    double scale_factor_;
 };
 
 void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, attributes const& vars,
@@ -186,8 +162,7 @@ void setup_transform_scaling(agg::trans_affine & tr,
 template <typename Converter, typename Processor>
 void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, Processor & proc, symbolizer_base const& sym)
 {
-    using vertex_converter_type = Converter;
-    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type,Processor>;
+    using apply_vertex_converter_type = detail::apply_vertex_converter<Converter,Processor>;
     using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
 
     auto const& geom = feature.get_geometry();
diff --git a/include/mapnik/markers_placements/point.hpp b/include/mapnik/markers_placements/point.hpp
index 65b95ce..13f174d 100644
--- a/include/mapnik/markers_placements/point.hpp
+++ b/include/mapnik/markers_placements/point.hpp
@@ -40,8 +40,8 @@ namespace mapnik {
 
 struct markers_placement_params
 {
-    box2d<double> const& size;
-    agg::trans_affine const& tr;
+    box2d<double> size;
+    agg::trans_affine tr;
     double spacing;
     double max_error;
     bool allow_overlap;
@@ -132,23 +132,8 @@ protected:
     // Rotates the size_ box and translates the position.
     box2d<double> perform_transform(double angle, double dx, double dy)
     {
-        double x1 = params_.size.minx();
-        double x2 = params_.size.maxx();
-        double y1 = params_.size.miny();
-        double y2 = params_.size.maxy();
         agg::trans_affine tr = params_.tr * agg::trans_affine_rotation(angle).translate(dx, dy);
-        double xA = x1, yA = y1,
-               xB = x2, yB = y1,
-               xC = x2, yC = y2,
-               xD = x1, yD = y2;
-        tr.transform(&xA, &yA);
-        tr.transform(&xB, &yB);
-        tr.transform(&xC, &yC);
-        tr.transform(&xD, &yD);
-        box2d<double> result(xA, yA, xC, yC);
-        result.expand_to_include(xB, yB);
-        result.expand_to_include(xD, yD);
-        return result;
+        return box2d<double>(params_.size, tr);
     }
 
     bool set_direction(double & angle)
diff --git a/include/mapnik/memory_datasource.hpp b/include/mapnik/memory_datasource.hpp
index cd2b611..9b58974 100644
--- a/include/mapnik/memory_datasource.hpp
+++ b/include/mapnik/memory_datasource.hpp
@@ -55,6 +55,7 @@ private:
     mapnik::layer_descriptor desc_;
     datasource::datasource_t type_;
     bool bbox_check_;
+    bool type_set_;
     mutable box2d<double> extent_;
     mutable bool dirty_extent_ = true;
 };
diff --git a/include/mapnik/miniz_png.hpp b/include/mapnik/miniz_png.hpp
deleted file mode 100644
index 6c46e44..0000000
--- a/include/mapnik/miniz_png.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-#ifndef MAPNIK_MINIZ_PNG_HPP
-#define MAPNIK_MINIZ_PNG_HPP
-
-// mapnik
-#include <mapnik/palette.hpp>
-#include <mapnik/config.hpp>
-
-// stl
-#include <vector>
-#include <iostream>
-#include <stdexcept>
-
-#include <mapnik/image.hpp>
-#include <mapnik/image_view.hpp>
-
-/* miniz.c porting issues:
-  - duplicate symbols in python bindings require moving miniz.c include to just cpp file
-  - due to http://code.google.com/p/miniz/issues/detail?id=7
-  - avoiding including miniz.c here requires fwd declaring the two structs below
-  - being able to fwd declare requires removing typedef from struct declarations in miniz.c
-  - being able to fwd declare also requires using pointers to structs
-  - if updated, need to apply c++11 fix: https://github.com/mapnik/mapnik/issues/1967
-*/
-
-// TODO: try using #define MINIZ_HEADER_FILE_ONLY
-struct tdefl_output_buffer;
-struct tdefl_compressor;
-
-namespace mapnik { namespace MiniZ {
-
-using mapnik::rgb;
-
-class MAPNIK_DECL PNGWriter {
-
-public:
-    PNGWriter(int level, int strategy);
-    ~PNGWriter();
-private:
-    inline void writeUInt32BE(unsigned char *target, unsigned int value);
-    size_t startChunk(const unsigned char header[], size_t length);
-    void finishChunk(size_t start);
-public:
-    void writeIHDR(unsigned int width, unsigned int height, unsigned char pixel_depth);
-    void writePLTE(std::vector<rgb> const& palette);
-    void writetRNS(std::vector<unsigned> const& alpha);
-    template<typename T>
-    void writeIDAT(T const& image);
-    template<typename T>
-    void writeIDATStripAlpha(T const& image);
-    void writeIEND();
-    void toStream(std::ostream& stream);
-
-private:
-    tdefl_compressor *compressor;
-    tdefl_output_buffer *buffer;
-    static const unsigned char preamble[];
-    static const unsigned char IHDR_tpl[];
-    static const unsigned char PLTE_tpl[];
-    static const unsigned char tRNS_tpl[];
-    static const unsigned char IDAT_tpl[];
-    static const unsigned char IEND_tpl[];
-};
-
-extern template MAPNIK_DECL void PNGWriter::writeIDAT<image_gray8>(image_gray8 const& image);
-extern template MAPNIK_DECL void PNGWriter::writeIDAT<image_view_gray8>(image_view_gray8 const& image);
-extern template MAPNIK_DECL void PNGWriter::writeIDAT<image_rgba8>(image_rgba8 const& image);
-extern template MAPNIK_DECL void PNGWriter::writeIDAT<image_view_rgba8>(image_view_rgba8 const& image);
-extern template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha<image_rgba8>(image_rgba8 const& image);
-extern template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha<image_view_rgba8>(image_view_rgba8 const& image);
-
-}}
-
-#endif // MAPNIK_MINIZ_PNG_HPP
diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp
index a5ac57c..310ed80 100644
--- a/include/mapnik/offset_converter.hpp
+++ b/include/mapnik/offset_converter.hpp
@@ -482,7 +482,6 @@ private:
         }
         start_v2.x = v2.x;
         start_v2.y = v2.y;
-        bool continue_loop = true;        
         vertex2d tmp_prev(vertex2d::no_init);
         
         while (i < points.size())
@@ -515,7 +514,6 @@ private:
             else if (v2.cmd == SEG_END)
             {
                 if (!is_polygon) break;
-                continue_loop = false;
                 v2.x = start_v2.x;
                 v2.y = start_v2.y;
             }
diff --git a/include/mapnik/params.hpp b/include/mapnik/params.hpp
index fc59289..a154cf7 100644
--- a/include/mapnik/params.hpp
+++ b/include/mapnik/params.hpp
@@ -50,7 +50,8 @@ struct value_holder : value_holder_base
 
     // perfect forwarding
     template <typename T>
-    value_holder(T && obj) noexcept
+    value_holder(T && obj)
+        noexcept(std::is_nothrow_constructible<value_holder_base, T && >::value)
         : value_holder_base(std::forward<T>(obj))
     {}
 };
diff --git a/include/mapnik/png_io.hpp b/include/mapnik/png_io.hpp
index 1b075b1..f17f857 100644
--- a/include/mapnik/png_io.hpp
+++ b/include/mapnik/png_io.hpp
@@ -27,7 +27,6 @@
 #include <mapnik/palette.hpp>
 #include <mapnik/octree.hpp>
 #include <mapnik/hextree.hpp>
-#include <mapnik/miniz_png.hpp>
 #include <mapnik/image.hpp>
 
 // zlib
@@ -53,7 +52,6 @@ struct png_options {
     double gamma;
     bool paletted;
     bool use_hextree;
-    bool use_miniz;
     png_options() :
         colors(256),
         compression(Z_DEFAULT_COMPRESSION),
@@ -61,8 +59,7 @@ struct png_options {
         trans_mode(-1),
         gamma(-1),
         paletted(true),
-        use_hextree(true),
-        use_miniz(false) {}
+        use_hextree(true) {}
 };
 
 template <typename T>
@@ -85,23 +82,6 @@ void save_as_png(T1 & file,
                 png_options const& opts)
 
 {
-    if (opts.use_miniz)
-    {
-        MiniZ::PNGWriter writer(opts.compression,opts.strategy);
-        if (opts.trans_mode == 0)
-        {
-            writer.writeIHDR(image.width(), image.height(), 24);
-            writer.writeIDATStripAlpha(image);
-        }
-        else
-        {
-            writer.writeIHDR(image.width(), image.height(), 32);
-            writer.writeIDAT(image);
-        }
-        writer.writeIEND();
-        writer.toStream(file);
-        return;
-    }
     png_voidp error_ptr=0;
     png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
                                                 error_ptr,0, 0);
@@ -273,19 +253,6 @@ void save_as_png(T & file, std::vector<mapnik::rgb> const& palette,
                  std::vector<unsigned> const&alpha,
                  png_options const& opts)
 {
-    if (opts.use_miniz)
-    {
-        MiniZ::PNGWriter writer(opts.compression,opts.strategy);
-        // image.width()/height() does not reflect the actual image dimensions; it
-        // refers to the quantized scanlines.
-        writer.writeIHDR(width, height, color_depth);
-        writer.writePLTE(palette);
-        writer.writetRNS(alpha);
-        writer.writeIDAT(image);
-        writer.writeIEND();
-        writer.toStream(file);
-        return;
-    }
     png_voidp error_ptr=0;
     png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
                                                 error_ptr,0, 0);
diff --git a/include/mapnik/pool.hpp b/include/mapnik/pool.hpp
index ecd16eb..c37aab4 100644
--- a/include/mapnik/pool.hpp
+++ b/include/mapnik/pool.hpp
@@ -24,7 +24,6 @@
 #define MAPNIK_POOL_HPP
 
 // mapnik
-#include <mapnik/util/singleton.hpp>
 #include <mapnik/util/noncopyable.hpp>
 
 // boost
diff --git a/include/mapnik/quad_tree.hpp b/include/mapnik/quad_tree.hpp
index 092bf4d..d60ee5d 100644
--- a/include/mapnik/quad_tree.hpp
+++ b/include/mapnik/quad_tree.hpp
@@ -179,12 +179,7 @@ public:
                       "Values stored in quad-tree must be standard layout types to allow serialisation");
         char header[16];
         std::memset(header,0,16);
-        header[0]='m';
-        header[1]='a';
-        header[2]='p';
-        header[3]='n';
-        header[4]='i';
-        header[5]='k';
+        std::strcpy(header,"mapnik-index");
         out.write(header,16);
         write_node(out,root_);
     }
diff --git a/include/mapnik/renderer_common.hpp b/include/mapnik/renderer_common.hpp
index 2134438..c9d181b 100644
--- a/include/mapnik/renderer_common.hpp
+++ b/include/mapnik/renderer_common.hpp
@@ -42,13 +42,16 @@ namespace mapnik {
 
 struct renderer_common : private util::noncopyable
 {
+    using detector_ptr = std::shared_ptr<label_collision_detector4>;
+
     renderer_common(Map const &m, attributes const& vars, unsigned offset_x, unsigned offset_y,
                        unsigned width, unsigned height, double scale_factor);
     renderer_common(Map const &m, attributes const& vars, unsigned offset_x, unsigned offset_y,
                        unsigned width, unsigned height, double scale_factor,
-                       std::shared_ptr<label_collision_detector4> detector);
+                       detector_ptr detector);
     renderer_common(Map const &m, request const &req, attributes const& vars, unsigned offset_x, unsigned offset_y,
                        unsigned width, unsigned height, double scale_factor);
+    ~renderer_common();
 
     unsigned width_;
     unsigned height_;
@@ -60,11 +63,18 @@ struct renderer_common : private util::noncopyable
     face_manager_freetype font_manager_;
     box2d<double> query_extent_;
     view_transform t_;
-    std::shared_ptr<label_collision_detector4> detector_;
+    detector_ptr detector_;
+
+protected:
+    // it's desirable to keep this class implicitly noncopyable to prevent
+    // inadvertent copying from other places;
+    // this copy constructor is therefore protected and should only be used
+    // by virtual_renderer_common
+    renderer_common(renderer_common const& other);
 
 private:
     renderer_common(Map const &m, unsigned width, unsigned height, double scale_factor,
-                    attributes const& vars, view_transform &&t, std::shared_ptr<label_collision_detector4> detector);
+                    attributes const& vars, view_transform && t, detector_ptr detector);
 };
 
 }
diff --git a/include/mapnik/renderer_common/apply_vertex_converter.hpp b/include/mapnik/renderer_common/apply_vertex_converter.hpp
index de239c9..503dc41 100644
--- a/include/mapnik/renderer_common/apply_vertex_converter.hpp
+++ b/include/mapnik/renderer_common/apply_vertex_converter.hpp
@@ -31,7 +31,7 @@ struct apply_vertex_converter
     apply_vertex_converter(VertexConverter & converter, Processor & proc)
         : converter_(converter), proc_(proc) {}
     template <typename Adapter>
-    void operator() (Adapter const& adapter) const
+    void operator() (Adapter const& adapter)
     {
         converter_.apply(adapter, proc_);
     }
diff --git a/include/mapnik/renderer_common/process_group_symbolizer.hpp b/include/mapnik/renderer_common/process_group_symbolizer.hpp
deleted file mode 100644
index edec308..0000000
--- a/include/mapnik/renderer_common/process_group_symbolizer.hpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-#ifndef MAPNIK_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP
-#define MAPNIK_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP
-
-// mapnik
-#include <mapnik/pixel_position.hpp>
-#include <mapnik/marker.hpp>
-#include <mapnik/feature.hpp>
-#include <mapnik/feature_factory.hpp>
-#include <mapnik/renderer_common.hpp>
-#include <mapnik/symbolizer.hpp>
-#include <mapnik/attribute_collector.hpp>
-#include <mapnik/group/group_layout_manager.hpp>
-#include <mapnik/group/group_symbolizer_helper.hpp>
-#include <mapnik/group/group_symbolizer_properties.hpp>
-#include <mapnik/text/glyph_positions.hpp>
-#include <mapnik/util/conversions.hpp>
-#include <mapnik/util/variant.hpp>
-#include <mapnik/label_collision_detector.hpp>
-#include <mapnik/util/noncopyable.hpp>
-#include <mapnik/svg/svg_path_adapter.hpp>
-#include <mapnik/svg/svg_path_attributes.hpp>
-
-// agg
-#include <agg_trans_affine.h>
-
-namespace mapnik {
-
-class text_symbolizer_helper;
-
-using svg::svg_path_adapter;
-using svg_attribute_type = agg::pod_bvector<svg::path_attributes>;
-
-struct virtual_renderer_common : private util::noncopyable
-{
-    virtual_renderer_common(renderer_common & common) :
-        width_(common.width_),
-        height_(common.height_),
-        scale_factor_(common.scale_factor_),
-        vars_(common.vars_),
-        shared_font_library_(common.shared_font_library_),
-        font_library_(*shared_font_library_),
-        font_manager_(common.font_manager_),
-        query_extent_(common.query_extent_),
-        t_(common.t_),
-        detector_(std::make_shared<label_collision_detector4>(common.detector_->extent())) {}
-
-    unsigned & width_;
-    unsigned & height_;
-    double & scale_factor_;
-    attributes & vars_;
-    // TODO: dirty hack for cairo renderer, figure out how to remove this
-    std::shared_ptr<font_library> & shared_font_library_;
-    font_library & font_library_;
-    face_manager_freetype & font_manager_;
-    box2d<double> & query_extent_;
-    view_transform & t_;
-    std::shared_ptr<label_collision_detector4> detector_;
-};
-
-
-// General:
-
-// The approach here is to run the normal symbolizers, but in
-// a 'virtual' blank environment where the changes that they
-// make are recorded (the detector, the render_* calls).
-//
-// The recorded boxes are then used to lay out the items and
-// the offsets from old to new positions can be used to perform
-// the actual rendering calls.
-
-// This should allow us to re-use as much as possible of the
-// existing symbolizer layout and rendering code while still
-// being able to interpose our own decisions about whether
-// a collision has occurred or not.
-
-// Thunk for rendering a particular instance of a point - this
-// stores all the arguments necessary to re-render this point
-// symbolizer at a later time.
-
-struct vector_marker_render_thunk  : util::noncopyable
-{
-    svg_path_ptr src_;
-    svg_attribute_type attrs_;
-    agg::trans_affine tr_;
-    double opacity_;
-    composite_mode_e comp_op_;
-    bool snap_to_pixels_;
-
-    vector_marker_render_thunk(svg_path_ptr const& src,
-                               svg_attribute_type const& attrs,
-                               agg::trans_affine const& marker_trans,
-                               double opacity,
-                               composite_mode_e comp_op,
-                               bool snap_to_pixels);
-
-    vector_marker_render_thunk(vector_marker_render_thunk && rhs);
-};
-
-struct raster_marker_render_thunk  : util::noncopyable
-{
-    image_rgba8 const& src_;
-    agg::trans_affine tr_;
-    double opacity_;
-    composite_mode_e comp_op_;
-    bool snap_to_pixels_;
-
-    raster_marker_render_thunk(image_rgba8 const& src,
-                               agg::trans_affine const& marker_trans,
-                               double opacity,
-                               composite_mode_e comp_op,
-                               bool snap_to_pixels);
-
-    raster_marker_render_thunk(raster_marker_render_thunk && rhs);
-};
-
-using helper_ptr = std::unique_ptr<text_symbolizer_helper>;
-
-struct text_render_thunk : util::noncopyable
-{
-    // helper is stored here in order
-    // to keep in scope the text rendering structures
-    helper_ptr helper_;
-    placements_list const& placements_;
-    double opacity_;
-    composite_mode_e comp_op_;
-    halo_rasterizer_enum halo_rasterizer_;
-
-    text_render_thunk(helper_ptr && helper,
-                      double opacity, composite_mode_e comp_op,
-                      halo_rasterizer_enum halo_rasterizer);
-
-    text_render_thunk(text_render_thunk && rhs);
-
-};
-
-// Variant type for render thunks to allow us to re-render them
-// via a static visitor later.
-
-using render_thunk = util::variant<vector_marker_render_thunk,
-                                   raster_marker_render_thunk,
-                                   text_render_thunk>;
-using render_thunk_ptr = std::unique_ptr<render_thunk>;
-using render_thunk_list = std::list<render_thunk_ptr>;
-
-// Base class for extracting the bounding boxes associated with placing
-// a symbolizer at a fake, virtual point - not real geometry.
-//
-// The bounding boxes can be used for layout, and the thunks are
-// used to re-render at locations according to the group layout.
-
-struct render_thunk_extractor
-{
-    render_thunk_extractor(box2d<double> & box,
-                           render_thunk_list & thunks,
-                           feature_impl & feature,
-                           attributes const& vars,
-                           proj_transform const& prj_trans,
-                           virtual_renderer_common & common,
-                           box2d<double> const& clipping_extent);
-
-    void operator()(markers_symbolizer const& sym) const;
-
-    void operator()(text_symbolizer const& sym) const;
-
-    void operator()(shield_symbolizer const& sym) const;
-
-    template <typename T>
-    void operator()(T const& ) const
-    {
-        // TODO: warning if unimplemented?
-    }
-
-private:
-    void extract_text_thunk(helper_ptr && helper, text_symbolizer const& sym) const;
-
-    box2d<double> & box_;
-    render_thunk_list & thunks_;
-    feature_impl & feature_;
-    attributes const& vars_;
-    proj_transform const& prj_trans_;
-    virtual_renderer_common & common_;
-    box2d<double> clipping_extent_;
-
-    void update_box() const;
-};
-
-template <typename F>
-void render_offset_placements(placements_list const& placements,
-                              pixel_position const& offset,
-                              F render_text) {
-
-    for (auto const& glyphs : placements)
-    {
-        // move the glyphs to the correct offset
-        pixel_position base_point = glyphs->get_base_point();
-        glyphs->set_base_point(base_point + offset);
-
-        // update the position of any marker
-        marker_info_ptr marker_info = glyphs->get_marker();
-        pixel_position marker_pos = glyphs->marker_pos();
-        if (marker_info)
-        {
-            glyphs->set_marker(marker_info, marker_pos + offset);
-        }
-
-        render_text(glyphs);
-
-        // Need to put the base_point back how it was in case something else calls this again
-        // (don't want to add offset twice) or calls with a different offset.
-        glyphs->set_base_point(base_point);
-        if (marker_info)
-        {
-            glyphs->set_marker(marker_info, marker_pos);
-        }
-    }
-}
-
-template <typename F>
-void render_group_symbolizer(group_symbolizer const& sym,
-                             feature_impl & feature,
-                             attributes const& vars,
-                             proj_transform const& prj_trans,
-                             box2d<double> const& clipping_extent,
-                             renderer_common & common,
-                             F render_thunks)
-{
-    // find all column names referenced in the group rules and symbolizers
-    std::set<std::string> columns;
-    group_attribute_collector column_collector(columns, false);
-    column_collector(sym);
-
-    group_symbolizer_properties_ptr props = get<group_symbolizer_properties_ptr>(sym, keys::group_properties);
-
-    // create a new context for the sub features of this group
-    context_ptr sub_feature_ctx = std::make_shared<mapnik::context_type>();
-
-    // populate new context with column names referenced in the group rules and symbolizers
-    for (auto const& col_name : columns)
-    {
-        sub_feature_ctx->push(col_name);
-    }
-
-    // keep track of the sub features that we'll want to symbolize
-    // along with the group rules that they matched
-    std::vector< std::pair<group_rule_ptr, feature_ptr> > matches;
-
-    // create a copied 'virtual' common renderer for processing sub feature symbolizers
-    // create an empty detector for it, so we are sure we won't hit anything
-    virtual_renderer_common virtual_renderer(common);
-
-    // keep track of which lists of render thunks correspond to
-    // entries in the group_layout_manager.
-    std::vector<render_thunk_list> layout_thunks;
-    size_t num_layout_thunks = 0;
-
-    // layout manager to store and arrange bboxes of matched features
-    group_layout_manager layout_manager(props->get_layout(), pixel_position(common.width_ / 2.0, common.height_ / 2.0));
-
-    // run feature or sub feature through the group rules & symbolizers
-    // for each index value in the range
-    value_integer start = get<value_integer>(sym, keys::start_column);
-    value_integer end = start + get<value_integer>(sym, keys::num_columns);
-    for (value_integer col_idx = start; col_idx < end; ++col_idx)
-    {
-        // create sub feature with indexed column values
-        feature_ptr sub_feature = feature_factory::create(sub_feature_ctx, col_idx);
-
-        // copy the necessary columns to sub feature
-        for(auto const& col_name : columns)
-        {
-            if (col_name.find('%') != std::string::npos)
-            {
-                if (col_name.size() == 1)
-                {
-                    // column name is '%' by itself, so give the index as the value
-                    sub_feature->put(col_name, col_idx);
-                }
-                else
-                {
-                    // indexed column
-                    std::string col_idx_str;
-                    if (mapnik::util::to_string(col_idx_str,col_idx))
-                    {
-                        std::string col_idx_name = col_name;
-                        boost::replace_all(col_idx_name, "%", col_idx_str);
-                        sub_feature->put(col_name, feature.get(col_idx_name));
-                    }
-                }
-            }
-            else
-            {
-                // non-indexed column
-                sub_feature->put(col_name, feature.get(col_name));
-            }
-        }
-
-        // add a single point geometry at pixel origin
-        double x = common.width_ / 2.0, y = common.height_ / 2.0, z = 0.0;
-        common.t_.backward(&x, &y);
-        prj_trans.forward(x, y, z);
-        // note that we choose a point in the middle of the screen to
-        // try to ensure that we don't get edge artefacts due to any
-        // symbolizers with avoid-edges set: only the avoid-edges of
-        // the group symbolizer itself should matter.
-        geometry::point<double> origin_pt(x,y);
-        sub_feature->set_geometry(origin_pt);
-        // get the layout for this set of properties
-        for (auto const& rule : props->get_rules())
-        {
-             if (util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*sub_feature,common.vars_),
-                                               *(rule->get_filter())).to_bool())
-             {
-                // add matched rule and feature to the list of things to draw
-                matches.emplace_back(rule, sub_feature);
-
-                // construct a bounding box around all symbolizers for the matched rule
-                bound_box bounds;
-                render_thunk_list thunks;
-                render_thunk_extractor extractor(bounds, thunks, *sub_feature, common.vars_, prj_trans,
-                                                 virtual_renderer, clipping_extent);
-
-                for (auto const& _sym : *rule)
-                {
-                    // TODO: construct layout and obtain bounding box
-                    util::apply_visitor(extractor, _sym);
-                }
-
-                // add the bounding box to the layout manager
-                layout_manager.add_member_bound_box(bounds);
-                layout_thunks.emplace_back(std::move(thunks));
-                ++num_layout_thunks;
-                break;
-            }
-        }
-    }
-
-    // create a symbolizer helper
-    group_symbolizer_helper helper(sym, feature, vars, prj_trans,
-                                   common.width_, common.height_,
-                                   common.scale_factor_, common.t_,
-                                   *common.detector_, clipping_extent);
-
-    for (size_t i = 0; i < matches.size(); ++i)
-    {
-        group_rule_ptr match_rule = matches[i].first;
-        feature_ptr match_feature = matches[i].second;
-        value_unicode_string rpt_key_value = "";
-
-        // get repeat key from matched group rule
-        expression_ptr rpt_key_expr = match_rule->get_repeat_key();
-
-        // if no repeat key was defined, use default from group symbolizer
-        if (!rpt_key_expr)
-        {
-            rpt_key_expr = get<expression_ptr>(sym, keys::repeat_key);
-        }
-
-        // evaluate the repeat key with the matched sub feature if we have one
-        if (rpt_key_expr)
-        {
-            rpt_key_value = util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*match_feature,common.vars_),
-                                                *rpt_key_expr).to_unicode();
-        }
-        helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
-    }
-
-    pixel_position_list positions = helper.get();
-    for (pixel_position const& pos : positions)
-    {
-        for (size_t layout_i = 0; layout_i < num_layout_thunks; ++layout_i)
-        {
-            pixel_position const& offset = layout_manager.offset_at(layout_i);
-            pixel_position render_offset = pos + offset;
-            render_thunks(layout_thunks[layout_i], render_offset);
-        }
-    }
-}
-
-} // namespace mapnik
-
-#endif // MAPNIK_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP
diff --git a/include/mapnik/renderer_common/render_group_symbolizer.hpp b/include/mapnik/renderer_common/render_group_symbolizer.hpp
new file mode 100644
index 0000000..e7b1b22
--- /dev/null
+++ b/include/mapnik/renderer_common/render_group_symbolizer.hpp
@@ -0,0 +1,66 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_RENDERER_COMMON_RENDER_GROUP_SYMBOLIZER_HPP
+#define MAPNIK_RENDERER_COMMON_RENDER_GROUP_SYMBOLIZER_HPP
+
+// mapnik
+#include <mapnik/pixel_position.hpp>
+#include <mapnik/renderer_common.hpp>
+#include <mapnik/renderer_common/render_thunk.hpp>
+#include <mapnik/symbolizer_base.hpp>
+#include <mapnik/text/glyph_positions.hpp>
+
+namespace mapnik {
+
+struct render_thunk_list_dispatch
+{
+    virtual void operator()(vector_marker_render_thunk const& thunk) = 0;
+    virtual void operator()(raster_marker_render_thunk const& thunk) = 0;
+    virtual void operator()(text_render_thunk const& thunk) = 0;
+
+    void render_list(render_thunk_list const& thunks, pixel_position const& offset)
+    {
+        offset_ = offset;
+
+        for (render_thunk_ptr const& thunk : thunks)
+        {
+            util::apply_visitor(std::ref(*this), *thunk);
+        }
+    }
+
+protected:
+    pixel_position offset_;
+};
+
+MAPNIK_DECL
+void render_group_symbolizer(group_symbolizer const& sym,
+                             feature_impl & feature,
+                             attributes const& vars,
+                             proj_transform const& prj_trans,
+                             box2d<double> const& clipping_extent,
+                             renderer_common & common,
+                             render_thunk_list_dispatch & render_thunks);
+
+} // namespace mapnik
+
+#endif // MAPNIK_RENDERER_COMMON_RENDER_GROUP_SYMBOLIZER_HPP
diff --git a/include/mapnik/renderer_common/render_markers_symbolizer.hpp b/include/mapnik/renderer_common/render_markers_symbolizer.hpp
new file mode 100644
index 0000000..42639fb
--- /dev/null
+++ b/include/mapnik/renderer_common/render_markers_symbolizer.hpp
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_RENDERER_COMMON_RENDER_MARKERS_SYMBOLIZER_HPP
+#define MAPNIK_RENDERER_COMMON_RENDER_MARKERS_SYMBOLIZER_HPP
+
+#include <mapnik/marker.hpp>
+#include <mapnik/markers_placement.hpp>
+#include <mapnik/renderer_common.hpp>
+#include <mapnik/symbolizer_base.hpp>
+
+namespace mapnik {
+
+struct markers_dispatch_params
+{
+    // placement
+    markers_placement_params placement_params;
+    marker_placement_enum placement_method;
+    value_bool ignore_placement;
+    // rendering
+    bool snap_to_pixels;
+    double scale_factor;
+    value_double opacity;
+
+    markers_dispatch_params(box2d<double> const& size,
+                            agg::trans_affine const& tr,
+                            symbolizer_base const& sym,
+                            feature_impl const& feature,
+                            attributes const& vars,
+                            double scale_factor = 1.0,
+                            bool snap_to_pixels = false);
+};
+
+struct markers_renderer_context : util::noncopyable
+{
+    virtual void render_marker(image_rgba8 const& src,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr) = 0;
+
+    virtual void render_marker(svg_path_ptr const& src,
+                               svg_path_adapter & path,
+                               svg_attribute_type const& attrs,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr) = 0;
+};
+
+MAPNIK_DECL
+void render_markers_symbolizer(markers_symbolizer const& sym,
+                               mapnik::feature_impl & feature,
+                               proj_transform const& prj_trans,
+                               renderer_common const& common,
+                               box2d<double> const& clip_box,
+                               markers_renderer_context & renderer_context);
+
+} // namespace mapnik
+
+#endif // MAPNIK_RENDERER_COMMON_RENDER_MARKERS_SYMBOLIZER_HPP
diff --git a/include/mapnik/renderer_common/render_thunk.hpp b/include/mapnik/renderer_common/render_thunk.hpp
new file mode 100644
index 0000000..2a957d2
--- /dev/null
+++ b/include/mapnik/renderer_common/render_thunk.hpp
@@ -0,0 +1,115 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_RENDERER_COMMON_RENDER_THUNK_HPP
+#define MAPNIK_RENDERER_COMMON_RENDER_THUNK_HPP
+
+// mapnik
+#include <mapnik/image_compositing.hpp> // composite_mode_e
+#include <mapnik/marker.hpp> // svg_attribute_type, svg_path_ptr
+#include <mapnik/symbolizer_enumerations.hpp> // halo_rasterizer_enum
+#include <mapnik/svg/svg_path_attributes.hpp>
+#include <mapnik/text/symbolizer_helpers.hpp>
+#include <mapnik/util/noncopyable.hpp>
+#include <mapnik/util/variant.hpp>
+
+// agg
+#include <agg_trans_affine.h>
+
+namespace mapnik {
+
+// Thunk for rendering a particular instance of a point - this
+// stores all the arguments necessary to re-render this point
+// symbolizer at a later time.
+
+struct vector_marker_render_thunk : util::movable
+{
+    svg_path_ptr src_;
+    svg_attribute_type attrs_;
+    agg::trans_affine tr_;
+    double opacity_;
+    composite_mode_e comp_op_;
+    bool snap_to_pixels_;
+
+    vector_marker_render_thunk(svg_path_ptr const& src,
+                               svg_attribute_type const& attrs,
+                               agg::trans_affine const& marker_trans,
+                               double opacity,
+                               composite_mode_e comp_op,
+                               bool snap_to_pixels)
+        : src_(src), attrs_(attrs), tr_(marker_trans), opacity_(opacity),
+          comp_op_(comp_op), snap_to_pixels_(snap_to_pixels)
+    {}
+};
+
+struct raster_marker_render_thunk : util::movable
+{
+    image_rgba8 const& src_;
+    agg::trans_affine tr_;
+    double opacity_;
+    composite_mode_e comp_op_;
+    bool snap_to_pixels_;
+
+    raster_marker_render_thunk(image_rgba8 const& src,
+                               agg::trans_affine const& marker_trans,
+                               double opacity,
+                               composite_mode_e comp_op,
+                               bool snap_to_pixels)
+        : src_(src), tr_(marker_trans), opacity_(opacity), comp_op_(comp_op),
+          snap_to_pixels_(snap_to_pixels)
+    {}
+};
+
+struct text_render_thunk : util::movable
+{
+    using helper_ptr = std::unique_ptr<text_symbolizer_helper>;
+    // helper is stored here in order
+    // to keep in scope the text rendering structures
+    helper_ptr helper_;
+    placements_list const& placements_;
+    double opacity_;
+    composite_mode_e comp_op_;
+    halo_rasterizer_enum halo_rasterizer_;
+
+    text_render_thunk(helper_ptr && helper,
+                      double opacity, composite_mode_e comp_op,
+                      halo_rasterizer_enum halo_rasterizer)
+        : helper_(std::move(helper)),
+          placements_(helper_->get()),
+          opacity_(opacity),
+          comp_op_(comp_op),
+          halo_rasterizer_(halo_rasterizer)
+    {}
+};
+
+// Variant type for render thunks to allow us to re-render them
+// via a static visitor later.
+
+using render_thunk = util::variant<vector_marker_render_thunk,
+                                   raster_marker_render_thunk,
+                                   text_render_thunk>;
+using render_thunk_ptr = std::unique_ptr<render_thunk>;
+using render_thunk_list = std::list<render_thunk_ptr>;
+
+} // namespace mapnik
+
+#endif // MAPNIK_RENDERER_COMMON_RENDER_THUNK_HPP
diff --git a/include/mapnik/renderer_common/render_thunk_extractor.hpp b/include/mapnik/renderer_common/render_thunk_extractor.hpp
new file mode 100644
index 0000000..3ea86f6
--- /dev/null
+++ b/include/mapnik/renderer_common/render_thunk_extractor.hpp
@@ -0,0 +1,97 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#ifndef MAPNIK_RENDERER_COMMON_RENDER_THUNK_EXTRACTOR_HPP
+#define MAPNIK_RENDERER_COMMON_RENDER_THUNK_EXTRACTOR_HPP
+
+// mapnik
+#include <mapnik/renderer_common.hpp>
+#include <mapnik/renderer_common/render_thunk.hpp>
+#include <mapnik/symbolizer_base.hpp>
+#include <mapnik/util/noncopyable.hpp>
+
+namespace mapnik {
+
+// The approach here is to run the normal symbolizers, but in
+// a 'virtual' blank environment where the changes that they
+// make are recorded (the detector, the render_* calls).
+//
+// The recorded boxes are then used to lay out the items and
+// the offsets from old to new positions can be used to perform
+// the actual rendering calls.
+
+// This should allow us to re-use as much as possible of the
+// existing symbolizer layout and rendering code while still
+// being able to interpose our own decisions about whether
+// a collision has occurred or not.
+
+struct virtual_renderer_common : renderer_common
+{
+    explicit virtual_renderer_common(renderer_common const& other);
+};
+
+// Base class for extracting the bounding boxes associated with placing
+// a symbolizer at a fake, virtual point - not real geometry.
+//
+// The bounding boxes can be used for layout, and the thunks are
+// used to re-render at locations according to the group layout.
+
+struct render_thunk_extractor
+{
+    render_thunk_extractor(box2d<double> & box,
+                           render_thunk_list & thunks,
+                           feature_impl & feature,
+                           attributes const& vars,
+                           proj_transform const& prj_trans,
+                           virtual_renderer_common & common,
+                           box2d<double> const& clipping_extent);
+
+    void operator()(markers_symbolizer const& sym) const;
+
+    void operator()(text_symbolizer const& sym) const;
+
+    void operator()(shield_symbolizer const& sym) const;
+
+    template <typename T>
+    void operator()(T const& ) const
+    {
+        // TODO: warning if unimplemented?
+    }
+
+private:
+    void extract_text_thunk(text_render_thunk::helper_ptr && helper,
+                            text_symbolizer const& sym) const;
+
+    box2d<double> & box_;
+    render_thunk_list & thunks_;
+    feature_impl & feature_;
+    attributes const& vars_;
+    proj_transform const& prj_trans_;
+    virtual_renderer_common & common_;
+    box2d<double> clipping_extent_;
+
+    void update_box() const;
+};
+
+} // namespace mapnik
+
+#endif // MAPNIK_RENDERER_COMMON_RENDER_THUNK_EXTRACTOR_HPP
diff --git a/include/mapnik/simplify_converter.hpp b/include/mapnik/simplify_converter.hpp
index 77d3674..92f81b4 100644
--- a/include/mapnik/simplify_converter.hpp
+++ b/include/mapnik/simplify_converter.hpp
@@ -76,16 +76,16 @@ struct sleeve
 
     bool inside(vertex2d const& q)
     {
-        bool inside=false;
+        bool _inside=false;
 
         for (unsigned i=0;i<4;++i)
         {
             if ((((v[i+1].y <= q.y) && (q.y < v[i].y)) ||
                  ((v[i].y <= q.y) && (q.y < v[i+1].y))) &&
                 (q.x < (v[i].x - v[i+1].x) * (q.y - v[i+1].y)/ (v[i].y - v[i+1].y) + v[i+1].x))
-                inside=!inside;
+                _inside=!_inside;
         }
-        return inside;
+        return _inside;
     }
 };
 
@@ -592,11 +592,11 @@ private:
         }
 
         // Slurp the points back out that haven't been marked as discarded
-        for (vertex2d const& vertex : vertices)
+        for (vertex2d const& v : vertices)
         {
-            if (vertex.cmd != SEG_END)
+            if (v.cmd != SEG_END)
             {
-                vertices_.emplace_back(vertex);
+                vertices_.emplace_back(v);
             }
         }
 
diff --git a/include/mapnik/span_image_filter.h b/include/mapnik/span_image_filter.hpp
similarity index 99%
rename from include/mapnik/span_image_filter.h
rename to include/mapnik/span_image_filter.hpp
index cd84301..972558c 100644
--- a/include/mapnik/span_image_filter.h
+++ b/include/mapnik/span_image_filter.hpp
@@ -26,6 +26,8 @@
 #include "agg_span_image_filter_gray.h"
 #include "agg_span_image_filter_rgba.h"
 
+#include <boost/optional.hpp>
+
 #include <limits>
 
 namespace mapnik
diff --git a/include/mapnik/svg/svg_converter.hpp b/include/mapnik/svg/svg_converter.hpp
index 27fee31..50d7708 100644
--- a/include/mapnik/svg/svg_converter.hpp
+++ b/include/mapnik/svg/svg_converter.hpp
@@ -224,7 +224,15 @@ public:
         attr.stroke_color.opacity(a * s.opacity());
         attr.stroke_flag = true;
     }
-
+    void dash_array(dash_array && dash)
+    {
+        path_attributes& attr = cur_attr();
+        attr.dash = std::move(dash);
+    }
+    void dash_offset(double offset)
+    {
+        cur_attr().dash_offset = offset;
+    }
     void even_odd(bool flag)
     {
         cur_attr().even_odd_flag = flag;
diff --git a/include/mapnik/svg/svg_path_adapter.hpp b/include/mapnik/svg/svg_path_adapter.hpp
index beb8222..3b09af4 100644
--- a/include/mapnik/svg/svg_path_adapter.hpp
+++ b/include/mapnik/svg/svg_path_adapter.hpp
@@ -49,7 +49,7 @@ public:
     using self_type = path_adapter<VertexContainer>;
 
     //--------------------------------------------------------------------
-    path_adapter(VertexContainer & vertices) : vertices_(vertices), iterator_(0) {}
+    path_adapter(VertexContainer & _vertices) : vertices_(_vertices), iterator_(0) {}
     //void remove_all() { vertices_.remove_all(); iterator_ = 0; }
     //void free_all()   { vertices_.free_all();   iterator_ = 0; }
 
diff --git a/include/mapnik/svg/svg_path_attributes.hpp b/include/mapnik/svg/svg_path_attributes.hpp
index 7e180fd..d1b48aa 100644
--- a/include/mapnik/svg/svg_path_attributes.hpp
+++ b/include/mapnik/svg/svg_path_attributes.hpp
@@ -30,6 +30,7 @@
 
 // mapnik
 #include <mapnik/gradient.hpp>
+#include <mapnik/symbolizer_base.hpp> // dash_array
 
 namespace mapnik {
 namespace svg {
@@ -56,7 +57,8 @@ struct path_attributes
     bool         even_odd_flag;
     bool         visibility_flag;
     bool         display_flag;
-
+    dash_array   dash;
+    double       dash_offset;
     // Empty constructor
     path_attributes() :
         fill_gradient(),
@@ -78,9 +80,10 @@ struct path_attributes
         stroke_none(false),
         even_odd_flag(false),
         visibility_flag(true),
-        display_flag(true)
-    {
-    }
+        display_flag(true),
+        dash(),
+        dash_offset(0.0)
+    {}
 
     // Copy constructor
     path_attributes(path_attributes const& attr)
@@ -103,7 +106,9 @@ struct path_attributes
           stroke_none(attr.stroke_none),
           even_odd_flag(attr.even_odd_flag),
           visibility_flag(attr.visibility_flag),
-          display_flag(attr.display_flag)
+          display_flag(attr.display_flag),
+          dash(attr.dash),
+          dash_offset(attr.dash_offset)
     {}
     // Copy constructor with new index value
     path_attributes(path_attributes const& attr, unsigned idx)
@@ -126,7 +131,9 @@ struct path_attributes
           stroke_none(attr.stroke_none),
           even_odd_flag(attr.even_odd_flag),
           visibility_flag(attr.visibility_flag),
-          display_flag(attr.display_flag)
+          display_flag(attr.display_flag),
+          dash(attr.dash),
+          dash_offset(attr.dash_offset)
     {}
 };
 
diff --git a/include/mapnik/svg/svg_path_grammar.hpp b/include/mapnik/svg/svg_path_grammar.hpp
index 431bf0a..93d27f1 100644
--- a/include/mapnik/svg/svg_path_grammar.hpp
+++ b/include/mapnik/svg/svg_path_grammar.hpp
@@ -73,46 +73,29 @@ namespace mapnik { namespace svg {
                 >> *(-lit(',') >> coord [ line_to_(_1,_a) ] ); // *line_to
 
             H = (lit('H')[_a = false] | lit('h')[_a = true])
-                >> +double_[ hline_to_(_1,_a) ] ; // +hline_to
+                >> (double_[ hline_to_(_1,_a) ] % -lit(',')) ; // +hline_to
 
             V = (lit('V')[_a = false] | lit('v')[_a = true])
-                >> +double_ [ vline_to_(_1,_a) ]; // +vline_to
+                >> (double_ [ vline_to_(_1,_a) ] % -lit(',')); // +vline_to
 
             L = (lit('L')[_a = false] | lit('l')[_a = true])
-                >> +coord [ line_to_(_1,_a) ]; // +line_to
+                >> (coord [ line_to_(_1,_a) ] % -lit(',')); // +line_to
 
             C = (lit('C')[_a = false] | lit('c')[_a = true])
-                >> +(coord
-                     >> -lit(',')
-                     >> coord
-                     >> -lit(',')
-                     >> coord) [ curve4_(_1,_2,_3,_a) ]; // +curve4
+                >> ((coord >> -lit(',') >> coord >> -lit(',') >> coord) [ curve4_(_1,_2,_3,_a) ] % -lit(',')); // +curve4
 
             S = (lit('S')[_a = false] | lit('s')[_a = true])
-                >> +(coord
-                     >> -lit(',')
-                     >> coord) [ curve4_smooth_(_1,_2,_a) ]; // +curve4_smooth (smooth curveto)
+                >> ((coord >> -lit(',') >> coord) [ curve4_smooth_(_1,_2,_a) ] % -lit(',')); // +curve4_smooth (smooth curveto)
 
             Q = (lit('Q')[_a = false] | lit('q')[_a = true])
-                >> +(coord
-                     >> -lit(',')
-                     >> coord) [ curve3_(_1,_2,_a) ]; // +curve3 (quadratic-bezier-curveto)
+                >> ((coord >> -lit(',') >> coord) [ curve3_(_1,_2,_a) ] % -lit(',')); // +curve3 (quadratic-bezier-curveto)
 
             T = (lit('T')[_a = false] | lit('t')[_a = true])
-                >> +(coord ) [ curve3_smooth_(_1,_a) ]; // +curve3_smooth (smooth-quadratic-bezier-curveto)
+                >> ((coord ) [ curve3_smooth_(_1,_a) ] % -lit(',')); // +curve3_smooth (smooth-quadratic-bezier-curveto)
 
             A = (lit('A')[_a = false] | lit('a')[_a = true])
-                >> +(coord
-                     >> -lit(',')
-                     >> double_
-                     >> -lit(',')
-                     >> int_
-                     >> -lit(',')
-                     >> int_
-                     >> -lit(',')
-                     >> coord) [arc_to_(_1,_2,_3,_4,_5,_a)]; // arc_to;
-
-
+                >> ((coord >> -lit(',') >> double_ >> -lit(',')
+                     >> int_ >> -lit(',') >> int_ >> -lit(',') >> coord) [arc_to_(_1,_2,_3,_4,_5,_a)] % -lit(',')); // arc_to;
 
             Z = no_case[lit('z')] [close_()]; // close path
 
diff --git a/include/mapnik/svg/svg_renderer_agg.hpp b/include/mapnik/svg/svg_renderer_agg.hpp
index ad574cc..3c714cd 100644
--- a/include/mapnik/svg/svg_renderer_agg.hpp
+++ b/include/mapnik/svg/svg_renderer_agg.hpp
@@ -43,6 +43,7 @@
 #include "agg_conv_stroke.h"
 #include "agg_conv_contour.h"
 #include "agg_conv_curve.h"
+#include "agg_conv_dash.h"
 #include "agg_color_rgba.h"
 #include "agg_bounding_rect.h"
 #include "agg_rendering_buffer.h"
@@ -60,12 +61,10 @@
 namespace mapnik  {
 namespace svg {
 
+// Arbitrary linear gradient specified by two control points. Gradient
+// value is taken as the normalised distance along the line segment
+// represented by the two points.
 
-/**
- * Arbitrary linear gradient specified by two control points. Gradient
- * value is taken as the normalised distance along the line segment
- * represented by the two points.
- */
 class linear_gradient_from_segment
 {
 public:
@@ -105,19 +104,28 @@ template <typename VertexSource, typename AttributeSource, typename ScanlineRend
 class svg_renderer_agg : util::noncopyable
 {
 public:
-    using curved_type = agg::conv_curve<VertexSource>           ;
-    using curved_stroked_type = agg::conv_stroke<curved_type>           ;
+    using curved_type = agg::conv_curve<VertexSource>;
+    // stroke
+    using curved_stroked_type = agg::conv_stroke<curved_type>;
     using curved_stroked_trans_type = agg::conv_transform<curved_stroked_type>;
-    using curved_trans_type = agg::conv_transform<curved_type>        ;
-    using curved_trans_contour_type = agg::conv_contour<curved_trans_type>    ;
-    using renderer_base = agg::renderer_base<PixelFormat>         ;
-    using vertex_source_type = VertexSource                            ;
-    using attribute_source_type = AttributeSource                         ;
+    // stroke dash-array
+    using curved_dashed_type = agg::conv_dash<curved_type>;
+    using curved_dashed_stroked_type = agg::conv_stroke<curved_dashed_type>;
+    using curved_dashed_stroked_trans_type = agg::conv_transform<curved_dashed_stroked_type>;
+    // fill
+    using curved_trans_type = agg::conv_transform<curved_type>;
+    using curved_trans_contour_type = agg::conv_contour<curved_trans_type>;
+    // renderer
+    using renderer_base = agg::renderer_base<PixelFormat>;
+    using vertex_source_type = VertexSource;
+    using attribute_source_type = AttributeSource;
 
     svg_renderer_agg(VertexSource & source, AttributeSource const& attributes)
         : source_(source),
           curved_(source_),
+          curved_dashed_(curved_),
           curved_stroked_(curved_),
+          curved_dashed_stroked_(curved_dashed_),
           attributes_(attributes) {}
 
     template <typename Rasterizer, typename Scanline, typename Renderer>
@@ -191,11 +199,11 @@ public:
 
                 // scale everything up since agg turns things into integers a bit too soon
                 int scaleup=255;
-                radius*=scaleup;
-                x1*=scaleup;
-                y1*=scaleup;
-                x2*=scaleup;
-                y2*=scaleup;
+                radius *= scaleup;
+                x1 *= scaleup;
+                y1 *= scaleup;
+                x2 *= scaleup;
+                y2 *= scaleup;
 
                 transform.scale(scaleup,scaleup);
                 interpolator_type     span_interpolator(transform);
@@ -217,10 +225,10 @@ public:
                                                               color_func_type>;
                 // scale everything up since agg turns things into integers a bit too soon
                 int scaleup=255;
-                x1*=scaleup;
-                y1*=scaleup;
-                x2*=scaleup;
-                y2*=scaleup;
+                x1 *= scaleup;
+                y1 *= scaleup;
+                x2 *= scaleup;
+                y2 *= scaleup;
 
                 transform.scale(scaleup,scaleup);
 
@@ -247,10 +255,11 @@ public:
 
     {
         using namespace agg;
-
         trans_affine transform;
+
         curved_stroked_trans_type curved_stroked_trans(curved_stroked_,transform);
-        curved_trans_type         curved_trans(curved_,transform);
+        curved_dashed_stroked_trans_type curved_dashed_stroked_trans(curved_dashed_stroked_, transform);
+        curved_trans_type curved_trans(curved_,transform);
         curved_trans_contour_type curved_trans_contour(curved_trans);
 
         curved_trans_contour.auto_detect_orientation(true);
@@ -274,7 +283,6 @@ public:
             if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
             {
                 ras.reset();
-
                 // https://github.com/mapnik/mapnik/issues/1129
                 if(std::fabs(curved_trans_contour.width()) <= 1)
                 {
@@ -288,7 +296,8 @@ public:
 
                 if(attr.fill_gradient.get_gradient_type() != NO_GRADIENT)
                 {
-                    render_gradient(ras, sl, ren, attr.fill_gradient, transform, attr.fill_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
+                    render_gradient(ras, sl, ren, attr.fill_gradient, transform,
+                                    attr.fill_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
                 }
                 else
                 {
@@ -304,37 +313,79 @@ public:
 
             if (attr.stroke_flag || attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
             {
-                curved_stroked_.width(attr.stroke_width);
-                //m_curved_stroked.line_join((attr.line_join == miter_join) ? miter_join_round : attr.line_join);
-                curved_stroked_.line_join(attr.line_join);
-                curved_stroked_.line_cap(attr.line_cap);
-                curved_stroked_.miter_limit(attr.miter_limit);
-                curved_stroked_.inner_join(inner_round);
-                curved_stroked_.approximation_scale(scl);
-
-                // If the *visual* line width is considerable we
-                // turn on processing of curve cusps.
-                //---------------------
-                if(attr.stroke_width * scl > 1.0)
-                {
-                    curved_.angle_tolerance(0.2);
-                }
-                ras.reset();
-                ras.add_path(curved_stroked_trans, attr.index);
-
-                if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
+                if (attr.dash.size() > 0)
                 {
-                    render_gradient(ras, sl, ren, attr.stroke_gradient, transform, attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
+                    curved_dashed_stroked_.width(attr.stroke_width);
+                    curved_dashed_stroked_.line_join(attr.line_join);
+                    curved_dashed_stroked_.line_cap(attr.line_cap);
+                    curved_dashed_stroked_.miter_limit(attr.miter_limit);
+                    curved_dashed_stroked_.inner_join(inner_round);
+                    curved_dashed_stroked_.approximation_scale(scl);
+
+                    // If the *visual* line width is considerable we
+                    // turn on processing of curve cups.
+                    //---------------------
+                    if (attr.stroke_width * scl > 1.0)
+                    {
+                        curved_.angle_tolerance(0.2);
+                    }
+                    ras.reset();
+                    curved_dashed_.remove_all_dashes();
+                    for (auto d : attr.dash)
+                    {
+                        curved_dashed_.add_dash(std::get<0>(d),std::get<1>(d));
+                    }
+                    curved_dashed_.dash_start(attr.dash_offset);
+                    ras.add_path(curved_dashed_stroked_trans, attr.index);
+                    if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
+                    {
+                        render_gradient(ras, sl, ren, attr.stroke_gradient, transform,
+                                        attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
+                    }
+                    else
+                    {
+                        ras.filling_rule(fill_non_zero);
+                        color = attr.stroke_color;
+                        color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
+                        ScanlineRenderer ren_s(ren);
+                        color.premultiply();
+                        ren_s.color(color);
+                        render_scanlines(ras, sl, ren_s);
+                    }
                 }
                 else
                 {
-                    ras.filling_rule(fill_non_zero);
-                    color = attr.stroke_color;
-                    color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
-                    ScanlineRenderer ren_s(ren);
-                    color.premultiply();
-                    ren_s.color(color);
-                    render_scanlines(ras, sl, ren_s);
+                    curved_stroked_.width(attr.stroke_width);
+                    curved_stroked_.line_join(attr.line_join);
+                    curved_stroked_.line_cap(attr.line_cap);
+                    curved_stroked_.miter_limit(attr.miter_limit);
+                    curved_stroked_.inner_join(inner_round);
+                    curved_stroked_.approximation_scale(scl);
+
+                    // If the *visual* line width is considerable we
+                    // turn on processing of curve cups.
+                    //---------------------
+                    if (attr.stroke_width * scl > 1.0)
+                    {
+                        curved_.angle_tolerance(0.2);
+                    }
+                    ras.reset();
+                    ras.add_path(curved_stroked_trans, attr.index);
+                    if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT)
+                    {
+                        render_gradient(ras, sl, ren, attr.stroke_gradient, transform,
+                                        attr.stroke_opacity * attr.opacity * opacity, symbol_bbox, curved_trans, attr.index);
+                    }
+                    else
+                    {
+                        ras.filling_rule(fill_non_zero);
+                        color = attr.stroke_color;
+                        color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity);
+                        ScanlineRenderer ren_s(ren);
+                        color.premultiply();
+                        ren_s.color(color);
+                        render_scanlines(ras, sl, ren_s);
+                    }
                 }
             }
         }
@@ -409,7 +460,7 @@ public:
                 // If the *visual* line width is considerable we
                 // turn on processing of curve cusps.
                 //---------------------
-                if(attr.stroke_width * scl > 1.0)
+                if (attr.stroke_width * scl > 1.0)
                 {
                     curved_.angle_tolerance(0.2);
                 }
@@ -431,7 +482,9 @@ private:
 
     VertexSource &         source_;
     curved_type            curved_;
+    curved_dashed_type     curved_dashed_;
     curved_stroked_type    curved_stroked_;
+    curved_dashed_stroked_type curved_dashed_stroked_;
     AttributeSource const& attributes_;
 };
 
diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp
index 40283b9..39d650d 100644
--- a/include/mapnik/symbolizer.hpp
+++ b/include/mapnik/symbolizer.hpp
@@ -317,12 +317,8 @@ struct evaluate_expression_wrapper<mapnik::dash_array>
         mapnik::value_type val = util::apply_visitor(mapnik::evaluate<T2,mapnik::value_type,T3>(feature,vars), expr);
         if (val.is_null()) return dash_array();
         dash_array dash;
-        std::vector<double> buf;
         std::string str = val.to_string();
-        if (util::parse_dasharray(str,buf))
-        {
-            util::add_dashes(buf,dash);
-        }
+        util::parse_dasharray(str,dash);
         return dash;
     }
 };
diff --git a/include/mapnik/symbolizer_base.hpp b/include/mapnik/symbolizer_base.hpp
index f960752..8ac4462 100644
--- a/include/mapnik/symbolizer_base.hpp
+++ b/include/mapnik/symbolizer_base.hpp
@@ -98,10 +98,8 @@ using value_base_type = util::variant<value_bool,
 
 struct strict_value : value_base_type
 {
-    // default ctor
-    strict_value()
-        : value_base_type() {}
-    // copy ctor
+    strict_value() = default;
+
     strict_value(const char* val)
         : value_base_type(val) {}
 
@@ -109,13 +107,15 @@ struct strict_value : value_base_type
     strict_value(T const& obj)
         : value_base_type(typename detail::mapnik_value_type<T>::type(obj))
     {}
-    // move ctor
-    template <typename T>
-    strict_value(T && obj) noexcept
-        : value_base_type(std::move(obj)) {}
 
+    template <typename T>
+    strict_value(T && obj)
+        noexcept(std::is_nothrow_constructible<value_base_type, T && >::value)
+        : value_base_type(std::forward<T>(obj))
+    {}
 };
-}
+
+} // namespace detail
 
 struct MAPNIK_DECL symbolizer_base
 {
diff --git a/include/mapnik/symbolizer_default_values.hpp b/include/mapnik/symbolizer_default_values.hpp
index b70ea19..ea5e5c0 100644
--- a/include/mapnik/symbolizer_default_values.hpp
+++ b/include/mapnik/symbolizer_default_values.hpp
@@ -24,7 +24,12 @@
 #define MAPNIK_SYMBOLIZER_DEFAULT_VALUES_HPP
 
 #include <mapnik/symbolizer_keys.hpp>
+#include <mapnik/symbolizer_enumerations.hpp>
+#include <mapnik/image_compositing.hpp>
+#include <mapnik/image_scaling.hpp>
+#include <mapnik/simplify.hpp>
 #include <mapnik/color.hpp>
+#include <mapnik/value_types.hpp>
 #include <type_traits>
 
 
diff --git a/include/mapnik/symbolizer_utils.hpp b/include/mapnik/symbolizer_utils.hpp
index 358628b..cea9900 100644
--- a/include/mapnik/symbolizer_utils.hpp
+++ b/include/mapnik/symbolizer_utils.hpp
@@ -404,9 +404,8 @@ struct set_symbolizer_property_impl<Symbolizer,dash_array,false>
         boost::optional<std::string> str = node.get_opt_attr<std::string>(name);
         if (str)
         {
-            std::vector<double> buf;
             dash_array dash;
-            if (util::parse_dasharray(*str,buf) && util::add_dashes(buf,dash))
+            if (util::parse_dasharray(*str,dash))
             {
                 put(sym,key,dash);
             }
diff --git a/include/mapnik/text/face.hpp b/include/mapnik/text/face.hpp
index 60a37e6..87692ca 100644
--- a/include/mapnik/text/face.hpp
+++ b/include/mapnik/text/face.hpp
@@ -23,8 +23,8 @@
 #define MAPNIK_FACE_HPP
 
 //mapnik
-#include <mapnik/text/glyph_info.hpp>
 #include <mapnik/config.hpp>
+#include <mapnik/text/glyph_info.hpp>
 #include <mapnik/util/noncopyable.hpp>
 
 // freetype2
@@ -36,7 +36,6 @@ extern "C"
 }
 
 //stl
-#include <unordered_map>
 #include <memory>
 #include <string>
 #include <vector>
diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp
index e956edd..72314c3 100644
--- a/include/mapnik/text/glyph_positions.hpp
+++ b/include/mapnik/text/glyph_positions.hpp
@@ -19,8 +19,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  *****************************************************************************/
-#ifndef MAPNIK_PLACEMENTS_LIST_HPP
-#define MAPNIK_PLACEMENTS_LIST_HPP
+
+#ifndef MAPNIK_TEXT_GLYPH_POSITIONS_HPP
+#define MAPNIK_TEXT_GLYPH_POSITIONS_HPP
+
 //mapnik
 #include <mapnik/box2d.hpp>
 #include <mapnik/pixel_position.hpp>
@@ -79,7 +81,7 @@ public:
     pixel_position const& get_base_point() const;
     void set_base_point(pixel_position const& base_point);
     void set_marker(marker_info_ptr marker, pixel_position const& marker_pos);
-    marker_info_ptr get_marker() const;
+    marker_info_ptr const& get_marker() const;
     pixel_position const& marker_pos() const;
 private:
     std::vector<glyph_position> data_;
@@ -88,8 +90,46 @@ private:
     pixel_position marker_pos_;
     box2d<double> bbox_;
 };
+
 using glyph_positions_ptr = std::unique_ptr<glyph_positions>;
 
 using placements_list = std::list<glyph_positions_ptr>;
-}
-#endif // PLACEMENTS_LIST_HPP
+
+struct scoped_glyph_positions_offset
+{
+    scoped_glyph_positions_offset(glyph_positions & glyphs, pixel_position const& offset)
+        : glyphs_(glyphs)
+        , base_point_(glyphs.get_base_point())
+        , marker_pos_(glyphs.marker_pos())
+    {
+        // move the glyphs to the correct offset
+        glyphs_.set_base_point(base_point_ + offset);
+
+        // update the position of any marker
+        if (auto const& marker_info = glyphs_.get_marker())
+        {
+            glyphs_.set_marker(marker_info, marker_pos_ + offset);
+        }
+    }
+
+    ~scoped_glyph_positions_offset()
+    {
+        // set the base_point back how it was
+        glyphs_.set_base_point(base_point_);
+
+        // restore marker as well, if there is any
+        if (auto const& marker_info = glyphs_.get_marker())
+        {
+            glyphs_.set_marker(marker_info, marker_pos_);
+        }
+    }
+
+private:
+    glyph_positions & glyphs_;
+    pixel_position base_point_;
+    pixel_position marker_pos_;
+};
+
+} // namespace mapnik
+
+#endif // MAPNIK_TEXT_GLYPH_POSITIONS_HPP
diff --git a/include/mapnik/text/renderer.hpp b/include/mapnik/text/renderer.hpp
index 1b6bac1..a4cd76e 100644
--- a/include/mapnik/text/renderer.hpp
+++ b/include/mapnik/text/renderer.hpp
@@ -58,8 +58,35 @@ public:
                    composite_mode_e halo_comp_op = src_over,
                    double scale_factor = 1.0,
                    stroker_ptr stroker = stroker_ptr());
+
+    void set_comp_op(composite_mode_e comp_op)
+    {
+        comp_op_ = comp_op;
+    }
+
+    void set_halo_comp_op(composite_mode_e halo_comp_op)
+    {
+        halo_comp_op_ = halo_comp_op;
+    }
+
+    void set_halo_rasterizer(halo_rasterizer_e rasterizer)
+    {
+        rasterizer_ = rasterizer;
+    }
+
+    void set_scale_factor(double scale_factor)
+    {
+        scale_factor_ = scale_factor;
+    }
+
+    void set_stroker(stroker_ptr stroker)
+    {
+        stroker_ = stroker;
+    }
+
     void set_transform(agg::trans_affine const& transform);
     void set_halo_transform(agg::trans_affine const& halo_transform);
+
 protected:
     using glyph_vector = std::vector<glyph_t>;
     void prepare_glyphs(glyph_positions const& positions);
diff --git a/include/mapnik/transform_expression_grammar_impl.hpp b/include/mapnik/transform_expression_grammar_impl.hpp
index f72126e..4b8c2b9 100644
--- a/include/mapnik/transform_expression_grammar_impl.hpp
+++ b/include/mapnik/transform_expression_grammar_impl.hpp
@@ -45,7 +45,6 @@ transform_expression_grammar<Iterator>::transform_expression_grammar()
     qi::_3_type _3;
     qi::_6_type _6;
     qi::_val_type _val;
-    qi::char_type char_;
     qi::double_type double_;
     qi::lit_type lit;
     qi::no_case_type no_case;
@@ -57,8 +56,7 @@ transform_expression_grammar<Iterator>::transform_expression_grammar()
     // the order provided.  The individual transform definitions are
     // separated by whitespace and/or a comma.
 
-    qi::no_skip_type no_skip;
-    start = transform_ % no_skip[char_(", ")] ;
+    start = transform_ % *lit(',') ;
 
     transform_ = matrix | translate | scale | rotate | skewX | skewY ;
 
diff --git a/include/mapnik/transform_path_adapter.hpp b/include/mapnik/transform_path_adapter.hpp
index cb68e91..0821270 100644
--- a/include/mapnik/transform_path_adapter.hpp
+++ b/include/mapnik/transform_path_adapter.hpp
@@ -56,16 +56,16 @@ struct transform_path_adapter
     using size_type = std::size_t;
     using value_type = typename select_value_type<Geometry, void>::type;
 
-    transform_path_adapter(Transform const& t,
-                           Geometry & geom,
+    transform_path_adapter(Transform const& _t,
+                           Geometry & _geom,
                            proj_transform const& prj_trans)
-        : t_(&t),
-          geom_(geom),
+        : t_(&_t),
+          geom_(_geom),
           prj_trans_(&prj_trans)  {}
 
-    explicit transform_path_adapter(Geometry & geom)
+    explicit transform_path_adapter(Geometry & _geom)
         : t_(0),
-          geom_(geom),
+          geom_(_geom),
           prj_trans_(0)  {}
 
     void set_proj_trans(proj_transform const& prj_trans)
diff --git a/include/mapnik/transform_processor.hpp b/include/mapnik/transform_processor.hpp
index bec6bff..2e4bfa3 100644
--- a/include/mapnik/transform_processor.hpp
+++ b/include/mapnik/transform_processor.hpp
@@ -111,11 +111,11 @@ struct transform_processor
               vars_(v),
               scale_factor_(scale_factor) {}
 
-        void operator() (identity_node const&)
+        void operator() (identity_node const&) const
         {
         }
 
-        void operator() (matrix_node const& node)
+        void operator() (matrix_node const& node) const
         {
             double a = eval(node.a_); // scale x;
             double b = eval(node.b_);
@@ -126,21 +126,21 @@ struct transform_processor
             transform_.multiply(agg::trans_affine(a, b, c, d, e, f));
         }
 
-        void operator() (translate_node const& node)
+        void operator() (translate_node const& node) const
         {
             double tx = eval(node.tx_) * scale_factor_;
             double ty = eval(node.ty_, 0.0) * scale_factor_;
             transform_.translate(tx, ty);
         }
 
-        void operator() (scale_node const& node)
+        void operator() (scale_node const& node) const
         {
             double sx = eval(node.sx_);
             double sy = eval(node.sy_, sx);
             transform_.scale(sx, sy);
         }
 
-        void operator() (rotate_node const& node)
+        void operator() (rotate_node const& node) const
         {
             double angle = deg2rad(eval(node.angle_));
             double cx = eval(node.cx_, 0.0);
@@ -150,7 +150,7 @@ struct transform_processor
             transform_.translate(cx, cy);
         }
 
-        void operator() (skewX_node const& node)
+        void operator() (skewX_node const& node) const
         {
             auto degrees = std::fmod(eval(node.angle_),90.0);
             if (degrees < -89.0) degrees = -89.0;
@@ -159,7 +159,7 @@ struct transform_processor
             transform_.multiply(agg::trans_affine_skewing(angle, 0.0));
         }
 
-        void operator() (skewY_node const& node)
+        void operator() (skewY_node const& node) const
         {
             auto degrees = std::fmod(eval(node.angle_),90.0);
             if (degrees < -89.0) degrees = -89.0;
diff --git a/include/mapnik/util/dasharray_parser.hpp b/include/mapnik/util/dasharray_parser.hpp
index 0ef1ae8..7c5d267 100644
--- a/include/mapnik/util/dasharray_parser.hpp
+++ b/include/mapnik/util/dasharray_parser.hpp
@@ -23,32 +23,13 @@
 #ifndef MAPNIK_UTIL_DASHARRAY_PARSER_HPP
 #define MAPNIK_UTIL_DASHARRAY_PARSER_HPP
 
+#include <mapnik/symbolizer_base.hpp>
 #include <vector>
 #include <string>
 
 namespace mapnik { namespace util {
 
-bool parse_dasharray(std::string const& value, std::vector<double>& dasharray);
-
-inline bool add_dashes(std::vector<double> & buf, std::vector<std::pair<double,double> > & dash)
-{
-    if (buf.empty()) return false;
-    size_t size = buf.size();
-    if (size % 2 == 1)
-    {
-        buf.insert(buf.end(),buf.begin(),buf.end());
-    }
-    std::vector<double>::const_iterator pos = buf.begin();
-    while (pos != buf.end())
-    {
-        if (*pos > 0.0 || *(pos+1) > 0.0) // avoid both dash and gap eq 0.0
-        {
-            dash.emplace_back(*pos,*(pos + 1));
-        }
-        pos +=2;
-    }
-    return !buf.empty();
-}
+bool parse_dasharray(std::string const& value, dash_array & dash);
 
 }}
 
diff --git a/include/mapnik/util/noncopyable.hpp b/include/mapnik/util/noncopyable.hpp
index 1cd4a4a..7a6a9f8 100644
--- a/include/mapnik/util/noncopyable.hpp
+++ b/include/mapnik/util/noncopyable.hpp
@@ -28,6 +28,15 @@ namespace mapnik { namespace util {
 namespace non_copyable_
 {
 
+class movable
+{
+protected:
+    constexpr movable() = default;
+    ~movable() = default;
+    movable( movable && ) = default;
+    movable& operator=(movable && ) = default;
+};
+
 class noncopyable
 {
 protected:
@@ -36,8 +45,10 @@ protected:
     noncopyable( noncopyable const& ) = delete;
     noncopyable& operator=(noncopyable const& ) = delete;
 };
+
 }
 
+using movable = non_copyable_::movable;
 using noncopyable = non_copyable_::noncopyable;
 
 }}
diff --git a/include/mapnik/util/recursive_wrapper.hpp b/include/mapnik/util/recursive_wrapper.hpp
deleted file mode 100644
index 16a670f..0000000
--- a/include/mapnik/util/recursive_wrapper.hpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-
-#ifndef MAPNIK_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
-#define MAPNIK_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
-
-#include <utility>
-
-namespace mapnik { namespace util {
-
-template <typename T>
-class recursive_wrapper
-{
-public:
-    using type = T;
-private:
-
-    T* p_;
-
-public:
-
-    ~recursive_wrapper();
-    recursive_wrapper();
-
-    recursive_wrapper(recursive_wrapper const& operand);
-    recursive_wrapper(T const& operand);
-    recursive_wrapper(recursive_wrapper&& operand);
-    recursive_wrapper(T&& operand);
-
-private:
-
-    void assign(const T& rhs);
-
-public:
-
-    inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
-    {
-        assign( rhs.get() );
-        return *this;
-    }
-
-    inline recursive_wrapper& operator=(T const& rhs)
-    {
-        assign( rhs );
-        return *this;
-    }
-
-    inline void swap(recursive_wrapper& operand) noexcept
-    {
-        T* temp = operand.p_;
-        operand.p_ = p_;
-        p_ = temp;
-    }
-
-
-    recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
-    {
-        swap(rhs);
-        return *this;
-    }
-
-    recursive_wrapper& operator=(T&& rhs)
-    {
-        get() = std::move(rhs);
-        return *this;
-    }
-
-
-public:
-
-    T& get() { return *get_pointer(); }
-    const T& get() const { return *get_pointer(); }
-    T* get_pointer() { return p_; }
-    const T* get_pointer() const { return p_; }
-    operator T const&() const { return this->get(); }
-    operator T&() { return this->get(); }
-};
-
-template <typename T>
-recursive_wrapper<T>::~recursive_wrapper()
-{
-    delete p_;
-}
-
-template <typename T>
-recursive_wrapper<T>::recursive_wrapper()
-    : p_(new T)
-{
-}
-
-template <typename T>
-recursive_wrapper<T>::recursive_wrapper(recursive_wrapper const& operand)
-    : p_(new T( operand.get() ))
-{
-}
-
-template <typename T>
-recursive_wrapper<T>::recursive_wrapper(T const& operand)
-    : p_(new T(operand))
-{
-}
-
-template <typename T>
-recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand)
-    : p_(operand.p_)
-{
-    operand.p_ = nullptr;
-}
-
-template <typename T>
-recursive_wrapper<T>::recursive_wrapper(T&& operand)
-    : p_(new T( std::move(operand) ))
-{
-}
-
-template <typename T>
-void recursive_wrapper<T>::assign(const T& rhs)
-{
-    this->get() = rhs;
-}
-
-template <typename T>
-inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
-{
-    lhs.swap(rhs);
-}
-
-}}
-
-#endif // MAPNIK_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
diff --git a/include/mapnik/util/spatial_index.hpp b/include/mapnik/util/spatial_index.hpp
index 021bed5..d207dba 100644
--- a/include/mapnik/util/spatial_index.hpp
+++ b/include/mapnik/util/spatial_index.hpp
@@ -30,12 +30,23 @@
 #include <mapnik/geom_util.hpp>
 // stl
 #include <type_traits>
+#include <cstring>
 
 using mapnik::box2d;
 using mapnik::query;
 
 namespace mapnik { namespace util {
 
+
+template <typename InputStream>
+bool check_spatial_index(InputStream& in)
+{
+    char header[17]; // mapnik-index
+    std::memset(header, 0, 17);
+    in.read(header,16);
+    return (std::strncmp(header, "mapnik-index",12) == 0);
+}
+
 template <typename Value, typename Filter, typename InputStream>
 class spatial_index
 {
@@ -44,7 +55,6 @@ public:
     static box2d<double> bounding_box( InputStream& in );
     static void query_first_n(Filter const& filter, InputStream & in, std::vector<Value>& pos, std::size_t count);
 private:
-
     spatial_index();
     ~spatial_index();
     spatial_index(spatial_index const&);
@@ -59,6 +69,7 @@ template <typename Value, typename Filter, typename InputStream>
 box2d<double> spatial_index<Value, Filter, InputStream>::bounding_box(InputStream& in)
 {
     static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout type");
+    if (!check_spatial_index(in)) throw std::runtime_error("Invalid index file (regenerate with shapeindex)");
     in.seekg(16 + 4, std::ios::beg);
     box2d<double> box;
     read_envelope(in, box);
@@ -69,7 +80,8 @@ box2d<double> spatial_index<Value, Filter, InputStream>::bounding_box(InputStrea
 template <typename Value, typename Filter, typename InputStream>
 void spatial_index<Value, Filter, InputStream>::query(Filter const& filter, InputStream& in, std::vector<Value>& results)
 {
-    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout types");
+    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout type");
+    if (!check_spatial_index(in)) throw std::runtime_error("Invalid index file (regenerate with shapeindex)");
     in.seekg(16, std::ios::beg);
     query_node(filter, in, results);
 }
@@ -104,7 +116,8 @@ void spatial_index<Value, Filter, InputStream>::query_node(Filter const& filter,
 template <typename Value, typename Filter, typename InputStream>
 void spatial_index<Value, Filter, InputStream>::query_first_n(Filter const& filter, InputStream& in, std::vector<Value>& results, std::size_t count)
 {
-    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout types");
+    static_assert(std::is_standard_layout<Value>::value, "Values stored in quad-tree must be standard layout type");
+    if (!check_spatial_index(in)) throw std::runtime_error("Invalid index file (regenerate with shapeindex)");
     in.seekg(16, std::ios::beg);
     query_first_n_impl(filter, in, results, count);
 }
diff --git a/include/mapnik/util/variant.hpp b/include/mapnik/util/variant.hpp
index 65eb34f..61832a0 100644
--- a/include/mapnik/util/variant.hpp
+++ b/include/mapnik/util/variant.hpp
@@ -24,889 +24,63 @@
 #define MAPNIK_UTIL_VARIANT_HPP
 
 #include <mapnik/config.hpp>
-
-#include <utility> // swap
-#include <typeinfo>
-#include <type_traits>
-#include <stdexcept> // runtime_error
-#include <new> // operator new
-#include <cstddef> // size_t
-#include <iosfwd>
-#include <string>
-
-#include "recursive_wrapper.hpp"
-
 #include <boost/mpl/vector.hpp> // spirit support
-
-#ifdef _MSC_VER
- // http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
- #ifdef NDEBUG
-  #define VARIANT_INLINE __forceinline
- #else
-  #define VARIANT_INLINE __declspec(noinline)
- #endif
-#else
- #ifdef NDEBUG
-  #define VARIANT_INLINE inline __attribute__((always_inline))
- #else
-  #define VARIANT_INLINE __attribute__((noinline))
- #endif
-#endif
-
-#define VARIANT_MAJOR_VERSION 0
-#define VARIANT_MINOR_VERSION 1
-#define VARIANT_PATCH_VERSION 0
-
-// translates to 100
-#define VARIANT_VERSION (VARIANT_MAJOR_VERSION*100000) + (VARIANT_MINOR_VERSION*100) + (VARIANT_PATCH_VERSION)
+#include <mapbox/variant/variant.hpp>
 
 namespace mapnik { namespace util {
 
-// static visitor
-template <typename R = void>
-struct static_visitor
-{
-    using result_type = R;
-protected:
-    static_visitor() {}
-    ~static_visitor() {}
-};
-
-namespace detail {
-
-static constexpr std::size_t invalid_value = std::size_t(-1);
-
-template <typename T, typename...Types>
-struct direct_type;
-
-template <typename T, typename First, typename...Types>
-struct direct_type<T, First, Types...>
-{
-    static constexpr std::size_t index = std::is_same<T, First>::value
-        ? sizeof...(Types) : direct_type<T, Types...>::index;
-};
-
-template <typename T>
-struct direct_type<T>
-{
-    static constexpr std::size_t index = invalid_value;
-};
-
-template <typename T, typename...Types>
-struct convertible_type;
-
-template <typename T, typename First, typename...Types>
-struct convertible_type<T, First, Types...>
-{
-    static constexpr std::size_t index = std::is_convertible<T, First>::value
-        ? sizeof...(Types) : convertible_type<T, Types...>::index;
-};
-
-template <typename T>
-struct convertible_type<T>
-{
-    static constexpr std::size_t index = invalid_value;
-};
-
-template <typename T, typename...Types>
-struct value_traits
-{
-    static constexpr std::size_t direct_index = direct_type<T, Types...>::index;
-    static constexpr std::size_t index =
-        (direct_index == invalid_value) ? convertible_type<T, Types...>::index : direct_index;
-};
-
-// check if T is in Types...
-template <typename T, typename...Types>
-struct has_type;
-
-template <typename T, typename First, typename... Types>
-struct has_type<T, First, Types...>
-{
-    static constexpr bool value = std::is_same<T, First>::value
-        || has_type<T, Types...>::value;
-};
-
-template <typename T>
-struct has_type<T> : std::false_type {};
-//
-
-template <typename T, typename...Types>
-struct is_valid_type;
-
-template <typename T, typename First, typename... Types>
-struct is_valid_type<T, First, Types...>
-{
-    static constexpr bool value = std::is_convertible<T, First>::value
-        || is_valid_type<T, Types...>::value;
-};
-
-template <typename T>
-struct is_valid_type<T> : std::false_type {};
-
-template <std::size_t N, typename ... Types>
-struct select_type
-{
-    static_assert(N < sizeof...(Types), "index out of bounds");
-};
-
-template <std::size_t N, typename T, typename ... Types>
-struct select_type<N, T, Types...>
-{
-    using type = typename select_type<N - 1, Types...>::type;
-};
-
-template <typename T, typename ... Types>
-struct select_type<0, T, Types...>
-{
-    using type = T;
-};
-
-
-template <typename T, typename R = void>
-struct enable_if_type { using type = R; };
-
-template <typename F, typename V, typename Enable = void>
-struct result_of_unary_visit
-{
-    using type = typename std::result_of<F(V&)>::type;
-};
-
-template <typename F, typename V>
-struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type >
-{
-    using type = typename F::result_type;
-};
-
-template <typename F, typename V, class Enable = void>
-struct result_of_binary_visit
-{
-    using type = typename std::result_of<F(V&,V&)>::type;
-};
-
-
-template <typename F, typename V>
-struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type >
-{
-    using type = typename F::result_type;
-};
-
-
-} // namespace detail
-
-
-template <std::size_t arg1, std::size_t ... others>
-struct static_max;
-
-template <std::size_t arg>
-struct static_max<arg>
-{
-    static const std::size_t value = arg;
-};
-
-template <std::size_t arg1, std::size_t arg2, std::size_t ... others>
-struct static_max<arg1, arg2, others...>
-{
-    static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value :
-        static_max<arg2, others...>::value;
-};
-
-template<typename... Types>
-struct variant_helper;
-
-template<typename T, typename... Types>
-struct variant_helper<T, Types...>
-{
-    VARIANT_INLINE static void destroy(const std::size_t id, void * data)
-    {
-        if (id == sizeof...(Types))
-        {
-            reinterpret_cast<T*>(data)->~T();
-        }
-        else
-        {
-            variant_helper<Types...>::destroy(id, data);
-        }
-    }
-
-    VARIANT_INLINE static void move(const std::size_t old_id, void * old_value, void * new_value)
-    {
-        if (old_id == sizeof...(Types))
-        {
-            new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
-            //std::memcpy(new_value, old_value, sizeof(T));
-            // ^^  DANGER: this should only be considered for relocatable types e.g built-in types
-            // Also, I don't see any measurable performance benefit just yet
-        }
-        else
-        {
-            variant_helper<Types...>::move(old_id, old_value, new_value);
-        }
-    }
-
-    VARIANT_INLINE static void copy(const std::size_t old_id, const void * old_value, void * new_value)
-    {
-        if (old_id == sizeof...(Types))
-        {
-            new (new_value) T(*reinterpret_cast<const T*>(old_value));
-        }
-        else
-        {
-            variant_helper<Types...>::copy(old_id, old_value, new_value);
-        }
-    }
-    VARIANT_INLINE static void direct_swap(const std::size_t id, void * lhs, void * rhs)
-    {
-        using std::swap; //enable ADL
-        if (id == sizeof...(Types))
-        {
-            // both lhs and rhs hold T
-            swap(*reinterpret_cast<T*>(lhs), *reinterpret_cast<T*>(rhs));
-        }
-        else
-        {
-            variant_helper<Types...>::direct_swap(id, lhs, rhs);
-        }
-    }
-};
-
-template<> struct variant_helper<>
-{
-    VARIANT_INLINE static void destroy(const std::size_t, void *) {}
-    VARIANT_INLINE static void move(const std::size_t, void *, void *) {}
-    VARIANT_INLINE static void copy(const std::size_t, const void *, void *) {}
-    VARIANT_INLINE static void direct_swap(const std::size_t, void *, void *) {}
-};
-
-namespace detail {
-
-template <typename T>
-struct unwrapper
-{
-    T const& operator() (T const& obj) const
-    {
-        return obj;
-    }
-
-    T& operator() (T & obj) const
-    {
-        return obj;
-    }
-};
-
-
 template <typename T>
-struct unwrapper<recursive_wrapper<T>>
-{
-    auto operator() (recursive_wrapper<T> const& obj) const
-        -> typename recursive_wrapper<T>::type const&
-    {
-        return obj.get();
-    }
-
-    auto operator() (recursive_wrapper<T> & obj) const
-        -> typename recursive_wrapper<T>::type &
-    {
-        return obj.get();
-    }
-};
-
-template <typename T>
-struct unwrapper<std::reference_wrapper<T>>
-{
-    auto operator() (std::reference_wrapper<T> const& obj) const
-        -> typename recursive_wrapper<T>::type const&
-    {
-        return obj.get();
-    }
-};
-
-template <typename F, typename V, typename R, typename...Types>
-struct dispatcher;
-
-template <typename F, typename V, typename R, typename T, typename...Types>
-struct dispatcher<F, V, R, T, Types...>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const& v, F f)
-    {
-        if (v.get_type_index() == sizeof...(Types))
-        {
-            return f(unwrapper<T>()(v. template get<T>()));
-        }
-        else
-        {
-            return dispatcher<F, V, R, Types...>::apply_const(v, f);
-        }
-    }
-
-    VARIANT_INLINE static result_type apply(V & v, F f)
-    {
-        if (v.get_type_index() == sizeof...(Types))
-        {
-            return f(unwrapper<T>()(v. template get<T>()));
-        }
-        else
-        {
-            return dispatcher<F, V, R, Types...>::apply(v, f);
-        }
-    }
-};
-
-template<typename F, typename V, typename R>
-struct dispatcher<F, V, R>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const&, F)
-    {
-        throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
-    }
-
-    VARIANT_INLINE static result_type apply(V &, F)
-    {
-        throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
-    }
-};
-
-
-template <typename F, typename V, typename R, typename T, typename...Types>
-struct binary_dispatcher_rhs;
-
-template <typename F, typename V, typename R, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
-    {
-        if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
-        {
-            return f(unwrapper<T0>()(lhs. template get<T0>()),
-                     unwrapper<T1>()(rhs. template get<T1>()));
-        }
-        else
-        {
-            return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f);
-        }
-    }
-
-    VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
-    {
-        if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
-        {
-            return f(unwrapper<T0>()(lhs. template get<T0>()),
-                     unwrapper<T1>()(rhs. template get<T1>()));
-        }
-        else
-        {
-            return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, f);
-        }
-    }
-
-};
-
-template<typename F, typename V, typename R, typename T>
-struct binary_dispatcher_rhs<F, V, R, T>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-    VARIANT_INLINE static result_type apply(V &, V &, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-};
-
-
-template <typename F, typename V, typename R,  typename T, typename...Types>
-struct binary_dispatcher_lhs;
-
-template <typename F, typename V, typename R, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
-    {
-        if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
-        {
-            return f(lhs. template get<T1>(), rhs. template get<T0>());
-        }
-        else
-        {
-            return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f);
-        }
-    }
-
-    VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
-    {
-        if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
-        {
-            return f(lhs. template get<T1>(), rhs. template get<T0>());
-        }
-        else
-        {
-            return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, f);
-        }
-    }
-
-};
-
-template<typename F, typename V, typename R, typename T>
-struct binary_dispatcher_lhs<F, V, R, T>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-
-    VARIANT_INLINE static result_type apply(V &, V &, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-};
-
-template <typename F, typename V, typename R, typename...Types>
-struct binary_dispatcher;
-
-template <typename F, typename V, typename R, typename T, typename...Types>
-struct binary_dispatcher<F, V, R, T, Types...>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f)
-    {
-        if (v0.get_type_index() == sizeof...(Types))
-        {
-            if (v0.get_type_index() == v1.get_type_index())
-            {
-                return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
-            }
-            else
-            {
-                return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, f);
-            }
-        }
-        else if (v1.get_type_index() == sizeof...(Types))
-        {
-            return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, f);
-        }
-        return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f);
-    }
-
-    VARIANT_INLINE static result_type apply(V & v0, V & v1, F f)
-    {
-        if (v0.get_type_index() == sizeof...(Types))
-        {
-            if (v0.get_type_index() == v1.get_type_index())
-            {
-                return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
-            }
-            else
-            {
-                return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, f);
-            }
-        }
-        else if (v1.get_type_index() == sizeof...(Types))
-        {
-            return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, f);
-        }
-        return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f);
-    }
-};
+using recursive_wrapper = typename mapbox::util::recursive_wrapper<T>;
 
-template<typename F, typename V, typename R>
-struct binary_dispatcher<F, V, R>
-{
-    using result_type = R;
-    VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-
-    VARIANT_INLINE static result_type apply(V &, V &, F)
-    {
-        throw std::runtime_error("binary dispatch: FAIL");
-    }
-};
-
-// comparator functors
-struct equal_comp
-{
-    template <typename T>
-    bool operator()(T const& lhs, T const& rhs) const
-    {
-        return lhs == rhs;
-    }
-};
-
-struct less_comp
-{
-    template <typename T>
-    bool operator()(T const& lhs, T const& rhs) const
-    {
-        return lhs < rhs;
-    }
-};
-
-template <typename Variant, typename Comp>
-class comparer
-{
-public:
-    explicit comparer(Variant const& lhs) noexcept
-        : lhs_(lhs) {}
-    comparer& operator=(comparer const&) = delete;
-    // visitor
-    template<typename T>
-    bool operator()(T const& rhs_content) const
-    {
-        T const& lhs_content = lhs_.template get<T>();
-        return Comp()(lhs_content, rhs_content);
-    }
-private:
-    Variant const& lhs_;
-};
-
-
-} // namespace detail
-
-struct no_init {};
 
 template<typename... Types>
-class variant
+class variant : public mapbox::util::variant<Types...>
 {
-private:
-
-    static const std::size_t data_size = static_max<sizeof(Types)...>::value;
-    static const std::size_t data_align = static_max<alignof(Types)...>::value;
-
-    using data_type = typename std::aligned_storage<data_size, data_align>::type;
-    using helper_type = variant_helper<Types...>;
-
-    std::size_t type_index;
-    data_type data;
-
 public:
-
     // tell spirit that this is an adapted variant
     struct adapted_variant_tag;
     using types = boost::mpl::vector<Types...>;
-
-    VARIANT_INLINE variant()
-        : type_index(sizeof...(Types) - 1)
-    {
-        new (&data) typename detail::select_type<0, Types...>::type();
-    }
-
-    VARIANT_INLINE variant(no_init)
-        : type_index(detail::invalid_value) {}
-
-    // http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
-    template <typename T, class = typename std::enable_if<
-                          detail::is_valid_type<typename std::remove_reference<T>::type, Types...>::value>::type>
-    VARIANT_INLINE variant(T && val) noexcept
-        : type_index(detail::value_traits<typename std::remove_reference<T>::type, Types...>::index)
-    {
-        constexpr std::size_t index = sizeof...(Types) - detail::value_traits<typename std::remove_reference<T>::type, Types...>::index - 1;
-        using target_type = typename detail::select_type<index, Types...>::type;
-        new (&data) target_type(std::forward<T>(val)); // nothrow
-    }
-
-    VARIANT_INLINE variant(variant<Types...> const& old)
-        : type_index(old.type_index)
-    {
-        helper_type::copy(old.type_index, &old.data, &data);
-    }
-
-    VARIANT_INLINE variant(variant<Types...>&& old) noexcept
-        : type_index(old.type_index)
-    {
-        helper_type::move(old.type_index, &old.data, &data);
-    }
-
-private:
-    VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
-    {
-        helper_type::destroy(type_index, &data);
-        type_index = detail::invalid_value;
-        helper_type::copy(rhs.type_index, &rhs.data, &data);
-        type_index = rhs.type_index;
-    }
-
-    VARIANT_INLINE void move_assign(variant<Types...> && rhs)
-    {
-        helper_type::destroy(type_index, &data);
-        type_index = detail::invalid_value;
-        helper_type::move(rhs.type_index, &rhs.data, &data);
-        type_index = rhs.type_index;
-    }
-
-public:
-    VARIANT_INLINE variant<Types...>& operator=(variant<Types...> && other)
-    {
-        move_assign(std::move(other));
-        return *this;
-    }
-
-    VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
-    {
-        copy_assign(other);
-        return *this;
-    }
-
-    // conversions
-    // move-assign
-    template <typename T>
-    VARIANT_INLINE variant<Types...>& operator=(T && rhs) noexcept
-    {
-        variant<Types...> temp(std::forward<T>(rhs));
-        move_assign(std::move(temp));
-        return *this;
-    }
-
-    // copy-assign
-    template <typename T>
-    VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
-    {
-        variant<Types...> temp(rhs);
-        copy_assign(temp);
-        return *this;
-    }
-
-    template<typename T>
-    VARIANT_INLINE bool is() const
-    {
-        static_assert(detail::has_type<T, Types...>::value, "invalid type in T in `is<T>()` for this variant");
-        return (type_index == detail::direct_type<T, Types...>::index);
-    }
-
-    VARIANT_INLINE bool valid() const
-    {
-        return (type_index != detail::invalid_value);
-    }
-
-    template<typename T, typename... Args>
-    VARIANT_INLINE void set(Args&&... args)
-    {
-        helper_type::destroy(type_index, &data);
-        new (&data) T(std::forward<Args>(args)...);
-        type_index = detail::direct_type<T, Types...>::index;
-    }
-
-    // get<T>()
-    template<typename T, typename std::enable_if<
-                         (detail::direct_type<T, Types...>::index != detail::invalid_value)
-                         >::type* = nullptr>
-    VARIANT_INLINE T& get()
-    {
-        if (type_index == detail::direct_type<T, Types...>::index)
-        {
-            return *reinterpret_cast<T*>(&data);
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    template <typename T, typename std::enable_if<
-                          (detail::direct_type<T, Types...>::index != detail::invalid_value)
-                          >::type* = nullptr>
-    VARIANT_INLINE T const& get() const
-    {
-        if (type_index == detail::direct_type<T, Types...>::index)
-        {
-            return *reinterpret_cast<T const*>(&data);
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    // get<T>() - T stored as recursive_wrapper<T>
-    template <typename T, typename std::enable_if<
-                          (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)
-                          >::type* = nullptr>
-    VARIANT_INLINE T& get()
-    {
-        if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
-        {
-            return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    template <typename T,typename std::enable_if<
-                         (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)
-                         >::type* = nullptr>
-    VARIANT_INLINE T const& get() const
-    {
-        if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
-        {
-            return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    // get<T>() - T stored as std::reference_wrapper<T>
-    template <typename T, typename std::enable_if<
-                          (detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)
-                          >::type* = nullptr>
-    VARIANT_INLINE T& get()
-    {
-        if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
-        {
-            return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    template <typename T,typename std::enable_if<
-                         (detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)
-                         >::type* = nullptr>
-    VARIANT_INLINE T const& get() const
-    {
-        if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
-        {
-            return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
-        }
-        else
-        {
-            throw std::runtime_error("in get<T>()");
-        }
-    }
-
-    VARIANT_INLINE std::size_t get_type_index() const
-    {
-        return type_index;
-    }
-
-    VARIANT_INLINE int which() const noexcept
-    {
-        return static_cast<int>(sizeof...(Types) - type_index - 1);
-    }
-    // visitor
-    // unary
-    template <typename F, typename V>
-    auto VARIANT_INLINE
-    static visit(V const& v, F f)
-        -> decltype(detail::dispatcher<F, V,
-                    typename detail::result_of_unary_visit<F,
-                    typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v, f))
-    {
-        using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type;
-        return detail::dispatcher<F, V, R, Types...>::apply_const(v, f);
-    }
-    // non-const
-    template <typename F, typename V>
-    auto VARIANT_INLINE
-    static visit(V & v, F f)
-        -> decltype(detail::dispatcher<F, V,
-                    typename detail::result_of_unary_visit<F,
-                    typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v, f))
-    {
-        using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type;
-        return detail::dispatcher<F, V, R, Types...>::apply(v, f);
-    }
-
-    // binary
-    // const
-    template <typename F, typename V>
-    auto VARIANT_INLINE
-    static binary_visit(V const& v0, V const& v1, F f)
-        -> decltype(detail::binary_dispatcher<F, V,
-                    typename detail::result_of_binary_visit<F,
-                    typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v0, v1, f))
-    {
-        using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type;
-        return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f);
-    }
-    // non-const
-    template <typename F, typename V>
-    auto VARIANT_INLINE
-    static binary_visit(V& v0, V& v1, F f)
-        -> decltype(detail::binary_dispatcher<F, V,
-                    typename detail::result_of_binary_visit<F,
-                    typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v0, v1, f))
-    {
-        using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type;
-        return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f);
-    }
-
-    ~variant() noexcept
-    {
-        helper_type::destroy(type_index, &data);
-    }
-
-    // comparison operators
-    // equality
-    VARIANT_INLINE bool operator==(variant const& rhs) const
-    {
-        if (this->get_type_index() != rhs.get_type_index())
-            return false;
-        detail::comparer<variant, detail::equal_comp> visitor(*this);
-        return visit(rhs, visitor);
-    }
-    // less than
-    VARIANT_INLINE bool operator<(variant const& rhs) const
-    {
-        if (this->get_type_index() != rhs.get_type_index())
-        {
-            return this->get_type_index() < rhs.get_type_index();
-            // ^^ borrowed from boost::variant
-        }
-        detail::comparer<variant, detail::less_comp> visitor(*this);
-        return visit(rhs, visitor);
-    }
+    // inherit ctor's
+    using mapbox::util::variant<Types...>::variant;
 };
 
 // unary visitor interface
-
 // const
-template <typename V, typename F>
-auto VARIANT_INLINE static apply_visitor(F f, V const& v) -> decltype(V::visit(v, f))
+template <typename F, typename V>
+auto VARIANT_INLINE static apply_visitor(F && f, V const& v) -> decltype(V::visit(v, f))
 {
-    return V::visit(v, f);
+    return V::visit(v, std::forward<F>(f));
 }
 // non-const
-template <typename V, typename F>
-auto VARIANT_INLINE static apply_visitor(F f, V & v) -> decltype(V::visit(v, f))
+template <typename F, typename V>
+auto VARIANT_INLINE static apply_visitor(F && f, V & v) -> decltype(V::visit(v, f))
 {
-    return V::visit(v, f);
+    return V::visit(v, std::forward<F>(f));
 }
 
 // binary visitor interface
 // const
-template <typename V, typename F>
-auto VARIANT_INLINE static apply_visitor(F f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, f))
+template <typename F, typename V>
+auto VARIANT_INLINE static apply_visitor(F && f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, f))
 {
-    return V::binary_visit(v0, v1, f);
+    return V::binary_visit(v0, v1, std::forward<F>(f));
 }
+
 // non-const
-template <typename V, typename F>
-auto VARIANT_INLINE static apply_visitor(F f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, f))
+template <typename F, typename V>
+auto VARIANT_INLINE static apply_visitor(F && f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, f))
 {
-    return V::binary_visit(v0, v1, f);
+    return V::binary_visit(v0, v1, std::forward<F>(f));
 }
 
 // getter interface
-template<typename ResultType, typename T>
+template <typename ResultType, typename T>
 ResultType & get(T & var)
 {
     return var.template get<ResultType>();
 }
 
-template<typename ResultType, typename T>
+template <typename ResultType, typename T>
 ResultType const& get(T const& var)
 {
     return var.template get<ResultType>();
diff --git a/include/mapnik/util/variant_io.hpp b/include/mapnik/util/variant_io.hpp
index 8839a7e..02f0be5 100644
--- a/include/mapnik/util/variant_io.hpp
+++ b/include/mapnik/util/variant_io.hpp
@@ -65,7 +65,7 @@ VARIANT_INLINE std::basic_ostream<charT, traits>&
 operator<< (std::basic_ostream<charT, traits>& out, variant<Types...> const& rhs)
 {
     detail::printer<std::basic_ostream<charT, traits>> visitor(out);
-    apply_visitor(visitor, rhs);
+    mapnik::util::apply_visitor(visitor, rhs);
     return out;
 }
 
diff --git a/include/mapnik/value.hpp b/include/mapnik/value.hpp
index 995916b..c6a95d3 100644
--- a/include/mapnik/value.hpp
+++ b/include/mapnik/value.hpp
@@ -37,392 +37,316 @@
 #include <iosfwd>
 #include <cstddef>
 #include <new>
+#include <type_traits>
 
 // icu
 #include <unicode/unistr.h>
 #include <unicode/ustring.h>
 
-namespace mapnik  {
+namespace mapnik {
+
+using value_base = util::variant<value_null, value_bool, value_integer,value_double, value_unicode_string>;
 
 inline void to_utf8(mapnik::value_unicode_string const& input, std::string & target)
 {
-    if (input.isEmpty()) return;
-
-    const int BUF_SIZE = 256;
-    char buf [BUF_SIZE];
-    int len;
-
-    UErrorCode err = U_ZERO_ERROR;
-    u_strToUTF8(buf, BUF_SIZE, &len, input.getBuffer(), input.length(), &err);
-    if (err == U_BUFFER_OVERFLOW_ERROR || err == U_STRING_NOT_TERMINATED_WARNING )
-    {
-        const std::unique_ptr<char[]> buf_ptr(new char [len+1]);
-        err = U_ZERO_ERROR;
-        u_strToUTF8(buf_ptr.get() , len + 1, &len, input.getBuffer(), input.length(), &err);
-        target.assign(buf_ptr.get() , static_cast<std::size_t>(len));
-    }
-    else
-    {
-        target.assign(buf, static_cast<std::size_t>(len));
-    }
+    target.clear(); // mimic previous target.assign(...) semantics
+    input.toUTF8String(target); // this appends to target
 }
 
-using value_base = util::variant<value_null, value_bool, value_integer,value_double, value_unicode_string>;
+namespace detail {
 
-namespace impl {
+namespace {
+template <typename T, typename U>
+struct both_arithmetic : std::integral_constant<bool,
+                                             std::is_arithmetic<T>::value &&
+                                             std::is_arithmetic<U>::value > {};
 
 struct equals
 {
-    bool operator() (value_integer lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) == rhs;
-    }
-
-    bool operator() (value_bool lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) == rhs;
-    }
-
-    bool operator() (value_double lhs, value_integer rhs) const
-    {
-        return lhs == static_cast<value_double>(rhs);
-    }
-
-    bool operator() (value_bool lhs, value_integer rhs) const
-    {
-        return static_cast<value_integer>(lhs) == rhs;
-    }
-
-    bool operator() (value_integer lhs, value_bool rhs) const
-    {
-        return lhs == static_cast<value_integer>(rhs);
-    }
-
-    bool operator() (value_double lhs, value_bool rhs) const
-    {
-        return lhs == static_cast<value_double>(rhs);
-    }
-
-    bool operator() (value_unicode_string const& lhs,
-                     value_unicode_string const& rhs) const
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
-        return (lhs == rhs) ? true: false;
+        return false;
     }
 
     template <typename T>
-    bool operator() (T lhs, T rhs) const
+    static auto apply(T const& lhs, T const& rhs)
+        -> decltype(lhs == rhs)
     {
         return lhs == rhs;
     }
-
-    template <typename T, typename U>
-    bool operator() (T const&, U const&) const
-    {
-        return false;
-    }
 };
 
-struct not_equals
+struct not_equal
 {
-    template <typename T, typename U>
-    bool operator() (const T &, const U &) const
+    // back compatibility shim to equate empty string with null for != test
+    // https://github.com/mapnik/mapnik/issues/1859
+    // TODO - consider removing entire specialization at Mapnik 3.1.x
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
+        if (rhs.isEmpty()) return false;
         return true;
     }
 
     template <typename T>
-    bool operator() (T lhs, T rhs) const
+    static auto apply(T const& lhs, T const& rhs)
+        ->decltype(lhs != rhs)
     {
         return lhs != rhs;
     }
-
-    bool operator() (value_bool lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) != rhs;
-    }
-
-    bool operator() (value_bool lhs, value_integer rhs) const
-    {
-        return static_cast<value_integer>(lhs) != rhs;
-    }
-
-    bool operator() (value_integer lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) != rhs;
-    }
-
-    bool operator() (value_double lhs, value_integer rhs) const
-    {
-        return  lhs != static_cast<value_double>(rhs);
-    }
-
-    bool operator() (value_integer lhs, value_bool rhs) const
-    {
-        return lhs != static_cast<value_integer>(rhs);
-    }
-
-    bool operator() (value_double lhs, value_bool rhs) const
-    {
-        return  lhs != static_cast<value_double>(rhs);
-    }
-
-    bool operator() (value_unicode_string const& lhs,
-                     value_unicode_string const& rhs) const
-    {
-        return  (lhs != rhs)? true : false;
-    }
-
-    // back compatibility shim to equate empty string with null for != test
-    // https://github.com/mapnik/mapnik/issues/1859
-    // TODO - consider removing entire specialization at Mapnik 3.x
-    bool operator() (value_null, value_unicode_string const& rhs) const
-    {
-        if (rhs.isEmpty()) return false;
-        return true;
-    }
-
 };
 
 struct greater_than
 {
-    template <typename T, typename U>
-    bool operator()(const T &, const U &) const
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
         return false;
     }
 
     template <typename T>
-    bool operator()(T lhs, T rhs) const
+    static auto  apply(T const& lhs, T const& rhs)
+        ->decltype(lhs > rhs)
     {
         return lhs > rhs;
     }
+};
 
-    bool operator() (value_bool lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) > rhs;
-    }
-
-    bool operator() (value_double lhs, value_bool rhs) const
-    {
-        return lhs > static_cast<value_double>(rhs);
-    }
-
-    bool operator() (value_bool lhs, value_integer rhs) const
-    {
-        return static_cast<value_integer>(lhs) > rhs;
-    }
-
-    bool operator() (value_integer lhs, value_bool rhs) const
-    {
-        return lhs > static_cast<value_integer>(rhs);
-    }
-
-    bool operator() (value_integer lhs, value_double rhs) const
+struct greater_or_equal
+{
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
-        return static_cast<value_double>(lhs) > rhs;
+        return false;
     }
 
-    bool operator() (value_double lhs, value_integer rhs) const
+    template <typename T>
+    static auto apply(T const& lhs, T const& rhs)
+        ->decltype(lhs >= rhs)
     {
-        return static_cast<value_double>(lhs) > rhs;
+        return lhs >= rhs;
     }
+};
 
-    bool operator() (value_unicode_string const& lhs, value_unicode_string const& rhs) const
+struct less_than
+{
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
-        return  (lhs > rhs) ? true : false ;
+        return false;
     }
 
-    bool operator() (value_null, value_null) const
+    template <typename T>
+    static auto apply(T const& lhs, T const& rhs)
+         ->decltype(lhs < rhs)
     {
-        return false;
+        return lhs < rhs;
     }
 };
 
-struct greater_or_equal
+struct less_or_equal
 {
-    template <typename T, typename U>
-    bool operator()(const T &, const U &) const
+    static bool apply(value_null, value_unicode_string const& rhs)
     {
         return false;
     }
 
     template <typename T>
-    bool operator() (T lhs, T rhs) const
+    static auto apply(T const& lhs, T const& rhs)
+        ->decltype(lhs <= rhs)
     {
-        return lhs >= rhs;
+        return lhs <= rhs;
     }
+};
 
-    bool operator() (value_bool lhs, value_double rhs) const
-    {
-        return static_cast<value_double>(lhs) >= rhs;
-    }
+}
 
-    bool operator() (value_double lhs, value_bool rhs) const
+template <typename Op, bool default_result>
+struct comparison
+{
+    // special case for unicode_strings (fixes MSVC C4800)
+    bool operator() (value_unicode_string const& lhs,
+                     value_unicode_string const& rhs) const
     {
-        return lhs >= static_cast<value_double>(rhs);
+        return Op::apply(lhs, rhs) ? true : false;
     }
 
-    bool operator() (value_bool lhs, value_integer rhs) const
-    {
-        return static_cast<value_integer>(lhs) >= rhs;
-    }
+    //////////////////////////////////////////////////////////////////////////
+    // special case for unicode_string and value_null
+    //////////////////////////////////////////////////////////////////////////
 
-    bool operator() (value_integer lhs, value_bool rhs) const
+    bool operator() (value_null const& lhs, value_unicode_string const& rhs) const
     {
-        return lhs >= static_cast<value_integer>(rhs);
+        return Op::apply(lhs, rhs);
     }
+    //////////////////////////////////////////////////////////////////////////
 
-    bool operator() (value_integer lhs, value_double rhs) const
-    {
-        return  static_cast<value_double>(lhs) >= rhs;
-    }
 
-    bool operator() (value_double lhs, value_integer rhs) const
+    // same types
+    template <typename T>
+    bool operator() (T lhs, T rhs) const
     {
-        return  lhs >= static_cast<value_double>(rhs);
+        return Op::apply(lhs, rhs);
     }
 
-    bool operator() (value_unicode_string const& lhs, value_unicode_string const& rhs) const
+    // both types are arithmetic - promote to the common type
+    template <typename T, typename U, typename std::enable_if<both_arithmetic<T,U>::value, int>::type = 0>
+    bool operator() (T const& lhs, U const& rhs) const
     {
-        return ( lhs >= rhs ) ? true : false ;
+        using common_type = typename std::common_type<T,U>::type;
+        return Op::apply(static_cast<common_type>(lhs),static_cast<common_type>(rhs));
     }
 
-    bool operator() (value_null, value_null) const
+    //
+    template <typename T, typename U, typename std::enable_if<!both_arithmetic<T,U>::value, int>::type = 0>
+    bool operator() (T const& lhs, U const& rhs) const
     {
-        return false;
+        return default_result;
     }
 };
 
-struct less_than
+template <typename V>
+struct add
 {
-    template <typename T, typename U>
-    bool operator()(const T &, const U &) const
+    using value_type = V;
+    value_type operator() (value_unicode_string const& lhs ,
+                           value_unicode_string const& rhs ) const
     {
-        return false;
+        return lhs + rhs;
     }
 
-    template <typename T>
-    bool operator()(T lhs, T rhs) const
+    value_type operator() (value_null const& lhs ,
+                           value_null const& rhs) const
     {
-        return lhs < rhs;
+        return lhs;
     }
 
-    bool operator() (value_bool lhs, value_double rhs) const
+    value_type operator() (value_unicode_string const& lhs, value_null) const
     {
-        return static_cast<value_double>(lhs) < rhs;
+        return lhs;
     }
 
-    bool operator() (value_double lhs, value_bool rhs) const
+    value_type operator() (value_null, value_unicode_string const& rhs) const
     {
-        return lhs < static_cast<value_double>(rhs);
+        return rhs;
     }
 
-    bool operator() (value_bool lhs, value_integer rhs) const
+    template <typename L>
+    value_type operator() (L const& lhs, value_null const&) const
     {
-        return static_cast<value_integer>(lhs) < rhs;
+        return lhs;
     }
 
-    bool operator() (value_integer lhs, value_bool rhs) const
+    template <typename R>
+    value_type operator() (value_null const&, R const& rhs) const
     {
-        return lhs < static_cast<value_integer>(rhs);
+        return rhs;
     }
 
-    bool operator() (value_integer lhs, value_double rhs) const
+    template <typename L>
+    value_type operator() (L const& lhs , value_unicode_string const& rhs) const
     {
-        return  static_cast<value_double>(lhs) < rhs;
+        std::string val;
+        if (util::to_string(val,lhs))
+            return value_unicode_string(val.c_str()) + rhs;
+        return rhs;
     }
 
-    bool operator() (value_double lhs, value_integer rhs) const
+    template <typename R>
+    value_type operator() (value_unicode_string const& lhs, R const& rhs) const
     {
-        return  lhs < static_cast<value_double>(rhs);
+        std::string val;
+        if (util::to_string(val,rhs))
+            return lhs + value_unicode_string(val.c_str());
+        return lhs;
     }
 
-    bool operator()(value_unicode_string const& lhs,
-                    value_unicode_string const& rhs ) const
+    template <typename T1, typename T2>
+    value_type operator() (T1 const& lhs, T2 const& rhs) const
     {
-        return (lhs < rhs) ? true : false ;
+        return typename std::common_type<T1,T2>::type{ lhs + rhs };
     }
 
-    bool operator() (value_null, value_null) const
+    value_type operator() (value_bool lhs, value_bool rhs) const
     {
-        return false;
+        return value_integer(lhs + rhs);
     }
 };
 
-struct less_or_equal
+template <typename V>
+struct sub
 {
-    template <typename T, typename U>
-    bool operator()(const T &, const U &) const
+    using value_type = V;
+
+    value_type operator() (value_null const& lhs ,
+                           value_null const& rhs) const
     {
-        return false;
+        return lhs;
     }
 
-    template <typename T>
-    bool operator()(T lhs, T rhs) const
+    value_type operator() (value_null, value_unicode_string const& rhs) const
     {
-        return lhs <= rhs;
+        return rhs;
+    }
+    value_type operator() (value_unicode_string const& lhs, value_null) const
+    {
+        return lhs;
     }
 
-    bool operator() (value_bool lhs, value_double rhs) const
+    template <typename R>
+    value_type operator() (value_unicode_string const& lhs, R const&) const
     {
-        return static_cast<value_double>(lhs) <= rhs;
+        return lhs;
     }
 
-    bool operator() (value_double lhs, value_bool rhs) const
+    template <typename L>
+    value_type operator() (L const&, value_unicode_string const& rhs) const
     {
-        return lhs <= static_cast<value_double>(rhs);
+        return rhs;
     }
 
-    bool operator() (value_bool lhs, value_integer rhs) const
+    template <typename L>
+    value_type operator() (L const& lhs, value_null const&) const
     {
-        return static_cast<value_integer>(lhs) <= rhs;
+        return lhs;
     }
 
-    bool operator() (value_integer lhs, value_bool rhs) const
+    template <typename R>
+    value_type operator() (value_null const&, R const& rhs) const
     {
-        return lhs <= static_cast<value_integer>(rhs);
+        return rhs;
     }
 
-    bool operator() (value_integer lhs, value_double rhs) const
+    template <typename T>
+    value_type operator() (T lhs, T rhs) const
     {
-        return static_cast<value_double>(lhs) <= rhs;
+        return lhs - rhs ;
     }
 
-    bool operator() (value_double lhs, value_integer rhs) const
+    value_type operator() (value_unicode_string const&,
+                           value_unicode_string const&) const
     {
-        return lhs <= static_cast<value_double>(rhs);
+        return value_type();
     }
 
-    bool operator()(value_unicode_string const& lhs,
-                    value_unicode_string const& rhs ) const
+    template <typename T1, typename T2>
+    value_type operator() (T1 const& lhs, T2 const& rhs) const
     {
-        return (lhs <= rhs) ? true : false ;
+        return typename std::common_type<T1,T2>::type{ lhs - rhs };
     }
 
-    bool operator() (value_null, value_null) const
+
+    value_type operator() (value_bool lhs, value_bool rhs) const
     {
-        return false;
+        return value_integer(lhs - rhs);
     }
 };
 
 template <typename V>
-struct add
+struct mult
 {
     using value_type = V;
-    value_type operator() (value_unicode_string const& lhs ,
-                           value_unicode_string const& rhs ) const
-    {
-        return lhs + rhs;
-    }
-
-    value_type operator() (value_double lhs, value_integer rhs) const
-    {
-        return lhs + rhs;
-    }
 
-    value_type operator() (value_integer lhs, value_double rhs) const
+    value_type operator() (value_null const& lhs ,
+                           value_null const& rhs) const
     {
-        return lhs + rhs;
+        return lhs;
     }
 
     value_type operator() (value_unicode_string const& lhs, value_null) const
@@ -435,56 +359,34 @@ struct add
         return rhs;
     }
 
-    template <typename R>
-    value_type operator() (value_unicode_string const& lhs, R const& rhs) const
+    template <typename L>
+    value_type operator() (L const& lhs, value_null const&) const
     {
-        std::string val;
-        if (util::to_string(val,rhs))
-            return lhs + value_unicode_string(val.c_str());
         return lhs;
     }
 
-    template <typename L>
-    value_type operator() (L const& lhs , value_unicode_string const& rhs) const
+    template <typename R>
+    value_type operator() (value_null const&, R const& rhs) const
     {
-        std::string val;
-        if (util::to_string(val,lhs))
-            return value_unicode_string(val.c_str()) + rhs;
         return rhs;
     }
 
-    template <typename T>
-    value_type operator() (T lhs, T rhs) const
-    {
-        return lhs + rhs ;
-    }
-
-    template <typename T1, typename T2>
-    value_type operator() (T1 const& lhs, T2 const&) const
+    template <typename R>
+    value_type operator() (value_unicode_string const& lhs, R const&) const
     {
         return lhs;
     }
 
-    value_type operator() (value_bool lhs, value_bool rhs) const
-    {
-        return value_integer(lhs + rhs);
-    }
-};
-
-template <typename V>
-struct sub
-{
-    using value_type = V;
-    template <typename T1, typename T2>
-    value_type operator() (T1 const& lhs, T2 const&) const
+    template <typename L>
+    value_type operator() (L const&, value_unicode_string const& rhs) const
     {
-        return lhs;
+        return rhs;
     }
 
     template <typename T>
     value_type operator() (T lhs, T rhs) const
     {
-        return lhs - rhs ;
+        return lhs * rhs;
     }
 
     value_type operator() (value_unicode_string const&,
@@ -493,67 +395,49 @@ struct sub
         return value_type();
     }
 
-    value_type operator() (value_double lhs, value_integer rhs) const
-    {
-        return lhs - rhs;
-    }
-
-    value_type operator() (value_integer lhs, value_double rhs) const
+    template <typename T1, typename T2>
+    value_type operator() (T1 const& lhs, T2 const& rhs) const
     {
-        return lhs - rhs;
+        return typename std::common_type<T1,T2>::type{ lhs * rhs };
     }
 
     value_type operator() (value_bool lhs, value_bool rhs) const
     {
-        return value_integer(lhs - rhs);
+        return value_integer(lhs * rhs);
     }
 };
 
 template <typename V>
-struct mult
+struct div
 {
     using value_type = V;
-    template <typename T1, typename T2>
-    value_type operator() (T1 const& lhs , T2 const& ) const
-    {
-        return lhs;
-    }
-    template <typename T>
-    value_type operator() (T lhs, T rhs) const
-    {
-        return lhs * rhs;
-    }
 
-    value_type operator() (value_unicode_string const&,
-                           value_unicode_string const&) const
+    value_type operator() (value_null const& lhs ,
+                           value_null const& rhs) const
     {
-        return value_type();
+        return lhs;
     }
 
-    value_type operator() (value_double lhs, value_integer rhs) const
+    value_type operator() (value_unicode_string const& lhs, value_null) const
     {
-        return lhs * rhs;
+        return lhs;
     }
 
-    value_type operator() (value_integer lhs, value_double rhs) const
+    value_type operator() (value_null, value_unicode_string const& rhs) const
     {
-        return lhs * rhs;
+        return rhs;
     }
 
-    value_type operator() (value_bool, value_bool) const
+    template <typename L>
+    value_type operator() (L const& lhs, value_null const&) const
     {
-        return value_integer(0);
+        return lhs;
     }
-};
 
-template <typename V>
-struct div
-{
-    using value_type = V;
-    template <typename T1, typename T2>
-    value_type operator() (T1 const& lhs, T2 const&) const
+    template <typename R>
+    value_type operator() (value_null const&, R const& rhs) const
     {
-        return lhs;
+        return rhs;
     }
 
     template <typename T>
@@ -563,9 +447,10 @@ struct div
         return lhs / rhs;
     }
 
-    value_type operator() (value_bool, value_bool) const
+    value_type operator() (value_bool lhs, value_bool rhs) const
     {
-        return false;
+        if (rhs == 0) return lhs;
+        return value_integer(lhs) / value_integer(rhs);
     }
 
     value_type operator() (value_unicode_string const&,
@@ -574,16 +459,24 @@ struct div
         return value_type();
     }
 
-    value_type operator() (value_double lhs, value_integer rhs) const
+    template <typename R>
+    value_type operator() (value_unicode_string const& lhs, R const&) const
     {
-        if (rhs == 0) return value_type();
-        return lhs / rhs;
+        return lhs;
     }
 
-    value_type operator() (value_integer lhs, value_double rhs) const
+    template <typename L>
+    value_type operator() (L const&, value_unicode_string const& rhs) const
+    {
+        return rhs;
+    }
+
+    template <typename T1, typename T2>
+    value_type operator() (T1 const& lhs, T2 const& rhs) const
     {
         if (rhs == 0) return value_type();
-        return lhs / rhs;
+        using common_type = typename std::common_type<T1,T2>::type;
+        return common_type(lhs)/common_type(rhs);
     }
 };
 
@@ -591,10 +484,11 @@ template <typename V>
 struct mod
 {
     using value_type = V;
+
     template <typename T1, typename T2>
     value_type operator() (T1 const& lhs, T2 const&) const
     {
-        return lhs;
+       return lhs;
     }
 
     template <typename T>
@@ -716,7 +610,7 @@ struct convert<value_double>
     value_double operator() (value_unicode_string const& val) const
     {
         std::string utf8;
-        to_utf8(val,utf8);
+        val.toUTF8String(utf8);
         return operator()(utf8);
     }
 
@@ -755,7 +649,7 @@ struct convert<value_integer>
     value_integer operator() (value_unicode_string const& val) const
     {
         std::string utf8;
-        to_utf8(val,utf8);
+        val.toUTF8String(utf8);
         return operator()(utf8);
     }
 
@@ -780,7 +674,7 @@ struct convert<std::string>
     std::string operator() (value_unicode_string const& val) const
     {
         std::string utf8;
-        to_utf8(val,utf8);
+        val.toUTF8String(utf8);
         return utf8;
     }
 
@@ -798,11 +692,11 @@ struct convert<std::string>
 
     std::string operator() (value_null const&) const
     {
-        return "";
+        return std::string();
     }
 };
 
-struct to_unicode
+struct to_unicode_impl
 {
 
     template <typename T>
@@ -828,30 +722,66 @@ struct to_unicode
 
     value_unicode_string operator() (value_bool val) const
     {
-        if (val) {
-            std::string str("true");
-            return value_unicode_string(str.c_str());
-        }
-        std::string str("false");
-        return value_unicode_string(str.c_str());
+        return value_unicode_string(val ? "true" : "false");
     }
 
     value_unicode_string operator() (value_null const&) const
     {
-        return value_unicode_string("");
+        return value_unicode_string();
     }
 };
 
-struct to_expression_string
+struct to_expression_string_impl
 {
-    explicit to_expression_string(char quote = '\'')
+    struct EscapingByteSink : U_NAMESPACE_QUALIFIER ByteSink
+    {
+        std::string dest_;
+        char quote_;
+
+        explicit EscapingByteSink(char quote)
+            : quote_(quote)
+        {}
+
+        virtual void Append(const char* data, int32_t n)
+        {
+            // reserve enough room to hold the appended chunk and quotes;
+            // if another chunk follows, or any character needs escaping,
+            // the string will grow naturally
+            if (dest_.empty())
+            {
+                dest_.reserve(2 + static_cast<std::size_t>(n));
+                dest_.append(1, quote_);
+            }
+            else
+            {
+                dest_.reserve(dest_.size() + n + 1);
+            }
+
+            for (auto end = data + n; data < end; ++data)
+            {
+                if (*data == '\\' || *data == quote_)
+                    dest_.append(1, '\\');
+                dest_.append(1, *data);
+            }
+        }
+
+        virtual void Flush()
+        {
+            if (dest_.empty())
+                dest_.append(2, quote_);
+            else
+                dest_.append(1, quote_);
+        }
+    };
+
+    explicit to_expression_string_impl(char quote = '\'')
         : quote_(quote) {}
 
     std::string operator() (value_unicode_string const& val) const
     {
-        std::string utf8;
-        to_utf8(val,utf8);
-        return quote_ + utf8 + quote_;
+        EscapingByteSink sink(quote_);
+        val.toUTF8(sink);
+        return sink.dest_;
     }
 
     std::string operator() (value_integer val) const
@@ -870,7 +800,7 @@ struct to_expression_string
 
     std::string operator() (value_bool val) const
     {
-        return val ? "true":"false";
+        return val ? "true" : "false";
     }
 
     std::string operator() (value_null const&) const
@@ -881,7 +811,8 @@ struct to_expression_string
     const char quote_;
 };
 
-} // namespace impl
+
+} // namespace detail
 
 namespace value_adl_barrier {
 
@@ -894,56 +825,70 @@ class value : public value_base
     friend const value operator%(value const&,value const&);
 
 public:
-    value () noexcept //-- comment out for VC++11
-        : value_base(value_null()) {}
-
-    value (value const& other) = default;
-
-    value( value && other) noexcept = default;
-
-    template <typename T>
-    value ( T const& val)
-        : value_base(typename detail::mapnik_value_type<T>::type(val)) {}
-
-    template <typename T>
-    value ( T && val)
-        : value_base(typename detail::mapnik_value_type<T>::type(val)) {}
-
-    value & operator=( value const& other) = default;
+    value() = default;
+
+    // conversion from type T is done via a temporary of type U, which
+    // is determined by mapnik_value_type;
+    // enable_if< decay<T> != value > is necessary to avoid ill-formed
+    // recursion in noexcept specifier; and it also prevents using this
+    // constructor where implicitly-declared copy/move should be used
+    // (e.g. value(value&))
+    template <typename T,
+              typename U = typename std::enable_if<
+                                !detail::is_same_decay<T, value>::value,
+                                detail::mapnik_value_type_decay<T>
+                            >::type::type>
+    value(T && val)
+        noexcept(noexcept(U(std::forward<T>(val))) &&
+                 std::is_nothrow_constructible<value_base, U && >::value)
+        : value_base(U(std::forward<T>(val))) {}
+
+    template <typename T,
+              typename U = typename std::enable_if<
+                                !detail::is_same_decay<T, value>::value,
+                                detail::mapnik_value_type_decay<T>
+                            >::type::type>
+    value& operator=(T && val)
+        noexcept(noexcept(U(std::forward<T>(val))) &&
+                 std::is_nothrow_assignable<value_base, U && >::value)
+    {
+        value_base::operator=(U(std::forward<T>(val)));
+        return *this;
+    }
 
     bool operator==(value const& other) const
     {
-        return util::apply_visitor(impl::equals(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::equals, false>(), *this, other);
     }
 
     bool operator!=(value const& other) const
     {
-        return util::apply_visitor(impl::not_equals(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::not_equal, true>(), *this, other);
     }
 
     bool operator>(value const& other) const
     {
-        return util::apply_visitor(impl::greater_than(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::greater_than, false>(), *this, other);
     }
 
     bool operator>=(value const& other) const
     {
-        return util::apply_visitor(impl::greater_or_equal(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::greater_or_equal, false>(), *this, other);
     }
 
     bool operator<(value const& other) const
     {
-        return util::apply_visitor(impl::less_than(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::less_than, false>(), *this, other);
     }
 
     bool operator<=(value const& other) const
     {
-        return util::apply_visitor(impl::less_or_equal(),*this,other);
+        return util::apply_visitor(detail::comparison<detail::less_or_equal, false>(), *this, other);
     }
 
     value operator- () const
     {
-        return util::apply_visitor(impl::negate<value>(), *this);
+        return util::apply_visitor(detail::negate<value>(), *this);
     }
 
     bool is_null() const;
@@ -951,63 +896,63 @@ public:
     template <typename T>
     T convert() const
     {
-        return util::apply_visitor(impl::convert<T>(),*this);
+        return util::apply_visitor(detail::convert<T>(),*this);
     }
 
     value_bool to_bool() const
     {
-        return util::apply_visitor(impl::convert<value_bool>(),*this);
+        return util::apply_visitor(detail::convert<value_bool>(),*this);
     }
 
     std::string to_expression_string(char quote = '\'') const
     {
-        return util::apply_visitor(impl::to_expression_string(quote),*this);
+        return util::apply_visitor(detail::to_expression_string_impl(quote),*this);
     }
 
     std::string to_string() const
     {
-        return util::apply_visitor(impl::convert<std::string>(),*this);
+        return util::apply_visitor(detail::convert<std::string>(),*this);
     }
 
     value_unicode_string to_unicode() const
     {
-        return util::apply_visitor(impl::to_unicode(),*this);
+        return util::apply_visitor(detail::to_unicode_impl(),*this);
     }
 
     value_double to_double() const
     {
-        return util::apply_visitor(impl::convert<value_double>(),*this);
+        return util::apply_visitor(detail::convert<value_double>(),*this);
     }
 
     value_integer to_int() const
     {
-        return util::apply_visitor(impl::convert<value_integer>(),*this);
+        return util::apply_visitor(detail::convert<value_integer>(),*this);
     }
 };
 
 inline const value operator+(value const& p1,value const& p2)
 {
-    return value(util::apply_visitor(impl::add<value>(),p1, p2));
+    return value(util::apply_visitor(detail::add<value>(),p1, p2));
 }
 
 inline const value operator-(value const& p1,value const& p2)
 {
-    return value(util::apply_visitor(impl::sub<value>(),p1, p2));
+    return value(util::apply_visitor(detail::sub<value>(),p1, p2));
 }
 
 inline const value operator*(value const& p1,value const& p2)
 {
-    return value(util::apply_visitor(impl::mult<value>(),p1, p2));
+    return value(util::apply_visitor(detail::mult<value>(),p1, p2));
 }
 
 inline const value operator/(value const& p1,value const& p2)
 {
-    return value(util::apply_visitor(impl::div<value>(),p1, p2));
+    return value(util::apply_visitor(detail::div<value>(),p1, p2));
 }
 
 inline const value operator%(value const& p1,value const& p2)
 {
-    return value(util::apply_visitor(impl::mod<value>(),p1, p2));
+    return value(util::apply_visitor(detail::mod<value>(),p1, p2));
 }
 
 template <typename charT, typename traits>
diff --git a/include/mapnik/value_hash.hpp b/include/mapnik/value_hash.hpp
index 4c12bbe..c1f1d60 100644
--- a/include/mapnik/value_hash.hpp
+++ b/include/mapnik/value_hash.hpp
@@ -68,7 +68,7 @@ template <typename T>
 std::size_t mapnik_hash_value(T const& val)
 {
     std::size_t seed = util::apply_visitor(detail::value_hasher(), val);
-    detail::hash_combine(seed, val.get_type_index());
+    detail::hash_combine(seed, val.which());
     return seed;
 }
 
diff --git a/include/mapnik/value_types.hpp b/include/mapnik/value_types.hpp
index a250b49..85a015b 100644
--- a/include/mapnik/value_types.hpp
+++ b/include/mapnik/value_types.hpp
@@ -80,6 +80,26 @@ struct MAPNIK_DECL value_null
         return true;
     }
 
+    bool operator>(value_null) const
+    {
+        return false;
+    }
+
+    bool operator>=(value_null) const
+    {
+        return true;
+    }
+
+    bool operator<(value_null) const
+    {
+        return false;
+    }
+
+    bool operator<=(value_null) const
+    {
+        return true;
+    }
+
     template <typename T>
     value_null operator+ (T const&) const
     {
@@ -207,6 +227,13 @@ struct mapnik_value_type<T, typename std::enable_if<detail::is_value_unicode_str
     using type = mapnik::value_unicode_string const&;
 };
 
+template <typename T>
+using mapnik_value_type_decay = mapnik_value_type<typename std::decay<T>::type>;
+
+template <typename T, typename U>
+using is_same_decay = std::is_same<typename std::decay<T>::type,
+                                   typename std::decay<U>::type>;
+
 } // namespace detail
 
 } // namespace mapnik
diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp
index a68d8b5..a105bec 100644
--- a/include/mapnik/version.hpp
+++ b/include/mapnik/version.hpp
@@ -25,7 +25,7 @@
 
 #define MAPNIK_MAJOR_VERSION 3
 #define MAPNIK_MINOR_VERSION 0
-#define MAPNIK_PATCH_VERSION 9
+#define MAPNIK_PATCH_VERSION 10
 
 #define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
 
diff --git a/include/mapnik/vertex.hpp b/include/mapnik/vertex.hpp
index b05f362..2e1d378 100644
--- a/include/mapnik/vertex.hpp
+++ b/include/mapnik/vertex.hpp
@@ -61,29 +61,12 @@ struct vertex<T,2>
     vertex(coord_type x_,coord_type y_,unsigned cmd_)
         : x(x_),y(y_),cmd(cmd_) {}
 
-    vertex(vertex<T,2> && rhs) noexcept
-        : x(std::move(rhs.x)),
-          y(std::move(rhs.y)),
-          cmd(std::move(rhs.cmd)) {}
-
-    vertex(vertex<T,2> const& rhs)
-        : x(rhs.x),
-          y(rhs.y),
-          cmd(rhs.cmd) {}
-
     template <typename T2>
     vertex(vertex<T2,2> const& rhs)
         : x(coord_type(rhs.x)),
           y(coord_type(rhs.y)),
           cmd(rhs.cmd) {}
 
-
-    vertex<T,2>& operator=(vertex<T,2> rhs)
-    {
-        swap(rhs);
-        return *this;
-    }
-
     template <typename T2>
     vertex<T,2>& operator=(vertex<T2,2> const& rhs)
     {
diff --git a/include/mapnik/vertex_processor.hpp b/include/mapnik/vertex_processor.hpp
index a3b63c1..7cd2c40 100644
--- a/include/mapnik/vertex_processor.hpp
+++ b/include/mapnik/vertex_processor.hpp
@@ -35,38 +35,38 @@ struct vertex_processor
         : proc_(proc) {}
 
     template <typename Geometry>
-    void operator() (Geometry const& geom)
+    void operator() (Geometry const& geom) const
     {
         util::apply_visitor(*this, geom);
     }
-    void operator() (geometry_empty const&)
+    void operator() (geometry_empty const&) const
     {
         // no-op
     }
-    
+
     template <typename T1>
-    void operator() (point<T1> const& pt)
+    void operator() (point<T1> const& pt) const
     {
         point_vertex_adapter<T1> va(pt);
         proc_(va);
     }
 
     template <typename T1>
-    void operator() (line_string<T1> const& line)
+    void operator() (line_string<T1> const& line) const
     {
         line_string_vertex_adapter<T1> va(line);
         proc_(va);
     }
 
     template <typename T1>
-    void operator() (polygon<T1> const& poly)
+    void operator() (polygon<T1> const& poly) const
     {
         polygon_vertex_adapter<T1> va(poly);
         proc_(va);
     }
 
     template <typename T1>
-    void operator() (multi_point<T1> const& multi_pt)
+    void operator() (multi_point<T1> const& multi_pt) const
     {
         for (auto const& pt : multi_pt)
         {
@@ -76,7 +76,7 @@ struct vertex_processor
     }
 
     template <typename T1>
-    void operator() (multi_line_string<T1> const& multi_line)
+    void operator() (multi_line_string<T1> const& multi_line) const
     {
         for (auto const& line : multi_line)
         {
@@ -86,7 +86,7 @@ struct vertex_processor
     }
 
     template <typename T1>
-    void operator() (multi_polygon<T1> const& multi_poly)
+    void operator() (multi_polygon<T1> const& multi_poly) const
     {
         for ( auto const& poly : multi_poly)
         {
@@ -96,7 +96,7 @@ struct vertex_processor
     }
 
     template <typename T1>
-    void operator() (geometry_collection<T1> const& collection)
+    void operator() (geometry_collection<T1> const& collection) const
     {
         for (auto const& geom : collection)
         {
diff --git a/include/mapnik/view_transform.hpp b/include/mapnik/view_transform.hpp
index 2f5108c..74fa89c 100644
--- a/include/mapnik/view_transform.hpp
+++ b/include/mapnik/view_transform.hpp
@@ -44,15 +44,15 @@ private:
     int offset_;
 public:
 
-    view_transform(int width, int height, box2d<double> const& extent,
-                   double offset_x = 0.0, double offset_y = 0.0)
-        : width_(width),
-          height_(height),
-          extent_(extent),
+    view_transform(int _width, int _height, box2d<double> const& _extent,
+                   double _offset_x = 0.0, double _offset_y = 0.0)
+        : width_(_width),
+          height_(_height),
+          extent_(_extent),
           sx_(extent_.width() > 0 ? static_cast<double>(width_) / extent_.width() : 1.0),
           sy_(extent_.height() > 0 ? static_cast<double>(height_) / extent_.height() : 1.0),
-          offset_x_(offset_x),
-          offset_y_(offset_y),
+          offset_x_(_offset_x),
+          offset_y_(_offset_y),
           offset_(0) {}
 
     view_transform(view_transform const&) = default;
@@ -62,9 +62,9 @@ public:
         return offset_;
     }
 
-    inline void set_offset(int offset)
+    inline void set_offset(int _offset)
     {
-        offset_ = offset;
+        offset_ = _offset;
     }
 
     inline double offset_x() const
diff --git a/include/mapnik/warning_ignore.hpp b/include/mapnik/warning_ignore.hpp
index 186087b..a9856b9 100644
--- a/include/mapnik/warning_ignore.hpp
+++ b/include/mapnik/warning_ignore.hpp
@@ -22,9 +22,8 @@
 
 
 #pragma GCC diagnostic ignored "-Wunknown-pragmas" // clang+gcc
-#pragma GCC diagnostic ignored "-Wno-unknown-pragmas" // clang
-#pragma GCC diagnostic ignored "-Wno-pragmas" // gcc
-#pragma GCC diagnostic ignored "-Wno-unsequenced"
+#pragma GCC diagnostic ignored "-Wpragmas" // gcc
+#pragma GCC diagnostic ignored "-Wunsequenced"
 #pragma GCC diagnostic ignored "-Wunused-function"
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #pragma GCC diagnostic ignored "-Wredeclared-class-member"
diff --git a/include/mapnik/wkb.hpp b/include/mapnik/wkb.hpp
index ac90df7..53f1e85 100644
--- a/include/mapnik/wkb.hpp
+++ b/include/mapnik/wkb.hpp
@@ -24,6 +24,7 @@
 #define MAPNIK_WKB_HPP
 
 // mapnik
+#include <mapnik/config.hpp>
 #include <mapnik/geometry.hpp>
 #include <mapnik/util/noncopyable.hpp>
 
diff --git a/localize.sh b/localize.sh
index d24bdcb..4b69c2f 100755
--- a/localize.sh
+++ b/localize.sh
@@ -10,6 +10,7 @@ fi
 export PATH=$(pwd)/utils/mapnik-render/:${PATH}
 export PATH=$(pwd)/utils/mapnik-index/:${PATH}
 export PATH=$(pwd)/utils/mapnik-config/:${PATH}
+export PATH=$(pwd)/utils/shapeindex/:${PATH}
 
 # mapnik-settings.env is an optional file to store
 # environment variables that should be used before
diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp
index d0dbf06..19aa16c 100644
--- a/plugins/input/csv/csv_datasource.cpp
+++ b/plugins/input/csv/csv_datasource.cpp
@@ -179,21 +179,21 @@ void csv_datasource::parse_csv(T & stream)
     char newline;
     bool has_newline;
     char detected_quote;
-    std::tie(newline, has_newline, detected_quote) = detail::autodect_newline_and_quote(stream, file_length);
+    char detected_separator;
+    std::tie(newline, has_newline, detected_separator, detected_quote) = detail::autodect_csv_flavour(stream, file_length);
     if (quote_ == 0) quote_ = detected_quote;
+    if (separator_ == 0) separator_ = detected_separator;
+
     // set back to start
+    MAPNIK_LOG_DEBUG(csv) << "csv_datasource: separator: '" << separator_
+                          << "' quote: '" << quote_ << "'";
+
+    // rewind stream
     stream.seekg(0, std::ios::beg);
+    //
     std::string csv_line;
     csv_utils::getline_csv(stream, csv_line, newline, quote_);
-    if (separator_ == 0)
-    {
-        separator_ = detail::detect_separator(csv_line);
-    }
-
-    MAPNIK_LOG_DEBUG(csv) << "csv_datasource: separator: '" << separator_
-                          << "' quote: '" << quote_ << "'";
     stream.seekg(0, std::ios::beg);
-
     int line_number = 0;
     if (!manual_headers_.empty())
     {
@@ -321,14 +321,23 @@ void csv_datasource::parse_csv(T & stream)
 
         try
         {
-            auto values = csv_utils::parse_line(csv_line, separator_, quote_);
+            auto const* line_start = csv_line.data();
+            auto const* line_end = line_start + csv_line.size();
+            auto values = csv_utils::parse_line(line_start, line_end, separator_, quote_, num_headers);
             unsigned num_fields = values.size();
-            if (num_fields > num_headers || num_fields < num_headers)
+            if (num_fields != num_headers)
             {
                 std::ostringstream s;
-                s << "CSV Plugin: # of columns("
-                  << num_fields << ") > # of headers("
-                  << num_headers << ") parsed for row " << line_number;
+                s << "CSV Plugin: # of columns(" << num_fields << ")";
+                if (num_fields > num_headers)
+                {
+                    s << " > ";
+                }
+                else
+                {
+                    s << " < ";
+                }
+                s << "# of headers(" << num_headers << ") parsed";
                 throw mapnik::datasource_exception(s.str());
             }
 
diff --git a/plugins/input/csv/csv_inline_featureset.cpp b/plugins/input/csv/csv_inline_featureset.cpp
index b0fe420..195574b 100644
--- a/plugins/input/csv/csv_inline_featureset.cpp
+++ b/plugins/input/csv/csv_inline_featureset.cpp
@@ -54,7 +54,9 @@ csv_inline_featureset::~csv_inline_featureset() {}
 
 mapnik::feature_ptr csv_inline_featureset::parse_feature(std::string const& str)
 {
-    auto values = csv_utils::parse_line(str, separator_, quote_);
+    auto const* start = str.data();
+    auto const* end = start + str.size();
+    auto values = csv_utils::parse_line(start, end, separator_, quote_, headers_.size());
     auto geom = detail::extract_geometry(values, locator_);
     if (!geom.is<mapnik::geometry::geometry_empty>())
     {
diff --git a/plugins/input/csv/csv_utils.hpp b/plugins/input/csv/csv_utils.hpp
index b820773..4171d45 100644
--- a/plugins/input/csv/csv_utils.hpp
+++ b/plugins/input/csv/csv_utils.hpp
@@ -47,7 +47,7 @@ namespace csv_utils
 {
 
 static const mapnik::csv_line_grammar<char const*> line_g;
-static const mapnik::csv_white_space_skipper<char const*> skipper;
+static const mapnik::csv_white_space_skipper skipper{};
 
 template <typename Iterator>
 static mapnik::csv_line parse_line(Iterator start, Iterator end, char separator, char quote, std::size_t num_columns)
@@ -136,54 +136,28 @@ std::size_t file_length(T & stream)
     return stream.tellg();
 }
 
-static inline char detect_separator(std::string const& str)
-{
-    char separator = ','; // default
-    int num_commas = std::count(str.begin(), str.end(), ',');
-    // detect tabs
-    int num_tabs = std::count(str.begin(), str.end(), '\t');
-    if (num_tabs > 0)
-    {
-        if (num_tabs > num_commas)
-        {
-            separator = '\t';
-            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
-        }
-    }
-    else // pipes
-    {
-        int num_pipes = std::count(str.begin(), str.end(), '|');
-        if (num_pipes > num_commas)
-        {
-            separator = '|';
-            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
-        }
-        else // semicolons
-        {
-            int num_semicolons = std::count(str.begin(), str.end(), ';');
-            if (num_semicolons > num_commas)
-            {
-                separator = ';';
-                MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
-            }
-        }
-    }
-    return separator;
-}
-
 template <typename T>
-std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t file_length)
+std::tuple<char, bool, char, char> autodect_csv_flavour(T & stream, std::size_t file_length)
 {
-    // autodetect newlines
-    char newline = '\n';
+    // autodetect newlines/quotes/separators
+    char newline = '\n'; // default
     bool has_newline = false;
-    bool has_quote = false;
-    char quote = '"';
+    bool has_single_quote = false;
+    char quote = '"'; // default
+    char separator = ','; // default
+    // local counters
+    int num_commas = 0;
+    int num_tabs = 0;
+    int num_pipes = 0;
+    int num_semicolons = 0;
+
     static std::size_t const max_size = 4000;
     std::size_t size = std::min(file_length, max_size);
-    for (std::size_t lidx = 0; lidx < size; ++lidx)
+    std::vector<char> buffer;
+    buffer.resize(size);
+    stream.read(buffer.data(), size);
+    for (auto c : buffer)
     {
-        char c = static_cast<char>(stream.get());
         switch (c)
         {
         case '\r':
@@ -194,18 +168,76 @@ std::tuple<char,bool,char> autodect_newline_and_quote(T & stream, std::size_t fi
             has_newline = true;
             break;
         case '\'':
-        case '"':
-            if (!has_quote)
+            if (!has_single_quote)
             {
                 quote = c;
-                has_quote = true;
+                has_single_quote = true;
             }
             break;
+        case ',':
+            if (!has_newline) ++num_commas;
+            break;
+        case '\t':
+            if (!has_newline) ++num_tabs;
+            break;
+        case '|':
+            if (!has_newline) ++num_pipes;
+            break;
+        case ';':
+           if (!has_newline) ++num_semicolons;
+            break;
+        }
+    }
+    // detect separator
+    if (num_tabs > 0 && num_tabs > num_commas)
+    {
+        separator = '\t';
+        MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected tab separator";
+    }
+    else // pipes/semicolons
+    {
+        if (num_pipes > num_commas)
+        {
+            separator = '|';
+            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected '|' separator";
+        }
+        else  if (num_semicolons > num_commas)
+        {
+            separator = ';';
+            MAPNIK_LOG_DEBUG(csv) << "csv_datasource: auto detected ';' separator";
         }
     }
-    return std::make_tuple(newline, has_newline, quote);
-}
 
+    if (has_newline && has_single_quote)
+    {
+        std::istringstream ss(std::string(buffer.begin(), buffer.end()));
+        std::size_t num_columns = 0;
+        for (std::string line; csv_utils::getline_csv(ss, line, newline, quote); )
+        {
+            if (size < file_length && ss.eof())
+            {
+                // we can't be sure last line
+                // is not truncated so skip it
+                break;
+            }
+            if (line.size() == 0) continue; // empty lines are not interesting
+            auto num_quotes = std::count(line.begin(), line.end(), quote);
+            if (num_quotes % 2 != 0)
+            {
+                quote = '"';
+                break;
+            }
+            auto columns = csv_utils::parse_line(line, separator, quote);
+            if (num_columns > 0 && num_columns != columns.size())
+            {
+                quote = '"';
+                break;
+            }
+            num_columns = columns.size();
+        }
+    }
+    return std::make_tuple(newline, has_newline, separator, quote);
+}
 
 struct geometry_column_locator
 {
diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp
index dbb80f9..d63e243 100644
--- a/plugins/input/geojson/geojson_datasource.cpp
+++ b/plugins/input/geojson/geojson_datasource.cpp
@@ -200,6 +200,20 @@ const mapnik::json::feature_grammar<base_iterator_type, mapnik::feature_impl> ge
 const mapnik::json::extract_bounding_box_grammar<base_iterator_type> geojson_datasource_static_bbox_grammar;
 }
 
+void geojson_datasource::initialise_descriptor(mapnik::feature_ptr const& feature)
+{
+    for ( auto const& kv : *feature)
+    {
+        auto const& name = std::get<0>(kv);
+        if (!desc_.has_name(name))
+        {
+            desc_.add_descriptor(mapnik::attribute_descriptor(name,
+                                                              mapnik::util::apply_visitor(attr_value_converter(),
+                                                                                          std::get<1>(kv))));
+        }
+    }
+}
+
 void geojson_datasource::initialise_disk_index(std::string const& filename)
 {
     // read extent
@@ -213,7 +227,7 @@ void geojson_datasource::initialise_disk_index(std::string const& filename)
     std::vector<value_type> positions;
     mapnik::util::spatial_index<value_type,
                                 mapnik::filter_in_box,
-                                std::ifstream>::query_first_n(filter, index, positions, 5);
+                                std::ifstream>::query_first_n(filter, index, positions, num_features_to_query_);
 
     mapnik::util::file file(filename_);
     if (!file.open()) throw mapnik::datasource_exception("GeoJSON Plugin: could not open: '" + filename_ + "'");
@@ -236,16 +250,7 @@ void geojson_datasource::initialise_disk_index(std::string const& filename)
         {
             throw std::runtime_error("Failed to parse geojson feature");
         }
-        for ( auto const& kv : *feature)
-        {
-            auto const& name = std::get<0>(kv);
-            if (!desc_.has_name(name))
-            {
-                desc_.add_descriptor(mapnik::attribute_descriptor(name,
-                                                                  mapnik::util::apply_visitor(attr_value_converter(),
-                                                                                              std::get<1>(kv))));
-            }
-        }
+        initialise_descriptor(feature);
     }
 }
 
@@ -285,20 +290,18 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end)
                 if (geometry_index == 0)
                 {
                     extent_ = box;
-                    for ( auto const& kv : *f)
-                    {
-                        desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv),
-                                                                          mapnik::util::apply_visitor(attr_value_converter(),
-                                                                                                      std::get<1>(kv))));
-                    }
                 }
                 else
                 {
                     extent_.expand_to_include(box);
                 }
                 values.emplace_back(box, std::make_pair(geometry_index,0));
+
+            }
+            if (geometry_index++ < num_features_to_query_)
+            {
+                initialise_descriptor(f);
             }
-            ++geometry_index;
         }
         // packing algorithm
         tree_ = std::make_unique<spatial_index_type>(values);
@@ -308,14 +311,16 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end)
         // bulk insert initialise r-tree
         tree_ = std::make_unique<spatial_index_type>(boxes);
         // calculate total extent
+        std::size_t feature_count = 0;
         for (auto const& item : boxes)
         {
             auto const& box = std::get<0>(item);
             auto const& geometry_index = std::get<1>(item);
-            if (!extent_.valid())
+            if (!extent_.valid()) extent_ = box;
+            else extent_.expand_to_include(box);
+            if (feature_count++ < num_features_to_query_)
             {
-                extent_ = box;
-                // parse first feature to extract attributes schema.
+                // parse first N features to extract attributes schema.
                 // NOTE: this doesn't yield correct answer for geoJSON in general, just an indication
                 Iterator itr2 = start + geometry_index.first;
                 Iterator end2 = itr2 + geometry_index.second;
@@ -327,16 +332,8 @@ void geojson_datasource::initialise_index(Iterator start, Iterator end)
                 {
                     throw std::runtime_error("Failed to parse geojson feature");
                 }
-                for ( auto const& kv : *feature)
-                {
-                    desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv),
-                                                                      mapnik::util::apply_visitor(attr_value_converter(),
-                                                                                                  std::get<1>(kv))));
-                }
-            }
-            else
-            {
-                extent_.expand_to_include(box);
+
+                initialise_descriptor(feature);
             }
         }
     }
@@ -399,12 +396,7 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end)
             if (geometry_index == 0)
             {
                 extent_ = box;
-                for ( auto const& kv : *f)
-                {
-                    desc_.add_descriptor(mapnik::attribute_descriptor(std::get<0>(kv),
-                                                                      mapnik::util::apply_visitor(attr_value_converter(),
-                                                                                                  std::get<1>(kv))));
-                }
+
             }
             else
             {
@@ -412,6 +404,10 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end)
             }
             values.emplace_back(box, std::make_pair(geometry_index,0));
         }
+        if (geometry_index < num_features_to_query_)
+        {
+            initialise_descriptor(f);
+        }
         ++geometry_index;
     }
     // packing algorithm
@@ -419,7 +415,7 @@ void geojson_datasource::parse_geojson(Iterator start, Iterator end)
 
 }
 
-geojson_datasource::~geojson_datasource() { }
+geojson_datasource::~geojson_datasource() {}
 
 const char * geojson_datasource::name()
 {
@@ -454,7 +450,7 @@ boost::optional<mapnik::datasource_geometry_t> geojson_datasource::get_geometry_
         std::vector<value_type> positions;
         mapnik::util::spatial_index<value_type,
                                     mapnik::filter_in_box,
-                                    std::ifstream>::query_first_n(filter, index, positions, 5);
+                                    std::ifstream>::query_first_n(filter, index, positions, num_features_to_query_);
 
         mapnik::util::file file(filename_);
 
@@ -494,7 +490,7 @@ boost::optional<mapnik::datasource_geometry_t> geojson_datasource::get_geometry_
     else if (cache_features_)
     {
         unsigned num_features = features_.size();
-        for (unsigned i = 0; i < num_features && i < 5; ++i)
+        for (unsigned i = 0; i < num_features && i < num_features_to_query_; ++i)
         {
             result = mapnik::util::to_ds_type(features_[i]->get_geometry());
             if (result)
@@ -519,7 +515,7 @@ boost::optional<mapnik::datasource_geometry_t> geojson_datasource::get_geometry_
         auto itr = tree_->qbegin(boost::geometry::index::intersects(extent_));
         auto end = tree_->qend();
         mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>();
-        for (std::size_t count = 0; itr !=end &&  count < 5; ++itr,++count)
+        for (std::size_t count = 0; itr !=end &&  count < num_features_to_query_; ++itr,++count)
         {
             geojson_datasource::item_type const& item = *itr;
             std::size_t file_offset = item.second.first;
diff --git a/plugins/input/geojson/geojson_datasource.hpp b/plugins/input/geojson/geojson_datasource.hpp
index 66f3383..a50bd95 100644
--- a/plugins/input/geojson/geojson_datasource.hpp
+++ b/plugins/input/geojson/geojson_datasource.hpp
@@ -94,6 +94,7 @@ public:
     void initialise_index(Iterator start, Iterator end);
     void initialise_disk_index(std::string const& filename);
 private:
+    void initialise_descriptor(mapnik::feature_ptr const&);
     mapnik::datasource::datasource_t type_;
     mapnik::layer_descriptor desc_;
     std::string filename_;
@@ -103,6 +104,7 @@ private:
     std::unique_ptr<spatial_index_type> tree_;
     bool cache_features_ = true;
     bool has_disk_index_ = false;
+    const std::size_t num_features_to_query_ = 5;
 };
 
 
diff --git a/plugins/input/ogr/ogr_converter.cpp b/plugins/input/ogr/ogr_converter.cpp
index f2eb602..207c0e1 100644
--- a/plugins/input/ogr/ogr_converter.cpp
+++ b/plugins/input/ogr/ogr_converter.cpp
@@ -25,9 +25,12 @@
 #include <mapnik/geometry.hpp>
 
 // ogr
+#pragma GCC diagnostic push
+#include <mapnik/warning_ignore.hpp>
 #include "ogr_converter.hpp"
 #include <ogr_core.h>
 #include <ogr_geometry.h>
+#pragma GCC diagnostic pop
 
 mapnik::geometry::geometry<double> ogr_converter::convert_geometry(OGRGeometry* ogr_geom)
 {
diff --git a/plugins/input/pgraster/build.py b/plugins/input/pgraster/build.py
index 7b859ee..7d120e4 100644
--- a/plugins/input/pgraster/build.py
+++ b/plugins/input/pgraster/build.py
@@ -60,8 +60,7 @@ if env['PLUGIN_LINKING'] == 'shared':
                                       SHLIBPREFIX='',
                                       SHLIBSUFFIX='.input',
                                       source=plugin_sources,
-                                      LIBS=libraries,
-                                      LINKFLAGS=env['CUSTOM_LDFLAGS'])
+                                      LIBS=libraries)
 
     # if the plugin links to libmapnik ensure it is built first
     Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME']))
diff --git a/plugins/input/shape/shape_datasource.cpp b/plugins/input/shape/shape_datasource.cpp
index 644e601..56d7a6a 100644
--- a/plugins/input/shape/shape_datasource.cpp
+++ b/plugins/input/shape/shape_datasource.cpp
@@ -98,11 +98,11 @@ shape_datasource::shape_datasource(parameters const& params)
         mapnik::progress_timer __stats2__(std::clog, "shape_datasource::init(get_column_description)");
 #endif
 
-        std::unique_ptr<shape_io> shape_ref = std::make_unique<shape_io>(shape_name_);
-        init(*shape_ref);
-        for (int i=0;i<shape_ref->dbf().num_fields();++i)
+        shape_io shape(shape_name_);
+        init(shape);
+        for (int i = 0; i < shape.dbf().num_fields(); ++i)
         {
-            field_descriptor const& fd = shape_ref->dbf().descriptor(i);
+            field_descriptor const& fd = shape.dbf().descriptor(i);
             std::string fld_name=fd.name_;
             switch (fd.type_)
             {
diff --git a/plugins/input/shape/shape_index_featureset.cpp b/plugins/input/shape/shape_index_featureset.cpp
index f91d7ac..c4b1918 100644
--- a/plugins/input/shape/shape_index_featureset.cpp
+++ b/plugins/input/shape/shape_index_featureset.cpp
@@ -52,6 +52,9 @@ shape_index_featureset<filterT>::shape_index_featureset(filterT const& filter,
       ctx_(std::make_shared<mapnik::context_type>()),
       shape_ptr_(std::move(shape_ptr)),
       tr_(new mapnik::transcoder(encoding)),
+      offsets_(),
+      itr_(),
+      attr_ids_(),
       row_limit_(row_limit),
       count_(0),
       feature_bbox_()
@@ -63,12 +66,13 @@ shape_index_featureset<filterT>::shape_index_featureset(filterT const& filter,
     if (index)
     {
 #if defined(MAPNIK_MEMORY_MAPPED_FILE)
-        mapnik::util::spatial_index<int, filterT,boost::interprocess::ibufferstream>::query(filter, index->file(), offsets_);
+        mapnik::util::spatial_index<mapnik::detail::node, filterT,boost::interprocess::ibufferstream>::query(filter, index->file(), offsets_);
 #else
-        mapnik::util::spatial_index<int, filterT, std::ifstream>::query(filter, index->file(), offsets_);
+        mapnik::util::spatial_index<mapnik::detail::node, filterT, std::ifstream>::query(filter, index->file(), offsets_);
 #endif
     }
-    std::sort(offsets_.begin(), offsets_.end());
+    std::sort(offsets_.begin(), offsets_.end(), [](mapnik::detail::node const& n0, mapnik::detail::node const& n1)
+              {return n0.offset != n1.offset ? n0.offset < n1.offset : n0.start < n1.start;});
     MAPNIK_LOG_DEBUG(shape) << "shape_index_featureset: Query size=" << offsets_.size();
     itr_ = offsets_.begin();
 }
@@ -83,7 +87,14 @@ feature_ptr shape_index_featureset<filterT>::next()
 
     while ( itr_ != offsets_.end())
     {
-        shape_ptr_->move_to(*itr_++);
+        int offset = itr_->offset;
+        shape_ptr_->move_to(offset);
+        std::vector<std::pair<int,int>> parts;
+        while (itr_ != offsets_.end() && itr_->offset == offset)
+        {
+            if (itr_->start!= -1) parts.emplace_back(itr_->start, itr_->end);
+            ++itr_;
+        }
         mapnik::value_integer feature_id = shape_ptr_->id();
         shape_file::record_type record(shape_ptr_->reclength_ * 2);
         shape_ptr_->shp().read_record(record);
@@ -124,7 +135,8 @@ feature_ptr shape_index_featureset<filterT>::next()
         {
             shape_io::read_bbox(record, feature_bbox_);
             if (!filter_.pass(feature_bbox_)) continue;
-            feature->set_geometry(shape_io::read_polyline(record));
+            if (parts.size() < 2) feature->set_geometry(shape_io::read_polyline(record));
+            else feature->set_geometry(shape_io::read_polyline_parts(record, parts));
             break;
         }
         case shape_io::shape_polygon:
@@ -133,7 +145,8 @@ feature_ptr shape_index_featureset<filterT>::next()
         {
             shape_io::read_bbox(record, feature_bbox_);
             if (!filter_.pass(feature_bbox_)) continue;
-            feature->set_geometry(shape_io::read_polygon(record));
+            if (parts.size() < 2) feature->set_geometry(shape_io::read_polygon(record));
+            else feature->set_geometry(shape_io::read_polygon_parts(record, parts));
             break;
         }
         default :
diff --git a/plugins/input/shape/shape_index_featureset.hpp b/plugins/input/shape/shape_index_featureset.hpp
index 80c8fa6..d104e0f 100644
--- a/plugins/input/shape/shape_index_featureset.hpp
+++ b/plugins/input/shape/shape_index_featureset.hpp
@@ -45,6 +45,21 @@ using mapnik::box2d;
 using mapnik::feature_ptr;
 using mapnik::context_ptr;
 
+namespace mapnik { namespace detail
+{
+struct node
+{
+    node() = default;
+    node(int offset_, int start_, int end_)
+        : offset(offset_),
+          start(start_),
+          end(end_) {}
+    int offset;
+    int start;
+    int end;
+};
+}} // ns
+
 template <typename filterT>
 class shape_index_featureset : public Featureset
 {
@@ -63,8 +78,8 @@ private:
     context_ptr ctx_;
     std::unique_ptr<shape_io> shape_ptr_;
     const std::unique_ptr<mapnik::transcoder> tr_;
-    std::vector<int> offsets_;
-    std::vector<int>::iterator itr_;
+    std::vector<mapnik::detail::node> offsets_;
+    std::vector<mapnik::detail::node>::iterator itr_;
     std::vector<int> attr_ids_;
     mapnik::value_integer row_limit_;
     mutable int count_;
diff --git a/plugins/input/shape/shape_io.cpp b/plugins/input/shape/shape_io.cpp
index bb9d783..5dd5919 100644
--- a/plugins/input/shape/shape_io.cpp
+++ b/plugins/input/shape/shape_io.cpp
@@ -125,6 +125,7 @@ mapnik::geometry::geometry<double> shape_io::read_polyline(shape_file::record_ty
         std::for_each(parts.begin(), parts.end(), [&](int & part) { part = record.read_ndr_integer();});
         int start, end;
         mapnik::geometry::multi_line_string<double> multi_line;
+        multi_line.reserve(num_parts);
         for (int k = 0; k < num_parts; ++k)
         {
             start = parts[k];
@@ -152,6 +153,34 @@ mapnik::geometry::geometry<double> shape_io::read_polyline(shape_file::record_ty
     return geom;
 }
 
+mapnik::geometry::geometry<double> shape_io::read_polyline_parts(shape_file::record_type & record, std::vector<std::pair<int, int>> const& parts)
+{
+    mapnik::geometry::geometry<double> geom; // default empty
+    int total_num_parts = record.read_ndr_integer();
+    int num_parts = parts.size();
+    mapnik::geometry::multi_line_string<double> multi_line;
+    multi_line.reserve(num_parts);
+    for (int k = 0; k < num_parts; ++k)
+    {
+        int start = parts[k].first;
+        int end = parts[k].second;
+        unsigned pos = 4 + 32 + 8 + 4 * total_num_parts + start * 16;
+        record.set_pos(pos);
+
+        mapnik::geometry::line_string<double> line;
+        line.reserve(end - start);
+        for (int j = start; j < end; ++j)
+        {
+            double x = record.read_double();
+            double y = record.read_double();
+            line.emplace_back(x, y);
+        }
+        multi_line.push_back(std::move(line));
+    }
+    geom = std::move(multi_line);
+    return geom;
+}
+
 
 mapnik::geometry::geometry<double> shape_io::read_polygon(shape_file::record_type & record)
 {
@@ -207,3 +236,53 @@ mapnik::geometry::geometry<double> shape_io::read_polygon(shape_file::record_typ
     mapnik::geometry::correct(geom);
     return geom;
 }
+
+mapnik::geometry::geometry<double> shape_io::read_polygon_parts(shape_file::record_type & record, std::vector<std::pair<int,int>> const& parts)
+{
+    mapnik::geometry::geometry<double> geom; // default empty
+    int total_num_parts = record.read_ndr_integer();
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::multi_polygon<double> multi_poly;
+    int num_parts = parts.size();
+    for (int k = 0; k < num_parts; ++k)
+    {
+        int start = parts[k].first;
+        int end = parts[k].second;
+        unsigned pos = 4 + 32 + 8 + 4 * total_num_parts + start * 16;
+        record.set_pos(pos);
+        mapnik::geometry::linear_ring<double> ring;
+        ring.reserve(end - start);
+        for (int j = start; j < end; ++j)
+        {
+            double x = record.read_double();
+            double y = record.read_double();
+            ring.emplace_back(x, y);
+        }
+        if (k == 0)
+        {
+            poly.set_exterior_ring(std::move(ring));
+        }
+        else if (mapnik::util::is_clockwise(ring))
+        {
+            multi_poly.emplace_back(std::move(poly));
+            poly.interior_rings.clear();
+            poly.set_exterior_ring(std::move(ring));
+        }
+        else
+        {
+            poly.add_hole(std::move(ring));
+        }
+    }
+
+    if (multi_poly.size() > 0) // multi
+    {
+        multi_poly.emplace_back(std::move(poly));
+        geom = std::move(multi_poly);
+    }
+    else
+    {
+        geom = std::move(poly);
+    }
+    mapnik::geometry::correct(geom);
+    return geom;
+}
diff --git a/plugins/input/shape/shape_io.hpp b/plugins/input/shape/shape_io.hpp
index ab28f99..1828ebd 100644
--- a/plugins/input/shape/shape_io.hpp
+++ b/plugins/input/shape/shape_io.hpp
@@ -23,16 +23,16 @@
 #ifndef SHAPE_IO_HPP
 #define SHAPE_IO_HPP
 
+// stl
+#include <memory>
+#include <ios>
 // mapnik
 #include <mapnik/box2d.hpp>
 #include <mapnik/util/noncopyable.hpp>
-
+#include <mapnik/util/spatial_index.hpp>
 // boost
 #include <boost/optional.hpp>
-
-// stl
-#include <memory>
-
+//
 #include "dbfile.hpp"
 #include "shapefile.hpp"
 
@@ -72,7 +72,13 @@ public:
 
     inline bool has_index() const
     {
-        return (index_ && index_->is_open());
+        if (index_ && index_->is_open())
+        {
+            bool status = mapnik::util::check_spatial_index(index_->file());
+            index_->seek(0);// rewind
+            return status;
+        }
+        return false;
     }
 
     inline int id() const { return id_;}
@@ -80,6 +86,8 @@ public:
     static void read_bbox(shape_file::record_type & record, mapnik::box2d<double> & bbox);
     static mapnik::geometry::geometry<double> read_polyline(shape_file::record_type & record);
     static mapnik::geometry::geometry<double> read_polygon(shape_file::record_type & record);
+    static mapnik::geometry::geometry<double> read_polyline_parts(shape_file::record_type & record,std::vector<std::pair<int,int>> const& parts);
+    static mapnik::geometry::geometry<double> read_polygon_parts(shape_file::record_type & record, std::vector<std::pair<int,int>> const& parts);
 
     shapeType type_;
     shape_file shp_;
diff --git a/plugins/input/shape/shapefile.hpp b/plugins/input/shape/shapefile.hpp
index 664dc88..15e5c77 100644
--- a/plugins/input/shape/shapefile.hpp
+++ b/plugins/input/shape/shapefile.hpp
@@ -105,6 +105,11 @@ struct shape_record
         pos += n;
     }
 
+    void set_pos(unsigned pos_)
+    {
+        pos = pos_;
+    }
+
     int read_ndr_integer()
     {
         std::int32_t val;
diff --git a/scripts/appveyor-system-info.ps1 b/scripts/appveyor-system-info.ps1
new file mode 100644
index 0000000..586ef2d
--- /dev/null
+++ b/scripts/appveyor-system-info.ps1
@@ -0,0 +1,84 @@
+$PSVersionTable
+$PSVersionTable.PSVersion
+
+$comp_name = $env:COMPUTERNAME
+$user_name = $env:USERNAME
+Write-Host $comp_name $user_name
+
+$on_appveyor = $false
+if($comp_name -like 'APPVYR*' -And $user_name -eq "appveyor"){
+    $on_appveyor = $true
+}
+
+
+$SystemManaged  = Get-WmiObject -Class Win32_ComputerSystem | % {$_.AutomaticManagedPagefile} 
+$total_physicalmem = gwmi Win32_ComputerSystem | % {[Math]::round($_.TotalPhysicalMemory/1MB,0)} 
+$physical_mem = get-ciminstance -class 'cim_physicalmemory' | % { $_.Capacity/1024/1024}
+
+$PF =gwmi Win32_PageFileUsage 
+$PageFileLocation = $PF.Name;
+$PageFileSize = $PF.AllocatedBaseSize 
+
+Write-Host "physical memory          : "$physical_mem
+Write-Host "total physical memory    : "$total_physicalmem
+Write-Host "page file system managed : "$SystemManaged
+Write-Host "page file location       : "$PageFileLocation
+Write-Host "page file size           : "$PageFileSize
+Write-Host "InitialSize              : "${CurrentPageFile}.InitialSize
+Write-Host "MaximumSize              : "$CurrentPageFile.MaximumSize
+
+if($on_appveyor -eq $true){
+
+    Write-Host !!!!!!! on AppVeyor: changing page file settings !!!!!!!!!!
+
+    $dirs = (
+        "C:\qt",
+        "C:\Users\appveyor\AppData\Local\Microsoft\Web Platform Installer",
+        "C:\Program Files\Microsoft SQL Server",
+        "C:\ProgramData\Package Cache"
+    )
+    Foreach($dir in $dirs){
+        if(Test-Path $dir) {
+            Write-Host found $dir
+            Remove-Item $dir -Force -Recurse
+        } else {
+            Write-Host not found $dir
+        }
+    }
+
+    #disable automatically managed page file settings
+    $c = Get-WmiObject Win32_computersystem -EnableAllPrivileges
+    if($c.AutomaticManagedPagefile){
+        Write-Host disabling managed page file settings
+        $c.AutomaticManagedPagefile = $false
+        $c.Put() | Out-Null
+    }
+
+    $new_page_size=18000
+    $CurrentPageFile = Get-WmiObject -Class Win32_PageFileSetting
+    if($CurrentPageFile.InitialSize -ne $new_page_size){
+        Write-Host "setting new page file size to $new_page_size"
+        $CurrentPageFile.InitialSize=$new_page_size
+        $CurrentPageFile.MaximumSize=$new_page_size
+        $CurrentPageFile.Put() | Out-Null
+    }
+
+    Write-Host "new ------------ "
+    Write-Host "system managed:" (Get-WmiObject -Class Win32_ComputerSystem | % {$_.AutomaticManagedPagefile})
+    Write-Host "page file size:" (gwmi Win32_PageFileUsage).AllocatedBaseSize
+    Write-Host "InitialSize: "${CurrentPageFile}.InitialSize
+    Write-Host "MaximumSize: "$CurrentPageFile.MaximumSize
+} else {
+    Write-Host not on AppVeyor, leaving page file as is
+}
+
+#list drives
+Get-WmiObject -Class Win32_LogicalDisk |
+    Where-Object {$_.DriveType -ne 5} |
+    Sort-Object -Property Name | 
+    Select-Object Name, VolumeName, FileSystem, Description, VolumeDirty, `
+        @{"Label"="DiskSize(GB)";"Expression"={"{0:N}" -f ($_.Size/1GB) -as [float]}}, `
+        @{"Label"="FreeSpace(GB)";"Expression"={"{0:N}" -f ($_.FreeSpace/1GB) -as [float]}}, `
+        @{"Label"="%Free";"Expression"={"{0:N}" -f ($_.FreeSpace/$_.Size*100) -as [float]}} |
+    Format-Table -AutoSize
+
diff --git a/scripts/build-appveyor.bat b/scripts/build-appveyor.bat
index 3ed98d9..3c8b3d0 100644
--- a/scripts/build-appveyor.bat
+++ b/scripts/build-appveyor.bat
@@ -5,12 +5,13 @@ SET EL=0
 ECHO =========== %~f0 ===========
 
 ECHO NUMBER_OF_PROCESSORS^: %NUMBER_OF_PROCESSORS%
-ECHO RAM [MB]^:
-powershell "get-ciminstance -class 'cim_physicalmemory' | %% { $_.Capacity/1024/1024}"
+powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+powershell .\scripts\appveyor-system-info.ps1
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 ::only build on AppVeyor, if explicitly stated
-ECHO APPVEYOR_REPO_COMMIT_MESSAGE^: %APPVEYOR_REPO_COMMIT_MESSAGE%
+ECHO APPVEYOR_REPO_COMMIT_MESSAGE^: "%APPVEYOR_REPO_COMMIT_MESSAGE%"
 ::SET BUILD_ON_APPVEYOR=0
 ::for /F "tokens=1 usebackq" %%i in (`powershell .\scripts\parse-commit-message.ps1 '[build appveyor]'`) DO SET BUILD_ON_APPVEYOR=%%i
 ::IF %BUILD_ON_APPVEYOR% EQU 0 ECHO not building, commit with [build appveyor] && GOTO DONE
@@ -26,8 +27,20 @@ ECHO ========
 
 SET PATH=C:\Python27;%PATH%
 SET PATH=C:\Program Files\7-Zip;%PATH%
-:: *nix style find command:
-SET PATH=C:\Program Files (x86)\Git\bin;%PATH%
+
+::update submodule variant
+git submodule update --init deps/mapbox/variant
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+
+::python bindings, including test data
+IF NOT EXIST bindings\python git clone --recursive https://github.com/mapnik/python-mapnik.git bindings/python
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+
+CD bindings\python & IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+git fetch & IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+git pull & IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+CD ..\.. & IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 ::cloning mapnik-gyp
 if EXIST mapnik-gyp ECHO mapnik-gyp already cloned && GOTO MAPNIK_GYP_ALREADY_HERE
diff --git a/scripts/build-local.bat b/scripts/build-local.bat
index 5d9affc..306e10c 100644
--- a/scripts/build-local.bat
+++ b/scripts/build-local.bat
@@ -8,14 +8,37 @@ SET APPVEYOR_REPO_COMMIT_MESSAGE=this is a [build appveyor] test
 SET APPVEYOR=true
 ::comment this to get complete AppVeyor behaviour
 SET LOCAL_BUILD_DONT_SKIP_TESTS=true
-
-SET MAPNIK_GIT=3.0.5
-SET BOOST_VERSION=59
 SET FASTBUILD=1
+
+FOR /F "tokens=1 usebackq" %%i in (`powershell .\scripts\parse-appveyor-yml.ps1`) DO SET BOOST_VERSION=%%i
+ECHO BOOST_VERSION found in appveyor.yml^: %BOOST_VERSION%
+IF "%BOOST_VERSION%"=="0" ECHO BOOST_VERSION not valid && SET ERRORLEVEL=1 && GOTO ERROR
+
+
+:: OVERRIDE PARAMETERS >>>>>>>>
+:NEXT-ARG
+
+IF '%1'=='' GOTO ARGS-DONE
+ECHO setting %1
+SET %1
+SHIFT
+GOTO NEXT-ARG
+
+:ARGS-DONE
+::<<<<< OVERRIDE PARAMETERS
+
+
 SET configuration=Release
 SET msvs_toolset=14
 SET platform=x64
 SET APPVEYOR_BUILD_FOLDER=%CD%
+
+ECHO pulling test data
+CALL git submodule update --init
+IF %ERRORLEVEL% NEQ 0 GOTO ERROR
+ECHO pulling test data, DONE
+
+SET TIME_START_LOCAL_BUILD=%TIME%
 CALL scripts\build-appveyor.bat
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
@@ -28,5 +51,7 @@ SET EL=%ERRORLEVEL%
 
 :DONE
 ECHO =========== DONE %~f0 ===========
+ECHO build started^: %TIME_START_LOCAL_BUILD%
+ECHO build finished^: %TIME%
 
 EXIT /b %EL%
diff --git a/scripts/msbuild-force-mp-and-buildinparallel.props b/scripts/msbuild-force-mp-and-buildinparallel.props
new file mode 100644
index 0000000..1b06765
--- /dev/null
+++ b/scripts/msbuild-force-mp-and-buildinparallel.props
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
+    </ClCompile>
+  </ItemDefinitionGroup>
+</Project>
\ No newline at end of file
diff --git a/scripts/parse-appveyor-yml.ps1 b/scripts/parse-appveyor-yml.ps1
new file mode 100644
index 0000000..f87ae7e
--- /dev/null
+++ b/scripts/parse-appveyor-yml.ps1
@@ -0,0 +1,7 @@
+$ErrorActionPreference = 'Stop'
+$boost_version='0'
+Get-Content .\appveyor.yml |
+    foreach { 
+        if ($_ -match "BOOST_VERSION: "){ $boost_version = $_.split()[-1] }
+    }
+Write-Host $boost_version
diff --git a/scripts/time-header b/scripts/time-header
new file mode 100755
index 0000000..97c3c69
--- /dev/null
+++ b/scripts/time-header
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+set -eu
+set -o pipefail
+
+CXXFLAGS="$(./utils/mapnik-config/mapnik-config --cflags)"
+CXX="$(./utils/mapnik-config/mapnik-config --cxx)"
+echo "Time taken to compile '$(basename $1)':"
+time ${CXX} $1 -I./test -I./deps/agg/include -Ideps -I./include $CXXFLAGS -o /tmp/header.out
\ No newline at end of file
diff --git a/scripts/travis-common.sh b/scripts/travis-common.sh
new file mode 100644
index 0000000..cfdc57f
--- /dev/null
+++ b/scripts/travis-common.sh
@@ -0,0 +1,109 @@
+#! /bin/bash
+
+# enabled VALUE
+#   - if VALUE is empty or falsy, returns 1 (false)
+#   - otherwise returns 0 (true)
+# enabled VALUE COMMAND ...
+#   - if VALUE is empty or falsy, returns 0 (true)
+#   - otherwise runs COMMAND and returns its result
+enabled () {
+    local value="$1"; shift
+    case $value in
+        ''|'0'|[Ff]alse|[Nn]o) test $# -ne 0;;
+        *) test $# -eq 0 || "$@";;
+    esac
+}
+
+# on NAME
+#   - if NAME == $TRAVIS_OS_NAME, returns 0 (true)
+#   - otherwise returns 1 (false)
+# on NAME COMMAND ...
+#   - if NAME == $TRAVIS_OS_NAME, runs COMMAND and returns its result
+#   - otherwise returns 0 (true)
+on () {
+    local name="$1"; shift
+    case $name in
+        $TRAVIS_OS_NAME) test $# -eq 0 || "$@";;
+        *) test $# -ne 0;;
+    esac
+}
+
+git_submodule_update () {
+    git submodule update "$@" && return
+    # failed, search pull requests for matching commits
+    git submodule foreach \
+        '
+        test "$sha1" = "`git rev-parse HEAD`" ||
+        git ls-remote origin "refs/pull/*/head" |
+        while read hash ref; do
+            if test "$hash" = "$sha1"; then
+                git config --add remote.origin.fetch "+$ref:$ref";
+            fi
+        done
+        '
+    # try again with added fetch refs
+    git submodule update "$@"
+}
+
+# install and call pip
+pip () {
+    if ! which pip >/dev/null; then
+        easy_install --user pip && \
+        export PATH="$HOME/Library/Python/2.7/bin:$PATH"
+    fi
+    command pip "$@"
+}
+
+# commit_message_contains TEXT
+#   - returns 0 (true) if TEXT is found in commit message
+#   - case-insensitive, plain-text search, not regex
+commit_message_contains () {
+    git log -1 --pretty='%B' "$TRAVIS_COMMIT" | grep -qiFe "$*"
+}
+
+commit_message_parse () {
+    if commit_message_contains '[skip tests]'; then
+        config_override "CPP_TESTS = False"
+    fi
+    if commit_message_contains '[skip utils]'; then
+        config_override "MAPNIK_INDEX = False"
+        config_override "MAPNIK_RENDER = False"
+        config_override "PGSQL2SQLITE = False"
+        config_override "SHAPEINDEX = False"
+        config_override "SVG2PNG = False"
+    fi
+}
+
+config_override () {
+    echo "Appending to config.py:" "$@"
+    echo "$@" >> ./config.py
+}
+
+configure () {
+    if enabled ${COVERAGE}; then
+        ./configure "$@" PGSQL2SQLITE=False SVG2PNG=False SVG_RENDERER=False \
+            CUSTOM_LDFLAGS='--coverage' CUSTOM_CXXFLAGS='--coverage' \
+            CUSTOM_CFLAGS='--coverage' DEBUG=True
+    elif enabled ${MASON_PUBLISH}; then
+        export MASON_NAME=mapnik
+        export MASON_VERSION=latest
+        export MASON_LIB_FILE=lib/libmapnik-wkt.a
+        source ./.mason/mason.sh
+        ./configure "$@" PREFIX=${MASON_PREFIX} \
+            PATH_REPLACE='' MAPNIK_BUNDLED_SHARE_DIRECTORY=True \
+            RUNTIME_LINK='static'
+    else
+        ./configure "$@"
+    fi
+    # print final config values, sorted and indented
+    sort -sk1,1 ./config.py | sed -e 's/^/	/'
+}
+
+coverage () {
+    ./mason_packages/.link/bin/cpp-coveralls \
+        --build-root . --gcov-options '\-lp' --exclude mason_packages \
+        --exclude .sconf_temp --exclude benchmark --exclude deps \
+        --exclude scons --exclude test --exclude demo --exclude docs \
+        --exclude fonts --exclude utils \
+        > /dev/null
+}
diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp
index 74e3c93..9fbcdca 100644
--- a/src/agg/agg_renderer.cpp
+++ b/src/agg/agg_renderer.cpp
@@ -121,11 +121,11 @@ struct setup_agg_bg_visitor
            mode_(mode),
            opacity_(opacity) {}
 
-    void operator() (marker_null const&) {}
+    void operator() (marker_null const&) const {}
 
-    void operator() (marker_svg const&) {}
+    void operator() (marker_svg const&) const {}
 
-    void operator() (marker_rgba8 const& marker)
+    void operator() (marker_rgba8 const& marker) const
     {
         mapnik::image_rgba8 const& bg_image = marker.get_data();
         std::size_t w = bg_image.width();
@@ -361,9 +361,9 @@ struct agg_render_marker_visitor
           opacity_(opacity),
           comp_op_(comp_op) {}
 
-    void operator() (marker_null const&) {}
+    void operator() (marker_null const&) const {}
 
-    void operator() (marker_svg const& marker)
+    void operator() (marker_svg const& marker) const
     {
         using color_type = agg::rgba8;
         using order_type = agg::order_rgba;
@@ -414,7 +414,7 @@ struct agg_render_marker_visitor
         svg_renderer.render(*ras_ptr_, sl, renb, mtx, opacity_, bbox);
     }
 
-    void operator() (marker_rgba8 const& marker)
+    void operator() (marker_rgba8 const& marker) const
     {
         using color_type = agg::rgba8;
         using order_type = agg::order_rgba;
diff --git a/src/agg/process_debug_symbolizer.cpp b/src/agg/process_debug_symbolizer.cpp
index c56add9..2016c45 100644
--- a/src/agg/process_debug_symbolizer.cpp
+++ b/src/agg/process_debug_symbolizer.cpp
@@ -165,12 +165,12 @@ struct RingRenderer {
 };
 
 template <typename BufferType>
-struct render_ring_visitor {
-
+struct render_ring_visitor
+{
     render_ring_visitor(RingRenderer<BufferType> & renderer)
      : renderer_(renderer) {}
 
-    void operator()(mapnik::geometry::multi_polygon<double> const& geom)
+    void operator()(mapnik::geometry::multi_polygon<double> const& geom) const
     {
         for (auto const& poly : geom)
         {
@@ -178,7 +178,7 @@ struct render_ring_visitor {
         }
     }
 
-    void operator()(mapnik::geometry::polygon<double> const& geom)
+    void operator()(mapnik::geometry::polygon<double> const& geom) const
     {
         agg::rgba8 red(255,0,0,255);
         agg::rgba8 green(0,255,255,255);
@@ -199,7 +199,7 @@ struct render_ring_visitor {
     }
 
     template<typename GeomType>
-    void operator()(GeomType const&) {}
+    void operator()(GeomType const&) const {}
 
     RingRenderer<BufferType> & renderer_;
 };
diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp
index f2c8adf..010d3f1 100644
--- a/src/agg/process_group_symbolizer.cpp
+++ b/src/agg/process_group_symbolizer.cpp
@@ -25,20 +25,16 @@
 #include <mapnik/agg_renderer.hpp>
 #include <mapnik/agg_rasterizer.hpp>
 #include <mapnik/agg_render_marker.hpp>
-#include <mapnik/image_util.hpp>
 #include <mapnik/image.hpp>
-#include <mapnik/util/variant.hpp>
 #include <mapnik/text/renderer.hpp>
-#include <mapnik/geom_util.hpp>
-#include <mapnik/symbolizer.hpp>
-#include <mapnik/pixel_position.hpp>
 #include <mapnik/text/glyph_positions.hpp>
-#include <mapnik/renderer_common/process_group_symbolizer.hpp>
 #include <mapnik/renderer_common/clipping_extent.hpp>
+#include <mapnik/renderer_common/render_group_symbolizer.hpp>
 #include <mapnik/svg/svg_renderer_agg.hpp>
 #include <mapnik/svg/svg_path_attributes.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/svg/svg_converter.hpp>
+
 // agg
 #include "agg_trans_affine.h"
 
@@ -55,7 +51,7 @@ template <typename T>
 struct thunk_renderer;
 
 template <>
-struct thunk_renderer<image_rgba8>
+struct thunk_renderer<image_rgba8> : render_thunk_list_dispatch
 {
     using renderer_type = agg_renderer<image_rgba8>;
     using buffer_type = renderer_type::buffer_type;
@@ -64,12 +60,13 @@ struct thunk_renderer<image_rgba8>
     thunk_renderer(renderer_type &ren,
                    std::unique_ptr<rasterizer> const& ras_ptr,
                    buffer_type *buf,
-                   renderer_common &common,
-                   pixel_position const &offset)
-        : ren_(ren), ras_ptr_(ras_ptr), buf_(buf), common_(common), offset_(offset)
+                   renderer_common &common)
+        : ren_(ren), ras_ptr_(ras_ptr), buf_(buf), common_(common),
+          tex_(*buf, HALO_RASTERIZER_FULL, src_over, src_over,
+               common.scale_factor_, common.font_manager_.get_stroker())
     {}
 
-    void operator()(vector_marker_render_thunk const &thunk) const
+    virtual void operator()(vector_marker_render_thunk const& thunk)
     {
         using blender_type = agg::comp_op_adaptor_rgba_pre<agg::rgba8, agg::order_rgba>; // comp blender
         using buf_type = agg::rendering_buffer;
@@ -95,7 +92,7 @@ struct thunk_renderer<image_rgba8>
         render_vector_marker(svg_renderer, *ras_ptr_, renb, thunk.src_->bounding_box(), offset_tr, thunk.opacity_, thunk.snap_to_pixels_);
     }
 
-    void operator()(raster_marker_render_thunk const &thunk) const
+    virtual void operator()(raster_marker_render_thunk const& thunk)
     {
         using blender_type = agg::comp_op_adaptor_rgba_pre<agg::rgba8, agg::order_rgba>; // comp blender
         using buf_type = agg::rendering_buffer;
@@ -113,32 +110,25 @@ struct thunk_renderer<image_rgba8>
         render_raster_marker(renb, *ras_ptr_, thunk.src_, offset_tr, thunk.opacity_, common_.scale_factor_, thunk.snap_to_pixels_);
     }
 
-    void operator()(text_render_thunk const &thunk) const
+    virtual void operator()(text_render_thunk const& thunk)
     {
-        text_renderer_type ren(*buf_, thunk.halo_rasterizer_, thunk.comp_op_, thunk.comp_op_,
-                               common_.scale_factor_, common_.font_manager_.get_stroker());
+        tex_.set_comp_op(thunk.comp_op_);
+        tex_.set_halo_comp_op(thunk.comp_op_);
+        tex_.set_halo_rasterizer(thunk.halo_rasterizer_);
 
-        render_offset_placements(
-            thunk.placements_,
-            offset_,
-            [&] (glyph_positions_ptr const& glyphs)
-            {
-                marker_info_ptr mark = glyphs->get_marker();
-                if (mark)
-                {
-                    ren_.render_marker(glyphs->marker_pos(),
-                                       *mark->marker_,
-                                       mark->transform_,
-                                       thunk.opacity_, thunk.comp_op_);
-                }
-                ren.render(*glyphs);
-            });
-    }
+        for (auto const& glyphs : thunk.placements_)
+        {
+            scoped_glyph_positions_offset tmp_off(*glyphs, offset_);
 
-    template <typename T>
-    void operator()(T const &) const
-    {
-        throw std::runtime_error("Rendering of this data type is not supported currently by the renderer");
+            if (auto const& mark = glyphs->get_marker())
+            {
+                ren_.render_marker(glyphs->marker_pos(),
+                                   *mark->marker_,
+                                   mark->transform_,
+                                   thunk.opacity_, thunk.comp_op_);
+            }
+            tex_.render(*glyphs);
+        }
     }
 
 private:
@@ -146,7 +136,7 @@ private:
     std::unique_ptr<rasterizer> const& ras_ptr_;
     buffer_type *buf_;
     renderer_common &common_;
-    pixel_position offset_;
+    text_renderer_type tex_;
 };
 
 template <typename T0, typename T1>
@@ -154,16 +144,11 @@ void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
                                   mapnik::feature_impl & feature,
                                   proj_transform const& prj_trans)
 {
+    thunk_renderer<buffer_type> ren(*this, ras_ptr, current_buffer_, common_);
+
     render_group_symbolizer(
         sym, feature, common_.vars_, prj_trans, clipping_extent(common_), common_,
-        [&](render_thunk_list const& thunks, pixel_position const& render_offset)
-        {
-            thunk_renderer<buffer_type> ren(*this, ras_ptr, current_buffer_, common_, render_offset);
-            for (render_thunk_ptr const& thunk : thunks)
-            {
-                util::apply_visitor(ren, *thunk);
-            }
-        });
+        ren);
 }
 
 template void agg_renderer<image_rgba8>::process(group_symbolizer const&,
diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp
index 17ec5bb..f5045c5 100644
--- a/src/agg/process_line_pattern_symbolizer.cpp
+++ b/src/agg/process_line_pattern_symbolizer.cpp
@@ -71,9 +71,9 @@ struct agg_renderer_process_visitor_l
           feature_(feature),
           prj_trans_(prj_trans) {}
 
-    void operator() (marker_null const&) {}
+    void operator() (marker_null const&) const {}
 
-    void operator() (marker_svg const& marker)
+    void operator() (marker_svg const& marker) const
     {
         using color = agg::rgba8;
         using order = agg::order_rgba;
@@ -147,7 +147,7 @@ struct agg_renderer_process_visitor_l
         mapnik::util::apply_visitor(vertex_processor_type(apply),feature_.get_geometry());
     }
 
-    void operator() (marker_rgba8 const& marker)
+    void operator() (marker_rgba8 const& marker) const
     {
         using color = agg::rgba8;
         using order = agg::order_rgba;
@@ -214,7 +214,7 @@ struct agg_renderer_process_visitor_l
         using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type, rasterizer_type>;
         using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
         apply_vertex_converter_type apply(converter, ras);
-        mapnik::util::apply_visitor(vertex_processor_type(apply),feature_.get_geometry());
+        mapnik::util::apply_visitor(vertex_processor_type(apply), feature_.get_geometry());
     }
 
   private:
diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp
index cb94282..b229f01 100644
--- a/src/agg/process_markers_symbolizer.cpp
+++ b/src/agg/process_markers_symbolizer.cpp
@@ -24,22 +24,15 @@
 #include <mapnik/agg_helpers.hpp>
 #include <mapnik/agg_renderer.hpp>
 #include <mapnik/agg_rasterizer.hpp>
-
-#include <mapnik/debug.hpp>
-#include <mapnik/feature.hpp>
-#include <mapnik/geom_util.hpp>
-#include <mapnik/marker_helpers.hpp>
-#include <mapnik/marker.hpp>
-#include <mapnik/marker_cache.hpp>
 #include <mapnik/agg_render_marker.hpp>
 #include <mapnik/svg/svg_renderer_agg.hpp>
 #include <mapnik/svg/svg_storage.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/svg/svg_path_attributes.hpp>
 #include <mapnik/symbolizer.hpp>
-#include <mapnik/parse_path.hpp>
-#include <mapnik/renderer_common/process_markers_symbolizer.hpp>
 #include <mapnik/renderer_common/clipping_extent.hpp>
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
+
 // agg
 #include "agg_basics.h"
 #include "agg_renderer_base.h"
@@ -52,114 +45,62 @@
 #include "agg_path_storage.h"
 #include "agg_conv_transform.h"
 
-
-// boost
-#include <boost/optional.hpp>
-
 namespace mapnik {
 
 namespace detail {
 
-template <typename SvgRenderer, typename Detector, typename RendererContext>
-struct vector_markers_rasterizer_dispatch : public vector_markers_dispatch<Detector>
+template <typename SvgRenderer, typename BufferType, typename RasterizerType>
+struct agg_markers_renderer_context : markers_renderer_context
 {
     using renderer_base = typename SvgRenderer::renderer_base;
     using vertex_source_type = typename SvgRenderer::vertex_source_type;
     using attribute_source_type = typename SvgRenderer::attribute_source_type;
     using pixfmt_type = typename renderer_base::pixfmt_type;
 
-    using BufferType = typename std::tuple_element<0,RendererContext>::type;
-    using RasterizerType = typename std::tuple_element<1,RendererContext>::type;
-
-    vector_markers_rasterizer_dispatch(svg_path_ptr const& src,
-                                       vertex_source_type & path,
-                                       svg_attribute_type const& attrs,
-                                       agg::trans_affine const& marker_trans,
-                                       symbolizer_base const& sym,
-                                       Detector & detector,
-                                       double scale_factor,
-                                       feature_impl & feature,
-                                       attributes const& vars,
-                                       bool snap_to_pixels,
-                                       RendererContext const& renderer_context)
-: vector_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        buf_(std::get<0>(renderer_context)),
+    agg_markers_renderer_context(symbolizer_base const& sym,
+                                 feature_impl const& feature,
+                                 attributes const& vars,
+                                 BufferType & buf,
+                                 RasterizerType & ras)
+      : buf_(buf),
         pixf_(buf_),
         renb_(pixf_),
-        svg_renderer_(path, attrs),
-        ras_(std::get<1>(renderer_context)),
-        snap_to_pixels_(snap_to_pixels)
+        ras_(ras)
     {
-        pixf_.comp_op(static_cast<agg::comp_op_e>(get<composite_mode_e, keys::comp_op>(sym, feature, vars)));
+        auto comp_op = get<composite_mode_e, keys::comp_op>(sym, feature, vars);
+        pixf_.comp_op(static_cast<agg::comp_op_e>(comp_op));
     }
 
-    ~vector_markers_rasterizer_dispatch() {}
-
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
+    virtual void render_marker(svg_path_ptr const& src,
+                               svg_path_adapter & path,
+                               svg_attribute_type const& attrs,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
     {
-        render_vector_marker(svg_renderer_, ras_, renb_, this->src_->bounding_box(),
-                             marker_tr, opacity, snap_to_pixels_);
+        SvgRenderer svg_renderer(path, attrs);
+        render_vector_marker(svg_renderer, ras_, renb_, src->bounding_box(),
+                             marker_tr, params.opacity, params.snap_to_pixels);
     }
 
-private:
-    BufferType & buf_;
-    pixfmt_type pixf_;
-    renderer_base renb_;
-    SvgRenderer svg_renderer_;
-    RasterizerType & ras_;
-    bool snap_to_pixels_;
-};
-
-template <typename Detector, typename RendererContext>
-struct raster_markers_rasterizer_dispatch : public raster_markers_dispatch<Detector>
-{
-    using BufferType = typename std::remove_reference<typename std::tuple_element<0,RendererContext>::type>::type;
-    using RasterizerType = typename std::tuple_element<1,RendererContext>::type;
 
-    using color_type = agg::rgba8;
-    using order_type = agg::order_rgba;
-    using pixel_type = agg::pixel32_type;
-    using blender_type = agg::comp_op_adaptor_rgba_pre<color_type, order_type>; // comp blender
-    using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba<blender_type, BufferType>;
-    using renderer_base = agg::renderer_base<pixfmt_comp_type>;
-
-    raster_markers_rasterizer_dispatch(image_rgba8 const& src,
-                                       agg::trans_affine const& marker_trans,
-                                       symbolizer_base const& sym,
-                                       Detector & detector,
-                                       double scale_factor,
-                                       feature_impl & feature,
-                                       attributes const& vars,
-                                       RendererContext const& renderer_context,
-                                       bool snap_to_pixels = false)
-    : raster_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        buf_(std::get<0>(renderer_context)),
-        pixf_(buf_),
-        renb_(pixf_),
-        ras_(std::get<1>(renderer_context)),
-        snap_to_pixels_(snap_to_pixels)
+    virtual void render_marker(image_rgba8 const& src,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
     {
-        pixf_.comp_op(static_cast<agg::comp_op_e>(get<composite_mode_e, keys::comp_op>(sym, feature, vars)));
-    }
-
-    ~raster_markers_rasterizer_dispatch() {}
-
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
-    {
-        // In the long term this should be a visitor pattern based on the type of render this->src_ provided that converts
-        // the destination pixel type required.
-        render_raster_marker(renb_, ras_, this->src_, marker_tr, opacity, this->scale_factor_, snap_to_pixels_);
+        // In the long term this should be a visitor pattern based on the type of
+        // render src provided that converts the destination pixel type required.
+        render_raster_marker(renb_, ras_, src, marker_tr, params.opacity,
+                             params.scale_factor, params.snap_to_pixels);
     }
 
 private:
     BufferType & buf_;
-    pixfmt_comp_type pixf_;
+    pixfmt_type pixf_;
     renderer_base renb_;
     RasterizerType & ras_;
-    bool snap_to_pixels_;
 };
 
-}
+} // namespace detail
 
 template <typename T0, typename T1>
 void agg_renderer<T0,T1>::process(markers_symbolizer const& sym,
@@ -194,16 +135,16 @@ void agg_renderer<T0,T1>::process(markers_symbolizer const& sym,
     buf_type render_buffer(current_buffer_->bytes(), current_buffer_->width(), current_buffer_->height(), current_buffer_->row_size());
     box2d<double> clip_box = clipping_extent(common_);
 
-    auto renderer_context = std::tie(render_buffer,*ras_ptr,pixmap_);
-    using context_type = decltype(renderer_context);
-    using vector_dispatch_type = detail::vector_markers_rasterizer_dispatch<svg_renderer_type, detector_type, context_type>;
-    using raster_dispatch_type = detail::raster_markers_rasterizer_dispatch<detector_type, context_type>;
+    using context_type = detail::agg_markers_renderer_context<svg_renderer_type,
+                                                              buf_type,
+                                                              rasterizer>;
+    context_type renderer_context(sym, feature, common_.vars_, render_buffer, *ras_ptr);
 
-    render_markers_symbolizer<vector_dispatch_type, raster_dispatch_type>(
+    render_markers_symbolizer(
         sym, feature, prj_trans, common_, clip_box, renderer_context);
 }
 
 template void agg_renderer<image_rgba8>::process(markers_symbolizer const&,
                                               mapnik::feature_impl &,
                                               proj_transform const&);
-}
+} // namespace mapnik
diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp
index 8dc40f1..1da740f 100644
--- a/src/agg/process_point_symbolizer.cpp
+++ b/src/agg/process_point_symbolizer.cpp
@@ -30,7 +30,6 @@
 #include <mapnik/symbolizer.hpp>
 #include <mapnik/marker.hpp>
 #include <mapnik/marker_cache.hpp>
-#include <mapnik/label_collision_detector.hpp>
 #include <mapnik/parse_path.hpp>
 #include <mapnik/pixel_position.hpp>
 #include <mapnik/renderer_common/process_point_symbolizer.hpp>
diff --git a/src/agg/process_polygon_pattern_symbolizer.cpp b/src/agg/process_polygon_pattern_symbolizer.cpp
index fb00b2a..8e340a9 100644
--- a/src/agg/process_polygon_pattern_symbolizer.cpp
+++ b/src/agg/process_polygon_pattern_symbolizer.cpp
@@ -75,9 +75,9 @@ struct agg_renderer_process_visitor_p
         feature_(feature),
         prj_trans_(prj_trans) {}
 
-    void operator() (marker_null const&) {}
+    void operator() (marker_null const&) const {}
 
-    void operator() (marker_svg const& marker)
+    void operator() (marker_svg const& marker) const
     {
         agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
         auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
@@ -180,7 +180,7 @@ struct agg_renderer_process_visitor_p
         agg::render_scanlines(*ras_ptr_, sl, rp);
     }
 
-    void operator() (marker_rgba8 const& marker)
+    void operator() (marker_rgba8 const& marker) const
     {
         using color = agg::rgba8;
         using order = agg::order_rgba;
diff --git a/src/build.py b/src/build.py
index bdc3438..c405eb1 100644
--- a/src/build.py
+++ b/src/build.py
@@ -154,7 +154,7 @@ source = Split(
     well_known_srs.cpp
     params.cpp
     image_filter_types.cpp
-    miniz_png.cpp
+    image_filter_grammar.cpp
     color.cpp
     conversions.cpp
     image_copy.cpp
@@ -252,8 +252,10 @@ source = Split(
     config_error.cpp
     color_factory.cpp
     renderer_common.cpp
+    renderer_common/render_group_symbolizer.cpp
+    renderer_common/render_markers_symbolizer.cpp
     renderer_common/render_pattern.cpp
-    renderer_common/process_group_symbolizer.cpp
+    renderer_common/render_thunk_extractor.cpp
     math.cpp
     """
     )
diff --git a/src/cairo/cairo_renderer.cpp b/src/cairo/cairo_renderer.cpp
index 1772307..83e1208 100644
--- a/src/cairo/cairo_renderer.cpp
+++ b/src/cairo/cairo_renderer.cpp
@@ -104,10 +104,10 @@ struct setup_marker_visitor
     setup_marker_visitor(cairo_context & context, renderer_common const& common)
         : context_(context), common_(common) {}
 
-    void operator() (marker_null const &) {}
-    void operator() (marker_svg const &) {}
+    void operator() (marker_null const &) const{}
+    void operator() (marker_svg const &) const {}
 
-    void operator() (marker_rgba8 const& marker)
+    void operator() (marker_rgba8 const& marker) const
     {
         mapnik::image_rgba8 const& bg_image = marker.get_data();
         std::size_t w = bg_image.width();
diff --git a/src/cairo/process_group_symbolizer.cpp b/src/cairo/process_group_symbolizer.cpp
index a2dc4ab..0c35ba0 100644
--- a/src/cairo/process_group_symbolizer.cpp
+++ b/src/cairo/process_group_symbolizer.cpp
@@ -25,13 +25,12 @@
 // mapnik
 #include <mapnik/marker.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
-#include <mapnik/make_unique.hpp>
 #include <mapnik/text/glyph_positions.hpp>
 #include <mapnik/cairo/cairo_renderer.hpp>
 #include <mapnik/cairo/cairo_render_vector.hpp>
 
 // mapnik symbolizer generics
-#include <mapnik/renderer_common/process_group_symbolizer.hpp>
+#include <mapnik/renderer_common/render_group_symbolizer.hpp>
 
 namespace mapnik
 {
@@ -47,20 +46,19 @@ namespace {
 // to render it, and the boxes themselves should already be
 // in the detector from the placement_finder.
 template <typename T>
-struct thunk_renderer
+struct thunk_renderer : render_thunk_list_dispatch
 {
     using renderer_type = cairo_renderer<T>;
 
     thunk_renderer(renderer_type & ren,
                    cairo_context & context,
                    cairo_face_manager & face_manager,
-                   renderer_common & common,
-                   pixel_position const& offset)
+                   renderer_common & common)
         : ren_(ren), context_(context), face_manager_(face_manager),
-          common_(common), offset_(offset)
+          common_(common)
     {}
 
-    void operator()(vector_marker_render_thunk const &thunk) const
+    virtual void operator()(vector_marker_render_thunk const& thunk)
     {
         cairo_save_restore guard(context_);
         context_.set_operator(thunk.comp_op_);
@@ -78,7 +76,7 @@ struct thunk_renderer
                                      thunk.opacity_);
     }
 
-    void operator()(raster_marker_render_thunk const& thunk) const
+    virtual void operator()(raster_marker_render_thunk const& thunk)
     {
         cairo_save_restore guard(context_);
         context_.set_operator(thunk.comp_op_);
@@ -88,32 +86,24 @@ struct thunk_renderer
         context_.add_image(offset_tr, thunk.src_, thunk.opacity_);
     }
 
-    void operator()(text_render_thunk const &thunk) const
+    virtual void operator()(text_render_thunk const& thunk)
     {
         cairo_save_restore guard(context_);
         context_.set_operator(thunk.comp_op_);
 
-        render_offset_placements(
-            thunk.placements_,
-            offset_,
-            [&] (glyph_positions_ptr const& glyphs)
-            {
-                marker_info_ptr mark = glyphs->get_marker();
-                if (mark)
-                {
-                    ren_.render_marker(glyphs->marker_pos(),
-                                       *mark->marker_,
-                                       mark->transform_,
-                                       thunk.opacity_, thunk.comp_op_);
-                }
-                context_.add_text(*glyphs, face_manager_, src_over, src_over, common_.scale_factor_);
-            });
-    }
+        for (auto const& glyphs : thunk.placements_)
+        {
+            scoped_glyph_positions_offset tmp_off(*glyphs, offset_);
 
-    template <typename T0>
-    void operator()(T0 const &) const
-    {
-        throw std::runtime_error("Rendering of this type is not supported by the cairo renderer.");
+            if (auto const& mark = glyphs->get_marker())
+            {
+                ren_.render_marker(glyphs->marker_pos(),
+                                   *mark->marker_,
+                                   mark->transform_,
+                                   thunk.opacity_, thunk.comp_op_);
+            }
+            context_.add_text(*glyphs, face_manager_, src_over, src_over, common_.scale_factor_);
+        }
     }
 
 private:
@@ -121,7 +111,6 @@ private:
     cairo_context & context_;
     cairo_face_manager & face_manager_;
     renderer_common & common_;
-    pixel_position offset_;
 };
 
 } // anonymous namespace
@@ -131,16 +120,11 @@ void cairo_renderer<T>::process(group_symbolizer const& sym,
                                   mapnik::feature_impl & feature,
                                   proj_transform const& prj_trans)
 {
+    thunk_renderer<T> ren(*this, context_, face_manager_, common_);
+
     render_group_symbolizer(
         sym, feature, common_.vars_, prj_trans, common_.query_extent_, common_,
-        [&](render_thunk_list const& thunks, pixel_position const& render_offset)
-        {
-            thunk_renderer<T> ren(*this, context_, face_manager_, common_, render_offset);
-            for (render_thunk_ptr const& thunk : thunks)
-            {
-                util::apply_visitor(ren, *thunk);
-            }
-        });
+        ren);
 }
 
 template void cairo_renderer<cairo_ptr>::process(group_symbolizer const&,
diff --git a/src/cairo/process_markers_symbolizer.cpp b/src/cairo/process_markers_symbolizer.cpp
index faffd3d..73b84ec 100644
--- a/src/cairo/process_markers_symbolizer.cpp
+++ b/src/cairo/process_markers_symbolizer.cpp
@@ -25,90 +25,46 @@
 // mapnik
 #include <mapnik/cairo/cairo_renderer.hpp>
 #include <mapnik/cairo/cairo_render_vector.hpp>
-#include <mapnik/markers_placement.hpp>
-#include <mapnik/svg/svg_path_adapter.hpp>
-#include <mapnik/util/noncopyable.hpp>
-#include <mapnik/pixel_position.hpp>
-#include <mapnik/attribute.hpp>
-#include <mapnik/marker.hpp>
-#include <mapnik/marker_cache.hpp>
-#include <mapnik/marker_helpers.hpp>
-#include <mapnik/renderer_common/process_markers_symbolizer.hpp>
-
-// agg
-#include "agg/include/agg_array.h"      // for pod_bvector
-#include "agg/include/agg_trans_affine.h"  // for trans_affine, etc
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
+#include <mapnik/symbolizer.hpp>
 
 namespace mapnik
 {
 
-class feature_impl;
-class proj_transform;
-
 namespace detail {
 
-template <typename RendererContext, typename Detector>
-struct vector_markers_dispatch_cairo  : public vector_markers_dispatch<Detector>
+struct cairo_markers_renderer_context : markers_renderer_context
 {
-    vector_markers_dispatch_cairo(svg_path_ptr const& src,
-                                  svg::svg_path_adapter & path,
-                                  svg_attribute_type const& attrs,
-                                  agg::trans_affine const& marker_trans,
-                                  markers_symbolizer const& sym,
-                                  Detector & detector,
-                                  double scale_factor,
-                                  feature_impl & feature,
-                                  mapnik::attributes const& vars,
-                                  bool /* snap_to_pixels */, // only used in agg renderer currently
-                                  RendererContext const& renderer_context)
-    : vector_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        path_(path),
-        attr_(attrs),
-        ctx_(std::get<0>(renderer_context))
+    explicit cairo_markers_renderer_context(cairo_context & ctx)
+      : ctx_(ctx)
     {}
 
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
+    virtual void render_marker(svg_path_ptr const& src,
+                               svg_path_adapter & path,
+                               svg_attribute_type const& attrs,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
     {
         render_vector_marker(ctx_,
-                             path_,
-                             attr_,
-                             this->src_->bounding_box(),
+                             path,
+                             attrs,
+                             src->bounding_box(),
                              marker_tr,
-                             opacity);
+                             params.opacity);
     }
 
-private:
-    svg::svg_path_adapter & path_;
-    svg_attribute_type const& attr_;
-    cairo_context & ctx_;
-};
-
-template <typename RendererContext, typename Detector>
-struct raster_markers_dispatch_cairo : public raster_markers_dispatch<Detector>
-{
-    raster_markers_dispatch_cairo(image_rgba8 const& src,
-                                  agg::trans_affine const& marker_trans,
-                                  markers_symbolizer const& sym,
-                                  Detector & detector,
-                                  double scale_factor,
-                                  feature_impl & feature,
-                                  mapnik::attributes const& vars,
-                                  RendererContext const& renderer_context)
-    : raster_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        ctx_(std::get<0>(renderer_context)) {}
-
-    ~raster_markers_dispatch_cairo() {}
-
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
+    virtual void render_marker(image_rgba8 const& src,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
     {
-        ctx_.add_image(marker_tr, this->src_, opacity);
+        ctx_.add_image(marker_tr, src, params.opacity);
     }
 
 private:
     cairo_context & ctx_;
 };
 
-}
+} // namespace detail
 
 template <typename T>
 void cairo_renderer<T>::process(markers_symbolizer const& sym,
@@ -120,17 +76,10 @@ void cairo_renderer<T>::process(markers_symbolizer const& sym,
     context_.set_operator(comp_op);
     box2d<double> clip_box = common_.query_extent_;
 
-    auto renderer_context = std::tie(context_);
-
-    using RendererContextType = decltype(renderer_context);
-    using vector_dispatch_type = detail::vector_markers_dispatch_cairo<RendererContextType,
-                                                                       label_collision_detector4>;
+    using context_type = detail::cairo_markers_renderer_context;
+    context_type renderer_context(context_);
 
-    using raster_dispatch_type = detail::raster_markers_dispatch_cairo<RendererContextType,
-                                                                       label_collision_detector4>;
-
-
-    render_markers_symbolizer<vector_dispatch_type, raster_dispatch_type>(
+    render_markers_symbolizer(
         sym, feature, prj_trans, common_, clip_box,
         renderer_context);
 }
@@ -139,6 +88,6 @@ template void cairo_renderer<cairo_ptr>::process(markers_symbolizer const&,
                                                  mapnik::feature_impl &,
                                                  proj_transform const&);
 
-}
+} // namespace mapnik
 
 #endif // HAVE_CAIRO
diff --git a/src/color.cpp b/src/color.cpp
index 2dcbe76..1804d51 100644
--- a/src/color.cpp
+++ b/src/color.cpp
@@ -25,22 +25,11 @@
 #include <mapnik/color_factory.hpp>
 #include <mapnik/config_error.hpp>
 
-// agg
-#include "agg_color_rgba.h"
-
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/spirit/include/karma.hpp>
-#include <boost/spirit/include/phoenix_statement.hpp>
-#include <boost/spirit/include/phoenix_core.hpp>
-#include <boost/spirit/include/phoenix_operator.hpp>
-#include <boost/spirit/include/phoenix_fusion.hpp>
-#include <boost/spirit/include/phoenix_function.hpp>
 #pragma GCC diagnostic pop
 
-// stl
-#include <sstream>
-
 namespace mapnik {
 
 color::color(std::string const& str, bool premultiplied)
@@ -52,22 +41,23 @@ color::color(std::string const& str, bool premultiplied)
 std::string color::to_string() const
 {
     namespace karma = boost::spirit::karma;
-    boost::spirit::karma::_1_type _1;
     boost::spirit::karma::eps_type eps;
     boost::spirit::karma::double_type double_;
-    boost::spirit::karma::string_type kstring;
-    boost::spirit::karma::uint_generator<uint8_t,10> color_generator;
+    boost::spirit::karma::uint_generator<uint8_t,10> color_;
     std::string str;
     std::back_insert_iterator<std::string> sink(str);
-    karma::generate(sink,
+    karma::generate(sink, eps(alpha() < 255)
                     // begin grammar
-                    kstring[ boost::phoenix::if_(alpha()==255) [_1="rgb("].else_[_1="rgba("]]
-                    << color_generator[_1 = red()] << ','
-                    << color_generator[_1 = green()] << ','
-                    << color_generator[_1 = blue()]
-                    << kstring[ boost::phoenix::if_(alpha()==255) [_1 = ')'].else_[_1 =',']]
-                    << eps(alpha()<255) << double_ [_1 = alpha()/255.0]
-                    << ')'
+                    << "rgba("
+                    << color_(red()) << ','
+                    << color_(green()) << ','
+                    << color_(blue()) << ','
+                    << double_(alpha()/255.0) << ')'
+                    |
+                    "rgb("
+                    << color_(red()) << ','
+                    << color_(green()) << ','
+                    << color_(blue()) << ')'
                     // end grammar
         );
     return str;
@@ -76,7 +66,6 @@ std::string color::to_string() const
 std::string color::to_hex_string() const
 {
     namespace karma = boost::spirit::karma;
-    boost::spirit::karma::_1_type _1;
     boost::spirit::karma::hex_type hex;
     boost::spirit::karma::eps_type eps;
     boost::spirit::karma::right_align_type right_align;
@@ -85,23 +74,34 @@ std::string color::to_hex_string() const
     karma::generate(sink,
                     // begin grammar
                     '#'
-                    << right_align(2,'0')[hex[_1 = red()]]
-                    << right_align(2,'0')[hex[_1 = green()]]
-                    << right_align(2,'0')[hex[_1 = blue()]]
-                    << eps(alpha() < 255) <<  right_align(2,'0')[hex [_1 = alpha()]]
+                    << right_align(2,'0')[hex(red())]
+                    << right_align(2,'0')[hex(green())]
+                    << right_align(2,'0')[hex(blue())]
+                    << eps(alpha() < 255) << right_align(2,'0')[hex(alpha())]
                     // end grammar
         );
     return str;
 }
 
+namespace  {
+
+static std::uint8_t multiply(std::uint8_t c, std::uint8_t a)
+{
+    std::uint32_t t = c * a + 128;
+    return std::uint8_t(((t >> 8) + t) >> 8);
+}
+
+}
+
 bool color::premultiply()
 {
     if (premultiplied_) return false;
-    agg::rgba8 pre_c = agg::rgba8(red_,green_,blue_,alpha_);
-    pre_c.premultiply();
-    red_ = pre_c.r;
-    green_ = pre_c.g;
-    blue_ = pre_c.b;
+    if (alpha_ != 255)
+    {
+        red_ = multiply(red_, alpha_);
+        green_ = multiply(green_, alpha_);
+        blue_ = multiply(blue_, alpha_);
+    }
     premultiplied_ = true;
     return true;
 }
@@ -109,13 +109,23 @@ bool color::premultiply()
 bool color::demultiply()
 {
     if (!premultiplied_) return false;
-    agg::rgba8 pre_c = agg::rgba8(red_,green_,blue_,alpha_);
-    pre_c.demultiply();
-    red_ = pre_c.r;
-    green_ = pre_c.g;
-    blue_ = pre_c.b;
+    if (alpha_ < 255)
+    {
+        if (alpha_ == 0)
+        {
+            red_ = green_ = blue_ = 0;
+        }
+        else
+        {
+            std::uint32_t r = (std::uint32_t(red_) * 255) / alpha_;
+            std::uint32_t g = (std::uint32_t(green_) * 255) / alpha_;
+            std::uint32_t b = (std::uint32_t(blue_) * 255) / alpha_;
+            red_ = (r > 255) ? 255 : r;
+            green_ = (g > 255) ? 255 : g;
+            blue_ = (b > 255) ? 255 : b;
+        }
+    }
     premultiplied_ = false;
     return true;
 }
-
 }
diff --git a/src/dasharray_parser.cpp b/src/dasharray_parser.cpp
index 760caea..963a290 100644
--- a/src/dasharray_parser.cpp
+++ b/src/dasharray_parser.cpp
@@ -36,7 +36,29 @@ namespace mapnik {
 
 namespace util {
 
-bool parse_dasharray(std::string const& value, std::vector<double>& dasharray)
+namespace {
+inline bool setup_dashes(std::vector<double> & buf, dash_array & dash)
+{
+    if (buf.empty()) return false;
+    size_t size = buf.size();
+    if (size % 2 == 1)
+    {
+        buf.insert(buf.end(),buf.begin(),buf.end());
+    }
+    std::vector<double>::const_iterator pos = buf.begin();
+    while (pos != buf.end())
+    {
+        if (*pos > 0.0 || *(pos+1) > 0.0) // avoid both dash and gap eq 0.0
+        {
+            dash.emplace_back(*pos,*(pos + 1));
+        }
+        pos +=2;
+    }
+    return !buf.empty();
+}
+}
+
+bool parse_dasharray(std::string const& value, dash_array & dash)
 {
     using namespace boost::spirit;
     qi::double_type double_;
@@ -49,18 +71,19 @@ bool parse_dasharray(std::string const& value, std::vector<double>& dasharray)
     // dasharray ::= (length | percentage) (comma-wsp dasharray)?
     // no support for 'percentage' as viewport is unknown at load_map
     //
+    std::vector<double> buf;
     auto first = value.begin();
     auto last = value.end();
     bool r = qi::phrase_parse(first, last,
-                          (double_[boost::phoenix::push_back(boost::phoenix::ref(dasharray), _1)] %
+                          (double_[boost::phoenix::push_back(boost::phoenix::ref(buf), _1)] %
                           no_skip[char_(", ")]
                           | lit("none")),
                           space);
-    if (first != last)
+    if (r &&  first == last)
     {
-        return false;
+        return setup_dashes(buf, dash);
     }
-    return r;
+    return false;
 }
 
 } // end namespace util
diff --git a/src/datasource_cache.cpp b/src/datasource_cache.cpp
index c712f4a..c561a7f 100644
--- a/src/datasource_cache.cpp
+++ b/src/datasource_cache.cpp
@@ -88,7 +88,7 @@ datasource_ptr datasource_cache::create(parameters const& params)
 #ifdef MAPNIK_THREADSAFE
         std::lock_guard<std::recursive_mutex> lock(instance_mutex_);
 #endif
-        itr=plugins_.find(*type);
+        itr = plugins_.find(*type);
         if (itr == plugins_.end())
         {
             std::string s("Could not create datasource for type: '");
@@ -105,7 +105,7 @@ datasource_ptr datasource_cache::create(parameters const& params)
         }
     }
 
-    if (! itr->second->valid())
+    if (!itr->second->valid())
     {
         throw std::runtime_error(std::string("Cannot load library: ") +
                                  itr->second->get_error());
diff --git a/src/expression_node.cpp b/src/expression_node.cpp
index 377d091..380c36a 100644
--- a/src/expression_node.cpp
+++ b/src/expression_node.cpp
@@ -37,6 +37,14 @@
 namespace mapnik
 {
 
+#if defined(BOOST_REGEX_HAS_ICU)
+static void fromUTF32toUTF8(std::basic_string<UChar32> const& src, std::string & dst)
+{
+    int32_t len = safe_cast<int32_t>(src.length());
+    value_unicode_string::fromUTF32(src.data(), len).toUTF8String(dst);
+}
+#endif
+
 struct _regex_match_impl : util::noncopyable {
 #if defined(BOOST_REGEX_HAS_ICU)
     _regex_match_impl(value_unicode_string const& ustr) :
@@ -94,10 +102,7 @@ std::string regex_match_node::to_string() const
     str_ +=".match('";
     auto const& pattern = impl_.get()->pattern_;
 #if defined(BOOST_REGEX_HAS_ICU)
-    std::string utf8;
-    value_unicode_string ustr = value_unicode_string::fromUTF32( &pattern.str()[0], safe_cast<int>(pattern.str().length()));
-    to_utf8(ustr,utf8);
-    str_ += utf8;
+    fromUTF32toUTF8(pattern.str(), str_);
 #else
     str_ += pattern.str();
 #endif
@@ -141,13 +146,9 @@ std::string regex_replace_node::to_string() const
     auto const& pattern = impl_.get()->pattern_;
     auto const& format = impl_.get()->format_;
 #if defined(BOOST_REGEX_HAS_ICU)
-    std::string utf8;
-    value_unicode_string ustr = value_unicode_string::fromUTF32( &pattern.str()[0], safe_cast<int>(pattern.str().length()));
-    to_utf8(ustr,utf8);
-    str_ += utf8;
+    fromUTF32toUTF8(pattern.str(), str_);
     str_ +="','";
-    to_utf8(format ,utf8);
-    str_ += utf8;
+    format.toUTF8String(str_);
 #else
     str_ += pattern.str();
     str_ +="','";
diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp
index b0f1374..efe654b 100644
--- a/src/font_engine_freetype.cpp
+++ b/src/font_engine_freetype.cpp
@@ -27,7 +27,6 @@
 #include <mapnik/text/face.hpp>
 #include <mapnik/util/fs.hpp>
 #include <mapnik/util/file_io.hpp>
-#include <mapnik/util/singleton.hpp>
 #include <mapnik/make_unique.hpp>
 
 #pragma GCC diagnostic push
@@ -361,7 +360,7 @@ face_ptr freetype_engine::create_face(std::string const& family_name,
 face_manager::face_manager(font_library & library,
                            freetype_engine::font_file_mapping_type const& font_file_mapping,
                            freetype_engine::font_memory_cache_type const& font_cache)
-    : face_ptr_cache_(),
+    : face_cache_(new face_cache()),
       library_(library),
       font_file_mapping_(font_file_mapping),
       font_memory_cache_(font_cache)
@@ -376,8 +375,8 @@ face_manager::face_manager(font_library & library,
 
 face_ptr face_manager::get_face(std::string const& name)
 {
-    auto itr = face_ptr_cache_.find(name);
-    if (itr != face_ptr_cache_.end())
+    auto itr = face_cache_->find(name);
+    if (itr != face_cache_->end())
     {
         return itr->second;
     }
@@ -391,7 +390,7 @@ face_ptr face_manager::get_face(std::string const& name)
                                                      freetype_engine::get_cache());
         if (face)
         {
-            face_ptr_cache_.emplace(name,face);
+            face_cache_->emplace(name, face);
         }
         return face;
     }
diff --git a/src/geometry_envelope.cpp b/src/geometry_envelope.cpp
index 0aa3466..5b3ade6 100644
--- a/src/geometry_envelope.cpp
+++ b/src/geometry_envelope.cpp
@@ -31,6 +31,7 @@ template MAPNIK_DECL mapnik::box2d<double> envelope(mapnik::base_symbolizer_help
 template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_empty const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(point<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(line_string<double> const& geom);
+template MAPNIK_DECL mapnik::box2d<double> envelope(linear_ring<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(polygon<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_point<double> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_line_string<double> const& geom);
@@ -40,6 +41,7 @@ template MAPNIK_DECL mapnik::box2d<double> envelope(geometry_collection<double>
 template MAPNIK_DECL mapnik::box2d<double> envelope(geometry<std::int64_t> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(point<std::int64_t> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(line_string<std::int64_t> const& geom);
+template MAPNIK_DECL mapnik::box2d<double> envelope(linear_ring<std::int64_t> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(polygon<std::int64_t> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_point<std::int64_t> const& geom);
 template MAPNIK_DECL mapnik::box2d<double> envelope(multi_line_string<std::int64_t> const& geom);
diff --git a/src/gradient.cpp b/src/gradient.cpp
index 037cf8f..0c9c6b8 100644
--- a/src/gradient.cpp
+++ b/src/gradient.cpp
@@ -26,24 +26,6 @@
 namespace mapnik
 {
 
-static const char * gradient_strings[] = {
-    "no-gradient",
-    "linear",
-    "radial",
-    ""
-};
-
-IMPLEMENT_ENUM( gradient_e, gradient_strings )
-
-static const char * gradient_unit_strings[] = {
-    "user-space-on-use",
-    "user-space-on-use-bounding-box",
-    "object-bounding-box",
-    ""
-};
-
-IMPLEMENT_ENUM( gradient_unit_e, gradient_unit_strings )
-
 gradient::gradient()
 : transform_(),
   x1_(0),
diff --git a/src/grid/process_group_symbolizer.cpp b/src/grid/process_group_symbolizer.cpp
index f731189..a93ea9d 100644
--- a/src/grid/process_group_symbolizer.cpp
+++ b/src/grid/process_group_symbolizer.cpp
@@ -29,25 +29,13 @@
 #include <mapnik/grid/grid_renderer_base.hpp>
 #include <mapnik/grid/grid.hpp>
 #include <mapnik/grid/grid_render_marker.hpp>
-#include <mapnik/attribute_collector.hpp>
-#include <mapnik/text/placement_finder.hpp>
-#include <mapnik/text/symbolizer_helpers.hpp>
 #include <mapnik/text/renderer.hpp>
 #include <mapnik/text/glyph_positions.hpp>
 #include <mapnik/svg/svg_renderer_agg.hpp>
 #include <mapnik/svg/svg_storage.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/svg/svg_path_attributes.hpp>
-#include <mapnik/group/group_layout_manager.hpp>
-#include <mapnik/group/group_symbolizer_helper.hpp>
-#include <mapnik/util/variant.hpp>
-#include <mapnik/geom_util.hpp>
-#include <mapnik/pixel_position.hpp>
-#include <mapnik/label_collision_detector.hpp>
-
-#include <mapnik/geom_util.hpp>
-#include <mapnik/renderer_common/process_point_symbolizer.hpp>
-#include <mapnik/renderer_common/process_group_symbolizer.hpp>
+#include <mapnik/renderer_common/render_group_symbolizer.hpp>
 
 // agg
 #include "agg_trans_affine.h"
@@ -61,7 +49,7 @@ namespace mapnik {
  * in the detector from the placement_finder.
  */
 template <typename T0>
-struct thunk_renderer
+struct thunk_renderer : render_thunk_list_dispatch
 {
     using renderer_type = grid_renderer<T0>;
     using buffer_type = typename renderer_type::buffer_type;
@@ -71,13 +59,13 @@ struct thunk_renderer
                    grid_rasterizer &ras,
                    buffer_type &pixmap,
                    renderer_common &common,
-                   feature_impl &feature,
-                   pixel_position const &offset)
+                   feature_impl &feature)
         : ren_(ren), ras_(ras), pixmap_(pixmap),
-          common_(common), feature_(feature), offset_(offset)
+          common_(common), feature_(feature),
+          tex_(pixmap, src_over, common.scale_factor_)
     {}
 
-    void operator()(vector_marker_render_thunk const &thunk) const
+    virtual void operator()(vector_marker_render_thunk const& thunk)
     {
         using buf_type = grid_rendering_buffer;
         using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
@@ -106,7 +94,7 @@ struct thunk_renderer
         pixmap_.add_feature(feature_);
     }
 
-    void operator()(raster_marker_render_thunk const &thunk) const
+    virtual void operator()(raster_marker_render_thunk const& thunk)
     {
         using buf_type = grid_rendering_buffer;
         using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
@@ -122,34 +110,28 @@ struct thunk_renderer
         pixmap_.add_feature(feature_);
     }
 
-    void operator()(text_render_thunk const &thunk) const
+    virtual void operator()(text_render_thunk const &thunk)
     {
-        text_renderer_type ren(pixmap_, thunk.comp_op_, common_.scale_factor_);
+        tex_.set_comp_op(thunk.comp_op_);
+
         value_integer feature_id = feature_.id();
 
-        render_offset_placements(
-            thunk.placements_,
-            offset_,
-            [&] (glyph_positions_ptr const& glyphs)
+        for (auto const& glyphs : thunk.placements_)
+        {
+            scoped_glyph_positions_offset tmp_off(*glyphs, offset_);
+
+            if (auto const& mark = glyphs->get_marker())
             {
-                marker_info_ptr mark = glyphs->get_marker();
-                if (mark)
-                {
-                    ren_.render_marker(feature_,
-                                       glyphs->marker_pos(),
-                                       *mark->marker_,
-                                       mark->transform_,
-                                       thunk.opacity_, thunk.comp_op_);
-                }
-                ren.render(*glyphs, feature_id);
-            });
-        pixmap_.add_feature(feature_);
-    }
+                ren_.render_marker(feature_,
+                                   glyphs->marker_pos(),
+                                   *mark->marker_,
+                                   mark->transform_,
+                                   thunk.opacity_, thunk.comp_op_);
+            }
+            tex_.render(*glyphs, feature_id);
+        }
 
-    template <typename T1>
-    void operator()(T1 const &) const
-    {
-        // TODO: warning if unimplemented?
+        pixmap_.add_feature(feature_);
     }
 
 private:
@@ -158,7 +140,7 @@ private:
     buffer_type &pixmap_;
     renderer_common &common_;
     feature_impl &feature_;
-    pixel_position offset_;
+    text_renderer_type tex_;
 };
 
 template <typename T>
@@ -166,16 +148,11 @@ void  grid_renderer<T>::process(group_symbolizer const& sym,
                                 mapnik::feature_impl & feature,
                                 proj_transform const& prj_trans)
 {
+    thunk_renderer<T> ren(*this, *ras_ptr, pixmap_, common_, feature);
+
     render_group_symbolizer(
         sym, feature, common_.vars_, prj_trans, common_.query_extent_, common_,
-        [&](render_thunk_list const& thunks, pixel_position const& render_offset)
-        {
-            thunk_renderer<T> ren(*this, *ras_ptr, pixmap_, common_, feature, render_offset);
-            for (render_thunk_ptr const& thunk : thunks)
-            {
-                util::apply_visitor(ren, *thunk);
-            }
-        });
+        ren);
 }
 
 template void grid_renderer<grid>::process(group_symbolizer const&,
diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp
index b79807e..2fa71c8 100644
--- a/src/grid/process_markers_symbolizer.cpp
+++ b/src/grid/process_markers_symbolizer.cpp
@@ -44,146 +44,92 @@ porting notes -->
 */
 
 // mapnik
-#include <mapnik/feature.hpp>
-#include <mapnik/geom_util.hpp>
-#include <mapnik/marker_helpers.hpp>
 #include <mapnik/grid/grid_rasterizer.hpp>
 #include <mapnik/grid/grid_renderer.hpp>
 #include <mapnik/grid/grid_renderer_base.hpp>
 #include <mapnik/grid/grid_render_marker.hpp>
 #include <mapnik/grid/grid.hpp>
-
-#include <mapnik/debug.hpp>
-#include <mapnik/geom_util.hpp>
-#include <mapnik/marker.hpp>
-#include <mapnik/marker_cache.hpp>
-#include <mapnik/marker_helpers.hpp>
 #include <mapnik/svg/svg_renderer_agg.hpp>
 #include <mapnik/svg/svg_storage.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/svg/svg_path_attributes.hpp>
-#include <mapnik/parse_path.hpp>
-#include <mapnik/renderer_common/process_markers_symbolizer.hpp>
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
 
 // agg
 #include "agg_basics.h"
 #include "agg_rendering_buffer.h"
 #include "agg_rasterizer_scanline_aa.h"
 
-// boost
-#include <boost/optional.hpp>
-
-// stl
-#include <algorithm>
-#include <tuple>
-
 namespace mapnik {
 
 namespace detail {
 
-template <typename SvgRenderer, typename Detector, typename RendererContext>
-struct vector_markers_rasterizer_dispatch : public vector_markers_dispatch<Detector>
+template <typename SvgRenderer, typename ScanlineRenderer,
+          typename BufferType, typename RasterizerType, typename PixMapType>
+struct grid_markers_renderer_context : markers_renderer_context
 {
     using renderer_base = typename SvgRenderer::renderer_base;
     using vertex_source_type = typename SvgRenderer::vertex_source_type;
     using attribute_source_type = typename SvgRenderer::attribute_source_type;
     using pixfmt_type = typename renderer_base::pixfmt_type;
 
-    using BufferType = typename std::tuple_element<0,RendererContext>::type;
-    using RasterizerType = typename std::tuple_element<1,RendererContext>::type;
-    using PixMapType = typename std::tuple_element<2,RendererContext>::type;
-
-    vector_markers_rasterizer_dispatch(svg_path_ptr const& src,
-                                       vertex_source_type & path,
-                                       svg_attribute_type const& attrs,
-                                       agg::trans_affine const& marker_trans,
-                                       markers_symbolizer const& sym,
-                                       Detector & detector,
-                                       double scale_factor,
-                                       mapnik::feature_impl & feature,
-                                       attributes const& vars,
-                                       bool snap_to_pixels,
-                                       RendererContext const& renderer_context)
-    : vector_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        buf_(std::get<0>(renderer_context)),
+    grid_markers_renderer_context(feature_impl const& feature,
+                                  BufferType & buf,
+                                  RasterizerType & ras,
+                                  PixMapType & pixmap)
+      : feature_(feature),
+        buf_(buf),
         pixf_(buf_),
         renb_(pixf_),
-        svg_renderer_(path, attrs),
-        ras_(std::get<1>(renderer_context)),
-        pixmap_(std::get<2>(renderer_context)),
+        ras_(ras),
+        pixmap_(pixmap),
         placed_(false)
     {}
 
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
+    virtual void render_marker(svg_path_ptr const& src,
+                               svg_path_adapter & path,
+                               svg_attribute_type const& attrs,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
     {
+        SvgRenderer svg_renderer_(path, attrs);
         agg::scanline_bin sl_;
-        svg_renderer_.render_id(ras_, sl_, renb_, this->feature_.id(), marker_tr, opacity, this->src_->bounding_box());
-        if (!placed_)
-        {
-            pixmap_.add_feature(this->feature_);
-            placed_ = true;
-        }
+        svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), marker_tr,
+                                params.opacity, src->bounding_box());
+        place_feature();
     }
 
-private:
-    BufferType & buf_;
-    pixfmt_type pixf_;
-    renderer_base renb_;
-    SvgRenderer svg_renderer_;
-    RasterizerType & ras_;
-    PixMapType & pixmap_;
-    bool placed_;
-};
-
-template <typename RendererBase, typename RendererType, typename Detector, typename RendererContext>
-struct raster_markers_rasterizer_dispatch : public raster_markers_dispatch<Detector>
-{
-    using pixfmt_type = typename RendererBase::pixfmt_type;
-    using color_type = typename RendererBase::pixfmt_type::color_type;
-
-    using BufferType = typename std::tuple_element<0,RendererContext>::type;
-    using RasterizerType = typename std::tuple_element<1,RendererContext>::type;
-    using PixMapType = typename std::tuple_element<2,RendererContext>::type;
-
-    raster_markers_rasterizer_dispatch(image_rgba8 const& src,
-                                       agg::trans_affine const& marker_trans,
-                                       markers_symbolizer const& sym,
-                                       Detector & detector,
-                                       double scale_factor,
-                                       mapnik::feature_impl & feature,
-                                       attributes const& vars,
-                                       RendererContext const& renderer_context)
-    : raster_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-        buf_(std::get<0>(renderer_context)),
-        pixf_(buf_),
-        renb_(pixf_),
-        ras_(std::get<1>(renderer_context)),
-        pixmap_(std::get<2>(renderer_context)),
-        placed_(false)
-    {}
+    virtual void render_marker(image_rgba8 const& src,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
+    {
+        // In the long term this should be a visitor pattern based on the type of
+        // render src provided that converts the destination pixel type required.
+        render_raster_marker(ScanlineRenderer(renb_), ras_, src, feature_,
+                             marker_tr, params.opacity);
+        place_feature();
+    }
 
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
+    void place_feature()
     {
-        // In the long term this should be a visitor pattern based on the type of render this->src_ provided that converts
-        // the destination pixel type required.
-        render_raster_marker(RendererType(renb_), ras_, this->src_, this->feature_, marker_tr, opacity);
         if (!placed_)
         {
-            pixmap_.add_feature(this->feature_);
+            pixmap_.add_feature(feature_);
             placed_ = true;
         }
     }
 
 private:
+    feature_impl const& feature_;
     BufferType & buf_;
     pixfmt_type pixf_;
-    RendererBase renb_;
+    renderer_base renb_;
     RasterizerType & ras_;
     PixMapType & pixmap_;
     bool placed_;
 };
 
-}
+} // namespace detail
 
 template <typename T>
 void grid_renderer<T>::process(markers_symbolizer const& sym,
@@ -193,7 +139,6 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
     using buf_type = grid_rendering_buffer;
     using pixfmt_type = typename grid_renderer_base_type::pixfmt_type;
     using renderer_type = agg::renderer_scanline_bin_solid<grid_renderer_base_type>;
-    using detector_type = label_collision_detector4;
 
     using namespace mapnik::svg;
     using svg_attribute_type = agg::pod_bvector<path_attributes>;
@@ -206,22 +151,20 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
     ras_ptr->reset();
     box2d<double> clip_box = common_.query_extent_;
 
-    auto renderer_context = std::tie(render_buf,*ras_ptr,pixmap_);
-    using context_type = decltype(renderer_context);
-    using vector_dispatch_type = detail::vector_markers_rasterizer_dispatch<svg_renderer_type,
-                                                                            detector_type,
-                                                                            context_type>;
-    using raster_dispatch_type = detail::raster_markers_rasterizer_dispatch<grid_renderer_base_type,
-                                                                            renderer_type,
-                                                                            detector_type,
-                                                                            context_type>;
-    render_markers_symbolizer<vector_dispatch_type, raster_dispatch_type>(
-        sym, feature, prj_trans, common_, clip_box,renderer_context);
+    using context_type = detail::grid_markers_renderer_context<svg_renderer_type,
+                                                               renderer_type,
+                                                               buf_type,
+                                                               grid_rasterizer,
+                                                               buffer_type>;
+    context_type renderer_context(feature, render_buf, *ras_ptr, pixmap_);
+
+    render_markers_symbolizer(
+        sym, feature, prj_trans, common_, clip_box, renderer_context);
 }
 
 template void grid_renderer<grid>::process(markers_symbolizer const&,
                                            mapnik::feature_impl &,
                                            proj_transform const&);
-}
+} // namespace mapnik
 
 #endif
diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp
index 731291d..130fc1f 100644
--- a/src/grid/process_point_symbolizer.cpp
+++ b/src/grid/process_point_symbolizer.cpp
@@ -30,7 +30,6 @@
 #include <mapnik/grid/grid.hpp>
 
 #include <mapnik/geom_util.hpp>
-#include <mapnik/label_collision_detector.hpp>
 #include <mapnik/marker.hpp>
 #include <mapnik/marker_cache.hpp>
 #include <mapnik/parse_path.hpp>
diff --git a/src/group/group_layout_manager.cpp b/src/group/group_layout_manager.cpp
index fe00e7d..ad9aaad 100644
--- a/src/group/group_layout_manager.cpp
+++ b/src/group/group_layout_manager.cpp
@@ -34,19 +34,21 @@ namespace mapnik
 // This visitor will process offsets for the given layout
 struct process_layout
 {
+    using bound_box = box2d<double>;
+
     // The vector containing the existing, centered item bounding boxes
-    vector<bound_box> const& member_boxes_;
+    std::vector<bound_box> const& member_boxes_;
 
     // The vector to populate with item offsets
-    vector<pixel_position> & member_offsets_;
+    std::vector<pixel_position> & member_offsets_;
 
     // The origin point of the member boxes
     // i.e. The member boxes are positioned around input_origin,
     //      and the offset values should position them around (0,0)
     pixel_position const& input_origin_;
 
-    process_layout(vector<bound_box> const& member_bboxes,
-                   vector<pixel_position> &member_offsets,
+    process_layout(std::vector<bound_box> const& member_bboxes,
+                   std::vector<pixel_position> &member_offsets,
                    pixel_position const& input_origin)
        : member_boxes_(member_bboxes),
          member_offsets_(member_offsets),
@@ -54,9 +56,12 @@ struct process_layout
     {
     }
 
-    // arrange group memebers in centered, horizontal row
+    // arrange group members in centered, horizontal row
     void operator()(simple_row_layout const& layout) const
     {
+        member_offsets_.clear();
+        member_offsets_.reserve(member_boxes_.size());
+
         double total_width = (member_boxes_.size() - 1) * layout.get_item_margin();
         for (auto const& box : member_boxes_)
         {
@@ -66,7 +71,7 @@ struct process_layout
         double x_offset = -(total_width / 2.0);
         for (auto const& box : member_boxes_)
         {
-            member_offsets_.push_back(pixel_position(x_offset - box.minx(), -input_origin_.y));
+            member_offsets_.emplace_back(x_offset - box.minx(), -input_origin_.y);
             x_offset += box.width() + layout.get_item_margin();
         }
     }
@@ -150,7 +155,7 @@ private:
     }
 };
 
-bound_box group_layout_manager::offset_box_at(size_t i)
+box2d<double> group_layout_manager::offset_box_at(size_t i)
 {
     handle_update();
     pixel_position const& offset = member_offsets_.at(i);
diff --git a/src/image_compositing.cpp b/src/image_compositing.cpp
index 12633cb..925f56e 100644
--- a/src/image_compositing.cpp
+++ b/src/image_compositing.cpp
@@ -190,7 +190,7 @@ struct composite_visitor
           dy_(dy) {}
 
     template <typename T>
-    void operator() (T & dst);
+    void operator() (T & dst) const;
 
   private:
     image_any const& src_;
@@ -201,19 +201,19 @@ struct composite_visitor
 };
 
 template <typename T>
-void composite_visitor::operator() (T & dst)
+void composite_visitor::operator() (T & dst) const
 {
     throw std::runtime_error("Error: Composite with " + std::string(typeid(dst).name()) + " is not supported");
 }
 
 template <>
-void composite_visitor::operator()<image_rgba8> (image_rgba8 & dst)
+void composite_visitor::operator()<image_rgba8> (image_rgba8 & dst) const
 {
     composite(dst, util::get<image_rgba8>(src_), mode_, opacity_, dx_, dy_);
 }
 
 template <>
-void composite_visitor::operator()<image_gray32f> (image_gray32f & dst)
+void composite_visitor::operator()<image_gray32f> (image_gray32f & dst) const
 {
     composite(dst, util::get<image_gray32f>(src_), mode_, opacity_, dx_, dy_);
 }
diff --git a/src/image_copy.cpp b/src/image_copy.cpp
index 01225c2..04fa75c 100644
--- a/src/image_copy.cpp
+++ b/src/image_copy.cpp
@@ -37,7 +37,7 @@ struct visitor_image_copy
 {
     using dst_type = typename T0::pixel_type;
 
-    T0 operator() (image_null const&)
+    T0 operator() (image_null const&) const
     {
         throw std::runtime_error("Can not cast a null image");
     }
@@ -48,7 +48,7 @@ struct visitor_image_copy
     }
 
     template <typename T1>
-    T0 operator() (T1 const& src)
+    T0 operator() (T1 const& src) const
     {
         T0 dst(safe_cast<int>(src.width()), safe_cast<int>(src.height()), false);
         for (std::size_t y = 0; y < dst.height(); ++y)
@@ -75,7 +75,7 @@ struct visitor_image_copy_so
         throw std::runtime_error("Can not cast a null image");
     }
 
-    T0 operator() (T0 const& src)
+    T0 operator() (T0 const& src) const
     {
         if (offset_ == src.get_offset() && scaling_ == src.get_scaling())
         {
@@ -91,7 +91,7 @@ struct visitor_image_copy_so
     }
 
     template <typename T1>
-    T0 operator() (T1 const& src)
+    T0 operator() (T1 const& src) const
     {
         double src_offset = src.get_offset();
         double src_scaling = src.get_scaling();
diff --git a/utils/mapnik-index/process_geojson_file.hpp b/src/image_filter_grammar.cpp
similarity index 70%
copy from utils/mapnik-index/process_geojson_file.hpp
copy to src/image_filter_grammar.cpp
index a4d468a..9a4bea3 100644
--- a/utils/mapnik-index/process_geojson_file.hpp
+++ b/src/image_filter_grammar.cpp
@@ -2,7 +2,7 @@
  *
  * This file is part of Mapnik (c++ mapping toolkit)
  *
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2016 Artem Pavlenko
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,17 +20,10 @@
  *
  *****************************************************************************/
 
-#ifndef MAPNIK_UTILS_PROCESS_GEOJSON_FILE_HPP
-#define MAPNIK_UTILS_PROCESS_GEOJSON_FILE_HPP
+#include <mapnik/image_filter_grammar.hpp>
+#include <mapnik/image_filter_grammar_impl.hpp>
+#include <mapnik/image_filter_types.hpp>
+#include <string>
+#include <vector>
 
-#include <utility>
-#include <mapnik/box2d.hpp>
-
-namespace mapnik { namespace detail {
-
-template <typename T>
-std::pair<bool, box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features);
-
-}}
-
-#endif // MAPNIK_UTILS_PROCESS_GEOJSON_FILE_HPP
+template struct mapnik::image_filter_grammar<std::string::const_iterator,std::vector<mapnik::filter::filter_type>>;
diff --git a/src/image_filter_types.cpp b/src/image_filter_types.cpp
index 6755757..d308043 100644
--- a/src/image_filter_types.cpp
+++ b/src/image_filter_types.cpp
@@ -22,7 +22,6 @@
 // mapnik
 #include <mapnik/image_filter_types.hpp>
 #include <mapnik/image_filter_grammar.hpp>
-#include <mapnik/image_filter_grammar_impl.hpp>
 
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
diff --git a/src/image_util.cpp b/src/image_util.cpp
index 79d7e0a..43c5991 100644
--- a/src/image_util.cpp
+++ b/src/image_util.cpp
@@ -111,7 +111,7 @@ MAPNIK_DECL void save_to_stream(T const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver_pal visitor(stream, t, palette);
             mapnik::util::apply_visitor(visitor, image);
@@ -141,7 +141,7 @@ MAPNIK_DECL void save_to_stream<image_rgba8>(image_rgba8 const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver_pal visitor(stream, t, palette);
             visitor(image);
@@ -172,7 +172,7 @@ MAPNIK_DECL void save_to_stream<image_view_rgba8>(image_view_rgba8 const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver_pal visitor(stream, t, palette);
             visitor(image);
@@ -200,7 +200,7 @@ MAPNIK_DECL void save_to_stream(T const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver visitor(stream, t);
             util::apply_visitor(visitor, image);
@@ -236,7 +236,7 @@ MAPNIK_DECL void save_to_stream<image_rgba8>(image_rgba8 const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver visitor(stream, t);
             visitor(image);
@@ -276,7 +276,7 @@ MAPNIK_DECL void save_to_stream<image_view_rgba8>(image_view_rgba8 const& image,
     {
         std::string t = type;
         std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-        if (t == "png" || boost::algorithm::starts_with(t, "png"))
+        if (boost::algorithm::starts_with(t, "png"))
         {
             png_saver visitor(stream, t);
             visitor(image);
@@ -2087,7 +2087,7 @@ struct visitor_view_to_stream
         : os_(os) {}
 
     template <typename T>
-    void operator() (T const& view)
+    void operator() (T const& view) const
     {
         for (std::size_t i=0;i<view.height();i++)
         {
diff --git a/src/image_util_png.cpp b/src/image_util_png.cpp
index f2b0517..f09d580 100644
--- a/src/image_util_png.cpp
+++ b/src/image_util_png.cpp
@@ -85,7 +85,7 @@ void handle_png_options(std::string const& type,
         }
         else if (key == "e" && val && *val == "miniz")
         {
-            opts.use_miniz = true;
+            throw image_writer_exception("miniz support has been removed from Mapnik");
         }
         else if (key == "c")
         {
@@ -168,7 +168,7 @@ void handle_png_options(std::string const& type,
     {
         throw image_writer_exception("invalid gamma parameter: unavailable for true color (non-paletted) images");
     }
-    if ((opts.use_miniz == false) && opts.compression > Z_BEST_COMPRESSION)
+    if (opts.compression > Z_BEST_COMPRESSION)
     {
         throw image_writer_exception("invalid compression value: (only -1 through 9 are valid)");
     }
diff --git a/src/mapped_memory_cache.cpp b/src/mapped_memory_cache.cpp
index db4fce8..f35a379 100644
--- a/src/mapped_memory_cache.cpp
+++ b/src/mapped_memory_cache.cpp
@@ -37,6 +37,8 @@
 namespace mapnik
 {
 
+template class singleton<mapped_memory_cache, CreateStatic>;
+
 void mapped_memory_cache::clear()
 {
 #ifdef MAPNIK_THREADSAFE
@@ -58,6 +60,7 @@ boost::optional<mapped_region_ptr> mapped_memory_cache::find(std::string const&
 #ifdef MAPNIK_THREADSAFE
     std::lock_guard<std::mutex> lock(mutex_);
 #endif
+
     using iterator_type = std::unordered_map<std::string, mapped_region_ptr>::const_iterator;
     boost::optional<mapped_region_ptr> result;
     iterator_type itr = cache_.find(uri);
@@ -76,7 +79,7 @@ boost::optional<mapped_region_ptr> mapped_memory_cache::find(std::string const&
             result.reset(region);
             if (update_cache)
             {
-                cache_.emplace(uri,*result);
+                cache_.emplace(uri, *result);
             }
             return result;
         }
diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp
index 6641218..9e40622 100644
--- a/src/marker_cache.cpp
+++ b/src/marker_cache.cpp
@@ -118,19 +118,19 @@ namespace detail
 
 struct visitor_create_marker
 {
-    marker operator() (image_rgba8 & data)
+    marker operator() (image_rgba8 & data) const
     {
         mapnik::premultiply_alpha(data);
         return mapnik::marker(mapnik::marker_rgba8(data));
     }
 
-    marker operator() (image_null &)
+    marker operator() (image_null &) const
     {
         throw std::runtime_error("Can not make marker from null image data type");
     }
 
     template <typename T>
-    marker operator() (T &)
+    marker operator() (T &) const
     {
         throw std::runtime_error("Can not make marker from this data type");
     }
diff --git a/src/memory_datasource.cpp b/src/memory_datasource.cpp
index f7bd61d..3c18220 100644
--- a/src/memory_datasource.cpp
+++ b/src/memory_datasource.cpp
@@ -73,7 +73,8 @@ memory_datasource::memory_datasource(parameters const& params)
       desc_(memory_datasource::name(),
             *params.get<std::string>("encoding","utf-8")),
       type_(datasource::Vector),
-      bbox_check_(*params.get<boolean_type>("bbox_check", true)) {}
+      bbox_check_(*params.get<boolean_type>("bbox_check", true)),
+      type_set_(false) {}
 
 memory_datasource::~memory_datasource() {}
 
@@ -81,6 +82,30 @@ void memory_datasource::push(feature_ptr feature)
 {
     // TODO - collect attribute descriptors?
     //desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer));
+    if (feature->get_raster())
+    {
+        // if a feature has a raster_ptr set it must be of raster type.
+        if (!type_set_)
+        {
+            type_ = datasource::Raster;
+            type_set_ = true;
+        }
+        else if (type_ == datasource::Vector)
+        {
+            throw std::runtime_error("Can not add a raster feature to a memory datasource that contains vectors");
+        }
+    }
+    else 
+    {
+        if (!type_set_)
+        {
+            type_set_ = true;
+        }
+        else if (type_ == datasource::Raster)
+        {
+            throw std::runtime_error("Can not add a vector feature to a memory datasource that contains rasters");
+        }
+    }
     features_.push_back(feature);
     dirty_extent_ = true;
 }
@@ -107,6 +132,7 @@ featureset_ptr memory_datasource::features_at_point(coord2d const& pt, double to
 void memory_datasource::set_envelope(box2d<double> const& box)
 {
     extent_ = box;
+    dirty_extent_ = false;
 }
 
 box2d<double> memory_datasource::envelope() const
diff --git a/src/miniz.c b/src/miniz.c
deleted file mode 100644
index c45bb42..0000000
--- a/src/miniz.c
+++ /dev/null
@@ -1,4834 +0,0 @@
-/* miniz.c v1.14 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
-   See "unlicense" statement at the end of this file.
-   Rich Geldreich <richgel99 at gmail.com>, last updated May 20, 2012
-   Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
-
-   Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
-   MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
-
-   * Change History
-     5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
-     5/19/12 v1.13 - From jason at cornsyrup.org and kelwert at mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
-       Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
-       Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
-       Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
-       "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
-       Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
-       Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
-       Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
-       Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
-       Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
-     4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
-      level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced at valvesoftware.com> for the feedback/bug report.
-     5/28/11 v1.11 - Added statement from unlicense.org
-     5/27/11 v1.10 - Substantial compressor optimizations:
-      Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
-      Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
-      Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
-      Refactored the compression code for better readability and maintainability.
-      Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
-      drop in throughput on some files).
-     5/15/11 v1.09 - Initial stable release.
-
-   * Low-level Deflate/Inflate implementation notes:
-
-     Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
-     greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
-     approximately as well as zlib.
-
-     Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
-     coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
-     block large enough to hold the entire file.
-
-     The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
-
-   * zlib-style API notes:
-
-     miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
-     zlib replacement in many apps:
-        The z_stream struct, optional memory allocation callbacks
-        deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
-        inflateInit/inflateInit2/inflate/inflateEnd
-        compress, compress2, compressBound, uncompress
-        CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
-        Supports raw deflate streams or standard zlib streams with adler-32 checking.
-
-     Limitations:
-      The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
-      I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
-      there are no guarantees that miniz.c pulls this off perfectly.
-
-   * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
-     Alex Evans. Supports 1-4 bytes/pixel images.
-
-   * ZIP archive API notes:
-
-     The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
-     get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
-     existing archives, create new archives, append new files to existing archives, or clone archive data from
-     one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
-     or you can specify custom file read/write callbacks.
-
-     - Archive reading: Just call this function to read a single file from a disk archive:
-
-      void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
-        size_t *pSize, mz_uint zip_flags);
-
-     For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
-     directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
-
-     - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
-
-     int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
-
-     The locate operation can optionally check file comments too, which (as one example) can be used to identify
-     multiple versions of the same file in an archive. This function uses a simple linear search through the central
-     directory, so it's not very fast.
-
-     Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
-     retrieve detailed info on each file by calling mz_zip_reader_file_stat().
-
-     - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
-     to disk and builds an exact image of the central directory in memory. The central directory image is written
-     all at once at the end of the archive file when the archive is finalized.
-
-     The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
-     which can be useful when the archive will be read from optical media. Also, the writer supports placing
-     arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
-     readable by any ZIP tool.
-
-     - Archive appending: The simple way to add a single file to an archive is to call this function:
-
-      mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
-        const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
-
-     The archive will be created if it doesn't already exist, otherwise it'll be appended to.
-     Note the appending is done in-place and is not an atomic operation, so if something goes wrong
-     during the operation it's possible the archive could be left without a central directory (although the local
-     file headers and file data will be fine, so the archive will be recoverable).
-
-     For more complex archive modification scenarios:
-     1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
-     preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
-     compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
-     you're done. This is safe but requires a bunch of temporary disk space or heap memory.
-
-     2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
-     append new files as needed, then finalize the archive which will write an updated central directory to the
-     original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
-     possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
-
-     - ZIP archive support limitations:
-     No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
-     Requires streams capable of seeking.
-
-   * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
-     below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
-
-   * Important: For best perf. be sure to customize the below macros for your target platform:
-     #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
-     #define MINIZ_LITTLE_ENDIAN 1
-     #define MINIZ_HAS_64BIT_REGISTERS 1
-*/
-
-#ifndef MINIZ_HEADER_INCLUDED
-#define MINIZ_HEADER_INCLUDED
-
-#include <stdlib.h>
-
-#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
-#include <time.h>
-#endif
-
-// Defines to completely disable specific portions of miniz.c:
-// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
-
-// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
-//#define MINIZ_NO_STDIO
-
-// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
-// get/set file times.
-//#define MINIZ_NO_TIME
-
-// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
-//#define MINIZ_NO_ARCHIVE_APIS
-
-// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
-//#define MINIZ_NO_ARCHIVE_WRITING_APIS
-
-// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
-//#define MINIZ_NO_ZLIB_APIS
-
-// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
-//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
-
-// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
-// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
-// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
-// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
-//#define MINIZ_NO_MALLOC
-
-#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
-// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
-#define MINIZ_X86_OR_X64_CPU 1
-#endif
-
-#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
-// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
-#define MINIZ_LITTLE_ENDIAN 1
-#endif
-
-#if MINIZ_X86_OR_X64_CPU
-// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
-#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
-#endif
-
-#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
-// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
-#define MINIZ_HAS_64BIT_REGISTERS 1
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// ------------------- zlib-style API Definitions.
-
-// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
-typedef unsigned long mz_ulong;
-
-// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
-void mz_free(void *p);
-
-#define MZ_ADLER32_INIT (1)
-// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
-mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
-
-#define MZ_CRC32_INIT (0)
-// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
-mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
-
-// Compression strategies.
-enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
-
-// Method
-#define MZ_DEFLATED 8
-
-#ifndef MINIZ_NO_ZLIB_APIS
-
-// Heap allocation callbacks.
-// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
-typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
-typedef void (*mz_free_func)(void *opaque, void *address);
-typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
-
-#define MZ_VERSION          "9.1.14"
-#define MZ_VERNUM           0x91E0
-#define MZ_VER_MAJOR        9
-#define MZ_VER_MINOR        1
-#define MZ_VER_REVISION     14
-#define MZ_VER_SUBREVISION  0
-
-// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
-enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
-
-// Return status codes. MZ_PARAM_ERROR is non-standard.
-enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
-
-// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
-enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
-
-// Window bits
-#define MZ_DEFAULT_WINDOW_BITS 15
-
-struct mz_internal_state;
-
-// Compression/decompression stream struct.
-typedef struct mz_stream_s
-{
-  const unsigned char *next_in;     // pointer to next byte to read
-  unsigned int avail_in;            // number of bytes available at next_in
-  mz_ulong total_in;                // total number of bytes consumed so far
-
-  unsigned char *next_out;          // pointer to next byte to write
-  unsigned int avail_out;           // number of bytes that can be written to next_out
-  mz_ulong total_out;               // total number of bytes produced so far
-
-  char *msg;                        // error msg (unused)
-  struct mz_internal_state *state;  // internal state, allocated by zalloc/zfree
-
-  mz_alloc_func zalloc;             // optional heap allocation function (defaults to malloc)
-  mz_free_func zfree;               // optional heap free function (defaults to free)
-  void *opaque;                     // heap alloc function user pointer
-
-  int data_type;                    // data_type (unused)
-  mz_ulong adler;                   // adler32 of the source or uncompressed data
-  mz_ulong reserved;                // not used
-} mz_stream;
-
-typedef mz_stream *mz_streamp;
-
-// Returns the version string of miniz.c.
-const char *mz_version(void);
-
-// mz_deflateInit() initializes a compressor with default options:
-// Parameters:
-//  pStream must point to an initialized mz_stream struct.
-//  level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
-//  level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
-//  (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
-// Return values:
-//  MZ_OK on success.
-//  MZ_STREAM_ERROR if the stream is bogus.
-//  MZ_PARAM_ERROR if the input parameters are bogus.
-//  MZ_MEM_ERROR on out of memory.
-int mz_deflateInit(mz_streamp pStream, int level);
-
-// mz_deflateInit2() is like mz_deflate(), except with more control:
-// Additional parameters:
-//   method must be MZ_DEFLATED
-//   window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
-//   mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
-int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
-
-// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
-int mz_deflateReset(mz_streamp pStream);
-
-// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
-// Parameters:
-//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
-//   flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
-// Return values:
-//   MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
-//   MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
-//   MZ_STREAM_ERROR if the stream is bogus.
-//   MZ_PARAM_ERROR if one of the parameters is invalid.
-//   MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
-int mz_deflate(mz_streamp pStream, int flush);
-
-// mz_deflateEnd() deinitializes a compressor:
-// Return values:
-//  MZ_OK on success.
-//  MZ_STREAM_ERROR if the stream is bogus.
-int mz_deflateEnd(mz_streamp pStream);
-
-// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
-mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
-
-// Single-call compression functions mz_compress() and mz_compress2():
-// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
-int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
-int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
-
-// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
-mz_ulong mz_compressBound(mz_ulong source_len);
-
-// Initializes a decompressor.
-int mz_inflateInit(mz_streamp pStream);
-
-// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
-// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
-int mz_inflateInit2(mz_streamp pStream, int window_bits);
-
-// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
-// Parameters:
-//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
-//   flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
-//   On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
-//   MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
-// Return values:
-//   MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
-//   MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
-//   MZ_STREAM_ERROR if the stream is bogus.
-//   MZ_DATA_ERROR if the deflate stream is invalid.
-//   MZ_PARAM_ERROR if one of the parameters is invalid.
-//   MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
-//   with more input data, or with more room in the output buffer (except when using single call decompression, described above).
-int mz_inflate(mz_streamp pStream, int flush);
-
-// Deinitializes a decompressor.
-int mz_inflateEnd(mz_streamp pStream);
-
-// Single-call decompression.
-// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
-int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
-
-// Returns a string description of the specified error code, or NULL if the error code is invalid.
-const char *mz_error(int err);
-
-// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
-// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
-#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
-  typedef unsigned char Byte;
-  typedef unsigned int uInt;
-  typedef mz_ulong uLong;
-  typedef Byte Bytef;
-  typedef uInt uIntf;
-  typedef char charf;
-  typedef int intf;
-  typedef void *voidpf;
-  typedef uLong uLongf;
-  typedef void *voidp;
-  typedef void *const voidpc;
-  #define Z_NULL                0
-  #define Z_NO_FLUSH            MZ_NO_FLUSH
-  #define Z_PARTIAL_FLUSH       MZ_PARTIAL_FLUSH
-  #define Z_SYNC_FLUSH          MZ_SYNC_FLUSH
-  #define Z_FULL_FLUSH          MZ_FULL_FLUSH
-  #define Z_FINISH              MZ_FINISH
-  #define Z_BLOCK               MZ_BLOCK
-  #define Z_OK                  MZ_OK
-  #define Z_STREAM_END          MZ_STREAM_END
-  #define Z_NEED_DICT           MZ_NEED_DICT
-  #define Z_ERRNO               MZ_ERRNO
-  #define Z_STREAM_ERROR        MZ_STREAM_ERROR
-  #define Z_DATA_ERROR          MZ_DATA_ERROR
-  #define Z_MEM_ERROR           MZ_MEM_ERROR
-  #define Z_BUF_ERROR           MZ_BUF_ERROR
-  #define Z_VERSION_ERROR       MZ_VERSION_ERROR
-  #define Z_PARAM_ERROR         MZ_PARAM_ERROR
-  #define Z_NO_COMPRESSION      MZ_NO_COMPRESSION
-  #define Z_BEST_SPEED          MZ_BEST_SPEED
-  #define Z_BEST_COMPRESSION    MZ_BEST_COMPRESSION
-  #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
-  #define Z_DEFAULT_STRATEGY    MZ_DEFAULT_STRATEGY
-  #define Z_FILTERED            MZ_FILTERED
-  #define Z_HUFFMAN_ONLY        MZ_HUFFMAN_ONLY
-  #define Z_RLE                 MZ_RLE
-  #define Z_FIXED               MZ_FIXED
-  #define Z_DEFLATED            MZ_DEFLATED
-  #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
-  #define alloc_func            mz_alloc_func
-  #define free_func             mz_free_func
-  #define internal_state        mz_internal_state
-  #define z_stream              mz_stream
-  #define deflateInit           mz_deflateInit
-  #define deflateInit2          mz_deflateInit2
-  #define deflateReset          mz_deflateReset
-  #define deflate               mz_deflate
-  #define deflateEnd            mz_deflateEnd
-  #define deflateBound          mz_deflateBound
-  #define compress              mz_compress
-  #define compress2             mz_compress2
-  #define compressBound         mz_compressBound
-  #define inflateInit           mz_inflateInit
-  #define inflateInit2          mz_inflateInit2
-  #define inflate               mz_inflate
-  #define inflateEnd            mz_inflateEnd
-  #define uncompress            mz_uncompress
-  #define crc32                 mz_crc32
-  #define adler32               mz_adler32
-  #define MAX_WBITS             15
-  #define MAX_MEM_LEVEL         9
-  #define zError                mz_error
-  #define ZLIB_VERSION          MZ_VERSION
-  #define ZLIB_VERNUM           MZ_VERNUM
-  #define ZLIB_VER_MAJOR        MZ_VER_MAJOR
-  #define ZLIB_VER_MINOR        MZ_VER_MINOR
-  #define ZLIB_VER_REVISION     MZ_VER_REVISION
-  #define ZLIB_VER_SUBREVISION  MZ_VER_SUBREVISION
-  #define zlibVersion           mz_version
-  #define zlib_version          mz_version()
-#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
-
-#endif // MINIZ_NO_ZLIB_APIS
-
-// ------------------- Types and macros
-
-typedef unsigned char mz_uint8;
-typedef signed short mz_int16;
-typedef unsigned short mz_uint16;
-typedef unsigned int mz_uint32;
-typedef unsigned int mz_uint;
-typedef long long mz_int64;
-typedef unsigned long long mz_uint64;
-typedef int mz_bool;
-
-#define MZ_FALSE (0)
-#define MZ_TRUE (1)
-
-// Works around MSVC's spammy "warning C4127: conditional expression is constant" message.
-#ifdef _MSC_VER
-   #define MZ_MACRO_END while (0, 0)
-#else
-   #define MZ_MACRO_END while (0)
-#endif
-
-// ------------------- ZIP archive reading/writing
-
-#ifndef MINIZ_NO_ARCHIVE_APIS
-
-enum
-{
-  MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
-  MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
-  MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
-};
-
-typedef struct
-{
-  mz_uint32 m_file_index;
-  mz_uint32 m_central_dir_ofs;
-  mz_uint16 m_version_made_by;
-  mz_uint16 m_version_needed;
-  mz_uint16 m_bit_flag;
-  mz_uint16 m_method;
-#ifndef MINIZ_NO_TIME
-  time_t m_time;
-#endif
-  mz_uint32 m_crc32;
-  mz_uint64 m_comp_size;
-  mz_uint64 m_uncomp_size;
-  mz_uint16 m_internal_attr;
-  mz_uint32 m_external_attr;
-  mz_uint64 m_local_header_ofs;
-  mz_uint32 m_comment_size;
-  char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
-  char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
-} mz_zip_archive_file_stat;
-
-typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
-typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
-
-struct mz_zip_internal_state_tag;
-typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
-
-typedef enum
-{
-  MZ_ZIP_MODE_INVALID = 0,
-  MZ_ZIP_MODE_READING = 1,
-  MZ_ZIP_MODE_WRITING = 2,
-  MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
-} mz_zip_mode;
-
-typedef struct
-{
-  mz_uint64 m_archive_size;
-  mz_uint64 m_central_directory_file_ofs;
-  mz_uint m_total_files;
-  mz_zip_mode m_zip_mode;
-
-  mz_uint m_file_offset_alignment;
-
-  mz_alloc_func m_pAlloc;
-  mz_free_func m_pFree;
-  mz_realloc_func m_pRealloc;
-  void *m_pAlloc_opaque;
-
-  mz_file_read_func m_pRead;
-  mz_file_write_func m_pWrite;
-  void *m_pIO_opaque;
-
-  mz_zip_internal_state *m_pState;
-
-} mz_zip_archive;
-
-typedef enum
-{
-  MZ_ZIP_FLAG_CASE_SENSITIVE                = 0x0100,
-  MZ_ZIP_FLAG_IGNORE_PATH                   = 0x0200,
-  MZ_ZIP_FLAG_COMPRESSED_DATA               = 0x0400,
-  MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
-} mz_zip_flags;
-
-// ZIP archive reading
-
-// Inits a ZIP archive reader.
-// These functions read and validate the archive's central directory.
-mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
-mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
-
-#ifndef MINIZ_NO_STDIO
-mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
-#endif
-
-// Returns the total number of files in the archive.
-mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
-
-// Returns detailed information about an archive file entry.
-mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
-
-// Determines if an archive file entry is a directory entry.
-mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
-mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
-
-// Retrieves the filename of an archive file entry.
-// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
-mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
-
-// Attempts to locates a file in the archive's central directory.
-// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
-// Returns -1 if the file cannot be found.
-int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
-
-// Extracts a archive file to a memory buffer using no memory allocation.
-mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
-mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
-
-// Extracts a archive file to a memory buffer.
-mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
-mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
-
-// Extracts a archive file to a dynamically allocated heap buffer.
-void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
-void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
-
-// Extracts a archive file using a callback function to output the file's data.
-mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
-mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
-
-#ifndef MINIZ_NO_STDIO
-// Extracts a archive file to a disk file and sets its last accessed and modified times.
-// This function only extracts files, not archive directory records.
-mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
-mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
-#endif
-
-// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
-mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
-
-// ZIP archive writing
-
-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
-
-// Inits a ZIP archive writer.
-mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
-mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
-
-#ifndef MINIZ_NO_STDIO
-mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
-#endif
-
-// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
-// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
-// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
-// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
-// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
-// the archive is finalized the file's central directory will be hosed.
-mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
-
-// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
-// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
-// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
-mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
-mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
-
-#ifndef MINIZ_NO_STDIO
-// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
-// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
-mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
-#endif
-
-// Adds a file to an archive by fully cloning the data from another archive.
-// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
-mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
-
-// Finalizes the archive by writing the central directory records followed by the end of central directory record.
-// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
-// An archive must be manually finalized by calling this function for it to be valid.
-mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
-mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
-
-// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
-// Note for the archive to be valid, it must have been finalized before ending.
-mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
-
-// Misc. high-level helper functions:
-
-// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
-// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
-mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
-
-// Reads a single file from an archive into a heap block.
-// Returns NULL on failure.
-void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);
-
-#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
-
-#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
-
-// ------------------- Low-level Decompression API Definitions
-
-// Decompression flags used by tinfl_decompress().
-// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
-// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
-// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
-// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
-enum
-{
-  TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
-  TINFL_FLAG_HAS_MORE_INPUT = 2,
-  TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
-  TINFL_FLAG_COMPUTE_ADLER32 = 8
-};
-
-// High level decompression functions:
-// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
-// On entry:
-//  pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
-// On return:
-//  Function returns a pointer to the decompressed data, or NULL on failure.
-//  *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
-//  The caller must call mz_free() on the returned block when it's no longer needed.
-void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
-
-// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
-// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
-#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
-size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
-
-// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
-// Returns 1 on success or 0 on failure.
-typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
-int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
-
-struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
-
-// Max size of LZ dictionary.
-#define TINFL_LZ_DICT_SIZE 32768
-
-// Return status.
-typedef enum
-{
-  TINFL_STATUS_BAD_PARAM = -3,
-  TINFL_STATUS_ADLER32_MISMATCH = -2,
-  TINFL_STATUS_FAILED = -1,
-  TINFL_STATUS_DONE = 0,
-  TINFL_STATUS_NEEDS_MORE_INPUT = 1,
-  TINFL_STATUS_HAS_MORE_OUTPUT = 2
-} tinfl_status;
-
-// Initializes the decompressor to its initial state.
-#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
-#define tinfl_get_adler32(r) (r)->m_check_adler32
-
-// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
-// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
-tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
-
-// Internal/private bits follow.
-enum
-{
-  TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
-  TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
-};
-
-typedef struct
-{
-  mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
-  mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
-} tinfl_huff_table;
-
-#if MINIZ_HAS_64BIT_REGISTERS
-  #define TINFL_USE_64BIT_BITBUF 1
-#endif
-
-#if TINFL_USE_64BIT_BITBUF
-  typedef mz_uint64 tinfl_bit_buf_t;
-  #define TINFL_BITBUF_SIZE (64)
-#else
-  typedef mz_uint32 tinfl_bit_buf_t;
-  #define TINFL_BITBUF_SIZE (32)
-#endif
-
-struct tinfl_decompressor_tag
-{
-  mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
-  tinfl_bit_buf_t m_bit_buf;
-  size_t m_dist_from_out_buf_start;
-  tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
-  mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
-};
-
-// ------------------- Low-level Compression API Definitions
-
-// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
-#define TDEFL_LESS_MEMORY 0
-
-// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
-// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
-enum
-{
-  TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
-};
-
-// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
-// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
-// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
-// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
-// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
-// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
-// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
-// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
-enum
-{
-  TDEFL_WRITE_ZLIB_HEADER             = 0x01000,
-  TDEFL_COMPUTE_ADLER32               = 0x02000,
-  TDEFL_GREEDY_PARSING_FLAG           = 0x04000,
-  TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
-  TDEFL_RLE_MATCHES                   = 0x10000,
-  TDEFL_FILTER_MATCHES                = 0x20000,
-  TDEFL_FORCE_ALL_STATIC_BLOCKS       = 0x40000,
-  TDEFL_FORCE_ALL_RAW_BLOCKS          = 0x80000
-};
-
-// High level compression functions:
-// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
-// On entry:
-//  pSrc_buf, src_buf_len: Pointer and size of source block to compress.
-//  flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
-// On return:
-//  Function returns a pointer to the compressed data, or NULL on failure.
-//  *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
-//  The caller must free() the returned block when it's no longer needed.
-void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
-
-// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
-// Returns 0 on failure.
-size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
-
-// Compresses an image to a compressed PNG file in memory.
-// On entry:
-//  pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. 
-//  The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
-// On return:
-//  Function returns a pointer to the compressed data, or NULL on failure.
-//  *pLen_out will be set to the size of the PNG image file.
-//  The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
-void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
-
-// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
-typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, size_t len, void *pUser);
-
-// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
-mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
-
-enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
-
-// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
-#if TDEFL_LESS_MEMORY
-enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
-#else
-enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
-#endif
-
-// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
-typedef enum
-{
-  TDEFL_STATUS_BAD_PARAM = -2,
-  TDEFL_STATUS_PUT_BUF_FAILED = -1,
-  TDEFL_STATUS_OKAY = 0,
-  TDEFL_STATUS_DONE = 1,
-} tdefl_status;
-
-// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
-typedef enum
-{
-  TDEFL_NO_FLUSH = 0,
-  TDEFL_SYNC_FLUSH = 2,
-  TDEFL_FULL_FLUSH = 3,
-  TDEFL_FINISH = 4
-} tdefl_flush;
-
-// tdefl's compression state structure.
-struct tdefl_compressor
-{
-  tdefl_put_buf_func_ptr m_pPut_buf_func;
-  void *m_pPut_buf_user;
-  mz_uint m_flags, m_max_probes[2];
-  int m_greedy_parsing;
-  mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
-  mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
-  mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
-  mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
-  tdefl_status m_prev_return_status;
-  const void *m_pIn_buf;
-  void *m_pOut_buf;
-  size_t *m_pIn_buf_size, *m_pOut_buf_size;
-  tdefl_flush m_flush;
-  const mz_uint8 *m_pSrc;
-  size_t m_src_buf_left, m_out_buf_ofs;
-  mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
-  mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
-  mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
-  mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
-  mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
-  mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
-  mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
-  mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
-};
-
-// Initializes the compressor.
-// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
-// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
-// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
-// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
-tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
-
-// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
-tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
-
-// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
-// tdefl_compress_buffer() always consumes the entire input buffer.
-tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
-
-tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
-mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
-
-// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
-#ifndef MINIZ_NO_ZLIB_APIS
-// Create tdefl_compress() flags given zlib-style compression parameters.
-// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
-// window_bits may be -15 (raw deflate) or 15 (zlib)
-// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
-mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
-#endif // #ifndef MINIZ_NO_ZLIB_APIS
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // MINIZ_HEADER_INCLUDED
-
-// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
-
-#ifndef MINIZ_HEADER_FILE_ONLY
-
-typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
-typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
-typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];
-
-#include <string.h>
-#include <assert.h>
-
-#define MZ_ASSERT(x) assert(x)
-
-#ifdef MINIZ_NO_MALLOC
-  #define MZ_MALLOC(x) NULL
-  #define MZ_FREE(x) (void)x, ((void)0)
-  #define MZ_REALLOC(p, x) NULL
-#else
-  #define MZ_MALLOC(x) malloc(x)
-  #define MZ_FREE(x) free(x)
-  #define MZ_REALLOC(p, x) realloc(p, x)
-#endif
-
-#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
-#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
-#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
-
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
-  #define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
-  #define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
-#else
-  #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
-  #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
-#endif
-
-#ifdef _MSC_VER
-  #define MZ_FORCEINLINE __forceinline
-#elif defined(__GNUC__)
-  #define MZ_FORCEINLINE inline __attribute__((__always_inline__))
-#else
-  #define MZ_FORCEINLINE inline
-#endif
-
-#ifdef __cplusplus
-  extern "C" {
-#endif
-
-// ------------------- zlib-style API's
-
-mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
-{
-  mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
-  if (!ptr) return MZ_ADLER32_INIT;
-  while (buf_len) {
-    for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
-      s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
-      s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
-    }
-    for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
-    s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
-  }
-  return (s2 << 16) + s1;
-}
-
-// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
-mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
-{
-  static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
-    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
-  mz_uint32 crcu32 = (mz_uint32)crc;
-  if (!ptr) return MZ_CRC32_INIT;
-  crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
-  return ~crcu32;
-}
-
-void mz_free(void *p)
-{
-  MZ_FREE(p);
-}
-
-#ifndef MINIZ_NO_ZLIB_APIS
-
-static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
-static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
-//static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }
-
-const char *mz_version(void)
-{
-  return MZ_VERSION;
-}
-
-int mz_deflateInit(mz_streamp pStream, int level)
-{
-  return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
-}
-
-int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
-{
-  tdefl_compressor *pComp;
-  mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
-
-  if (!pStream) return MZ_STREAM_ERROR;
-  if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;
-
-  pStream->data_type = 0;
-  pStream->adler = MZ_ADLER32_INIT;
-  pStream->msg = NULL;
-  pStream->reserved = 0;
-  pStream->total_in = 0;
-  pStream->total_out = 0;
-  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
-  if (!pStream->zfree) pStream->zfree = def_free_func;
-
-  pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
-  if (!pComp)
-    return MZ_MEM_ERROR;
-
-  pStream->state = (struct mz_internal_state *)pComp;
-
-  if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
-  {
-    mz_deflateEnd(pStream);
-    return MZ_PARAM_ERROR;
-  }
-
-  return MZ_OK;
-}
-
-int mz_deflateReset(mz_streamp pStream)
-{
-  if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
-  pStream->total_in = pStream->total_out = 0;
-  tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
-  return MZ_OK;
-}
-
-int mz_deflate(mz_streamp pStream, int flush)
-{
-  size_t in_bytes, out_bytes;
-  mz_ulong orig_total_in, orig_total_out;
-  int mz_status = MZ_OK;
-
-  if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
-  if (!pStream->avail_out) return MZ_BUF_ERROR;
-
-  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
-
-  if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
-    return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
-
-  orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
-  for ( ; ; )
-  {
-    tdefl_status defl_status;
-    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
-
-    defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
-    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
-    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);
-
-    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
-    pStream->total_out += (mz_uint)out_bytes;
-
-    if (defl_status < 0)
-    {
-      mz_status = MZ_STREAM_ERROR;
-      break;
-    }
-    else if (defl_status == TDEFL_STATUS_DONE)
-    {
-      mz_status = MZ_STREAM_END;
-      break;
-    }
-    else if (!pStream->avail_out)
-      break;
-    else if ((!pStream->avail_in) && (flush != MZ_FINISH))
-    {
-      if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
-        break;
-      return MZ_BUF_ERROR; // Can't make forward progress without some input.
-    }
-  }
-  return mz_status;
-}
-
-int mz_deflateEnd(mz_streamp pStream)
-{
-  if (!pStream) return MZ_STREAM_ERROR;
-  if (pStream->state)
-  {
-    pStream->zfree(pStream->opaque, pStream->state);
-    pStream->state = NULL;
-  }
-  return MZ_OK;
-}
-
-mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
-{
-  (void)pStream;
-  // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
-  return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
-}
-
-int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
-{
-  int status;
-  mz_stream stream;
-  memset(&stream, 0, sizeof(stream));
-
-  // In case mz_ulong is 64-bits (argh I hate longs).
-  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
-
-  stream.next_in = pSource;
-  stream.avail_in = (mz_uint32)source_len;
-  stream.next_out = pDest;
-  stream.avail_out = (mz_uint32)*pDest_len;
-
-  status = mz_deflateInit(&stream, level);
-  if (status != MZ_OK) return status;
-
-  status = mz_deflate(&stream, MZ_FINISH);
-  if (status != MZ_STREAM_END)
-  {
-    mz_deflateEnd(&stream);
-    return (status == MZ_OK) ? MZ_BUF_ERROR : status;
-  }
-
-  *pDest_len = stream.total_out;
-  return mz_deflateEnd(&stream);
-}
-
-int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
-{
-  return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
-}
-
-mz_ulong mz_compressBound(mz_ulong source_len)
-{
-  return mz_deflateBound(NULL, source_len);
-}
-
-typedef struct
-{
-  tinfl_decompressor m_decomp;
-  mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
-  mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
-  tinfl_status m_last_status;
-} inflate_state;
-
-int mz_inflateInit2(mz_streamp pStream, int window_bits)
-{
-  inflate_state *pDecomp;
-  if (!pStream) return MZ_STREAM_ERROR;
-  if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
-
-  pStream->data_type = 0;
-  pStream->adler = 0;
-  pStream->msg = NULL;
-  pStream->total_in = 0;
-  pStream->total_out = 0;
-  pStream->reserved = 0;
-  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
-  if (!pStream->zfree) pStream->zfree = def_free_func;
-
-  pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
-  if (!pDecomp) return MZ_MEM_ERROR;
-
-  pStream->state = (struct mz_internal_state *)pDecomp;
-
-  tinfl_init(&pDecomp->m_decomp);
-  pDecomp->m_dict_ofs = 0;
-  pDecomp->m_dict_avail = 0;
-  pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
-  pDecomp->m_first_call = 1;
-  pDecomp->m_has_flushed = 0;
-  pDecomp->m_window_bits = window_bits;
-
-  return MZ_OK;
-}
-
-int mz_inflateInit(mz_streamp pStream)
-{
-   return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
-}
-
-int mz_inflate(mz_streamp pStream, int flush)
-{
-  inflate_state* pState;
-  mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
-  size_t in_bytes, out_bytes, orig_avail_in;
-  tinfl_status status;
-
-  if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
-  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
-  if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
-
-  pState = (inflate_state*)pStream->state;
-  if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
-  orig_avail_in = pStream->avail_in;
-
-  first_call = pState->m_first_call; pState->m_first_call = 0;
-  if (pState->m_last_status < 0) return MZ_DATA_ERROR;
-
-  if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
-  pState->m_has_flushed |= (flush == MZ_FINISH);
-
-  if ((flush == MZ_FINISH) && (first_call))
-  {
-    // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
-    decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
-    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
-    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
-    pState->m_last_status = status;
-    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
-    pStream->adler = tinfl_get_adler32(&pState->m_decomp);
-    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
-
-    if (status < 0)
-      return MZ_DATA_ERROR;
-    else if (status != TINFL_STATUS_DONE)
-    {
-      pState->m_last_status = TINFL_STATUS_FAILED;
-      return MZ_BUF_ERROR;
-    }
-    return MZ_STREAM_END;
-  }
-  // flush != MZ_FINISH then we must assume there's more input.
-  if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
-
-  if (pState->m_dict_avail)
-  {
-    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
-    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
-    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
-    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
-    return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
-  }
-
-  for ( ; ; )
-  {
-    in_bytes = pStream->avail_in;
-    out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
-
-    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
-    pState->m_last_status = status;
-
-    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
-    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
-
-    pState->m_dict_avail = (mz_uint)out_bytes;
-
-    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
-    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
-    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
-    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
-
-    if (status < 0)
-       return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
-    else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
-      return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
-    else if (flush == MZ_FINISH)
-    {
-       // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
-       if (status == TINFL_STATUS_DONE)
-          return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
-       // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
-       else if (!pStream->avail_out)
-          return MZ_BUF_ERROR;
-    }
-    else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
-      break;
-  }
-
-  return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
-}
-
-int mz_inflateEnd(mz_streamp pStream)
-{
-  if (!pStream)
-    return MZ_STREAM_ERROR;
-  if (pStream->state)
-  {
-    pStream->zfree(pStream->opaque, pStream->state);
-    pStream->state = NULL;
-  }
-  return MZ_OK;
-}
-
-int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
-{
-  mz_stream stream;
-  int status;
-  memset(&stream, 0, sizeof(stream));
-
-  // In case mz_ulong is 64-bits (argh I hate longs).
-  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
-
-  stream.next_in = pSource;
-  stream.avail_in = (mz_uint32)source_len;
-  stream.next_out = pDest;
-  stream.avail_out = (mz_uint32)*pDest_len;
-
-  status = mz_inflateInit(&stream);
-  if (status != MZ_OK)
-    return status;
-
-  status = mz_inflate(&stream, MZ_FINISH);
-  if (status != MZ_STREAM_END)
-  {
-    mz_inflateEnd(&stream);
-    return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
-  }
-  *pDest_len = stream.total_out;
-
-  return mz_inflateEnd(&stream);
-}
-
-const char *mz_error(int err)
-{
-  static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
-  {
-    { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
-    { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
-  };
-  mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
-  return NULL;
-}
-
-#endif //MINIZ_NO_ZLIB_APIS
-
-// ------------------- Low-level Decompression (completely independent from all compression API's)
-
-#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
-#define TINFL_MEMSET(p, c, l) memset(p, c, l)
-
-#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
-#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
-#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
-#define TINFL_CR_FINISH }
-
-// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
-// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
-#define TINFL_GET_BYTE(state_index, c) do { \
-  if (pIn_buf_cur >= pIn_buf_end) { \
-    for ( ; ; ) { \
-      if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
-        TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
-        if (pIn_buf_cur < pIn_buf_end) { \
-          c = *pIn_buf_cur++; \
-          break; \
-        } \
-      } else { \
-        c = 0; \
-        break; \
-      } \
-    } \
-  } else c = *pIn_buf_cur++; } MZ_MACRO_END
-
-#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
-#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
-#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
-
-// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
-// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
-// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
-// bit buffer contains >=15 bits (deflate's max. Huffman code size).
-#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
-  do { \
-    temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
-    if (temp >= 0) { \
-      code_len = temp >> 9; \
-      if ((code_len) && (num_bits >= code_len)) \
-      break; \
-    } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
-       code_len = TINFL_FAST_LOOKUP_BITS; \
-       do { \
-          temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
-       } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
-    } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
-  } while (num_bits < 15);
-
-// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
-// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
-// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
-// The slow path is only executed at the very end of the input buffer.
-#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
-  int temp; mz_uint code_len, c; \
-  if (num_bits < 15) { \
-    if ((pIn_buf_end - pIn_buf_cur) < 2) { \
-       TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
-    } else { \
-       bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
-    } \
-  } \
-  if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
-    code_len = temp >> 9, temp &= 511; \
-  else { \
-    code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
-  } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
-
-tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
-{
-  static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
-  static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
-  static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
-  static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
-  static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
-  static const int s_min_table_sizes[3] = { 257, 1, 4 };
-
-  tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
-  const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
-  mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
-  size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
-
-  // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
-  if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
-
-  num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
-  TINFL_CR_BEGIN
-
-  bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
-  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
-  {
-    TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
-    counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
-    if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
-    if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
-  }
-
-  do
-  {
-    TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
-    if (r->m_type == 0)
-    {
-      TINFL_SKIP_BITS(5, num_bits & 7);
-      for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
-      if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
-      while ((counter) && (num_bits))
-      {
-        TINFL_GET_BITS(51, dist, 8);
-        while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
-        *pOut_buf_cur++ = (mz_uint8)dist;
-        counter--;
-      }
-      while (counter)
-      {
-        size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
-        while (pIn_buf_cur >= pIn_buf_end)
-        {
-          if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
-          {
-            TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
-          }
-          else
-          {
-            TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
-          }
-        }
-        n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
-        TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
-      }
-    }
-    else if (r->m_type == 3)
-    {
-      TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
-    }
-    else
-    {
-      if (r->m_type == 1)
-      {
-        mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
-        r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
-        for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
-      }
-      else
-      {
-        for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
-        MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
-        r->m_table_sizes[2] = 19;
-      }
-      for ( ; (int)r->m_type >= 0; r->m_type--)
-      {
-        int tree_next, tree_cur; tinfl_huff_table *pTable;
-        mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
-        for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
-        used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
-        for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
-        if ((65536 != total) && (used_syms > 1))
-        {
-          TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
-        }
-        for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
-        {
-          mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
-          cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
-          if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
-          if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
-          rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
-          for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
-          {
-            tree_cur -= ((rev_code >>= 1) & 1);
-            if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
-          }
-          tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
-        }
-        if (r->m_type == 2)
-        {
-          for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
-          {
-            mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
-            if ((dist == 16) && (!counter))
-            {
-              TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
-            }
-            num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
-            TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
-          }
-          if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
-          {
-            TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
-          }
-          TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
-        }
-      }
-      for ( ; ; )
-      {
-        mz_uint8 *pSrc;
-        for ( ; ; )
-        {
-          if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
-          {
-            TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
-            if (counter >= 256)
-              break;
-            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
-            *pOut_buf_cur++ = (mz_uint8)counter;
-          }
-          else
-          {
-            int sym2; mz_uint code_len;
-#if TINFL_USE_64BIT_BITBUF
-            if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
-#else
-            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
-#endif
-            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
-              code_len = sym2 >> 9;
-            else
-            {
-              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
-            }
-            counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
-            if (counter & 256)
-              break;
-
-#if !TINFL_USE_64BIT_BITBUF
-            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
-#endif
-            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
-              code_len = sym2 >> 9;
-            else
-            {
-              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
-            }
-            bit_buf >>= code_len; num_bits -= code_len;
-
-            pOut_buf_cur[0] = (mz_uint8)counter;
-            if (sym2 & 256)
-            {
-              pOut_buf_cur++;
-              counter = sym2;
-              break;
-            }
-            pOut_buf_cur[1] = (mz_uint8)sym2;
-            pOut_buf_cur += 2;
-          }
-        }
-        if ((counter &= 511) == 256) break;
-
-        num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
-        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
-
-        TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
-        num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
-        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
-
-        dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
-        if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
-        {
-          TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
-        }
-
-        pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
-
-        if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
-        {
-          while (counter--)
-          {
-            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
-            *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
-          }
-          continue;
-        }
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
-        else if ((counter >= 9) && (counter <= dist))
-        {
-          const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
-          do
-          {
-            ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
-            ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
-            pOut_buf_cur += 8;
-          } while ((pSrc += 8) < pSrc_end);
-          if ((counter &= 7) < 3)
-          {
-            if (counter)
-            {
-              pOut_buf_cur[0] = pSrc[0];
-              if (counter > 1)
-                pOut_buf_cur[1] = pSrc[1];
-              pOut_buf_cur += counter;
-            }
-            continue;
-          }
-        }
-#endif
-        do
-        {
-          pOut_buf_cur[0] = pSrc[0];
-          pOut_buf_cur[1] = pSrc[1];
-          pOut_buf_cur[2] = pSrc[2];
-          pOut_buf_cur += 3; pSrc += 3;
-        } while ((int)(counter -= 3) > 2);
-        if ((int)counter > 0)
-        {
-          pOut_buf_cur[0] = pSrc[0];
-          if ((int)counter > 1)
-            pOut_buf_cur[1] = pSrc[1];
-          pOut_buf_cur += counter;
-        }
-      }
-    }
-  } while (!(r->m_final & 1));
-  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
-  {
-    TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
-  }
-  TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
-  TINFL_CR_FINISH
-
-common_exit:
-  r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
-  *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
-  if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
-  {
-    const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
-    mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
-    while (buf_len)
-    {
-      for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
-      {
-        s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
-        s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
-      }
-      for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
-      s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
-    }
-    r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
-  }
-  return status;
-}
-
-// Higher level helper functions.
-void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
-{
-  tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
-  *pOut_len = 0;
-  tinfl_init(&decomp);
-  for ( ; ; )
-  {
-    size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
-    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
-      (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
-    if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
-    {
-      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
-    }
-    src_buf_ofs += src_buf_size;
-    *pOut_len += dst_buf_size;
-    if (status == TINFL_STATUS_DONE) break;
-    new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
-    pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
-    if (!pNew_buf)
-    {
-      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
-    }
-    pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
-  }
-  return pBuf;
-}
-
-size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
-{
-  tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
-  status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
-  return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
-}
-
-int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
-{
-  int result = 0;
-  tinfl_decompressor decomp;
-  mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
-  if (!pDict)
-    return TINFL_STATUS_FAILED;
-  tinfl_init(&decomp);
-  for ( ; ; )
-  {
-    size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
-    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
-      (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
-    in_buf_ofs += in_buf_size;
-    if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
-      break;
-    if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
-    {
-      result = (status == TINFL_STATUS_DONE);
-      break;
-    }
-    dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
-  }
-  MZ_FREE(pDict);
-  *pIn_buf_size = in_buf_ofs;
-  return result;
-}
-
-// ------------------- Low-level Compression (independent from all decompression API's)
-
-// Purposely making these tables static for faster init and thread safety.
-static const mz_uint16 s_tdefl_len_sym[256] = {
-  257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
-  273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
-  277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
-  279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
-  281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
-  282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
-  283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
-  284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };
-
-static const mz_uint8 s_tdefl_len_extra[256] = {
-  0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
-  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
-  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };
-
-static const mz_uint8 s_tdefl_small_dist_sym[512] = {
-  0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
-  11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
-  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
-  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
-  14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
-  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
-  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
-  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
-  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
-  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
-  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
-  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };
-
-static const mz_uint8 s_tdefl_small_dist_extra[512] = {
-  0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
-  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-  7,7,7,7,7,7,7,7 };
-
-static const mz_uint8 s_tdefl_large_dist_sym[128] = {
-  0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
-  26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
-  28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };
-
-static const mz_uint8 s_tdefl_large_dist_extra[128] = {
-  0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
-  12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
-  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };
-
-// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
-typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
-static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
-{
-  mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
-  for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
-  while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
-  for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
-  {
-    const mz_uint32* pHist = &hist[pass << 8];
-    mz_uint offsets[256], cur_ofs = 0;
-    for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
-    for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
-    { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
-  }
-  return pCur_syms;
-}
-
-// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair at cs.mu.oz.au, Jyrki Katajainen, jyrki at diku.dk, November 1996.
-static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
-{
-  int root, leaf, next, avbl, used, dpth;
-  if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
-  A[0].m_key += A[1].m_key; root = 0; leaf = 2;
-  for (next=1; next < n-1; next++)
-  {
-    if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
-    if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
-  }
-  A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
-  avbl = 1; used = dpth = 0; root = n-2; next = n-1;
-  while (avbl>0)
-  {
-    while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
-    while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
-    avbl = 2*used; dpth++; used = 0;
-  }
-}
-
-// Limits canonical Huffman code table's max code size.
-enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
-static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
-{
-  int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
-  for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
-  for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
-  while (total != (1UL << max_code_size))
-  {
-    pNum_codes[max_code_size]--;
-    for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
-    total--;
-  }
-}
-
-static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
-{
-  int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
-  if (static_table)
-  {
-    for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
-  }
-  else
-  {
-    tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
-    int num_used_syms = 0;
-    const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
-    for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }
-
-    pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
-
-    for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;
-
-    tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
-
-    MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
-    for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
-      for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
-  }
-
-  next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);
-
-  for (i = 0; i < table_len; i++)
-  {
-    mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
-    code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
-    d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
-  }
-}
-
-#define TDEFL_PUT_BITS(b, l) do { \
-  mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
-  d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
-  while (d->m_bits_in >= 8) { \
-    if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
-      *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
-      d->m_bit_buffer >>= 8; \
-      d->m_bits_in -= 8; \
-  } \
-} MZ_MACRO_END
-
-#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
-  if (rle_repeat_count < 3) { \
-    d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
-    while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
-  } else { \
-    d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
-} rle_repeat_count = 0; } }
-
-#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
-  if (rle_z_count < 3) { \
-    d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
-  } else if (rle_z_count <= 10) { \
-    d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
-  } else { \
-    d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
-} rle_z_count = 0; } }
-
-static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
-
-static void tdefl_start_dynamic_block(tdefl_compressor *d)
-{
-  int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
-  mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
-
-  d->m_huff_count[0][256] = 1;
-
-  tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
-  tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
-
-  for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
-  for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;
-
-  memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
-  memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
-  total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;
-
-  memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
-  for (i = 0; i < total_code_sizes_to_pack; i++)
-  {
-    mz_uint8 code_size = code_sizes_to_pack[i];
-    if (!code_size)
-    {
-      TDEFL_RLE_PREV_CODE_SIZE();
-      if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
-    }
-    else
-    {
-      TDEFL_RLE_ZERO_CODE_SIZE();
-      if (code_size != prev_code_size)
-      {
-        TDEFL_RLE_PREV_CODE_SIZE();
-        d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
-      }
-      else if (++rle_repeat_count == 6)
-      {
-        TDEFL_RLE_PREV_CODE_SIZE();
-      }
-    }
-    prev_code_size = code_size;
-  }
-  if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }
-
-  tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
-
-  TDEFL_PUT_BITS(2, 2);
-
-  TDEFL_PUT_BITS(num_lit_codes - 257, 5);
-  TDEFL_PUT_BITS(num_dist_codes - 1, 5);
-
-  for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
-  num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
-  for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
-
-  for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
-  {
-    mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
-    TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
-    if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
-  }
-}
-
-static void tdefl_start_static_block(tdefl_compressor *d)
-{
-  mz_uint i;
-  mz_uint8 *p = &d->m_huff_code_sizes[0][0];
-
-  for (i = 0; i <= 143; ++i) *p++ = 8;
-  for ( ; i <= 255; ++i) *p++ = 9;
-  for ( ; i <= 279; ++i) *p++ = 7;
-  for ( ; i <= 287; ++i) *p++ = 8;
-
-  memset(d->m_huff_code_sizes[1], 5, 32);
-
-  tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
-  tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
-
-  TDEFL_PUT_BITS(1, 2);
-}
-
-static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
-
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
-static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
-{
-  mz_uint flags;
-  mz_uint8 *pLZ_codes;
-  mz_uint8 *pOutput_buf = d->m_pOutput_buf;
-  mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
-  mz_uint64 bit_buffer = d->m_bit_buffer;
-  mz_uint bits_in = d->m_bits_in;
-
-#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }
-
-  flags = 1;
-  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
-  {
-    if (flags == 1)
-      flags = *pLZ_codes++ | 0x100;
-
-    if (flags & 1)
-    {
-      mz_uint s0, s1, n0, n1, sym, num_extra_bits;
-      mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;
-
-      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
-      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
-      TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
-
-      // This sequence coaxes MSVC into using cmov's vs. jmp's.
-      s0 = s_tdefl_small_dist_sym[match_dist & 511];
-      n0 = s_tdefl_small_dist_extra[match_dist & 511];
-      s1 = s_tdefl_large_dist_sym[match_dist >> 8];
-      n1 = s_tdefl_large_dist_extra[match_dist >> 8];
-      sym = (match_dist < 512) ? s0 : s1;
-      num_extra_bits = (match_dist < 512) ? n0 : n1;
-
-      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
-      TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
-      TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
-    }
-    else
-    {
-      mz_uint lit = *pLZ_codes++;
-      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
-      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
-
-      if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
-      {
-        flags >>= 1;
-        lit = *pLZ_codes++;
-        MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
-        TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
-
-        if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
-        {
-          flags >>= 1;
-          lit = *pLZ_codes++;
-          MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
-          TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
-        }
-      }
-    }
-
-    if (pOutput_buf >= d->m_pOutput_buf_end)
-      return MZ_FALSE;
-
-    *(mz_uint64*)pOutput_buf = bit_buffer;
-    pOutput_buf += (bits_in >> 3);
-    bit_buffer >>= (bits_in & ~7);
-    bits_in &= 7;
-  }
-
-#undef TDEFL_PUT_BITS_FAST
-
-  d->m_pOutput_buf = pOutput_buf;
-  d->m_bits_in = 0;
-  d->m_bit_buffer = 0;
-
-  while (bits_in)
-  {
-    mz_uint32 n = MZ_MIN(bits_in, 16);
-    TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
-    bit_buffer >>= n;
-    bits_in -= n;
-  }
-
-  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
-
-  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
-}
-#else
-static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
-{
-  mz_uint flags;
-  mz_uint8 *pLZ_codes;
-
-  flags = 1;
-  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
-  {
-    if (flags == 1)
-      flags = *pLZ_codes++ | 0x100;
-    if (flags & 1)
-    {
-      mz_uint sym, num_extra_bits;
-      mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;
-
-      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
-      TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
-      TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
-
-      if (match_dist < 512)
-      {
-        sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
-      }
-      else
-      {
-        sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
-      }
-      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
-      TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
-      TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
-    }
-    else
-    {
-      mz_uint lit = *pLZ_codes++;
-      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
-      TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
-    }
-  }
-
-  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
-
-  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
-}
-#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
-
-static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
-{
-  if (static_block)
-    tdefl_start_static_block(d);
-  else
-    tdefl_start_dynamic_block(d);
-  return tdefl_compress_lz_codes(d);
-}
-
-static int tdefl_flush_block(tdefl_compressor *d, int flush)
-{
-  mz_uint saved_bit_buf, saved_bits_in;
-  mz_uint8 *pSaved_output_buf;
-  mz_bool comp_block_succeeded = MZ_FALSE;
-  int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
-  mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
-
-  d->m_pOutput_buf = pOutput_buf_start;
-  d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
-
-  MZ_ASSERT(!d->m_output_flush_remaining);
-  d->m_output_flush_ofs = 0;
-  d->m_output_flush_remaining = 0;
-
-  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
-  d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
-
-  if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
-  {
-    TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
-  }
-
-  TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
-
-  pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;
-
-  if (!use_raw_block)
-    comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
-
-  // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
-  if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
-       ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
-  {
-    mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
-    TDEFL_PUT_BITS(0, 2);
-    if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
-    for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
-    {
-      TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
-    }
-    for (i = 0; i < d->m_total_lz_bytes; ++i)
-    {
-      TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
-    }
-  }
-  // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
-  else if (!comp_block_succeeded)
-  {
-    d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
-    tdefl_compress_block(d, MZ_TRUE);
-  }
-
-  if (flush)
-  {
-    if (flush == TDEFL_FINISH)
-    {
-      if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
-      if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
-    }
-    else
-    {
-      mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
-    }
-  }
-
-  MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
-
-  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
-  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
-
-  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;
-
-  if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
-  {
-    if (d->m_pPut_buf_func)
-    {
-      *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
-      if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
-        return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
-    }
-    else if (pOutput_buf_start == d->m_output_buf)
-    {
-      int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
-      memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
-      d->m_out_buf_ofs += bytes_to_copy;
-      if ((n -= bytes_to_copy) != 0)
-      {
-        d->m_output_flush_ofs = bytes_to_copy;
-        d->m_output_flush_remaining = n;
-      }
-    }
-    else
-    {
-      d->m_out_buf_ofs += n;
-    }
-  }
-
-  return d->m_output_flush_remaining;
-}
-
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
-#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
-static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
-{
-  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
-  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
-  const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
-  mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
-  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
-  for ( ; ; )
-  {
-    for ( ; ; )
-    {
-      if (--num_probes_left == 0) return;
-      #define TDEFL_PROBE \
-        next_probe_pos = d->m_next[probe_pos]; \
-        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
-        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
-        if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
-      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
-    }
-    if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
-    do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
-                   (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
-    if (!probe_len)
-    {
-      *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
-    }
-    else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
-    {
-      *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
-      c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
-    }
-  }
-}
-#else
-static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
-{
-  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
-  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
-  const mz_uint8 *s = d->m_dict + pos, *p, *q;
-  mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
-  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
-  for ( ; ; )
-  {
-    for ( ; ; )
-    {
-      if (--num_probes_left == 0) return;
-      #define TDEFL_PROBE \
-        next_probe_pos = d->m_next[probe_pos]; \
-        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
-        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
-        if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
-      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
-    }
-    if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
-    if (probe_len > match_len)
-    {
-      *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
-      c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
-    }
-  }
-}
-#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
-
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
-static mz_bool tdefl_compress_fast(tdefl_compressor *d)
-{
-  // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
-  mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
-  mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
-  mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
-
-  while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
-  {
-    const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
-    mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
-    mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
-    d->m_src_buf_left -= num_bytes_to_process;
-    lookahead_size += num_bytes_to_process;
-
-    while (num_bytes_to_process)
-    {
-      mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
-      memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
-      if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
-        memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
-      d->m_pSrc += n;
-      dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
-      num_bytes_to_process -= n;
-    }
-
-    dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
-    if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;
-
-    while (lookahead_size >= 4)
-    {
-      mz_uint cur_match_dist, cur_match_len = 1;
-      mz_uint8 *pCur_dict = d->m_dict + cur_pos;
-      mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
-      mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
-      mz_uint probe_pos = d->m_hash[hash];
-      d->m_hash[hash] = (mz_uint16)lookahead_pos;
-
-      if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
-      {
-        const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
-        const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
-        mz_uint32 probe_len = 32;
-        do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
-          (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
-        cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
-        if (!probe_len)
-          cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
-
-        if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
-        {
-          cur_match_len = 1;
-          *pLZ_code_buf++ = (mz_uint8)first_trigram;
-          *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
-          d->m_huff_count[0][(mz_uint8)first_trigram]++;
-        }
-        else
-        {
-          mz_uint32 s0, s1;
-          cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
-
-          MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
-
-          cur_match_dist--;
-
-          pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
-          *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
-          pLZ_code_buf += 3;
-          *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
-
-          s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
-          s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
-          d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
-
-          d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
-        }
-      }
-      else
-      {
-        *pLZ_code_buf++ = (mz_uint8)first_trigram;
-        *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
-        d->m_huff_count[0][(mz_uint8)first_trigram]++;
-      }
-
-      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
-
-      total_lz_bytes += cur_match_len;
-      lookahead_pos += cur_match_len;
-      dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
-      cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
-      MZ_ASSERT(lookahead_size >= cur_match_len);
-      lookahead_size -= cur_match_len;
-
-      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
-      {
-        int n;
-        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
-        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
-        if ((n = tdefl_flush_block(d, 0)) != 0)
-          return (n < 0) ? MZ_FALSE : MZ_TRUE;
-        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
-      }
-    }
-
-    while (lookahead_size)
-    {
-      mz_uint8 lit = d->m_dict[cur_pos];
-
-      total_lz_bytes++;
-      *pLZ_code_buf++ = lit;
-      *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
-      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
-
-      d->m_huff_count[0][lit]++;
-
-      lookahead_pos++;
-      dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
-      cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
-      lookahead_size--;
-
-      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
-      {
-        int n;
-        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
-        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
-        if ((n = tdefl_flush_block(d, 0)) != 0)
-          return (n < 0) ? MZ_FALSE : MZ_TRUE;
-        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
-      }
-    }
-  }
-
-  d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
-  d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
-  return MZ_TRUE;
-}
-#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
-
-static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
-{
-  d->m_total_lz_bytes++;
-  *d->m_pLZ_code_buf++ = lit;
-  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
-  d->m_huff_count[0][lit]++;
-}
-
-static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
-{
-  mz_uint32 s0, s1;
-
-  MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
-
-  d->m_total_lz_bytes += match_len;
-
-  d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
-
-  match_dist -= 1;
-  d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
-  d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;
-
-  *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
-
-  s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
-  d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
-
-  if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
-}
-
-static mz_bool tdefl_compress_normal(tdefl_compressor *d)
-{
-  const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
-  tdefl_flush flush = d->m_flush;
-
-  while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
-  {
-    mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
-    // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
-    if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
-    {
-      mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
-      mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
-      mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
-      const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
-      src_buf_left -= num_bytes_to_process;
-      d->m_lookahead_size += num_bytes_to_process;
-      while (pSrc != pSrc_end)
-      {
-        mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
-        hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
-        d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
-        dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
-      }
-    }
-    else
-    {
-      while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
-      {
-        mz_uint8 c = *pSrc++;
-        mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
-        src_buf_left--;
-        d->m_dict[dst_pos] = c;
-        if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
-          d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
-        if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
-        {
-          mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
-          mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
-          d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
-        }
-      }
-    }
-    d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
-    if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
-      break;
-
-    // Simple lazy/greedy parsing state machine.
-    len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
-    if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
-    {
-      if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
-      {
-        mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
-        cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
-        if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
-      }
-    }
-    else
-    {
-      tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
-    }
-    if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
-    {
-      cur_match_dist = cur_match_len = 0;
-    }
-    if (d->m_saved_match_len)
-    {
-      if (cur_match_len > d->m_saved_match_len)
-      {
-        tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
-        if (cur_match_len >= 128)
-        {
-          tdefl_record_match(d, cur_match_len, cur_match_dist);
-          d->m_saved_match_len = 0; len_to_move = cur_match_len;
-        }
-        else
-        {
-          d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
-        }
-      }
-      else
-      {
-        tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
-        len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
-      }
-    }
-    else if (!cur_match_dist)
-      tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
-    else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
-    {
-      tdefl_record_match(d, cur_match_len, cur_match_dist);
-      len_to_move = cur_match_len;
-    }
-    else
-    {
-      d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
-    }
-    // Move the lookahead forward by len_to_move bytes.
-    d->m_lookahead_pos += len_to_move;
-    MZ_ASSERT(d->m_lookahead_size >= len_to_move);
-    d->m_lookahead_size -= len_to_move;
-    d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
-    // Check if it's time to flush the current LZ codes to the internal output buffer.
-    if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
-         ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
-    {
-      int n;
-      d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
-      if ((n = tdefl_flush_block(d, 0)) != 0)
-        return (n < 0) ? MZ_FALSE : MZ_TRUE;
-    }
-  }
-
-  d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
-  return MZ_TRUE;
-}
-
-static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
-{
-  if (d->m_pIn_buf_size)
-  {
-    *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
-  }
-
-  if (d->m_pOut_buf_size)
-  {
-    size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
-    memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
-    d->m_output_flush_ofs += (mz_uint)n;
-    d->m_output_flush_remaining -= (mz_uint)n;
-    d->m_out_buf_ofs += n;
-
-    *d->m_pOut_buf_size = d->m_out_buf_ofs;
-  }
-
-  return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
-}
-
-tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
-{
-  if (!d)
-  {
-    if (pIn_buf_size) *pIn_buf_size = 0;
-    if (pOut_buf_size) *pOut_buf_size = 0;
-    return TDEFL_STATUS_BAD_PARAM;
-  }
-
-  d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
-  d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
-  d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
-  d->m_out_buf_ofs = 0;
-  d->m_flush = flush;
-
-  if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
-        (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
-  {
-    if (pIn_buf_size) *pIn_buf_size = 0;
-    if (pOut_buf_size) *pOut_buf_size = 0;
-    return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
-  }
-  d->m_wants_to_finish |= (flush == TDEFL_FINISH);
-
-  if ((d->m_output_flush_remaining) || (d->m_finished))
-    return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
-
-#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
-  if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
-      ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
-      ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
-  {
-    if (!tdefl_compress_fast(d))
-      return d->m_prev_return_status;
-  }
-  else
-#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
-  {
-    if (!tdefl_compress_normal(d))
-      return d->m_prev_return_status;
-  }
-
-  if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
-    d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
-
-  if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
-  {
-    if (tdefl_flush_block(d, flush) < 0)
-      return d->m_prev_return_status;
-    d->m_finished = (flush == TDEFL_FINISH);
-    if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
-  }
-
-  return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
-}
-
-tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
-{
-  MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
-}
-
-tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
-{
-  d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
-  d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
-  d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
-  if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
-  d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
-  d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
-  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
-  d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
-  d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
-  d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
-  d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
-  d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
-  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
-  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
-  return TDEFL_STATUS_OKAY;
-}
-
-tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
-{
-  return d->m_prev_return_status;
-}
-
-mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
-{
-  return d->m_adler32;
-}
-
-mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
-{
-  tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
-  pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
-  succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
-  succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
-  MZ_FREE(pComp); return succeeded;
-}
-
-struct tdefl_output_buffer
-{
-  size_t m_size, m_capacity;
-  mz_uint8 *m_pBuf;
-  mz_bool m_expandable;
-};
-
-static mz_bool tdefl_output_buffer_putter(const void *pBuf, size_t len, void *pUser)
-{
-  tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
-  size_t new_size = p->m_size + len;
-  if (new_size > p->m_capacity)
-  {
-    size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
-    do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
-    pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
-    p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
-  }
-  memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
-  return MZ_TRUE;
-}
-
-void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
-{
-  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
-  if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
-  out_buf.m_expandable = MZ_TRUE;
-  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
-  *pOut_len = out_buf.m_size; return out_buf.m_pBuf;
-}
-
-size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
-{
-  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
-  if (!pOut_buf) return 0;
-  out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
-  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
-  return out_buf.m_size;
-}
-
-#ifndef MINIZ_NO_ZLIB_APIS
-static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32,  16, 32, 128, 256,  512, 768, 1500 };
-
-// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
-mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
-{
-  mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
-  if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
-
-  if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
-  else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
-  else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
-  else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
-  else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;
-
-  return comp_flags;
-}
-#endif //MINIZ_NO_ZLIB_APIS
-
-#ifdef _MSC_VER
-#pragma warning (push)
-#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
-#endif
-
-// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
-// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
-void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
-{
-  tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
-  if (!pComp) return NULL;
-  MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
-  // write dummy header
-  for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
-  // compress image data
-  tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, TDEFL_DEFAULT_MAX_PROBES | TDEFL_WRITE_ZLIB_HEADER);
-  for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + y * bpl, bpl, TDEFL_NO_FLUSH); }
-  if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
-  // write real header
-  *pLen_out = out_buf.m_size-41;
-  {
-    mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
-      0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8, (mz_uint8)"\0\0\04\02\06"[num_chans],0,0,0,0,0,0,0,
-      (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
-    c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
-    memcpy(out_buf.m_pBuf, pnghdr, 41);
-  }
-  // write footer (IDAT CRC-32, followed by IEND chunk)
-  if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
-  c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
-  // compute final size of file, grab compressed data buffer and return
-  *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
-}
-
-#ifdef _MSC_VER
-#pragma warning (pop)
-#endif
-
-// ------------------- .ZIP archive reading
-
-#ifndef MINIZ_NO_ARCHIVE_APIS
-
-#ifdef MINIZ_NO_STDIO
-  #define MZ_FILE void *
-#else
-  #include <stdio.h>
-  #include <sys/stat.h>
-
-  #if defined(_MSC_VER)
-    static FILE *mz_fopen(const char *pFilename, const char *pMode)
-    {
-      FILE* pFile = NULL;
-      fopen_s(&pFile, pFilename, pMode);
-      return pFile;
-    }
-    static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
-    {
-      FILE* pFile = NULL;
-      if (freopen_s(&pFile, pPath, pMode, pStream))
-        return NULL;
-      return pFile;
-    }
-  #else
-    static FILE *mz_fopen(const char *pFilename, const char *pMode)
-    {
-      return fopen(pFilename, pMode);
-    }
-    static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
-    {
-      return freopen(pPath, pMode, pStream);
-    }
-  #endif // #if defined(_MSC_VER)
-
-  #if defined(_MSC_VER) || defined(__MINGW64__)
-    #ifndef MINIZ_NO_TIME
-      #include <sys/utime.h>
-    #endif
-    #define MZ_FILE FILE
-    #define MZ_FOPEN mz_fopen
-    #define MZ_FCLOSE fclose
-    #define MZ_FREAD fread
-    #define MZ_FWRITE fwrite
-    #define MZ_FTELL64 _ftelli64
-    #define MZ_FSEEK64 _fseeki64
-    #define MZ_FILE_STAT_STRUCT _stat
-    #define MZ_FILE_STAT _stat
-    #define MZ_FFLUSH fflush
-    #define MZ_FREOPEN mz_freopen
-    #define MZ_DELETE_FILE remove
-  #elif defined(__MINGW32__)
-    #ifndef MINIZ_NO_TIME
-      #include <sys/utime.h>
-    #endif
-    #define MZ_FILE FILE
-    #define MZ_FOPEN mz_fopen
-    #define MZ_FCLOSE fclose
-    #define MZ_FREAD fread
-    #define MZ_FWRITE fwrite
-    #define MZ_FTELL64 ftello64
-    #define MZ_FSEEK64 fseeko64
-    #define MZ_FILE_STAT_STRUCT _stat
-    #define MZ_FILE_STAT _stat
-    #define MZ_FFLUSH fflush
-    #define MZ_FREOPEN mz_freopen
-    #define MZ_DELETE_FILE remove
-  #elif defined(__TINYC__)
-    #ifndef MINIZ_NO_TIME
-      #include <sys\utime.h>
-    #endif
-    #define MZ_FILE FILE
-    #define MZ_FOPEN mz_fopen
-    #define MZ_FCLOSE fclose
-    #define MZ_FREAD fread
-    #define MZ_FWRITE fwrite
-    #define MZ_FTELL64 ftell
-    #define MZ_FSEEK64 fseek
-    #define MZ_FILE_STAT_STRUCT stat
-    #define MZ_FILE_STAT stat
-    #define MZ_FFLUSH fflush
-    #define MZ_FREOPEN mz_freopen
-    #define MZ_DELETE_FILE remove
-  #else
-    #ifndef MINIZ_NO_TIME
-      #include <utime.h>
-    #endif
-    #define MZ_FILE FILE
-    #define MZ_FOPEN mz_fopen
-    #define MZ_FCLOSE fclose
-    #define MZ_FREAD fread
-    #define MZ_FWRITE fwrite
-    #define MZ_FTELL64 ftello
-    #define MZ_FSEEK64 fseeko
-    #define MZ_FILE_STAT_STRUCT stat
-    #define MZ_FILE_STAT stat
-    #define MZ_FFLUSH fflush
-    #define MZ_FREOPEN mz_freopen
-    #define MZ_DELETE_FILE remove
-  #endif // #ifdef _MSC_VER
-#endif // #ifdef MINIZ_NO_STDIO
-
-#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
-
-// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
-enum
-{
-  // ZIP archive identifiers and record sizes
-  MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
-  MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
-  // Central directory header record offsets
-  MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
-  MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
-  MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
-  MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
-  // Local directory header offsets
-  MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
-  MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
-  MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
-  // End of central directory offsets
-  MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
-  MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
-};
-
-typedef struct
-{
-  void *m_p;
-  size_t m_size, m_capacity;
-  mz_uint m_element_size;
-} mz_zip_array;
-
-struct mz_zip_internal_state_tag
-{
-  mz_zip_array m_central_dir;
-  mz_zip_array m_central_dir_offsets;
-  mz_zip_array m_sorted_central_dir_offsets;
-  MZ_FILE *m_pFile;
-  void *m_pMem;
-  size_t m_mem_size;
-  size_t m_mem_capacity;
-};
-
-#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
-#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
-
-static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
-{
-  pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
-  memset(pArray, 0, sizeof(mz_zip_array));
-}
-
-static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
-{
-  void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
-  if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
-  if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
-  pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
-  return MZ_TRUE;
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
-{
-  if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
-  return MZ_TRUE;
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
-{
-  if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
-  pArray->m_size = new_size;
-  return MZ_TRUE;
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
-{
-  return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
-{
-  size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
-  memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
-  return MZ_TRUE;
-}
-
-#ifndef MINIZ_NO_TIME
-static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
-{
-  struct tm tm;
-  memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
-  tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
-  tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
-  return mktime(&tm);
-}
-
-static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
-{
-#ifdef _MSC_VER
-  struct tm tm_struct;
-  struct tm *tm = &tm_struct;
-  errno_t err = localtime_s(tm, &time);
-  if (err)
-  {
-    *pDOS_date = 0; *pDOS_time = 0;
-    return;
-  }
-#else
-  struct tm *tm = localtime(&time);
-#endif
-  *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
-  *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
-}
-#endif
-
-#ifndef MINIZ_NO_STDIO
-static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
-{
-#ifdef MINIZ_NO_TIME
-  (void)pFilename; *pDOS_date = *pDOS_time = 0;
-#else
-  struct MZ_FILE_STAT_STRUCT file_stat; if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE;
-  mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
-#endif // #ifdef MINIZ_NO_TIME
-  return MZ_TRUE;
-}
-
-static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
-{
-#ifndef MINIZ_NO_TIME
-  struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
-  return !utime(pFilename, &t);
-#else
-  (void)pFilename, (void)access_time, (void)modified_time;
-  return MZ_TRUE;
-#endif // #ifndef MINIZ_NO_TIME
-}
-#endif
-
-static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
-{
-  (void)flags;
-  if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
-    return MZ_FALSE;
-
-  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
-  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
-  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
-
-  pZip->m_zip_mode = MZ_ZIP_MODE_READING;
-  pZip->m_archive_size = 0;
-  pZip->m_central_directory_file_ofs = 0;
-  pZip->m_total_files = 0;
-
-  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
-    return MZ_FALSE;
-  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
-  return MZ_TRUE;
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
-{
-  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
-  const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
-  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
-  mz_uint8 l = 0, r = 0;
-  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
-  pE = pL + MZ_MIN(l_len, r_len);
-  while (pL < pE)
-  {
-    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
-      break;
-    pL++; pR++;
-  }
-  return (pL == pE) ? (l_len < r_len) : (l < r);
-}
-
-#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END
-
-// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
-static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
-{
-  mz_zip_internal_state *pState = pZip->m_pState;
-  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
-  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
-  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
-  const int size = pZip->m_total_files;
-  int start = (size - 2) >> 1, end;
-  while (start >= 0)
-  {
-    int child, root = start;
-    for ( ; ; )
-    {
-      if ((child = (root << 1) + 1) >= size)
-        break;
-      child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
-      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
-        break;
-      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
-    }
-    start--;
-  }
-
-  end = size - 1;
-  while (end > 0)
-  {
-    int child, root = 0;
-    MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
-    for ( ; ; )
-    {
-      if ((child = (root << 1) + 1) >= end)
-        break;
-      child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
-      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
-        break;
-      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
-    }
-    end--;
-  }
-}
-
-static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
-{
-  mz_uint cdir_size, num_this_disk, cdir_disk_index;
-  mz_uint64 cdir_ofs;
-  mz_int64 cur_file_ofs;
-  const mz_uint8 *p;
-  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
-  // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
-  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  // Find the end of central directory record by scanning the file from the end towards the beginning.
-  cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
-  for ( ; ; )
-  {
-    int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
-    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
-      return MZ_FALSE;
-    for (i = n - 4; i >= 0; --i)
-      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
-        break;
-    if (i >= 0)
-    {
-      cur_file_ofs += i;
-      break;
-    }
-    if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
-      return MZ_FALSE;
-    cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
-  }
-  // Read and verify the end of central directory record.
-  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
-      ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
-    return MZ_FALSE;
-
-  num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
-  cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
-  if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
-    return MZ_FALSE;
-
-  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-
-  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
-  if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
-    return MZ_FALSE;
-
-  pZip->m_central_directory_file_ofs = cdir_ofs;
-
-  if (pZip->m_total_files)
-  {
-     mz_uint i, n;
-    // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
-    if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
-        (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) ||
-        (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
-      return MZ_FALSE;
-    if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
-      return MZ_FALSE;
-
-    // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
-    p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
-    for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
-    {
-      mz_uint total_header_size, comp_size, decomp_size, disk_index;
-      if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
-        return MZ_FALSE;
-      MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
-      MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
-      comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
-      decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
-      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
-        return MZ_FALSE;
-      disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
-      if ((disk_index != num_this_disk) && (disk_index != 1))
-        return MZ_FALSE;
-      if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
-        return MZ_FALSE;
-      if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
-        return MZ_FALSE;
-      n -= total_header_size; p += total_header_size;
-    }
-  }
-
-  if ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0)
-    mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
-
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
-{
-  if ((!pZip) || (!pZip->m_pRead))
-    return MZ_FALSE;
-  if (!mz_zip_reader_init_internal(pZip, flags))
-    return MZ_FALSE;
-  pZip->m_archive_size = size;
-  if (!mz_zip_reader_read_central_dir(pZip, flags))
-  {
-    mz_zip_reader_end(pZip);
-    return MZ_FALSE;
-  }
-  return MZ_TRUE;
-}
-
-static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
-{
-  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
-  size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
-  memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
-  return s;
-}
-
-mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
-{
-  if (!mz_zip_reader_init_internal(pZip, flags))
-    return MZ_FALSE;
-  pZip->m_archive_size = size;
-  pZip->m_pRead = mz_zip_mem_read_func;
-  pZip->m_pIO_opaque = pZip;
-  pZip->m_pState->m_pMem = (void *)pMem;
-  pZip->m_pState->m_mem_size = size;
-  if (!mz_zip_reader_read_central_dir(pZip, flags))
-  {
-    mz_zip_reader_end(pZip);
-    return MZ_FALSE;
-  }
-  return MZ_TRUE;
-}
-
-#ifndef MINIZ_NO_STDIO
-static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
-{
-  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
-  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
-  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
-    return 0;
-  return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
-}
-
-mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
-{
-  mz_uint64 file_size;
-  MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
-  if (!pFile)
-    return MZ_FALSE;
-  if (MZ_FSEEK64(pFile, 0, SEEK_END))
-    return MZ_FALSE;
-  file_size = MZ_FTELL64(pFile);
-  if (!mz_zip_reader_init_internal(pZip, flags))
-  {
-    MZ_FCLOSE(pFile);
-    return MZ_FALSE;
-  }
-  pZip->m_pRead = mz_zip_file_read_func;
-  pZip->m_pIO_opaque = pZip;
-  pZip->m_pState->m_pFile = pFile;
-  pZip->m_archive_size = file_size;
-  if (!mz_zip_reader_read_central_dir(pZip, flags))
-  {
-    mz_zip_reader_end(pZip);
-    return MZ_FALSE;
-  }
-  return MZ_TRUE;
-}
-#endif // #ifndef MINIZ_NO_STDIO
-
-mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
-{
-  return pZip ? pZip->m_total_files : 0;
-}
-
-static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
-{
-  if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
-    return NULL;
-  return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
-}
-
-mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
-{
-  mz_uint m_bit_flag;
-  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
-  if (!p)
-    return MZ_FALSE;
-  m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
-  return (m_bit_flag & 1);
-}
-
-mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
-{
-  mz_uint filename_len, internal_attr, external_attr;
-  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
-  if (!p)
-    return MZ_FALSE;
-
-  internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
-  external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
-  if ((!internal_attr) && ((external_attr & 0x10) != 0))
-    return MZ_TRUE;
-
-  filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
-  if (filename_len)
-  {
-    if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
-      return MZ_TRUE;
-  }
-
-  return MZ_FALSE;
-}
-
-mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
-{
-  mz_uint n;
-  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
-  if ((!p) || (!pStat))
-    return MZ_FALSE;
-
-  // Unpack the central directory record.
-  pStat->m_file_index = file_index;
-  pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
-  pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
-  pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
-  pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
-  pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
-#ifndef MINIZ_NO_TIME
-  pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
-#endif
-  pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
-  pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
-  pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
-  pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
-  pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
-  pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
-
-  // Copy as much of the filename and comment as possible.
-  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
-  memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
-
-  n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
-  pStat->m_comment_size = n;
-  memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
-
-  return MZ_TRUE;
-}
-
-mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
-{
-  mz_uint n;
-  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
-  if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
-  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
-  if (filename_buf_size)
-  {
-    n = MZ_MIN(n, filename_buf_size - 1);
-    memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
-    pFilename[n] = '\0';
-  }
-  return n + 1;
-}
-
-static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
-{
-  mz_uint i;
-  if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
-    return 0 == memcmp(pA, pB, len);
-  for (i = 0; i < len; ++i)
-    if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
-      return MZ_FALSE;
-  return MZ_TRUE;
-}
-
-static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
-{
-  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
-  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
-  mz_uint8 l = 0, r = 0;
-  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
-  pE = pL + MZ_MIN(l_len, r_len);
-  while (pL < pE)
-  {
-    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
-      break;
-    pL++; pR++;
-  }
-  return (pL == pE) ? (int)(l_len - r_len) : (l - r);
-}
-
-static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
-{
-  mz_zip_internal_state *pState = pZip->m_pState;
-  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
-  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
-  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
-  const int size = pZip->m_total_files;
-  const mz_uint filename_len = (mz_uint)strlen(pFilename);
-  int l = 0, h = size - 1;
-  while (l <= h)
-  {
-    int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
-    if (!comp)
-      return file_index;
-    else if (comp < 0)
-      l = m + 1;
-    else
-      h = m - 1;
-  }
-  return -1;
-}
-
-int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
-{
-  mz_uint file_index; size_t name_len, comment_len;
-  if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
-    return -1;
-  if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_p))
-    return mz_zip_reader_locate_file_binary_search(pZip, pName);
-  name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
-  comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
-  for (file_index = 0; file_index < pZip->m_total_files; file_index++)
-  {
-    const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
-    mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
-    const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
-    if (filename_len < name_len)
-      continue;
-    if (comment_len)
-    {
-      mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
-      const char *pFile_comment = pFilename + filename_len + file_extra_len;
-      if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
-        continue;
-    }
-    if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
-    {
-      int ofs = filename_len - 1;
-      do
-      {
-        if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
-          break;
-      } while (--ofs >= 0);
-      ofs++;
-      pFilename += ofs; filename_len -= ofs;
-    }
-    if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
-      return file_index;
-  }
-  return -1;
-}
-
-mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
-{
-  int status = TINFL_STATUS_DONE;
-  mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
-  mz_zip_archive_file_stat file_stat;
-  void *pRead_buf;
-  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
-  tinfl_decompressor inflator;
-
-  if ((buf_size) && (!pBuf))
-    return MZ_FALSE;
-
-  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
-    return MZ_FALSE;
-
-  if (!file_stat.m_comp_size)
-    return MZ_TRUE;
-
-  // Encryption and patch files are not supported.
-  if (file_stat.m_bit_flag & (1 | 32))
-    return MZ_FALSE;
-
-  // This function only supports stored and deflate.
-  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
-    return MZ_FALSE;
-
-  // Ensure supplied output buffer is large enough.
-  needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
-  if (buf_size < needed_size)
-    return MZ_FALSE;
-
-  // Read and parse the local directory entry.
-  cur_file_ofs = file_stat.m_local_header_ofs;
-  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
-    return MZ_FALSE;
-
-  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
-  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
-    return MZ_FALSE;
-
-  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
-  {
-    // The file is stored or the caller has requested the compressed data.
-    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
-      return MZ_FALSE;
-    return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
-  }
-
-  // Decompress the file either directly from memory or from a file input buffer.
-  tinfl_init(&inflator);
-
-  if (pZip->m_pState->m_pMem)
-  {
-    // Read directly from the archive in memory.
-    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
-    read_buf_size = read_buf_avail = file_stat.m_comp_size;
-    comp_remaining = 0;
-  }
-  else if (pUser_read_buf)
-  {
-    // Use a user provided read buffer.
-    if (!user_read_buf_size)
-      return MZ_FALSE;
-    pRead_buf = (mz_uint8 *)pUser_read_buf;
-    read_buf_size = user_read_buf_size;
-    read_buf_avail = 0;
-    comp_remaining = file_stat.m_uncomp_size;
-  }
-  else
-  {
-    // Temporarily allocate a read buffer.
-    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
-#ifdef _MSC_VER
-    if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
-#else
-    if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
-#endif
-      return MZ_FALSE;
-    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
-      return MZ_FALSE;
-    read_buf_avail = 0;
-    comp_remaining = file_stat.m_comp_size;
-  }
-
-  do
-  {
-    size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
-    if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
-    {
-      read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
-      if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
-      {
-        status = TINFL_STATUS_FAILED;
-        break;
-      }
-      cur_file_ofs += read_buf_avail;
-      comp_remaining -= read_buf_avail;
-      read_buf_ofs = 0;
-    }
-    in_buf_size = (size_t)read_buf_avail;
-    status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
-    read_buf_avail -= in_buf_size;
-    read_buf_ofs += in_buf_size;
-    out_buf_ofs += out_buf_size;
-  } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
-
-  if (status == TINFL_STATUS_DONE)
-  {
-    // Make sure the entire file was decompressed, and check its CRC.
-    if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
-      status = TINFL_STATUS_FAILED;
-  }
-
-  if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-
-  return status == TINFL_STATUS_DONE;
-}
-
-mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
-{
-  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
-  if (file_index < 0)
-    return MZ_FALSE;
-  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
-}
-
-mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
-{
-  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
-}
-
-mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
-{
-  return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
-}
-
-void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
-{
-  mz_uint64 comp_size, uncomp_size, alloc_size;
-  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
-  void *pBuf;
-
-  if (pSize)
-    *pSize = 0;
-  if (!p)
-    return NULL;
-
-  comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
-  uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
-
-  alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
-#ifdef _MSC_VER
-  if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
-#else
-  if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
-#endif
-    return NULL;
-  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
-    return NULL;
-
-  if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
-  {
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-    return NULL;
-  }
-
-  if (pSize) *pSize = (size_t)alloc_size;
-  return pBuf;
-}
-
-void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
-{
-  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
-  if (file_index < 0)
-  {
-    if (pSize) *pSize = 0;
-    return MZ_FALSE;
-  }
-  return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
-}
-
-mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
-{
-  int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
-  mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
-  mz_zip_archive_file_stat file_stat;
-  void *pRead_buf = NULL; void *pWrite_buf = NULL;
-  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
-
-  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
-    return MZ_FALSE;
-
-  if (!file_stat.m_comp_size)
-    return MZ_TRUE;
-
-  // Encryption and patch files are not supported.
-  if (file_stat.m_bit_flag & (1 | 32))
-    return MZ_FALSE;
-
-  // This function only supports stored and deflate.
-  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
-    return MZ_FALSE;
-
-  // Read and parse the local directory entry.
-  cur_file_ofs = file_stat.m_local_header_ofs;
-  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
-    return MZ_FALSE;
-
-  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
-  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
-    return MZ_FALSE;
-
-  // Decompress the file either directly from memory or from a file input buffer.
-  if (pZip->m_pState->m_pMem)
-  {
-    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
-    read_buf_size = read_buf_avail = file_stat.m_comp_size;
-    comp_remaining = 0;
-  }
-  else
-  {
-    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
-    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
-      return MZ_FALSE;
-    read_buf_avail = 0;
-    comp_remaining = file_stat.m_comp_size;
-  }
-
-  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
-  {
-    // The file is stored or the caller has requested the compressed data.
-    if (pZip->m_pState->m_pMem)
-    {
-#ifdef _MSC_VER
-      if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
-#else
-      if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
-#endif
-        return MZ_FALSE;
-      if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
-        status = TINFL_STATUS_FAILED;
-      else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
-        file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
-      cur_file_ofs += file_stat.m_comp_size;
-      out_buf_ofs += file_stat.m_comp_size;
-      comp_remaining = 0;
-    }
-    else
-    {
-      while (comp_remaining)
-      {
-        read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
-        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
-        {
-          status = TINFL_STATUS_FAILED;
-          break;
-        }
-
-        if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
-          file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
-
-        if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
-        {
-          status = TINFL_STATUS_FAILED;
-          break;
-        }
-        cur_file_ofs += read_buf_avail;
-        out_buf_ofs += read_buf_avail;
-        comp_remaining -= read_buf_avail;
-      }
-    }
-  }
-  else
-  {
-    tinfl_decompressor inflator;
-    tinfl_init(&inflator);
-
-    if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
-      status = TINFL_STATUS_FAILED;
-    else
-    {
-      do
-      {
-        mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
-        size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
-        if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
-        {
-          read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
-          if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
-          {
-            status = TINFL_STATUS_FAILED;
-            break;
-          }
-          cur_file_ofs += read_buf_avail;
-          comp_remaining -= read_buf_avail;
-          read_buf_ofs = 0;
-        }
-
-        in_buf_size = (size_t)read_buf_avail;
-        status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
-        read_buf_avail -= in_buf_size;
-        read_buf_ofs += in_buf_size;
-
-        if (out_buf_size)
-        {
-          if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
-          {
-            status = TINFL_STATUS_FAILED;
-            break;
-          }
-          file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
-          if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
-          {
-            status = TINFL_STATUS_FAILED;
-            break;
-          }
-        }
-      } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
-    }
-  }
-
-  if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
-  {
-    // Make sure the entire file was decompressed, and check its CRC.
-    if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
-      status = TINFL_STATUS_FAILED;
-  }
-
-  if (!pZip->m_pState->m_pMem)
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-  if (pWrite_buf)
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
-
-  return status == TINFL_STATUS_DONE;
-}
-
-mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
-{
-  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
-  if (file_index < 0)
-    return MZ_FALSE;
-  return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
-}
-
-#ifndef MINIZ_NO_STDIO
-static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
-{
-  (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
-}
-
-mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
-{
-  mz_bool status;
-  mz_zip_archive_file_stat file_stat;
-  MZ_FILE *pFile;
-  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
-    return MZ_FALSE;
-  pFile = MZ_FOPEN(pDst_filename, "wb");
-  if (!pFile)
-    return MZ_FALSE;
-  status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
-  if (MZ_FCLOSE(pFile) == EOF)
-    return MZ_FALSE;
-#ifndef MINIZ_NO_TIME
-  if (status)
-    mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
-#endif
-  return status;
-}
-#endif // #ifndef MINIZ_NO_STDIO
-
-mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
-{
-  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
-    return MZ_FALSE;
-
-  if (pZip->m_pState)
-  {
-    mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
-    mz_zip_array_clear(pZip, &pState->m_central_dir);
-    mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
-    mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
-
-#ifndef MINIZ_NO_STDIO
-    if (pState->m_pFile)
-    {
-      MZ_FCLOSE(pState->m_pFile);
-      pState->m_pFile = NULL;
-    }
-#endif // #ifndef MINIZ_NO_STDIO
-
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
-  }
-  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
-
-  return MZ_TRUE;
-}
-
-#ifndef MINIZ_NO_STDIO
-mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
-{
-  int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
-  if (file_index < 0)
-    return MZ_FALSE;
-  return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
-}
-#endif
-
-// ------------------- .ZIP archive writing
-
-#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
-
-static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
-static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
-#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
-#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
-
-mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
-{
-  if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
-    return MZ_FALSE;
-
-  if (pZip->m_file_offset_alignment)
-  {
-    // Ensure user specified file offset alignment is a power of 2.
-    if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
-      return MZ_FALSE;
-  }
-
-  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
-  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
-  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
-
-  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
-  pZip->m_archive_size = existing_size;
-  pZip->m_central_directory_file_ofs = 0;
-  pZip->m_total_files = 0;
-
-  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
-    return MZ_FALSE;
-  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
-  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
-  return MZ_TRUE;
-}
-
-static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
-{
-  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
-  mz_zip_internal_state *pState = pZip->m_pState;
-  mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
-#ifdef _MSC_VER
-  if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
-#else
-  if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
-#endif
-    return 0;
-  if (new_size > pState->m_mem_capacity)
-  {
-    void *pNew_block;
-    size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
-    if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
-      return 0;
-    pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
-  }
-  memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
-  pState->m_mem_size = (size_t)new_size;
-  return n;
-}
-
-mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
-{
-  pZip->m_pWrite = mz_zip_heap_write_func;
-  pZip->m_pIO_opaque = pZip;
-  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
-    return MZ_FALSE;
-  if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
-  {
-    if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
-    {
-      mz_zip_writer_end(pZip);
-      return MZ_FALSE;
-    }
-    pZip->m_pState->m_mem_capacity = initial_allocation_size;
-  }
-  return MZ_TRUE;
-}
-
-#ifndef MINIZ_NO_STDIO
-static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
-{
-  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
-  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
-  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
-    return 0;
-  return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
-}
-
-mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
-{
-  MZ_FILE *pFile;
-  pZip->m_pWrite = mz_zip_file_write_func;
-  pZip->m_pIO_opaque = pZip;
-  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
-    return MZ_FALSE;
-  if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
-  {
-    mz_zip_writer_end(pZip);
-    return MZ_FALSE;
-  }
-  pZip->m_pState->m_pFile = pFile;
-  if (size_to_reserve_at_beginning)
-  {
-    mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
-    do
-    {
-      size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
-      if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
-      {
-        mz_zip_writer_end(pZip);
-        return MZ_FALSE;
-      }
-      cur_ofs += n; size_to_reserve_at_beginning -= n;
-    } while (size_to_reserve_at_beginning);
-  }
-  return MZ_TRUE;
-}
-#endif // #ifndef MINIZ_NO_STDIO
-
-mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
-{
-  mz_zip_internal_state *pState;
-  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
-    return MZ_FALSE;
-  // No sense in trying to write to an archive that's already at the support max size
-  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  pState = pZip->m_pState;
-
-  if (pState->m_pFile)
-  {
-#ifdef MINIZ_NO_STDIO
-    pFilename; return MZ_FALSE;
-#else
-    // Archive is being read from stdio - try to reopen as writable.
-    if (pZip->m_pIO_opaque != pZip)
-      return MZ_FALSE;
-    if (!pFilename)
-      return MZ_FALSE;
-    pZip->m_pWrite = mz_zip_file_write_func;
-    if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
-    {
-      // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
-      mz_zip_reader_end(pZip);
-      return MZ_FALSE;
-    }
-#endif // #ifdef MINIZ_NO_STDIO
-  }
-  else if (pState->m_pMem)
-  {
-    // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
-    if (pZip->m_pIO_opaque != pZip)
-      return MZ_FALSE;
-    pState->m_mem_capacity = pState->m_mem_size;
-    pZip->m_pWrite = mz_zip_heap_write_func;
-  }
-  // Archive is being read via a user provided read function - make sure the user has specified a write function too.
-  else if (!pZip->m_pWrite)
-    return MZ_FALSE;
-
-  // Start writing new files at the archive's current central directory location.
-  pZip->m_archive_size = pZip->m_central_directory_file_ofs;
-  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
-  pZip->m_central_directory_file_ofs = 0;
-
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
-{
-  return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
-}
-
-typedef struct
-{
-  mz_zip_archive *m_pZip;
-  mz_uint64 m_cur_archive_file_ofs;
-  mz_uint64 m_comp_size;
-} mz_zip_writer_add_state;
-
-static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
-{
-  mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
-  if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
-    return MZ_FALSE;
-  pState->m_cur_archive_file_ofs += len;
-  pState->m_comp_size += len;
-  return MZ_TRUE;
-}
-
-static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
-{
-  (void)pZip;
-  memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
-  return MZ_TRUE;
-}
-
-static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
-{
-  (void)pZip;
-  memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
-  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
-  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
-  return MZ_TRUE;
-}
-
-static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
-{
-  mz_zip_internal_state *pState = pZip->m_pState;
-  mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
-  size_t orig_central_dir_size = pState->m_central_dir.m_size;
-  mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
-
-  // No zip64 support yet
-  if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
-    return MZ_FALSE;
-
-  if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
-      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
-      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
-      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
-      (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
-  {
-    // Try to push the central directory array back into its original state.
-    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
-    return MZ_FALSE;
-  }
-
-  return MZ_TRUE;
-}
-
-static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
-{
-  // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
-  if (*pArchive_name == '/')
-    return MZ_FALSE;
-  while (*pArchive_name)
-  {
-    if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
-      return MZ_FALSE;
-    pArchive_name++;
-  }
-  return MZ_TRUE;
-}
-
-static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
-{
-  mz_uint32 n;
-  if (!pZip->m_file_offset_alignment)
-    return 0;
-  n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
-  return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
-}
-
-static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
-{
-  char buf[4096];
-  memset(buf, 0, MZ_MIN(sizeof(buf), n));
-  while (n)
-  {
-    mz_uint32 s = MZ_MIN(sizeof(buf), n);
-    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
-      return MZ_FALSE;
-    cur_file_ofs += s; n -= s;
-  }
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
-{
-  mz_uint16 method = 0, dos_time = 0, dos_date = 0;
-  mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
-  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
-  size_t archive_name_size;
-  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
-  tdefl_compressor *pComp = NULL;
-  mz_bool store_data_uncompressed;
-  mz_zip_internal_state *pState;
-
-  if ((int)level_and_flags < 0)
-    level_and_flags = MZ_DEFAULT_LEVEL;
-  level = level_and_flags & 0xF;
-  store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
-
-  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
-    return MZ_FALSE;
-
-  pState = pZip->m_pState;
-
-  if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
-    return MZ_FALSE;
-  // No zip64 support yet
-  if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
-    return MZ_FALSE;
-  if (!mz_zip_writer_validate_archive_name(pArchive_name))
-    return MZ_FALSE;
-
-#ifndef MINIZ_NO_TIME
-  {
-    time_t cur_time; time(&cur_time);
-    mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
-  }
-#endif // #ifndef MINIZ_NO_TIME
-
-  archive_name_size = strlen(pArchive_name);
-  if (archive_name_size > 0xFFFF)
-    return MZ_FALSE;
-
-  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
-
-  // no zip64 support yet
-  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
-  {
-    // Set DOS Subdirectory attribute bit.
-    ext_attributes |= 0x10;
-    // Subdirectories cannot contain data.
-    if ((buf_size) || (uncomp_size))
-      return MZ_FALSE;
-  }
-
-  // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
-  if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
-    return MZ_FALSE;
-
-  if ((!store_data_uncompressed) && (buf_size))
-  {
-    if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
-      return MZ_FALSE;
-  }
-
-  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
-  {
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-    return MZ_FALSE;
-  }
-  local_dir_header_ofs += num_alignment_padding_bytes;
-  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
-  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
-
-  MZ_CLEAR_OBJ(local_dir_header);
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
-  {
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-    return MZ_FALSE;
-  }
-  cur_archive_file_ofs += archive_name_size;
-
-  if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
-  {
-    uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
-    uncomp_size = buf_size;
-    if (uncomp_size <= 3)
-    {
-      level = 0;
-      store_data_uncompressed = MZ_TRUE;
-    }
-  }
-
-  if (store_data_uncompressed)
-  {
-    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-      return MZ_FALSE;
-    }
-
-    cur_archive_file_ofs += buf_size;
-    comp_size = buf_size;
-
-    if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
-      method = MZ_DEFLATED;
-  }
-  else if (buf_size)
-  {
-    mz_zip_writer_add_state state;
-
-    state.m_pZip = pZip;
-    state.m_cur_archive_file_ofs = cur_archive_file_ofs;
-    state.m_comp_size = 0;
-
-    if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
-        (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-      return MZ_FALSE;
-    }
-
-    comp_size = state.m_comp_size;
-    cur_archive_file_ofs = state.m_cur_archive_file_ofs;
-
-    method = MZ_DEFLATED;
-  }
-
-  pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-  pComp = NULL;
-
-  // no zip64 support yet
-  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
-    return MZ_FALSE;
-
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
-    return MZ_FALSE;
-
-  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
-    return MZ_FALSE;
-
-  pZip->m_total_files++;
-  pZip->m_archive_size = cur_archive_file_ofs;
-
-  return MZ_TRUE;
-}
-
-#ifndef MINIZ_NO_STDIO
-mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
-{
-  mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
-  mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
-  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
-  size_t archive_name_size;
-  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
-  MZ_FILE *pSrc_file = NULL;
-
-  if ((int)level_and_flags < 0)
-    level_and_flags = MZ_DEFAULT_LEVEL;
-  level = level_and_flags & 0xF;
-
-  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
-    return MZ_FALSE;
-  if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
-    return MZ_FALSE;
-  if (!mz_zip_writer_validate_archive_name(pArchive_name))
-    return MZ_FALSE;
-
-  archive_name_size = strlen(pArchive_name);
-  if (archive_name_size > 0xFFFF)
-    return MZ_FALSE;
-
-  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
-
-  // no zip64 support yet
-  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
-    return MZ_FALSE;
-
-  pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
-  if (!pSrc_file)
-    return MZ_FALSE;
-  MZ_FSEEK64(pSrc_file, 0, SEEK_END);
-  uncomp_size = MZ_FTELL64(pSrc_file);
-  MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
-
-  if (uncomp_size > 0xFFFFFFFF)
-  {
-    // No zip64 support yet
-    MZ_FCLOSE(pSrc_file);
-    return MZ_FALSE;
-  }
-  if (uncomp_size <= 3)
-    level = 0;
-
-  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
-    return MZ_FALSE;
-  local_dir_header_ofs += num_alignment_padding_bytes;
-  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
-  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
-
-  MZ_CLEAR_OBJ(local_dir_header);
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
-  {
-    MZ_FCLOSE(pSrc_file);
-    return MZ_FALSE;
-  }
-  cur_archive_file_ofs += archive_name_size;
-
-  if (uncomp_size)
-  {
-    mz_uint64 uncomp_remaining = uncomp_size;
-    void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
-    if (!pRead_buf)
-    {
-      MZ_FCLOSE(pSrc_file);
-      return MZ_FALSE;
-    }
-
-    if (!level)
-    {
-      while (uncomp_remaining)
-      {
-        mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
-        if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
-        {
-          pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-          MZ_FCLOSE(pSrc_file);
-          return MZ_FALSE;
-        }
-        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
-        uncomp_remaining -= n;
-        cur_archive_file_ofs += n;
-      }
-      comp_size = uncomp_size;
-    }
-    else
-    {
-      mz_bool result = MZ_FALSE;
-      mz_zip_writer_add_state state;
-      tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
-      if (!pComp)
-      {
-        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-        MZ_FCLOSE(pSrc_file);
-        return MZ_FALSE;
-      }
-
-      state.m_pZip = pZip;
-      state.m_cur_archive_file_ofs = cur_archive_file_ofs;
-      state.m_comp_size = 0;
-
-      if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
-      {
-        pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-        MZ_FCLOSE(pSrc_file);
-        return MZ_FALSE;
-      }
-
-      for ( ; ; )
-      {
-        size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
-        tdefl_status status;
-
-        if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
-          break;
-
-        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
-        uncomp_remaining -= in_buf_size;
-
-        status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
-        if (status == TDEFL_STATUS_DONE)
-        {
-          result = MZ_TRUE;
-          break;
-        }
-        else if (status != TDEFL_STATUS_OKAY)
-          break;
-      }
-
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
-
-      if (!result)
-      {
-        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-        MZ_FCLOSE(pSrc_file);
-        return MZ_FALSE;
-      }
-
-      comp_size = state.m_comp_size;
-      cur_archive_file_ofs = state.m_cur_archive_file_ofs;
-
-      method = MZ_DEFLATED;
-    }
-
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-  }
-
-  MZ_FCLOSE(pSrc_file); pSrc_file = NULL;
-
-  // no zip64 support yet
-  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
-    return MZ_FALSE;
-
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
-    return MZ_FALSE;
-
-  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
-    return MZ_FALSE;
-
-  pZip->m_total_files++;
-  pZip->m_archive_size = cur_archive_file_ofs;
-
-  return MZ_TRUE;
-}
-#endif // #ifndef MINIZ_NO_STDIO
-
-mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
-{
-  mz_uint n, bit_flags, num_alignment_padding_bytes;
-  mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
-  mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
-  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
-  mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
-  size_t orig_central_dir_size;
-  mz_zip_internal_state *pState;
-  void *pBuf; const mz_uint8 *pSrc_central_header;
-
-  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
-    return MZ_FALSE;
-  if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
-    return MZ_FALSE;
-  pState = pZip->m_pState;
-
-  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
-
-  // no zip64 support yet
-  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
-  cur_dst_file_ofs = pZip->m_archive_size;
-
-  if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
-    return MZ_FALSE;
-  cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
-
-  if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
-    return MZ_FALSE;
-  cur_dst_file_ofs += num_alignment_padding_bytes;
-  local_dir_header_ofs = cur_dst_file_ofs;
-  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
-
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
-  cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
-
-  n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
-  comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
-
-  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
-    return MZ_FALSE;
-
-  while (comp_bytes_remaining)
-  {
-    n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
-    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-      return MZ_FALSE;
-    }
-    cur_src_file_ofs += n;
-
-    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-      return MZ_FALSE;
-    }
-    cur_dst_file_ofs += n;
-
-    comp_bytes_remaining -= n;
-  }
-
-  bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
-  if (bit_flags & 8)
-  {
-    // Copy data descriptor
-    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-      return MZ_FALSE;
-    }
-
-    n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
-    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
-    {
-      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-      return MZ_FALSE;
-    }
-
-    cur_src_file_ofs += n;
-    cur_dst_file_ofs += n;
-  }
-  pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
-
-  // no zip64 support yet
-  if (cur_dst_file_ofs > 0xFFFFFFFF)
-    return MZ_FALSE;
-
-  orig_central_dir_size = pState->m_central_dir.m_size;
-
-  memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
-  MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
-  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
-    return MZ_FALSE;
-
-  n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
-  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
-  {
-    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
-    return MZ_FALSE;
-  }
-
-  if (pState->m_central_dir.m_size > 0xFFFFFFFF)
-    return MZ_FALSE;
-  n = (mz_uint32)pState->m_central_dir.m_size;
-  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
-  {
-    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
-    return MZ_FALSE;
-  }
-
-  pZip->m_total_files++;
-  pZip->m_archive_size = cur_dst_file_ofs;
-
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
-{
-  mz_zip_internal_state *pState;
-  mz_uint64 central_dir_ofs, central_dir_size;
-  mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];
-
-  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
-    return MZ_FALSE;
-
-  pState = pZip->m_pState;
-
-  // no zip64 support yet
-  if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
-    return MZ_FALSE;
-
-  central_dir_ofs = 0;
-  central_dir_size = 0;
-  if (pZip->m_total_files)
-  {
-    // Write central directory
-    central_dir_ofs = pZip->m_archive_size;
-    central_dir_size = pState->m_central_dir.m_size;
-    pZip->m_central_directory_file_ofs = central_dir_ofs;
-    if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
-      return MZ_FALSE;
-    pZip->m_archive_size += central_dir_size;
-  }
-
-  // Write end of central directory record
-  MZ_CLEAR_OBJ(hdr);
-  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
-  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
-  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
-  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
-  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);
-
-  if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
-    return MZ_FALSE;
-#ifndef MINIZ_NO_STDIO
-  if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
-    return MZ_FALSE;
-#endif // #ifndef MINIZ_NO_STDIO
-
-  pZip->m_archive_size += sizeof(hdr);
-
-  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
-{
-  if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
-    return MZ_FALSE;
-  if (pZip->m_pWrite != mz_zip_heap_write_func)
-    return MZ_FALSE;
-  if (!mz_zip_writer_finalize_archive(pZip))
-    return MZ_FALSE;
-
-  *pBuf = pZip->m_pState->m_pMem;
-  *pSize = pZip->m_pState->m_mem_size;
-  pZip->m_pState->m_pMem = NULL;
-  pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
-  return MZ_TRUE;
-}
-
-mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
-{
-  mz_zip_internal_state *pState;
-  mz_bool status = MZ_TRUE;
-  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
-    return MZ_FALSE;
-
-  pState = pZip->m_pState;
-  pZip->m_pState = NULL;
-  mz_zip_array_clear(pZip, &pState->m_central_dir);
-  mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
-  mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
-
-#ifndef MINIZ_NO_STDIO
-  if (pState->m_pFile)
-  {
-    MZ_FCLOSE(pState->m_pFile);
-    pState->m_pFile = NULL;
-  }
-#endif // #ifndef MINIZ_NO_STDIO
-
-  if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
-  {
-    pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
-    pState->m_pMem = NULL;
-  }
-
-  pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
-  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
-  return status;
-}
-
-#ifndef MINIZ_NO_STDIO
-mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
-{
-  mz_bool status, created_new_archive = MZ_FALSE;
-  mz_zip_archive zip_archive;
-  struct MZ_FILE_STAT_STRUCT file_stat;
-  MZ_CLEAR_OBJ(zip_archive);
-  if ((int)level_and_flags < 0)
-     level_and_flags = MZ_DEFAULT_LEVEL;
-  if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
-    return MZ_FALSE;
-  if (!mz_zip_writer_validate_archive_name(pArchive_name))
-    return MZ_FALSE;
-  if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
-  {
-    // Create a new archive.
-    if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
-      return MZ_FALSE;
-    created_new_archive = MZ_TRUE;
-  }
-  else
-  {
-    // Append to an existing archive.
-    if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
-      return MZ_FALSE;
-    if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
-    {
-      mz_zip_reader_end(&zip_archive);
-      return MZ_FALSE;
-    }
-  }
-  status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
-  // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
-  if (!mz_zip_writer_finalize_archive(&zip_archive))
-    status = MZ_FALSE;
-  if (!mz_zip_writer_end(&zip_archive))
-    status = MZ_FALSE;
-  if ((!status) && (created_new_archive))
-  {
-    // It's a new archive and something went wrong, so just delete it.
-    int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
-    (void)ignoredStatus;
-  }
-  return status;
-}
-
-void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
-{
-  int file_index;
-  mz_zip_archive zip_archive;
-  void *p = NULL;
-
-  if (pSize)
-    *pSize = 0;
-
-  if ((!pZip_filename) || (!pArchive_name))
-    return NULL;
-
-  MZ_CLEAR_OBJ(zip_archive);
-  if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
-    return NULL;
-
-  if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
-    p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
-
-  mz_zip_reader_end(&zip_archive);
-  return p;
-}
-
-#endif // #ifndef MINIZ_NO_STDIO
-
-#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
-
-#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // MINIZ_HEADER_FILE_ONLY
-
-/*
-  This is free and unencumbered software released into the public domain.
-
-  Anyone is free to copy, modify, publish, use, compile, sell, or
-  distribute this software, either in source code form or as a compiled
-  binary, for any purpose, commercial or non-commercial, and by any
-  means.
-
-  In jurisdictions that recognize copyright laws, the author or authors
-  of this software dedicate any and all copyright interest in the
-  software to the public domain. We make this dedication for the benefit
-  of the public at large and to the detriment of our heirs and
-  successors. We intend this dedication to be an overt act of
-  relinquishment in perpetuity of all present and future rights to this
-  software under copyright law.
-
-  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 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.
-
-  For more information, please refer to <http://unlicense.org/>
-*/
diff --git a/src/miniz_png.cpp b/src/miniz_png.cpp
deleted file mode 100644
index 746cffa..0000000
--- a/src/miniz_png.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-// mapnik
-#include <mapnik/palette.hpp>
-#include <mapnik/miniz_png.hpp>
-#include <mapnik/image.hpp>
-#include <mapnik/image_view.hpp>
-
-// miniz
-#define MINIZ_NO_ARCHIVE_APIS
-#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
-
-#pragma GCC diagnostic push
-#include <mapnik/warning_ignore.hpp>
-extern "C" {
-#include "miniz.c"
-}
-#pragma GCC diagnostic pop
-
-// zlib
-#include <zlib.h>
-
-// stl
-#include <vector>
-#include <iostream>
-#include <stdexcept>
-
-namespace mapnik { namespace MiniZ {
-
-PNGWriter::PNGWriter(int level, int strategy)
-{
-    buffer = nullptr;
-    compressor = nullptr;
-
-    if (level == -1)
-    {
-        level = MZ_DEFAULT_LEVEL; // 6
-    }
-    else if (level < 0 || level > 10)
-    {
-        throw std::runtime_error("compression level must be between 0 and 10");
-    }
-    mz_uint flags = s_tdefl_num_probes[level] | TDEFL_WRITE_ZLIB_HEADER;
-    if (level <= 3)
-    {
-        flags |= TDEFL_GREEDY_PARSING_FLAG;
-    }
-    if (strategy == Z_FILTERED) flags |= TDEFL_FILTER_MATCHES;
-    else if (strategy == Z_HUFFMAN_ONLY) flags &= ~TDEFL_MAX_PROBES_MASK;
-    else if (strategy == Z_RLE) flags |= TDEFL_RLE_MATCHES;
-    else if (strategy == Z_FIXED) flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
-
-    buffer = (tdefl_output_buffer *)MZ_MALLOC(sizeof(tdefl_output_buffer));
-    if (buffer == nullptr)
-    {
-        throw std::bad_alloc();
-    }
-
-    buffer->m_pBuf = nullptr;
-    buffer->m_capacity = 8192;
-    buffer->m_expandable = MZ_TRUE;
-    buffer->m_pBuf = (mz_uint8 *)MZ_MALLOC(buffer->m_capacity);
-    if (buffer->m_pBuf == nullptr)
-    {
-        throw std::bad_alloc();
-    }
-
-    compressor = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
-    if (compressor == nullptr)
-    {
-        throw std::bad_alloc();
-    }
-
-    // Reset output buffer.
-    buffer->m_size = 0;
-    tdefl_status tdstatus = tdefl_init(compressor, tdefl_output_buffer_putter, buffer, flags);
-    if (tdstatus != TDEFL_STATUS_OKAY)
-    {
-        throw std::runtime_error("tdefl_init failed");
-    }
-
-    // Write preamble.
-    mz_bool status = tdefl_output_buffer_putter(preamble, 8, buffer);
-    if (status != MZ_TRUE)
-    {
-        throw std::bad_alloc();
-    }
-}
-
-PNGWriter::~PNGWriter()
-{
-    if (compressor)
-    {
-        MZ_FREE(compressor);
-    }
-    if (buffer)
-    {
-        if (buffer->m_pBuf)
-        {
-            MZ_FREE(buffer->m_pBuf);
-        }
-        MZ_FREE(buffer);
-    }
-}
-
-inline void PNGWriter::writeUInt32BE(mz_uint8 *target, mz_uint32 value)
-{
-    target[0] = (value >> 24) & 0xFF;
-    target[1] = (value >> 16) & 0xFF;
-    target[2] = (value >> 8) & 0xFF;
-    target[3] = value & 0xFF;
-}
-
-size_t PNGWriter::startChunk(const mz_uint8 header[], size_t length)
-{
-    size_t start = buffer->m_size;
-    mz_bool status = tdefl_output_buffer_putter(header, length, buffer);
-    if (status != MZ_TRUE)
-    {
-        throw std::bad_alloc();
-    }
-    return start;
-}
-
-void PNGWriter::finishChunk(size_t start)
-{
-    // Write chunk length at the beginning of the chunk.
-    size_t payloadLength = buffer->m_size - start - 4 - 4;
-    writeUInt32BE(buffer->m_pBuf + start, static_cast<mz_uint32>(payloadLength));
-    // Write CRC32 checksum. Don't include the 4-byte length, but /do/ include
-    // the 4-byte chunk name.
-    mz_uint32 crc = mz_crc32(MZ_CRC32_INIT, buffer->m_pBuf + start + 4, payloadLength + 4);
-    mz_uint8 checksum[] = { static_cast<mz_uint8>(crc >> 24),
-                            static_cast<mz_uint8>(crc >> 16),
-                            static_cast<mz_uint8>(crc >> 8),
-                            static_cast<mz_uint8>(crc) };
-    mz_bool status = tdefl_output_buffer_putter(checksum, 4, buffer);
-    if (status != MZ_TRUE)
-    {
-        throw std::bad_alloc();
-    }
-}
-
-void PNGWriter::writeIHDR(mz_uint32 width, mz_uint32 height, mz_uint8 pixel_depth)
-{
-    // Write IHDR chunk.
-    size_t IHDR = startChunk(IHDR_tpl, 21);
-    writeUInt32BE(buffer->m_pBuf + IHDR + 8, width);
-    writeUInt32BE(buffer->m_pBuf + IHDR + 12, height);
-
-    if (pixel_depth == 32)
-    {
-        // Alpha full color image.
-        buffer->m_pBuf[IHDR + 16] = 8; // bit depth
-        buffer->m_pBuf[IHDR + 17] = 6; // color type (6 == true color with alpha)
-    }
-    else if (pixel_depth == 24)
-    {
-        // Full color image.
-        buffer->m_pBuf[IHDR + 16] = 8; // bit depth
-        buffer->m_pBuf[IHDR + 17] = 2; // color type (2 == true color without alpha)
-    }
-    else
-    {
-        // Paletted image.
-        buffer->m_pBuf[IHDR + 16] = pixel_depth; // bit depth
-        buffer->m_pBuf[IHDR + 17] = 3; // color type (3 == indexed color)
-    }
-
-    buffer->m_pBuf[IHDR + 18] = 0; // compression method
-    buffer->m_pBuf[IHDR + 19] = 0; // filter method
-    buffer->m_pBuf[IHDR + 20] = 0; // interlace method
-    finishChunk(IHDR);
-}
-
-void PNGWriter::writePLTE(std::vector<rgb> const& palette)
-{
-    // Write PLTE chunk.
-    size_t PLTE = startChunk(PLTE_tpl, 8);
-    const mz_uint8 *colors = reinterpret_cast<const mz_uint8 *>(&palette[0]);
-    mz_bool status = tdefl_output_buffer_putter(colors, palette.size() * 3, buffer);
-    if (status != MZ_TRUE)
-    {
-        throw std::bad_alloc();
-    }
-    finishChunk(PLTE);
-}
-
-void PNGWriter::writetRNS(std::vector<unsigned> const& alpha)
-{
-    if (alpha.size() == 0)
-    {
-        return;
-    }
-
-    std::vector<unsigned char> transparency(alpha.size());
-    unsigned char transparencySize = 0; // Stores position of biggest to nonopaque value.
-    for(unsigned i = 0; i < alpha.size(); i++)
-    {
-        transparency[i] = alpha[i];
-        if (alpha[i] < 255)
-        {
-            transparencySize = i + 1;
-        }
-    }
-    if (transparencySize > 0)
-    {
-        // Write tRNS chunk.
-        size_t tRNS = startChunk(tRNS_tpl, 8);
-        mz_bool status = tdefl_output_buffer_putter(&transparency[0], transparencySize, buffer);
-        if (status != MZ_TRUE)
-        {
-            throw std::bad_alloc();
-        }
-        finishChunk(tRNS);
-    }
-}
-
-template<typename T>
-void PNGWriter::writeIDAT(T const& image)
-{
-    // Write IDAT chunk.
-    size_t IDAT = startChunk(IDAT_tpl, 8);
-    mz_uint8 filter_type = 0;
-    tdefl_status status;
-
-    int bytes_per_pixel = sizeof(typename T::pixel_type);
-    int stride = image.width() * bytes_per_pixel;
-
-    for (unsigned int y = 0; y < image.height(); y++)
-    {
-        // Write filter_type
-        status = tdefl_compress_buffer(compressor, &filter_type, 1, TDEFL_NO_FLUSH);
-        if (status != TDEFL_STATUS_OKAY)
-        {
-            throw std::runtime_error("failed to compress image");
-        }
-
-        // Write scanline
-        status = tdefl_compress_buffer(compressor, (mz_uint8 *)image.get_row(y), stride, TDEFL_NO_FLUSH);
-        if (status != TDEFL_STATUS_OKAY)
-        {
-            throw std::runtime_error("failed to compress image");
-        }
-    }
-
-    status = tdefl_compress_buffer(compressor, nullptr, 0, TDEFL_FINISH);
-    if (status != TDEFL_STATUS_DONE)
-    {
-        throw std::runtime_error("failed to compress image");
-    }
-
-    finishChunk(IDAT);
-}
-
-template<typename T>
-void PNGWriter::writeIDATStripAlpha(T const& image) {
-    // Write IDAT chunk.
-    size_t IDAT = startChunk(IDAT_tpl, 8);
-    mz_uint8 filter_type = 0;
-    tdefl_status status;
-
-    size_t stride = image.width() * 3;
-    size_t i, j;
-    mz_uint8 *scanline = (mz_uint8 *)MZ_MALLOC(stride);
-
-    for (unsigned int y = 0; y < image.height(); y++) {
-        // Write filter_type
-        status = tdefl_compress_buffer(compressor, &filter_type, 1, TDEFL_NO_FLUSH);
-        if (status != TDEFL_STATUS_OKAY)
-        {
-            MZ_FREE(scanline);
-            throw std::runtime_error("failed to compress image");
-        }
-
-        // Strip alpha bytes from scanline
-        mz_uint8 *row = (mz_uint8 *)image.get_row(y);
-        for (i = 0, j = 0; j < stride; i += 4, j += 3) {
-            scanline[j] = row[i];
-            scanline[j+1] = row[i+1];
-            scanline[j+2] = row[i+2];
-        }
-
-        // Write scanline
-        status = tdefl_compress_buffer(compressor, scanline, stride, TDEFL_NO_FLUSH);
-        if (status != TDEFL_STATUS_OKAY) {
-            MZ_FREE(scanline);
-            throw std::runtime_error("failed to compress image");
-        }
-    }
-
-    MZ_FREE(scanline);
-
-    status = tdefl_compress_buffer(compressor, nullptr, 0, TDEFL_FINISH);
-    if (status != TDEFL_STATUS_DONE) throw std::runtime_error("failed to compress image");
-
-    finishChunk(IDAT);
-}
-
-void PNGWriter::writeIEND()
-{
-    // Write IEND chunk.
-    size_t IEND = startChunk(IEND_tpl, 8);
-    finishChunk(IEND);
-}
-
-void PNGWriter::toStream(std::ostream& stream)
-{
-    stream.write((char *)buffer->m_pBuf, buffer->m_size);
-}
-
-const mz_uint8 PNGWriter::preamble[] = {
-    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
-};
-
-const mz_uint8 PNGWriter::IHDR_tpl[] = {
-    0x00, 0x00, 0x00, 0x0D, // chunk length
-    'I', 'H', 'D', 'R',     // "IHDR"
-    0x00, 0x00, 0x00, 0x00, // image width (4 bytes)
-    0x00, 0x00, 0x00, 0x00, // image height (4 bytes)
-    0x00,                   // bit depth (1 byte)
-    0x00,                   // color type (1 byte)
-    0x00,                   // compression method (1 byte), has to be 0
-    0x00,                   // filter method (1 byte)
-    0x00                    // interlace method (1 byte)
-};
-
-const mz_uint8 PNGWriter::PLTE_tpl[] = {
-    0x00, 0x00, 0x00, 0x00, // chunk length
-    'P', 'L', 'T', 'E'      // "IDAT"
-};
-
-const mz_uint8 PNGWriter::tRNS_tpl[] = {
-    0x00, 0x00, 0x00, 0x00, // chunk length
-    't', 'R', 'N', 'S'      // "IDAT"
-};
-
-const mz_uint8 PNGWriter::IDAT_tpl[] = {
-    0x00, 0x00, 0x00, 0x00, // chunk length
-    'I', 'D', 'A', 'T'      // "IDAT"
-};
-
-const mz_uint8 PNGWriter::IEND_tpl[] = {
-    0x00, 0x00, 0x00, 0x00, // chunk length
-    'I', 'E', 'N', 'D'      // "IEND"
-};
-
-template void PNGWriter::writeIDAT<image_gray8>(image_gray8 const& image);
-template void PNGWriter::writeIDAT<image_view_gray8>(image_view_gray8 const& image);
-template void PNGWriter::writeIDAT<image_rgba8>(image_rgba8 const& image);
-template void PNGWriter::writeIDAT<image_view_rgba8>(image_view_rgba8 const& image);
-template void PNGWriter::writeIDATStripAlpha<image_rgba8>(image_rgba8 const& image);
-template void PNGWriter::writeIDATStripAlpha<image_view_rgba8>(image_view_rgba8 const& image);
-
-}}
diff --git a/src/renderer_common.cpp b/src/renderer_common.cpp
index 37223e3..d45e3a0 100644
--- a/src/renderer_common.cpp
+++ b/src/renderer_common.cpp
@@ -29,10 +29,24 @@
 
 namespace mapnik {
 
+// copy constructor exclusively for virtual_renderer_common
+renderer_common::renderer_common(renderer_common const& other)
+    : width_(other.width_),
+      height_(other.height_),
+      scale_factor_(other.scale_factor_),
+      vars_(other.vars_),
+      shared_font_library_(other.shared_font_library_),
+      font_library_(other.font_library_),
+      font_manager_(other.font_manager_),
+      query_extent_(other.query_extent_),
+      t_(other.t_),
+      detector_(other.detector_)
+{}
+
 renderer_common::renderer_common(Map const& map, unsigned width, unsigned height, double scale_factor,
                                  attributes const& vars,
                                  view_transform && t,
-                                 std::shared_ptr<label_collision_detector4> detector)
+                                 detector_ptr detector)
    : width_(width),
      height_(height),
      scale_factor_(scale_factor),
@@ -57,7 +71,7 @@ renderer_common::renderer_common(Map const &m, attributes const& vars, unsigned
 
 renderer_common::renderer_common(Map const &m, attributes const& vars, unsigned offset_x, unsigned offset_y,
                                  unsigned width, unsigned height, double scale_factor,
-                                 std::shared_ptr<label_collision_detector4> detector)
+                                 detector_ptr detector)
    : renderer_common(m, width, height, scale_factor,
                      vars,
                      view_transform(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
@@ -74,4 +88,10 @@ renderer_common::renderer_common(Map const &m, request const &req, attributes co
                                       req.width() + req.buffer_size() ,req.height() + req.buffer_size())))
 {}
 
+renderer_common::~renderer_common()
+{
+    // defined in .cpp to make this destructible elsewhere without
+    // having to #include <mapnik/label_collision_detector.hpp>
+}
+
 }
diff --git a/src/renderer_common/process_group_symbolizer.cpp b/src/renderer_common/process_group_symbolizer.cpp
deleted file mode 100644
index ebef914..0000000
--- a/src/renderer_common/process_group_symbolizer.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*****************************************************************************
- *
- * This file is part of Mapnik (c++ mapping toolkit)
- *
- * Copyright (C) 2015 Artem Pavlenko
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- *****************************************************************************/
-
-// mapnik
-#include <mapnik/renderer_common/process_group_symbolizer.hpp>
-#include <mapnik/renderer_common/process_markers_symbolizer.hpp>
-#include <mapnik/make_unique.hpp>
-
-namespace mapnik {
-
-vector_marker_render_thunk::vector_marker_render_thunk(svg_path_ptr const& src,
-                                                       svg_attribute_type const& attrs,
-                                                       agg::trans_affine const& marker_trans,
-                                                       double opacity,
-                                                       composite_mode_e comp_op,
-                                                       bool snap_to_pixels)
-    : src_(src), attrs_(attrs), tr_(marker_trans), opacity_(opacity),
-      comp_op_(comp_op), snap_to_pixels_(snap_to_pixels)
-{}
-
-vector_marker_render_thunk::vector_marker_render_thunk(vector_marker_render_thunk && rhs)
-  : src_(std::move(rhs.src_)),
-    attrs_(std::move(rhs.attrs_)),
-    tr_(std::move(rhs.tr_)),
-    opacity_(std::move(rhs.opacity_)),
-    comp_op_(std::move(rhs.comp_op_)),
-    snap_to_pixels_(std::move(rhs.snap_to_pixels_)) {}
-
-
-raster_marker_render_thunk::raster_marker_render_thunk(image_rgba8 const& src,
-                                                       agg::trans_affine const& marker_trans,
-                                                       double opacity,
-                                                       composite_mode_e comp_op,
-                                                       bool snap_to_pixels)
-    : src_(src), tr_(marker_trans), opacity_(opacity), comp_op_(comp_op),
-      snap_to_pixels_(snap_to_pixels)
-{}
-
-raster_marker_render_thunk::raster_marker_render_thunk(raster_marker_render_thunk && rhs)
-      : src_(rhs.src_),
-        tr_(std::move(rhs.tr_)),
-        opacity_(std::move(rhs.opacity_)),
-        comp_op_(std::move(rhs.comp_op_)),
-        snap_to_pixels_(std::move(rhs.snap_to_pixels_)) {}
-
-
-text_render_thunk::text_render_thunk(helper_ptr && helper,
-                                     double opacity, composite_mode_e comp_op,
-                                     halo_rasterizer_enum halo_rasterizer)
-    : helper_(std::move(helper)),
-      placements_(helper_->get()),
-      opacity_(opacity),
-      comp_op_(comp_op),
-      halo_rasterizer_(halo_rasterizer)
-{}
-
-text_render_thunk::text_render_thunk(text_render_thunk && rhs)
-      : helper_(std::move(rhs.helper_)),
-        placements_(std::move(rhs.placements_)),
-        opacity_(std::move(rhs.opacity_)),
-        comp_op_(std::move(rhs.comp_op_)),
-        halo_rasterizer_(std::move(rhs.halo_rasterizer_)) {}
-
-namespace detail {
-
-template <typename Detector, typename RendererContext>
-struct vector_marker_thunk_dispatch : public vector_markers_dispatch<Detector>
-{
-    vector_marker_thunk_dispatch(svg_path_ptr const& src,
-                                 svg_path_adapter & path,
-                                 svg_attribute_type const& attrs,
-                                 agg::trans_affine const& marker_trans,
-                                 symbolizer_base const& sym,
-                                 Detector & detector,
-                                 double scale_factor,
-                                 feature_impl & feature,
-                                 attributes const& vars,
-                                 bool snap_to_pixels,
-                                 RendererContext const& renderer_context)
-        : vector_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-          attrs_(attrs), comp_op_(get<composite_mode_e, keys::comp_op>(sym, feature, vars)),
-          snap_to_pixels_(snap_to_pixels), thunks_(std::get<0>(renderer_context))
-    {}
-
-    ~vector_marker_thunk_dispatch() {}
-
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
-    {
-        vector_marker_render_thunk thunk(this->src_, this->attrs_, marker_tr, opacity, comp_op_, snap_to_pixels_);
-        thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
-    }
-
-private:
-    svg_attribute_type const& attrs_;
-    composite_mode_e comp_op_;
-    bool snap_to_pixels_;
-    render_thunk_list & thunks_;
-};
-
-template <typename Detector, typename RendererContext>
-struct raster_marker_thunk_dispatch : public raster_markers_dispatch<Detector>
-{
-    raster_marker_thunk_dispatch(image_rgba8 const& src,
-                                 agg::trans_affine const& marker_trans,
-                                 symbolizer_base const& sym,
-                                 Detector & detector,
-                                 double scale_factor,
-                                 feature_impl & feature,
-                                 attributes const& vars,
-                                 RendererContext const& renderer_context,
-                                 bool snap_to_pixels = false)
-        : raster_markers_dispatch<Detector>(src, marker_trans, sym, detector, scale_factor, feature, vars),
-          comp_op_(get<composite_mode_e, keys::comp_op>(sym, feature, vars)),
-          snap_to_pixels_(snap_to_pixels), thunks_(std::get<0>(renderer_context))
-    {}
-
-    ~raster_marker_thunk_dispatch() {}
-
-    void render_marker(agg::trans_affine const& marker_tr, double opacity)
-    {
-        raster_marker_render_thunk thunk(this->src_, marker_tr, opacity, comp_op_, snap_to_pixels_);
-        thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
-    }
-
-private:
-    composite_mode_e comp_op_;
-    bool snap_to_pixels_;
-    render_thunk_list & thunks_;
-};
-
-} // end detail ns
-
-render_thunk_extractor::render_thunk_extractor(box2d<double> & box,
-                                               render_thunk_list & thunks,
-                                               feature_impl & feature,
-                                               attributes const& vars,
-                                               proj_transform const& prj_trans,
-                                               virtual_renderer_common & common,
-                                               box2d<double> const& clipping_extent)
-    : box_(box), thunks_(thunks), feature_(feature), vars_(vars), prj_trans_(prj_trans),
-      common_(common), clipping_extent_(clipping_extent)
-{}
-
-void render_thunk_extractor::operator()(markers_symbolizer const& sym) const
-{
-    auto renderer_context = std::tie(thunks_);
-    using context_type = decltype(renderer_context);
-    using vector_dispatch_type = detail::vector_marker_thunk_dispatch<label_collision_detector4, context_type>;
-    using raster_dispatch_type = detail::raster_marker_thunk_dispatch<label_collision_detector4, context_type>;
-
-    render_markers_symbolizer<vector_dispatch_type, raster_dispatch_type>(
-            sym, feature_, prj_trans_, common_, clipping_extent_, renderer_context);
-
-    update_box();
-}
-
-void render_thunk_extractor::operator()(text_symbolizer const& sym) const
-{
-    box2d<double> clip_box = clipping_extent_;
-    helper_ptr helper = std::make_unique<text_symbolizer_helper>(
-        sym, feature_, vars_, prj_trans_,
-        common_.width_, common_.height_,
-        common_.scale_factor_,
-        common_.t_, common_.font_manager_, *common_.detector_,
-        clip_box, agg::trans_affine());
-
-    extract_text_thunk(std::move(helper), sym);
-}
-
-void render_thunk_extractor::operator()(shield_symbolizer const& sym) const
-{
-    box2d<double> clip_box = clipping_extent_;
-    helper_ptr helper = std::make_unique<text_symbolizer_helper>(
-        sym, feature_, vars_, prj_trans_,
-        common_.width_, common_.height_,
-        common_.scale_factor_,
-        common_.t_, common_.font_manager_, *common_.detector_,
-        clip_box, agg::trans_affine());
-
-    extract_text_thunk(std::move(helper), sym);
-}
-
-void render_thunk_extractor::extract_text_thunk(helper_ptr && helper, text_symbolizer const& sym) const
-{
-    double opacity = get<double>(sym, keys::opacity, feature_, common_.vars_, 1.0);
-    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, common_.vars_, src_over);
-    halo_rasterizer_enum halo_rasterizer = get<halo_rasterizer_enum>(sym, keys::halo_rasterizer, feature_, common_.vars_, HALO_RASTERIZER_FULL);
-
-    text_render_thunk thunk(std::move(helper), opacity, comp_op, halo_rasterizer);
-    thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
-
-    update_box();
-}
-
-void render_thunk_extractor::update_box() const
-{
-    label_collision_detector4 & detector = *common_.detector_;
-
-    for (auto const& label : detector)
-    {
-        if (box_.width() > 0 && box_.height() > 0)
-        {
-            box_.expand_to_include(label.get().box);
-        }
-        else
-        {
-            box_ = label.get().box;
-        }
-    }
-
-    detector.clear();
-}
-
-} // namespace mapnik
diff --git a/src/renderer_common/render_group_symbolizer.cpp b/src/renderer_common/render_group_symbolizer.cpp
new file mode 100644
index 0000000..9892dfc
--- /dev/null
+++ b/src/renderer_common/render_group_symbolizer.cpp
@@ -0,0 +1,194 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+// mapnik
+#include <mapnik/attribute_collector.hpp>
+#include <mapnik/feature_factory.hpp>
+#include <mapnik/group/group_layout_manager.hpp>
+#include <mapnik/group/group_symbolizer_helper.hpp>
+#include <mapnik/group/group_symbolizer_properties.hpp>
+#include <mapnik/renderer_common/render_group_symbolizer.hpp>
+#include <mapnik/renderer_common/render_thunk_extractor.hpp>
+#include <mapnik/util/conversions.hpp>
+
+namespace mapnik {
+
+void render_group_symbolizer(group_symbolizer const& sym,
+                             feature_impl & feature,
+                             attributes const& vars,
+                             proj_transform const& prj_trans,
+                             box2d<double> const& clipping_extent,
+                             renderer_common & common,
+                             render_thunk_list_dispatch & render_thunks)
+{
+    // find all column names referenced in the group rules and symbolizers
+    std::set<std::string> columns;
+    group_attribute_collector column_collector(columns, false);
+    column_collector(sym);
+
+    auto props = get<group_symbolizer_properties_ptr>(sym, keys::group_properties);
+
+    // create a new context for the sub features of this group
+    context_ptr sub_feature_ctx = std::make_shared<mapnik::context_type>();
+
+    // populate new context with column names referenced in the group rules and symbolizers
+    for (auto const& col_name : columns)
+    {
+        sub_feature_ctx->push(col_name);
+    }
+
+    // keep track of the sub features that we'll want to symbolize
+    // along with the group rules that they matched
+    std::vector< std::pair<group_rule_ptr, feature_ptr> > matches;
+
+    // create a copied 'virtual' common renderer for processing sub feature symbolizers
+    // create an empty detector for it, so we are sure we won't hit anything
+    virtual_renderer_common virtual_renderer(common);
+
+    // keep track of which lists of render thunks correspond to
+    // entries in the group_layout_manager.
+    std::vector<render_thunk_list> layout_thunks;
+
+    // layout manager to store and arrange bboxes of matched features
+    group_layout_manager layout_manager(props->get_layout());
+    layout_manager.set_input_origin(common.width_ * 0.5, common.height_ * 0.5);
+
+    // run feature or sub feature through the group rules & symbolizers
+    // for each index value in the range
+    value_integer start = get<value_integer>(sym, keys::start_column);
+    value_integer end = start + get<value_integer>(sym, keys::num_columns);
+    for (value_integer col_idx = start; col_idx < end; ++col_idx)
+    {
+        // create sub feature with indexed column values
+        feature_ptr sub_feature = feature_factory::create(sub_feature_ctx, col_idx);
+
+        // copy the necessary columns to sub feature
+        for(auto const& col_name : columns)
+        {
+            if (col_name.find('%') != std::string::npos)
+            {
+                if (col_name.size() == 1)
+                {
+                    // column name is '%' by itself, so give the index as the value
+                    sub_feature->put(col_name, col_idx);
+                }
+                else
+                {
+                    // indexed column
+                    std::string col_idx_str;
+                    if (mapnik::util::to_string(col_idx_str,col_idx))
+                    {
+                        std::string col_idx_name = col_name;
+                        boost::replace_all(col_idx_name, "%", col_idx_str);
+                        sub_feature->put(col_name, feature.get(col_idx_name));
+                    }
+                }
+            }
+            else
+            {
+                // non-indexed column
+                sub_feature->put(col_name, feature.get(col_name));
+            }
+        }
+
+        // add a single point geometry at pixel origin
+        double x = common.width_ / 2.0, y = common.height_ / 2.0, z = 0.0;
+        common.t_.backward(&x, &y);
+        prj_trans.forward(x, y, z);
+        // note that we choose a point in the middle of the screen to
+        // try to ensure that we don't get edge artefacts due to any
+        // symbolizers with avoid-edges set: only the avoid-edges of
+        // the group symbolizer itself should matter.
+        geometry::point<double> origin_pt(x,y);
+        sub_feature->set_geometry(origin_pt);
+        // get the layout for this set of properties
+        for (auto const& rule : props->get_rules())
+        {
+             if (util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*sub_feature,common.vars_),
+                                               *(rule->get_filter())).to_bool())
+             {
+                // add matched rule and feature to the list of things to draw
+                matches.emplace_back(rule, sub_feature);
+
+                // construct a bounding box around all symbolizers for the matched rule
+                box2d<double> bounds;
+                render_thunk_list thunks;
+                render_thunk_extractor extractor(bounds, thunks, *sub_feature, common.vars_, prj_trans,
+                                                 virtual_renderer, clipping_extent);
+
+                for (auto const& _sym : *rule)
+                {
+                    // TODO: construct layout and obtain bounding box
+                    util::apply_visitor(extractor, _sym);
+                }
+
+                // add the bounding box to the layout manager
+                layout_manager.add_member_bound_box(bounds);
+                layout_thunks.emplace_back(std::move(thunks));
+                break;
+            }
+        }
+    }
+
+    // create a symbolizer helper
+    group_symbolizer_helper helper(sym, feature, vars, prj_trans,
+                                   common.width_, common.height_,
+                                   common.scale_factor_, common.t_,
+                                   *common.detector_, clipping_extent);
+
+    for (size_t i = 0; i < matches.size(); ++i)
+    {
+        group_rule_ptr match_rule = matches[i].first;
+        feature_ptr match_feature = matches[i].second;
+        value_unicode_string rpt_key_value = "";
+
+        // get repeat key from matched group rule
+        expression_ptr rpt_key_expr = match_rule->get_repeat_key();
+
+        // if no repeat key was defined, use default from group symbolizer
+        if (!rpt_key_expr)
+        {
+            rpt_key_expr = get<expression_ptr>(sym, keys::repeat_key);
+        }
+
+        // evaluate the repeat key with the matched sub feature if we have one
+        if (rpt_key_expr)
+        {
+            rpt_key_value = util::apply_visitor(evaluate<feature_impl,value_type,attributes>(*match_feature,common.vars_),
+                                                *rpt_key_expr).to_unicode();
+        }
+        helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
+    }
+
+    pixel_position_list const& positions = helper.get();
+    for (pixel_position const& pos : positions)
+    {
+        for (size_t layout_i = 0; layout_i < layout_thunks.size(); ++layout_i)
+        {
+            pixel_position const& offset = layout_manager.offset_at(layout_i);
+            pixel_position render_offset = pos + offset;
+            render_thunks.render_list(layout_thunks[layout_i], render_offset);
+        }
+    }
+}
+
+} // namespace mapnik
diff --git a/include/mapnik/renderer_common/process_markers_symbolizer.hpp b/src/renderer_common/render_markers_symbolizer.cpp
similarity index 82%
rename from include/mapnik/renderer_common/process_markers_symbolizer.hpp
rename to src/renderer_common/render_markers_symbolizer.cpp
index f67715b..12da737 100644
--- a/include/mapnik/renderer_common/process_markers_symbolizer.hpp
+++ b/src/renderer_common/render_markers_symbolizer.cpp
@@ -2,7 +2,7 @@
  *
  * This file is part of Mapnik (c++ mapping toolkit)
  *
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2016 Artem Pavlenko
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,24 +20,24 @@
  *
  *****************************************************************************/
 
-#ifndef MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
-#define MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
-
 #include <mapnik/svg/svg_storage.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/vertex_converters.hpp>
 #include <mapnik/marker_cache.hpp>
 #include <mapnik/marker_helpers.hpp>
 #include <mapnik/geometry_type.hpp>
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
+#include <mapnik/symbolizer.hpp>
 
 namespace mapnik {
 
-template <typename VD, typename RD, typename RendererType, typename ContextType>
+namespace detail {
+
+template <typename Detector, typename RendererType, typename ContextType>
 struct render_marker_symbolizer_visitor
 {
-    using vector_dispatch_type = VD;
-    using raster_dispatch_type = RD;
-    using buffer_type = typename std::tuple_element<0,ContextType>::type;
+    using vector_dispatch_type = vector_markers_dispatch<Detector>;
+    using raster_dispatch_type = raster_markers_dispatch<Detector>;
 
     using vertex_converter_type = vertex_converter<clip_line_tag,
                                                    clip_poly_tag,
@@ -53,7 +53,7 @@ struct render_marker_symbolizer_visitor
                                      proj_transform const& prj_trans,
                                      RendererType const& common,
                                      box2d<double> const& clip_box,
-                                     ContextType const& renderer_context)
+                                     ContextType & renderer_context)
         : filename_(filename),
           sym_(sym),
           feature_(feature),
@@ -62,9 +62,9 @@ struct render_marker_symbolizer_visitor
           clip_box_(clip_box),
           renderer_context_(renderer_context) {}
 
-    void operator() (marker_null const&) {}
+    void operator() (marker_null const&) const {}
 
-    void operator() (marker_svg const& mark)
+    void operator() (marker_svg const& mark) const
     {
         using namespace mapnik::svg;
         bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
@@ -91,13 +91,13 @@ struct render_marker_symbolizer_visitor
             vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse->source());
             svg_path_adapter svg_path(stl_storage);
             build_ellipse(sym_, feature_, common_.vars_, *marker_ellipse, svg_path);
-            svg_attribute_type attributes;
-            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
+            svg_attribute_type s_attributes;
+            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), s_attributes, sym_, feature_, common_.vars_);
             auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
             if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
             vector_dispatch_type rasterizer_dispatch(marker_ellipse,
                                                      svg_path,
-                                                     result ? attributes : (*stock_vector_marker)->attributes(),
+                                                     result ? s_attributes : (*stock_vector_marker)->attributes(),
                                                      image_tr,
                                                      sym_,
                                                      *common_.detector_,
@@ -139,11 +139,11 @@ struct render_marker_symbolizer_visitor
             if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
             vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
             svg_path_adapter svg_path(stl_storage);
-            svg_attribute_type attributes;
-            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
+            svg_attribute_type s_attributes;
+            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), s_attributes, sym_, feature_, common_.vars_);
             vector_dispatch_type rasterizer_dispatch(*stock_vector_marker,
                                                      svg_path,
-                                                     result ? attributes : (*stock_vector_marker)->attributes(),
+                                                     result ? s_attributes : (*stock_vector_marker)->attributes(),
                                                      image_tr,
                                                      sym_,
                                                      *common_.detector_,
@@ -179,7 +179,7 @@ struct render_marker_symbolizer_visitor
         }
     }
 
-    void operator() (marker_rgba8 const& mark)
+    void operator() (marker_rgba8 const& mark) const
     {
         using namespace mapnik::svg;
         bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
@@ -243,33 +243,57 @@ struct render_marker_symbolizer_visitor
     proj_transform const& prj_trans_;
     RendererType const& common_;
     box2d<double> const& clip_box_;
-    ContextType const& renderer_context_;
+    ContextType & renderer_context_;
 };
 
-template <typename VD, typename RD, typename RendererType, typename ContextType>
+} // namespace detail
+
+markers_dispatch_params::markers_dispatch_params(box2d<double> const& size,
+                                                 agg::trans_affine const& tr,
+                                                 symbolizer_base const& sym,
+                                                 feature_impl const& feature,
+                                                 attributes const& vars,
+                                                 double scale,
+                                                 bool snap)
+    : placement_params{
+        size,
+        tr,
+        get<value_double, keys::spacing>(sym, feature, vars),
+        get<value_double, keys::max_error>(sym, feature, vars),
+        get<value_bool, keys::allow_overlap>(sym, feature, vars),
+        get<value_bool, keys::avoid_edges>(sym, feature, vars),
+        get<direction_enum, keys::direction>(sym, feature, vars)}
+    , placement_method(get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars))
+    , ignore_placement(get<value_bool, keys::ignore_placement>(sym, feature, vars))
+    , snap_to_pixels(snap)
+    , scale_factor(scale)
+    , opacity(get<value_double, keys::opacity>(sym, feature, vars))
+{
+    placement_params.spacing *= scale;
+}
+
 void render_markers_symbolizer(markers_symbolizer const& sym,
                                mapnik::feature_impl & feature,
                                proj_transform const& prj_trans,
-                               RendererType const& common,
+                               renderer_common const& common,
                                box2d<double> const& clip_box,
-                               ContextType const& renderer_context)
+                               markers_renderer_context & renderer_context)
 {
-    using namespace mapnik::svg;
+    using Detector = decltype(*common.detector_);
+    using RendererType = renderer_common;
+    using ContextType = markers_renderer_context;
+    using VisitorType = detail::render_marker_symbolizer_visitor<Detector,
+                                                                 RendererType,
+                                                                 ContextType>;
+
     std::string filename = get<std::string>(sym, keys::file, feature, common.vars_, "shape://ellipse");
     if (!filename.empty())
     {
-        std::shared_ptr<mapnik::marker const> mark = mapnik::marker_cache::instance().find(filename, true);
-        render_marker_symbolizer_visitor<VD,RD,RendererType,ContextType> visitor(filename,
-                                                                                 sym,
-                                                                                 feature,
-                                                                                 prj_trans,
-                                                                                 common,
-                                                                                 clip_box,
-                                                                                 renderer_context);
+        auto mark = mapnik::marker_cache::instance().find(filename, true);
+        VisitorType visitor(filename, sym, feature, prj_trans, common, clip_box,
+                            renderer_context);
         util::apply_visitor(visitor, *mark);
     }
 }
 
 } // namespace mapnik
-
-#endif // MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
diff --git a/src/renderer_common/render_thunk_extractor.cpp b/src/renderer_common/render_thunk_extractor.cpp
new file mode 100644
index 0000000..c437262
--- /dev/null
+++ b/src/renderer_common/render_thunk_extractor.cpp
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2016 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+// mapnik
+#include <mapnik/label_collision_detector.hpp>
+#include <mapnik/make_unique.hpp>
+#include <mapnik/renderer_common/render_markers_symbolizer.hpp>
+#include <mapnik/renderer_common/render_thunk_extractor.hpp>
+
+namespace mapnik {
+
+virtual_renderer_common::virtual_renderer_common(renderer_common const& other)
+    : renderer_common(other)
+{
+    // replace collision detector with my own so that I don't pollute the original
+    detector_ = std::make_shared<label_collision_detector4>(other.detector_->extent());
+}
+
+namespace detail {
+
+struct thunk_markers_renderer_context : markers_renderer_context
+{
+    thunk_markers_renderer_context(symbolizer_base const& sym,
+                                   feature_impl const& feature,
+                                   attributes const& vars,
+                                   render_thunk_list & thunks)
+        : comp_op_(get<composite_mode_e, keys::comp_op>(sym, feature, vars))
+        , thunks_(thunks)
+    {}
+
+    virtual void render_marker(svg_path_ptr const& src,
+                               svg_path_adapter & path,
+                               svg_attribute_type const& attrs,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
+    {
+        vector_marker_render_thunk thunk(src, attrs, marker_tr, params.opacity,
+                                         comp_op_, params.snap_to_pixels);
+        thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
+    }
+
+    virtual void render_marker(image_rgba8 const& src,
+                               markers_dispatch_params const& params,
+                               agg::trans_affine const& marker_tr)
+    {
+        raster_marker_render_thunk thunk(src, marker_tr, params.opacity,
+                                         comp_op_, params.snap_to_pixels);
+        thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
+    }
+
+private:
+    composite_mode_e comp_op_;
+    render_thunk_list & thunks_;
+};
+
+} // namespace detail
+
+render_thunk_extractor::render_thunk_extractor(box2d<double> & box,
+                                               render_thunk_list & thunks,
+                                               feature_impl & feature,
+                                               attributes const& vars,
+                                               proj_transform const& prj_trans,
+                                               virtual_renderer_common & common,
+                                               box2d<double> const& clipping_extent)
+    : box_(box), thunks_(thunks), feature_(feature), vars_(vars), prj_trans_(prj_trans),
+      common_(common), clipping_extent_(clipping_extent)
+{}
+
+void render_thunk_extractor::operator()(markers_symbolizer const& sym) const
+{
+    using context_type = detail::thunk_markers_renderer_context;
+    context_type renderer_context(sym, feature_, vars_, thunks_);
+
+    render_markers_symbolizer(
+            sym, feature_, prj_trans_, common_, clipping_extent_, renderer_context);
+
+    update_box();
+}
+
+void render_thunk_extractor::operator()(text_symbolizer const& sym) const
+{
+    auto helper = std::make_unique<text_symbolizer_helper>(
+        sym, feature_, vars_, prj_trans_,
+        common_.width_, common_.height_,
+        common_.scale_factor_,
+        common_.t_, common_.font_manager_, *common_.detector_,
+        clipping_extent_, agg::trans_affine::identity);
+
+    extract_text_thunk(std::move(helper), sym);
+}
+
+void render_thunk_extractor::operator()(shield_symbolizer const& sym) const
+{
+    auto helper = std::make_unique<text_symbolizer_helper>(
+        sym, feature_, vars_, prj_trans_,
+        common_.width_, common_.height_,
+        common_.scale_factor_,
+        common_.t_, common_.font_manager_, *common_.detector_,
+        clipping_extent_, agg::trans_affine::identity);
+
+    extract_text_thunk(std::move(helper), sym);
+}
+
+void render_thunk_extractor::extract_text_thunk(text_render_thunk::helper_ptr && helper,
+                                                text_symbolizer const& sym) const
+{
+    double opacity = get<double>(sym, keys::opacity, feature_, common_.vars_, 1.0);
+    composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, common_.vars_, src_over);
+    halo_rasterizer_enum halo_rasterizer = get<halo_rasterizer_enum>(sym, keys::halo_rasterizer, feature_, common_.vars_, HALO_RASTERIZER_FULL);
+
+    text_render_thunk thunk(std::move(helper), opacity, comp_op, halo_rasterizer);
+    thunks_.push_back(std::make_unique<render_thunk>(std::move(thunk)));
+
+    update_box();
+}
+
+void render_thunk_extractor::update_box() const
+{
+    label_collision_detector4 & detector = *common_.detector_;
+
+    for (auto const& label : detector)
+    {
+        if (box_.width() > 0 && box_.height() > 0)
+        {
+            box_.expand_to_include(label.get().box);
+        }
+        else
+        {
+            box_ = label.get().box;
+        }
+    }
+
+    detector.clear();
+}
+
+} // namespace mapnik
diff --git a/src/save_map.cpp b/src/save_map.cpp
index b8db98a..f3d8915 100644
--- a/src/save_map.cpp
+++ b/src/save_map.cpp
@@ -47,7 +47,6 @@
 #include <mapnik/group/group_symbolizer_properties.hpp>
 #include <mapnik/util/variant.hpp>
 #include <mapnik/util/variant_io.hpp>
-
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/algorithm/string.hpp>
@@ -105,7 +104,7 @@ void serialize_raster_colorizer(ptree & sym_node,
     raster_colorizer dfl;
     if (colorizer->get_default_mode() != dfl.get_default_mode() || explicit_defaults)
     {
-        set_attr(col_node, "default-mode", colorizer->get_default_mode());
+        set_attr(col_node, "default-mode", colorizer->get_default_mode().as_string());
     }
     if (colorizer->get_default_color() != dfl.get_default_color() || explicit_defaults)
     {
@@ -404,7 +403,7 @@ void serialize_style( ptree & map_node, std::string const& name, feature_type_st
     filter_mode_e filter_mode = style.get_filter_mode();
     if (filter_mode != dfl.get_filter_mode() || explicit_defaults)
     {
-        set_attr(style_node, "filter-mode", filter_mode);
+        set_attr(style_node, "filter-mode", filter_mode.as_string());
     }
 
     double opacity = style.get_opacity();
diff --git a/src/svg/output/svg_renderer.cpp b/src/svg/output/svg_renderer.cpp
index c110e5e..5691646 100644
--- a/src/svg/output/svg_renderer.cpp
+++ b/src/svg/output/svg_renderer.cpp
@@ -27,7 +27,6 @@
 #include <mapnik/svg/output/svg_renderer.hpp>
 #include <mapnik/map.hpp>
 #include <mapnik/layer.hpp>
-#include <mapnik/label_collision_detector.hpp>
 #include <mapnik/feature_type_style.hpp>
 #include <mapnik/font_set.hpp>
 
diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp
index 5885e8d..6613d5c 100644
--- a/src/svg/svg_parser.cpp
+++ b/src/svg/svg_parser.cpp
@@ -29,6 +29,7 @@
 #include <mapnik/svg/svg_parser_exception.hpp>
 #include <mapnik/util/file_io.hpp>
 #include <mapnik/util/utf_conv_win.hpp>
+#include <mapnik/util/dasharray_parser.hpp>
 #include "agg_ellipse.h"
 #include "agg_rounded_rect.h"
 #include "agg_span_gradient.h"
@@ -123,7 +124,7 @@ template <typename T>
 double parse_double(T & error_messages, const char* str)
 {
     using namespace boost::spirit::qi;
-    qi::double_type double_;
+    double_type double_;
     double val = 0.0;
     if (!parse(str, str + std::strlen(str),double_,val))
     {
@@ -132,24 +133,40 @@ double parse_double(T & error_messages, const char* str)
     return val;
 }
 
-
-// parse a double that might end with a %
-// if it does then set the ref bool true and divide the result by 100
-
-template <typename T>
-double parse_double_optional_percent(T & error_messages, const char* str, bool &percent)
+// https://www.w3.org/TR/SVG/coords.html#Units
+template <typename T, int DPI = 90>
+double parse_svg_value(T & error_messages, const char* str, bool & percent)
 {
-    using namespace boost::spirit::qi;
+    using skip_type = boost::spirit::ascii::space_type;
     using boost::phoenix::ref;
-    qi::_1_type _1;
     qi::double_type double_;
-    qi::char_type char_;
-
+    qi::lit_type lit;
+    qi::_1_type _1;
     double val = 0.0;
-    if (!parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false]
-               >> -char_('%')[ref(val) /= 100.0, ref(percent) = true]))
-    {
-        error_messages.emplace_back("Failed to parse double (optional %) from " + std::string(str));
+    qi::symbols<char, double> units;
+    units.add
+        ("px", 1.0)
+        ("pt", DPI/72.0)
+        ("pc", DPI/6.0)
+        ("mm", DPI/25.4)
+        ("cm", DPI/2.54)
+        ("in", (double)DPI)
+        ;
+    const char* cur = str; // phrase_parse modifies the first iterator
+    const char* end = str + std::strlen(str);
+    if (!qi::phrase_parse(cur, end,
+                      double_[ref(val) = _1][ref(percent) = false]
+                      > - (units[ ref(val) *= _1]
+                           |
+                           lit('%')[ref(val) *= 0.01][ref(percent) = true]),
+                      skip_type()))
+    {
+        error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) + "'");
+    }
+    else if (cur != end)
+    {
+        error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) +
+                                    "', trailing garbage: '" + cur + "'");
     }
     return val;
 }
@@ -159,9 +176,9 @@ bool parse_double_list(T & error_messages, const char* str, double* list)
 {
     using namespace boost::spirit::qi;
     using boost::phoenix::ref;
-    qi::_1_type _1;
-    qi::double_type double_;
-    qi::lit_type lit;
+    _1_type _1;
+    double_type double_;
+    lit_type lit;
     using skip_type = boost::spirit::ascii::space_type;
 
     if (!phrase_parse(str, str + std::strlen(str),
@@ -411,7 +428,8 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
     }
     else if (std::strcmp(name, "stroke-width") == 0)
     {
-        parser.path_.stroke_width(parse_double(parser.error_messages_, value));
+        bool percent;
+        parser.path_.stroke_width(parse_svg_value(parser.error_messages_, value, percent));
     }
     else if (std::strcmp(name, "stroke-opacity") == 0)
     {
@@ -439,7 +457,19 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
     {
         parser.path_.miter_limit(parse_double(parser.error_messages_,value));
     }
-
+    else if (std::strcmp(name,"stroke-dasharray") == 0)
+    {
+        dash_array dash;
+        if (util::parse_dasharray(value, dash))
+        {
+            parser.path_.dash_array(std::move(dash));
+        }
+    }
+    else if (std::strcmp(name,"stroke-dashoffset") == 0)
+    {
+        double offset = parse_double(parser.error_messages_, value);
+        parser.path_.dash_offset(offset);
+    }
     else if(std::strcmp(name,  "opacity") == 0)
     {
         double opacity = parse_double(parser.error_messages_, value);
@@ -455,7 +485,6 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
     }
 }
 
-
 void parse_attr(svg_parser & parser, rapidxml::xml_node<char> const* node)
 {
     for (rapidxml::xml_attribute<char> const* attr = node->first_attribute();
@@ -493,12 +522,12 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
     auto const* width_attr = node->first_attribute("width");
     if (width_attr)
     {
-        width = parse_double_optional_percent(parser.error_messages_, width_attr->value(), has_percent_width);
+        width = parse_svg_value(parser.error_messages_, width_attr->value(), has_percent_width);
     }
     auto const* height_attr = node->first_attribute("height");
     if (height_attr)
     {
-        height = parse_double_optional_percent(parser.error_messages_, height_attr->value(), has_percent_height);
+        height = parse_svg_value(parser.error_messages_, height_attr->value(), has_percent_height);
     }
     auto const* viewbox_attr = node->first_attribute("viewBox");
     if (viewbox_attr)
@@ -589,18 +618,18 @@ void parse_line(svg_parser & parser, rapidxml::xml_node<char> const* node)
     double y1 = 0.0;
     double x2 = 0.0;
     double y2 = 0.0;
-
+    bool percent;
     auto const* x1_attr = node->first_attribute("x1");
-    if (x1_attr) x1 = parse_double(parser.error_messages_, x1_attr->value());
+    if (x1_attr) x1 = parse_svg_value(parser.error_messages_, x1_attr->value(), percent);
 
     auto const* y1_attr = node->first_attribute("y1");
-    if (y1_attr) y1 = parse_double(parser.error_messages_, y1_attr->value());
+    if (y1_attr) y1 = parse_svg_value(parser.error_messages_, y1_attr->value(), percent);
 
     auto const* x2_attr = node->first_attribute("x2");
-    if (x2_attr) x2 = parse_double(parser.error_messages_, x2_attr->value());
+    if (x2_attr) x2 = parse_svg_value(parser.error_messages_, x2_attr->value(), percent);
 
     auto const* y2_attr = node->first_attribute("y2");
-    if (y2_attr) y2 = parse_double(parser.error_messages_, y2_attr->value());
+    if (y2_attr) y2 = parse_svg_value(parser.error_messages_, y2_attr->value(), percent);
 
     parser.path_.begin_path();
     parser.path_.move_to(x1, y1);
@@ -613,23 +642,23 @@ void parse_circle(svg_parser & parser, rapidxml::xml_node<char> const* node)
     double cx = 0.0;
     double cy = 0.0;
     double r = 0.0;
-
+    bool percent;
     auto * attr = node->first_attribute("cx");
     if (attr != nullptr)
     {
-        cx = parse_double(parser.error_messages_, attr->value());
+        cx = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("cy");
     if (attr != nullptr)
     {
-        cy = parse_double(parser.error_messages_, attr->value());
+        cy = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("r");
     if (attr != nullptr)
     {
-        r = parse_double(parser.error_messages_, attr->value());
+        r = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     parser.path_.begin_path();
@@ -654,29 +683,29 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node<char> const  * node)
     double cy = 0.0;
     double rx = 0.0;
     double ry = 0.0;
-
+    bool percent;
     auto * attr = node->first_attribute("cx");
     if (attr != nullptr)
     {
-        cx = parse_double(parser.error_messages_, attr->value());
+        cx = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("cy");
     if (attr)
     {
-        cy = parse_double(parser.error_messages_, attr->value());
+        cy = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("rx");
     if (attr != nullptr)
     {
-        rx = parse_double(parser.error_messages_, attr->value());
+        rx = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("ry");
     if (attr != nullptr)
     {
-        ry = parse_double(parser.error_messages_, attr->value());
+        ry = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     if (rx != 0.0 && ry != 0.0)
@@ -709,35 +738,35 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
     double h = 0.0;
     double rx = 0.0;
     double ry = 0.0;
-
+    bool percent;
     auto * attr = node->first_attribute("x");
     if (attr != nullptr)
     {
-        x = parse_double(parser.error_messages_, attr->value());
+        x = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("y");
     if (attr != nullptr)
     {
-        y = parse_double(parser.error_messages_, attr->value());
+        y = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     attr = node->first_attribute("width");
     if (attr != nullptr)
     {
-        w = parse_double(parser.error_messages_, attr->value());
+        w = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
     attr = node->first_attribute("height");
     if (attr)
     {
-        h = parse_double(parser.error_messages_, attr->value());
+        h = parse_svg_value(parser.error_messages_, attr->value(), percent);
     }
 
     bool rounded = true;
     attr = node->first_attribute("rx");
     if (attr != nullptr)
     {
-        rx = parse_double(parser.error_messages_, attr->value());
+        rx = parse_svg_value(parser.error_messages_, attr->value(), percent);
         if ( rx > 0.5 * w ) rx = 0.5 * w;
     }
     else rounded = false;
@@ -745,7 +774,7 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
     attr = node->first_attribute("ry");
     if (attr != nullptr)
     {
-        ry = parse_double(parser.error_messages_, attr->value());
+        ry = parse_svg_value(parser.error_messages_, attr->value(), percent);
         if ( ry > 0.5 * h ) ry = 0.5 * h;
         if (!rounded)
         {
@@ -924,19 +953,19 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
     auto * attr = node->first_attribute("cx");
     if (attr != nullptr)
     {
-        cx = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        cx = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
 
     attr = node->first_attribute("cy");
     if (attr != nullptr)
     {
-        cy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        cy = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
 
     attr = node->first_attribute("fx");
     if (attr != nullptr)
     {
-        fx = parse_double_optional_percent(parser.error_messages_,attr->value(), has_percent);
+        fx = parse_svg_value(parser.error_messages_,attr->value(), has_percent);
     }
     else
         fx = cx;
@@ -944,7 +973,7 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
     attr = node->first_attribute("fy");
     if (attr != nullptr)
     {
-        fy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        fy = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
     else
         fy = cy;
@@ -952,7 +981,7 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
     attr = node->first_attribute("r");
     if (attr != nullptr)
     {
-        r = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        r = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
     // this logic for detecting %'s will not support mixed coordinates.
     if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
@@ -981,25 +1010,25 @@ void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
     auto * attr = node->first_attribute("x1");
     if (attr != nullptr)
     {
-        x1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        x1 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
 
     attr = node->first_attribute("x2");
     if (attr != nullptr)
     {
-        x2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        x2 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
 
     attr = node->first_attribute("y1");
     if (attr != nullptr)
     {
-        y1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        y1 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
 
     attr = node->first_attribute("y2");
     if (attr != nullptr)
     {
-        y2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
+        y2 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
     }
     // this logic for detecting %'s will not support mixed coordinates.
     if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
diff --git a/src/svg/svg_path_parser.cpp b/src/svg/svg_path_parser.cpp
index ba0ce07..986f99b 100644
--- a/src/svg/svg_path_parser.cpp
+++ b/src/svg/svg_path_parser.cpp
@@ -44,9 +44,9 @@ namespace mapnik { namespace svg {
         svg_path_grammar<iterator_type,skip_type,PathType> g(p);
         iterator_type first = wkt;
         iterator_type last =  wkt + std::strlen(wkt);
-        return qi::phrase_parse(first, last, g, skip_type());
+        bool status = qi::phrase_parse(first, last, g, skip_type());
+        return (status && (first == last));
     }
+    template bool MAPNIK_DECL parse_path<svg_converter_type>(const char*, svg_converter_type&);
 
-    template bool parse_path<svg_converter_type>(const char*, svg_converter_type&);
-
-    }}
+}}
diff --git a/src/text/glyph_positions.cpp b/src/text/glyph_positions.cpp
index da3b300..fdcb3a8 100644
--- a/src/text/glyph_positions.cpp
+++ b/src/text/glyph_positions.cpp
@@ -74,7 +74,7 @@ void glyph_positions::set_marker(marker_info_ptr mark, pixel_position const& mar
     marker_pos_ = marker_pos;
 }
 
-marker_info_ptr glyph_positions::get_marker() const
+marker_info_ptr const& glyph_positions::get_marker() const
 {
     return marker_info_;
 }
diff --git a/src/text/renderer.cpp b/src/text/renderer.cpp
index 227c35d..163d11c 100644
--- a/src/text/renderer.cpp
+++ b/src/text/renderer.cpp
@@ -60,7 +60,9 @@ void text_renderer::prepare_glyphs(glyph_positions const& positions)
     FT_Vector pen;
     FT_Error  error;
 
+    glyphs_.clear();
     glyphs_.reserve(positions.size());
+
     for (auto const& glyph_pos : positions)
     {
         glyph_info const& glyph = glyph_pos.glyph;
@@ -121,7 +123,6 @@ agg_text_renderer<T>::agg_text_renderer (pixmap_type & pixmap,
 template <typename T>
 void agg_text_renderer<T>::render(glyph_positions const& pos)
 {
-    glyphs_.clear();
     prepare_glyphs(pos);
     FT_Error  error;
     FT_Vector start;
@@ -232,7 +233,6 @@ void agg_text_renderer<T>::render(glyph_positions const& pos)
 template <typename T>
 void grid_text_renderer<T>::render(glyph_positions const& pos, value_integer feature_id)
 {
-    glyphs_.clear();
     prepare_glyphs(pos);
     FT_Error  error;
     FT_Vector start;
diff --git a/src/tiff_reader.cpp b/src/tiff_reader.cpp
index 37e59b5..1f88540 100644
--- a/src/tiff_reader.cpp
+++ b/src/tiff_reader.cpp
@@ -598,13 +598,9 @@ image_any tiff_reader<T>::read(unsigned x, unsigned y, unsigned width, unsigned
 }
 
 template <typename T>
-void tiff_reader<T>::read_generic(std::size_t, std::size_t, image_rgba8& image)
+void tiff_reader<T>::read_generic(std::size_t, std::size_t, image_rgba8&)
 {
-    TIFF* tif = open(stream_);
-    if (tif)
-    {
-        throw std::runtime_error("tiff_reader: TODO - tiff is not stripped or tiled");
-    }
+    throw std::runtime_error("tiff_reader: TODO - tiff is not stripped or tiled");
 }
 
 template <typename T>
diff --git a/src/warp.cpp b/src/warp.cpp
index b250d6e..4a468a8 100644
--- a/src/warp.cpp
+++ b/src/warp.cpp
@@ -181,10 +181,10 @@ struct warp_image_visitor
           nodata_value_(nodata_value)
     {}
 
-    void operator() (image_null const&) {}
+    void operator() (image_null const&) const {}
 
     template <typename T>
-    void operator() (T const& source)
+    void operator() (T const& source) const
     {
         using image_type = T;
         //source and target image data types must match
diff --git a/test/catch.hpp b/test/catch.hpp
index 5b616a2..b7ac696 100644
--- a/test/catch.hpp
+++ b/test/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  CATCH v1.1 build 1 (master branch)
- *  Generated: 2015-03-27 18:00:16.346230
+ *  Catch v1.3.2
+ *  Generated: 2015-12-28 15:07:07.166291
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -13,9 +13,13 @@
 
 #define TWOBLUECUBES_CATCH_HPP_INCLUDED
 
-// #included from: internal/catch_suppress_warnings.h
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
 
-#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+// #included from: internal/catch_suppress_warnings.h
 
 #ifdef __clang__
 #   ifdef __ICC // icpc defines the __clang__ macro
@@ -30,6 +34,8 @@
 #       pragma clang diagnostic ignored "-Wpadded"
 #       pragma clang diagnostic ignored "-Wc++98-compat"
 #       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
 #    endif
 #elif defined __GNUC__
 #    pragma GCC diagnostic ignored "-Wvariadic-macros"
@@ -37,7 +43,6 @@
 #    pragma GCC diagnostic push
 #    pragma GCC diagnostic ignored "-Wpadded"
 #endif
-
 #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
 #  define CATCH_IMPL
 #endif
@@ -69,16 +74,42 @@
 // #included from: catch_compiler_capabilities.h
 #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
 
-// Much of the following code is based on Boost (1.53)
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
 
 #ifdef __clang__
 
 #  if __has_feature(cxx_nullptr)
-#    define CATCH_CONFIG_CPP11_NULLPTR
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
 #  endif
 
 #  if __has_feature(cxx_noexcept)
-#    define CATCH_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
 #  endif
 
 #endif // __clang__
@@ -87,52 +118,30 @@
 // Borland
 #ifdef __BORLANDC__
 
-#if (__BORLANDC__ > 0x582 )
-//#define CATCH_CONFIG_SFINAE // Not confirmed
-#endif
-
 #endif // __BORLANDC__
 
 ////////////////////////////////////////////////////////////////////////////////
 // EDG
 #ifdef __EDG_VERSION__
 
-#if (__EDG_VERSION__ > 238 )
-//#define CATCH_CONFIG_SFINAE // Not confirmed
-#endif
-
 #endif // __EDG_VERSION__
 
 ////////////////////////////////////////////////////////////////////////////////
 // Digital Mars
 #ifdef __DMC__
 
-#if (__DMC__ > 0x840 )
-//#define CATCH_CONFIG_SFINAE // Not confirmed
-#endif
-
 #endif // __DMC__
 
 ////////////////////////////////////////////////////////////////////////////////
 // GCC
 #ifdef __GNUC__
 
-#if __GNUC__ < 3
-
-#if (__GNUC_MINOR__ >= 96 )
-//#define CATCH_CONFIG_SFINAE
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
 #endif
 
-#elif __GNUC__ >= 3
-
-// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
-
-#endif // __GNUC__ < 3
-
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
-
-#define CATCH_CONFIG_CPP11_NULLPTR
-#endif
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
 
 #endif // __GNUC__
 
@@ -141,36 +150,101 @@
 #ifdef _MSC_VER
 
 #if (_MSC_VER >= 1600)
-#define CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
-#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
-//#define CATCH_CONFIG_SFINAE // Not confirmed
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
 #endif
 
 #endif // _MSC_VER
 
+////////////////////////////////////////////////////////////////////////////////
+
 // Use variadic macros if the compiler supports them
 #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
     ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
     ( defined __GNUC__ && __GNUC__ >= 3 ) || \
     ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
 
-#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
-#define CATCH_CONFIG_VARIADIC_MACROS
-#endif
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
 
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
 // C++ language feature support
 
-// detect language version:
-#if (__cplusplus == 201103L)
-#  define CATCH_CPP11
-#  define CATCH_CPP11_OR_GREATER
-#elif (__cplusplus >= 201103L)
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
 #  define CATCH_CPP11_OR_GREATER
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
 // noexcept support:
@@ -182,10 +256,38 @@
 #  define CATCH_NOEXCEPT_IS(x)
 #endif
 
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
 namespace Catch {
 
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
     class NonCopyable {
-#ifdef CATCH_CPP11_OR_GREATER
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         NonCopyable( NonCopyable const& )              = delete;
         NonCopyable( NonCopyable && )                  = delete;
         NonCopyable& operator = ( NonCopyable const& ) = delete;
@@ -248,7 +350,7 @@ namespace Catch {
         SourceLineInfo();
         SourceLineInfo( char const* _file, std::size_t _line );
         SourceLineInfo( SourceLineInfo const& other );
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         SourceLineInfo( SourceLineInfo && )                  = default;
         SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
         SourceLineInfo& operator = ( SourceLineInfo && )     = default;
@@ -270,6 +372,9 @@ namespace Catch {
 
     void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
 
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
     // Use this in variadic streaming macros to allow
     //    >> +StreamEndStop
     // as well as
@@ -355,7 +460,7 @@ namespace Catch {
     template<typename T>
     class Ptr {
     public:
-        Ptr() : m_p( NULL ){}
+        Ptr() : m_p( CATCH_NULL ){}
         Ptr( T* p ) : m_p( p ){
             if( m_p )
                 m_p->addRef();
@@ -371,7 +476,7 @@ namespace Catch {
         void reset() {
             if( m_p )
                 m_p->release();
-            m_p = NULL;
+            m_p = CATCH_NULL;
         }
         Ptr& operator = ( T* p ){
             Ptr temp( p );
@@ -384,12 +489,11 @@ namespace Catch {
             return *this;
         }
         void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
-        T* get() { return m_p; }
-        const T* get() const{ return m_p; }
+        T* get() const{ return m_p; }
         T& operator*() const { return *m_p; }
         T* operator->() const { return m_p; }
-        bool operator !() const { return m_p == NULL; }
-        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
 
     private:
         T* m_p;
@@ -486,9 +590,13 @@ namespace Catch {
     struct ITestCaseRegistry {
         virtual ~ITestCaseRegistry();
         virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
-
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
     };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
 }
 
 namespace Catch {
@@ -521,27 +629,32 @@ struct NameAndDesc {
     const char* description;
 };
 
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
 struct AutoReg {
 
-    AutoReg(    TestFunction function,
-                SourceLineInfo const& lineInfo,
-                NameAndDesc const& nameAndDesc );
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
 
     template<typename C>
-    AutoReg(    void (C::*method)(),
-                char const* className,
-                NameAndDesc const& nameAndDesc,
-                SourceLineInfo const& lineInfo ) {
-        registerTestCase(   new MethodTestCase<C>( method ),
-                            className,
-                            nameAndDesc,
-                            lineInfo );
-    }
-
-    void registerTestCase(  ITestCase* testCase,
-                            char const* className,
-                            NameAndDesc const& nameAndDesc,
-                            SourceLineInfo const& lineInfo );
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
 
     ~AutoReg();
 
@@ -550,6 +663,11 @@ private:
     void operator= ( AutoReg const& );
 };
 
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
 } // end namespace Catch
 
 #ifdef CATCH_CONFIG_VARIADIC_MACROS
@@ -573,6 +691,10 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) );
+
 #else
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
@@ -594,6 +716,9 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) );
 #endif
 
 // #included from: internal/catch_capture.hpp
@@ -637,11 +762,11 @@ namespace Catch {
 
     // ResultDisposition::Flags enum
     struct ResultDisposition { enum Flags {
-        Normal = 0x00,
+        Normal = 0x01,
 
-        ContinueOnFailure = 0x01,   // Failures fail test, but execution continues
-        FalseTest = 0x02,           // Prefix expression with !
-        SuppressFail = 0x04         // Failures are reported but do not fail the test
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
     }; };
 
     inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
@@ -689,7 +814,7 @@ namespace Catch {
         AssertionResult();
         AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
         ~AssertionResult();
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
          AssertionResult( AssertionResult const& )              = default;
          AssertionResult( AssertionResult && )                  = default;
          AssertionResult& operator = ( AssertionResult const& ) = default;
@@ -716,117 +841,438 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
 namespace Catch {
+namespace Matchers {
+    namespace Impl {
 
-    struct TestFailureException{};
+    namespace Generic {
+        template<typename ExpressionT> class AllOf;
+        template<typename ExpressionT> class AnyOf;
+        template<typename ExpressionT> class Not;
+    }
 
-    template<typename T> class ExpressionLhs;
+    template<typename ExpressionT>
+    struct Matcher : SharedImpl<IShared>
+    {
+        typedef ExpressionT ExpressionType;
 
-    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+        virtual ~Matcher() {}
+        virtual Ptr<Matcher> clone() const = 0;
+        virtual bool match( ExpressionT const& expr ) const = 0;
+        virtual std::string toString() const = 0;
 
-    struct CopyableStream {
-        CopyableStream() {}
-        CopyableStream( CopyableStream const& other ) {
-            oss << other.oss.str();
-        }
-        CopyableStream& operator=( CopyableStream const& other ) {
-            oss.str("");
-            oss << other.oss.str();
-            return *this;
-        }
-        std::ostringstream oss;
+        Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const;
+        Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const;
+        Generic::Not<ExpressionT> operator ! () const;
     };
 
-    class ResultBuilder {
-    public:
-        ResultBuilder(  char const* macroName,
-                        SourceLineInfo const& lineInfo,
-                        char const* capturedExpression,
-                        ResultDisposition::Flags resultDisposition );
-
-        template<typename T>
-        ExpressionLhs<T const&> operator->* ( T const& operand );
-        ExpressionLhs<bool> operator->* ( bool value );
+    template<typename DerivedT, typename ExpressionT>
+    struct MatcherImpl : Matcher<ExpressionT> {
 
-        template<typename T>
-        ResultBuilder& operator << ( T const& value ) {
-            m_stream.oss << value;
-            return *this;
+        virtual Ptr<Matcher<ExpressionT> > clone() const {
+            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
         }
+    };
 
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
-        ResultBuilder& setResultType( ResultWas::OfType result );
-        ResultBuilder& setResultType( bool result );
-        ResultBuilder& setLhs( std::string const& lhs );
-        ResultBuilder& setRhs( std::string const& rhs );
-        ResultBuilder& setOp( std::string const& op );
+    namespace Generic {
+        template<typename ExpressionT>
+        class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> {
+        public:
+            explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {}
+            Not( Not const& other ) : m_matcher( other.m_matcher ) {}
 
-        void endExpression();
+            virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE {
+                return !m_matcher->match( expr );
+            }
 
-        std::string reconstructExpression() const;
-        AssertionResult build() const;
+            virtual std::string toString() const CATCH_OVERRIDE {
+                return "not " + m_matcher->toString();
+            }
+        private:
+            Ptr< Matcher<ExpressionT> > m_matcher;
+        };
 
-        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
-        void captureResult( ResultWas::OfType resultType );
-        void captureExpression();
-        void react();
-        bool shouldDebugBreak() const;
-        bool allowThrows() const;
+        template<typename ExpressionT>
+        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+        public:
 
-    private:
-        AssertionInfo m_assertionInfo;
-        AssertionResultData m_data;
-        struct ExprComponents {
-            ExprComponents() : testFalse( false ) {}
-            bool testFalse;
-            std::string lhs, rhs, op;
-        } m_exprComponents;
-        CopyableStream m_stream;
+            AllOf() {}
+            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
 
-        bool m_shouldDebugBreak;
-        bool m_shouldThrow;
-    };
+            AllOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( !m_matchers[i]->match( expr ) )
+                        return false;
+                return true;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " and ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-} // namespace Catch
+            AllOf operator && ( Matcher<ExpressionT> const& other ) const {
+                AllOf allOfExpr( *this );
+                allOfExpr.add( other );
+                return allOfExpr;
+            }
 
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+        template<typename ExpressionT>
+        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+        public:
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#endif
+            AnyOf() {}
+            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
 
-#include <cstddef>
+            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( m_matchers[i]->match( expr ) )
+                        return true;
+                return false;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " or ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-namespace Catch {
-namespace Internal {
+            AnyOf operator || ( Matcher<ExpressionT> const& other ) const {
+                AnyOf anyOfExpr( *this );
+                anyOfExpr.add( other );
+                return anyOfExpr;
+            }
 
-    enum Operator {
-        IsEqualTo,
-        IsNotEqualTo,
-        IsLessThan,
-        IsGreaterThan,
-        IsLessThanOrEqualTo,
-        IsGreaterThanOrEqualTo
-    };
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
-    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
-    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
-    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
-    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
-    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
-    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+    } // namespace Generic
 
-    template<typename T>
-    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+    template<typename ExpressionT>
+    Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const {
+        Generic::AllOf<ExpressionT> allOfExpr;
+        allOfExpr.add( *this );
+        allOfExpr.add( other );
+        return allOfExpr;
+    }
+
+    template<typename ExpressionT>
+    Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const {
+        Generic::AnyOf<ExpressionT> anyOfExpr;
+        anyOfExpr.add( *this );
+        anyOfExpr.add( other );
+        return anyOfExpr;
+    }
+
+    template<typename ExpressionT>
+    Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const {
+        return Generic::Not<ExpressionT>( *this );
+    }
+
+    namespace StdString {
+
+        inline std::string makeString( std::string const& str ) { return str; }
+        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+            :   m_caseSensitivity( caseSensitivity ),
+                m_str( adjustString( str ) )
+            {}
+            std::string adjustString( std::string const& str ) const {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? toLower( str )
+                    : str;
+
+            }
+            std::string toStringSuffix() const
+            {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? " (case insensitive)"
+                    : "";
+            }
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct Equals : MatcherImpl<Equals, std::string> {
+            Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            :   m_data( str, caseSensitivity )
+            {}
+            Equals( Equals const& other ) : m_data( other.m_data ){}
+
+            virtual ~Equals();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.m_str == m_data.adjustString( expr );;
+            }
+            virtual std::string toString() const {
+                return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct Contains : MatcherImpl<Contains, std::string> {
+            Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            Contains( Contains const& other ) : m_data( other.m_data ){}
+
+            virtual ~Contains();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
+            }
+            virtual std::string toString() const {
+                return "contains: \"" + m_data.m_str  + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct StartsWith : MatcherImpl<StartsWith, std::string> {
+            StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+
+            StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~StartsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.adjustString( expr ).find( m_data.m_str ) == 0;
+            }
+            virtual std::string toString() const {
+                return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct EndsWith : MatcherImpl<EndsWith, std::string> {
+            EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~EndsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.adjustString( expr ).find( m_data.m_str ) == expr.size() - m_data.m_str.size();
+            }
+            virtual std::string toString() const {
+                return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+    } // namespace StdString
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    template<typename ExpressionT>
+    inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) {
+        return Impl::Generic::Not<ExpressionT>( m );
+    }
+
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+
+    inline Impl::StdString::Equals      Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( str, caseSensitivity );
+    }
+    inline Impl::StdString::Equals      Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( substr, caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
+        return Impl::StdString::StartsWith( substr );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
+        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
+        return Impl::StdString::EndsWith( substr );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
+        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str("");
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+        ResultBuilder& setLhs( std::string const& lhs );
+        ResultBuilder& setRhs( std::string const& rhs );
+        ResultBuilder& setOp( std::string const& op );
+
+        void endExpression();
+
+        std::string reconstructExpression() const;
+        AssertionResult build() const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        struct ExprComponents {
+            ExprComponents() : testFalse( false ) {}
+            bool testFalse;
+            std::string lhs, rhs, op;
+        } m_exprComponents;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    inline T& opCast(T const& t) { return const_cast<T&>(t); }
 
 // nullptr_t support based on pull request #154 from Konstantin Baumann
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
@@ -949,13 +1395,51 @@ namespace Internal {
         return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
     }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
     // pointer to nullptr_t (when comparing against nullptr)
     template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
     }
     template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
     }
 #endif // CATCH_CONFIG_CPP11_NULLPTR
 
@@ -969,40 +1453,6 @@ namespace Internal {
 // #included from: catch_tostring.h
 #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
 
-// #included from: catch_sfinae.hpp
-#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
-
-// Try to detect if the current compiler supports SFINAE
-
-namespace Catch {
-
-    struct TrueType {
-        static const bool value = true;
-        typedef void Enable;
-        char sizer[1];
-    };
-    struct FalseType {
-        static const bool value = false;
-        typedef void Disable;
-        char sizer[2];
-    };
-
-#ifdef CATCH_CONFIG_SFINAE
-
-    template<bool> struct NotABooleanExpression;
-
-    template<bool c> struct If : NotABooleanExpression<c> {};
-    template<> struct If<true> : TrueType {};
-    template<> struct If<false> : FalseType {};
-
-    template<int size> struct SizedIf;
-    template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
-    template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
-
-#endif // CATCH_CONFIG_SFINAE
-
-} // end namespace Catch
-
 #include <sstream>
 #include <iomanip>
 #include <limits>
@@ -1055,8 +1505,11 @@ inline id performOptionalSelector( id obj, SEL sel ) {
 
 #endif
 
-#ifdef CATCH_CPP11_OR_GREATER
+#ifdef CATCH_CONFIG_CPP11_TUPLE
 #include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
 #include <type_traits>
 #endif
 
@@ -1084,6 +1537,11 @@ std::string toString( char value );
 std::string toString( signed char value );
 std::string toString( unsigned char value );
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t );
 #endif
@@ -1096,34 +1554,15 @@ std::string toString( std::nullptr_t );
 
 namespace Detail {
 
-    extern std::string unprintableString;
-
-// SFINAE is currently disabled by default for all compilers.
-// If the non SFINAE version of IsStreamInsertable is ambiguous for you
-// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
-#ifdef CATCH_CONFIG_SFINAE
-
-    template<typename T>
-    class IsStreamInsertableHelper {
-        template<int N> struct TrueIfSizeable : TrueType {};
-
-        template<typename T2>
-        static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
-        static FalseType dummy(...);
-
-    public:
-        typedef SizedIf<sizeof(dummy((T*)0))> type;
-    };
-
-    template<typename T>
-    struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
-
-#else
+    extern const std::string unprintableString;
 
     struct BorgType {
         template<typename T> BorgType( T const& );
     };
 
+    struct TrueType { char sizer[1]; };
+    struct FalseType { char sizer[2]; };
+
     TrueType& testStreamable( std::ostream& );
     FalseType testStreamable( FalseType );
 
@@ -1136,9 +1575,7 @@ namespace Detail {
         enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
     };
 
-#endif
-
-#if defined(CATCH_CPP11_OR_GREATER)
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
     template<typename T,
              bool IsEnum = std::is_enum<T>::value
              >
@@ -1160,7 +1597,7 @@ namespace Detail {
 #endif
     template<bool C>
     struct StringMakerBase {
-#if defined(CATCH_CPP11_OR_GREATER)
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
         template<typename T>
         static std::string convert( T const& v )
         {
@@ -1200,7 +1637,7 @@ struct StringMaker<T*> {
     template<typename U>
     static std::string convert( U* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1210,7 +1647,7 @@ template<typename R, typename C>
 struct StringMaker<R C::*> {
     static std::string convert( R C::* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1233,7 +1670,7 @@ std::string toString( std::vector<T,Allocator> const& v ) {
     return Detail::rangeToString( v.begin(), v.end() );
 }
 
-#ifdef CATCH_CPP11_OR_GREATER
+#ifdef CATCH_CONFIG_CPP11_TUPLE
 
 // toString for tuples
 namespace TupleDetail {
@@ -1273,7 +1710,7 @@ struct StringMaker<std::tuple<Types...>> {
         return os.str();
     }
 };
-#endif
+#endif // CATCH_CONFIG_CPP11_TUPLE
 
 namespace Detail {
     template<typename T>
@@ -1318,13 +1755,13 @@ namespace Catch {
 template<typename T>
 class ExpressionLhs {
     ExpressionLhs& operator = ( ExpressionLhs const& );
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
     ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
 #  endif
 
 public:
     ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
     ExpressionLhs( ExpressionLhs const& ) = default;
     ExpressionLhs( ExpressionLhs && )     = default;
 #  endif
@@ -1405,11 +1842,11 @@ private:
 namespace Catch {
 
     template<typename T>
-    inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
+    inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
         return ExpressionLhs<T const&>( *this, operand );
     }
 
-    inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
+    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
         return ExpressionLhs<bool>( *this, value );
     }
 
@@ -1482,6 +1919,7 @@ namespace Catch {
     class AssertionResult;
     struct AssertionInfo;
     struct SectionInfo;
+    struct SectionEndInfo;
     struct MessageInfo;
     class ScopedMessageBuilder;
     struct Counts;
@@ -1493,7 +1931,8 @@ namespace Catch {
         virtual void assertionEnded( AssertionResult const& result ) = 0;
         virtual bool sectionStarted(    SectionInfo const& sectionInfo,
                                         Counts& assertions ) = 0;
-        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
         virtual void pushScopedMessage( MessageInfo const& message ) = 0;
         virtual void popScopedMessage( MessageInfo const& message ) = 0;
 
@@ -1581,7 +2020,7 @@ namespace Catch {
     do { \
         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
         try { \
-            ( __catchResult->*expr ).endExpression(); \
+            ( __catchResult <= expr ).endExpression(); \
         } \
         catch( ... ) { \
             __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
@@ -1614,16 +2053,16 @@ namespace Catch {
     } while( Catch::alwaysFalse() )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
         if( __catchResult.allowThrows() ) \
             try { \
                 expr; \
                 __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
             } \
             catch( ... ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+                __catchResult.captureExpectedException( matcher ); \
             } \
         else \
             __catchResult.captureResult( Catch::ResultWas::Ok ); \
@@ -1676,41 +2115,26 @@ namespace Catch {
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
         try { \
-            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+            std::string matcherAsString = (matcher).toString(); \
             __catchResult \
                 .setLhs( Catch::toString( arg ) ) \
                 .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
                 .setOp( "matches" ) \
-                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+                .setResultType( (matcher).match( arg ) ); \
             __catchResult.captureExpression(); \
         } catch( ... ) { \
             __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
         } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
-
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
-
-namespace Catch {
-
-    struct SectionInfo {
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name,
-                std::string const& _description = std::string() );
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
 
-        std::string name;
-        std::string description;
-        SourceLineInfo lineInfo;
-    };
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
 
-} // end namespace Catch
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
 
 // #included from: catch_totals.hpp
 #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
@@ -1782,6 +2206,31 @@ namespace Catch {
     };
 }
 
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
 // #included from: catch_timer.h
 #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
 
@@ -2022,6 +2471,8 @@ using namespace Generators;
 #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
 
 #include <string>
+#include <vector>
+
 // #included from: catch_interfaces_registry_hub.h
 #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
 
@@ -2046,7 +2497,8 @@ namespace Catch {
 
     struct IMutableRegistryHub {
         virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
         virtual void registerTest( TestCase const& testInfo ) = 0;
         virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
     };
@@ -2058,364 +2510,144 @@ namespace Catch {
 
 }
 
-
 namespace Catch {
 
     typedef std::string(*exceptionTranslateFunction)();
 
-    struct IExceptionTranslator {
-        virtual ~IExceptionTranslator();
-        virtual std::string translate() const = 0;
-    };
-
-    struct IExceptionTranslatorRegistry {
-        virtual ~IExceptionTranslatorRegistry();
-
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
-
-            ExceptionTranslator( std::string(*translateFunction)( T& ) )
-            : m_translateFunction( translateFunction )
-            {}
-
-            virtual std::string translate() const {
-                try {
-                    throw;
-                }
-                catch( T& ex ) {
-                    return m_translateFunction( ex );
-                }
-            }
-
-        protected:
-            std::string(*m_translateFunction)( T& );
-        };
-
-    public:
-        template<typename T>
-        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
-    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
-    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
-
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
-
-#include <cmath>
-#include <limits>
-
-namespace Catch {
-namespace Detail {
-
-    class Approx {
-    public:
-        explicit Approx ( double value )
-        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-            m_scale( 1.0 ),
-            m_value( value )
-        {}
-
-        Approx( Approx const& other )
-        :   m_epsilon( other.m_epsilon ),
-            m_scale( other.m_scale ),
-            m_value( other.m_value )
-        {}
-
-        static Approx custom() {
-            return Approx( 0 );
-        }
-
-        Approx operator()( double value ) {
-            Approx approx( value );
-            approx.epsilon( m_epsilon );
-            approx.scale( m_scale );
-            return approx;
-        }
-
-        friend bool operator == ( double lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
-        }
-
-        friend bool operator == ( Approx const& lhs, double rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        friend bool operator != ( double lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        friend bool operator != ( Approx const& lhs, double rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        Approx& epsilon( double newEpsilon ) {
-            m_epsilon = newEpsilon;
-            return *this;
-        }
-
-        Approx& scale( double newScale ) {
-            m_scale = newScale;
-            return *this;
-        }
-
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << "Approx( " << Catch::toString( m_value ) << " )";
-            return oss.str();
-        }
-
-    private:
-        double m_epsilon;
-        double m_scale;
-        double m_value;
-    };
-}
-
-template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
-    return value.toString();
-}
-
-} // end namespace Catch
-
-// #included from: internal/catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-    template<typename ExpressionT>
-    struct Matcher : SharedImpl<IShared>
-    {
-        typedef ExpressionT ExpressionType;
-
-        virtual ~Matcher() {}
-        virtual Ptr<Matcher> clone() const = 0;
-        virtual bool match( ExpressionT const& expr ) const = 0;
-        virtual std::string toString() const = 0;
-    };
-
-    template<typename DerivedT, typename ExpressionT>
-    struct MatcherImpl : Matcher<ExpressionT> {
-
-        virtual Ptr<Matcher<ExpressionT> > clone() const {
-            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
-        }
-    };
-
-    namespace Generic {
-
-        template<typename ExpressionT>
-        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AllOf() {}
-            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AllOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( !m_matchers[i]->match( expr ) )
-                        return false;
-                return true;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " and ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-        template<typename ExpressionT>
-        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AnyOf() {}
-            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( m_matchers[i]->match( expr ) )
-                        return true;
-                return false;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " or ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-    }
+    struct IExceptionTranslator;
+    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
 
-    namespace StdString {
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
 
-        inline std::string makeString( std::string const& str ) { return str; }
-        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
 
-        struct Equals : MatcherImpl<Equals, std::string> {
-            Equals( std::string const& str ) : m_str( str ){}
-            Equals( Equals const& other ) : m_str( other.m_str ){}
+        virtual std::string translateActiveException() const = 0;
+    };
 
-            virtual ~Equals();
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
 
-            virtual bool match( std::string const& expr ) const {
-                return m_str == expr;
-            }
-            virtual std::string toString() const {
-                return "equals: \"" + m_str + "\"";
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+                try {
+                    if( it == itEnd )
+                        throw;
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
             }
 
-            std::string m_str;
+        protected:
+            std::string(*m_translateFunction)( T& );
         };
 
-        struct Contains : MatcherImpl<Contains, std::string> {
-            Contains( std::string const& substr ) : m_substr( substr ){}
-            Contains( Contains const& other ) : m_substr( other.m_substr ){}
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
 
-            virtual ~Contains();
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) != std::string::npos;
-            }
-            virtual std::string toString() const {
-                return "contains: \"" + m_substr + "\"";
-            }
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
 
-            std::string m_substr;
-        };
+#include <cmath>
+#include <limits>
 
-        struct StartsWith : MatcherImpl<StartsWith, std::string> {
-            StartsWith( std::string const& substr ) : m_substr( substr ){}
-            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+namespace Catch {
+namespace Detail {
 
-            virtual ~StartsWith();
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == 0;
-            }
-            virtual std::string toString() const {
-                return "starts with: \"" + m_substr + "\"";
-            }
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
 
-            std::string m_substr;
-        };
+        static Approx custom() {
+            return Approx( 0 );
+        }
 
-        struct EndsWith : MatcherImpl<EndsWith, std::string> {
-            EndsWith( std::string const& substr ) : m_substr( substr ){}
-            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.scale( m_scale );
+            return approx;
+        }
 
-            virtual ~EndsWith();
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+        }
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == expr.size() - m_substr.size();
-            }
-            virtual std::string toString() const {
-                return "ends with: \"" + m_substr + "\"";
-            }
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
 
-            std::string m_substr;
-        };
-    } // namespace StdString
-    } // namespace Impl
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
 
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
 
-    inline Impl::StdString::Equals      Equals( std::string const& str ) {
-        return Impl::StdString::Equals( str );
-    }
-    inline Impl::StdString::Equals      Equals( const char* str ) {
-        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
-    }
-    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
-        return Impl::StdString::Contains( substr );
-    }
-    inline Impl::StdString::Contains    Contains( const char* substr ) {
-        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
-        return Impl::StdString::StartsWith( substr );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
-        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
-        return Impl::StdString::EndsWith( substr );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
-        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
-    }
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
 
-} // namespace Matchers
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
 
-using namespace Matchers;
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
 
-} // namespace Catch
+    private:
+        double m_epsilon;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
 
 // #included from: internal/catch_interfaces_tag_alias_registry.h
 #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
@@ -2450,12 +2682,12 @@ namespace Catch {
     template<typename T>
     class Option {
     public:
-        Option() : nullableValue( NULL ) {}
+        Option() : nullableValue( CATCH_NULL ) {}
         Option( T const& _value )
         : nullableValue( new( storage ) T( _value ) )
         {}
         Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
         {}
 
         ~Option() {
@@ -2479,7 +2711,7 @@ namespace Catch {
         void reset() {
             if( nullableValue )
                 nullableValue->~T();
-            nullableValue = NULL;
+            nullableValue = CATCH_NULL;
         }
 
         T& operator*() { return *nullableValue; }
@@ -2491,10 +2723,10 @@ namespace Catch {
             return nullableValue ? *nullableValue : defaultValue;
         }
 
-        bool some() const { return nullableValue != NULL; }
-        bool none() const { return nullableValue == NULL; }
+        bool some() const { return nullableValue != CATCH_NULL; }
+        bool none() const { return nullableValue == CATCH_NULL; }
 
-        bool operator !() const { return nullableValue == NULL; }
+        bool operator !() const { return nullableValue == CATCH_NULL; }
         operator SafeBool::type() const {
             return SafeBool::makeSafe( some() );
         }
@@ -2552,6 +2784,8 @@ namespace Catch {
 
         TestCaseInfo( TestCaseInfo const& other );
 
+        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
         bool isHidden() const;
         bool throws() const;
         bool okToFail() const;
@@ -2664,7 +2898,7 @@ namespace Catch {
 
     inline size_t registerTestMethods() {
         size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( NULL, 0 );
+        int noClasses = objc_getClassList( CATCH_NULL, 0 );
 
         Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
         objc_getClassList( classes, noClasses );
@@ -2806,7 +3040,7 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wweak-vtables"
 #endif
 
-// #included from: ../catch_runner.hpp
+// #included from: ../catch_session.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
 
 // #included from: internal/catch_commandline.hpp
@@ -2831,6 +3065,67 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wpadded"
 #endif
 
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_wildcard( NoWildcard ),
+            m_pattern( adjustCase( pattern ) )
+        {
+            if( startsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 1 );
+                m_wildcard = WildcardAtStart;
+            }
+            if( endsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+            }
+        }
+        virtual ~WildcardPattern();
+        virtual bool matches( std::string const& str ) const {
+            switch( m_wildcard ) {
+                case NoWildcard:
+                    return m_pattern == adjustCase( str );
+                case WildcardAtStart:
+                    return endsWith( adjustCase( str ), m_pattern );
+                case WildcardAtEnd:
+                    return startsWith( adjustCase( str ), m_pattern );
+                case WildcardAtBothEnds:
+                    return contains( adjustCase( str ), m_pattern );
+            }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+            throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+        }
+    private:
+        std::string adjustCase( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+        }
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard;
+        std::string m_pattern;
+    };
+}
+
 #include <string>
 #include <vector>
 
@@ -2842,50 +3137,18 @@ namespace Catch {
             virtual bool matches( TestCaseInfo const& testCase ) const = 0;
         };
         class NamePattern : public Pattern {
-            enum WildcardPosition {
-                NoWildcard = 0,
-                WildcardAtStart = 1,
-                WildcardAtEnd = 2,
-                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-            };
-
         public:
-            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
-                if( startsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 1 );
-                    m_wildcard = WildcardAtStart;
-                }
-                if( endsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 0, m_name.size()-1 );
-                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-                }
-            }
+            NamePattern( std::string const& name )
+            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+            {}
             virtual ~NamePattern();
             virtual bool matches( TestCaseInfo const& testCase ) const {
-                switch( m_wildcard ) {
-                    case NoWildcard:
-                        return m_name == toLower( testCase.name );
-                    case WildcardAtStart:
-                        return endsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtEnd:
-                        return startsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtBothEnds:
-                        return contains( toLower( testCase.name ), m_name );
-                }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-                throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+                return m_wildcardPattern.matches( toLower( testCase.name ) );
             }
         private:
-            std::string m_name;
-            WildcardPosition m_wildcard;
+            WildcardPattern m_wildcardPattern;
         };
+
         class TagPattern : public Pattern {
         public:
             TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
@@ -2896,6 +3159,7 @@ namespace Catch {
         private:
             std::string m_tag;
         };
+
         class ExcludedPattern : public Pattern {
         public:
             ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
@@ -3093,28 +3357,62 @@ namespace Catch {
 // #included from: catch_stream.h
 #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
 
-#include <streambuf>
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
 
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
+#include <streambuf>
 
 namespace Catch {
 
-    class Stream {
+    class StreamBufBase : public std::streambuf {
     public:
-        Stream();
-        Stream( std::streambuf* _streamBuf, bool _isOwned );
-        void release();
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
 
-        std::streambuf* streamBuf;
+#include <streambuf>
+#include <ostream>
+#include <fstream>
 
-    private:
-        bool isOwned;
-    };
+namespace Catch {
 
     std::ostream& cout();
     std::ostream& cerr();
+
+    struct IStream {
+        virtual ~IStream() CATCH_NOEXCEPT;
+        virtual std::ostream& stream() const = 0;
+    };
+
+    class FileStream : public IStream {
+        mutable std::ofstream m_ofs;
+    public:
+        FileStream( std::string const& filename );
+        virtual ~FileStream() CATCH_NOEXCEPT;
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class CoutStream : public IStream {
+        mutable std::ostream m_os;
+    public:
+        CoutStream();
+        virtual ~CoutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class DebugOutStream : public IStream {
+        std::auto_ptr<StreamBufBase> m_streamBuf;
+        mutable std::ostream m_os;
+    public:
+        DebugOutStream();
+        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
 }
 
 #include <memory>
@@ -3142,6 +3440,7 @@ namespace Catch {
             showHelp( false ),
             showInvisibles( false ),
             forceColour( false ),
+            filenamesAsTags( false ),
             abortAfter( -1 ),
             rngSeed( 0 ),
             verbosity( Verbosity::Normal ),
@@ -3161,6 +3460,7 @@ namespace Catch {
         bool showHelp;
         bool showInvisibles;
         bool forceColour;
+        bool filenamesAsTags;
 
         int abortAfter;
         unsigned int rngSeed;
@@ -3170,11 +3470,11 @@ namespace Catch {
         ShowDurations::OrNot showDurations;
         RunTests::InWhatOrder runOrder;
 
-        std::string reporterName;
         std::string outputFilename;
         std::string name;
         std::string processName;
 
+        std::vector<std::string> reporterNames;
         std::vector<std::string> testsOrTags;
     };
 
@@ -3186,12 +3486,11 @@ namespace Catch {
     public:
 
         Config()
-        :   m_os( Catch::cout().rdbuf() )
         {}
 
         Config( ConfigData const& data )
         :   m_data( data ),
-            m_os( Catch::cout().rdbuf() )
+            m_stream( openStream() )
         {
             if( !data.testsOrTags.empty() ) {
                 TestSpecParser parser( ITagAliasRegistry::get() );
@@ -3202,12 +3501,6 @@ namespace Catch {
         }
 
         virtual ~Config() {
-            m_os.rdbuf( Catch::cout().rdbuf() );
-            m_stream.release();
-        }
-
-        void setFilename( std::string const& filename ) {
-            m_data.outputFilename = filename;
         }
 
         std::string const& getFilename() const {
@@ -3223,18 +3516,7 @@ namespace Catch {
 
         bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
 
-        void setStreamBuf( std::streambuf* buf ) {
-            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
-        }
-
-        void useStream( std::string const& streamName ) {
-            Stream stream = createStream( streamName );
-            setStreamBuf( stream.streamBuf );
-            m_stream.release();
-            m_stream = stream;
-        }
-
-        std::string getReporterName() const { return m_data.reporterName; }
+        std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
 
         int abortAfter() const { return m_data.abortAfter; }
 
@@ -3245,7 +3527,7 @@ namespace Catch {
 
         // IConfig interface
         virtual bool allowThrows() const        { return !m_data.noThrow; }
-        virtual std::ostream& stream() const    { return m_os; }
+        virtual std::ostream& stream() const    { return m_stream->stream(); }
         virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
         virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
         virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
@@ -3255,10 +3537,22 @@ namespace Catch {
         virtual bool forceColour() const { return m_data.forceColour; }
 
     private:
+
+        IStream const* openStream() {
+            if( m_data.outputFilename.empty() )
+                return new CoutStream();
+            else if( m_data.outputFilename[0] == '%' ) {
+                if( m_data.outputFilename == "%debug" )
+                    return new DebugOutStream();
+                else
+                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+            }
+            else
+                return new FileStream( m_data.outputFilename );
+        }
         ConfigData m_data;
 
-        Stream m_stream;
-        mutable std::ostream m_os;
+        std::auto_ptr<IStream const> m_stream;
         TestSpec m_testSpec;
     };
 
@@ -3514,7 +3808,7 @@ namespace Clara {
         template<typename ConfigT>
         struct IArgFunction {
             virtual ~IArgFunction() {}
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
             IArgFunction()                      = default;
             IArgFunction( IArgFunction const& ) = default;
 #  endif
@@ -3527,11 +3821,11 @@ namespace Clara {
         template<typename ConfigT>
         class BoundArgFunction {
         public:
-            BoundArgFunction() : functionObj( NULL ) {}
+            BoundArgFunction() : functionObj( CATCH_NULL ) {}
             BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {}
             BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL;
                 delete functionObj;
                 functionObj = newFunctionObj;
                 return *this;
@@ -3547,7 +3841,7 @@ namespace Clara {
             bool takesArg() const { return functionObj->takesArg(); }
 
             bool isSet() const {
-                return functionObj != NULL;
+                return functionObj != CATCH_NULL;
             }
         private:
             IArgFunction<ConfigT>* functionObj;
@@ -3765,12 +4059,7 @@ namespace Clara {
             }
         };
 
-        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
-#if defined(__cplusplus) && __cplusplus > 199711L
-        typedef std::unique_ptr<Arg> ArgAutoPtr;
-#else
-        typedef std::auto_ptr<Arg> ArgAutoPtr;
-#endif
+        typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr;
 
         friend void addOptName( Arg& arg, std::string const& optName )
         {
@@ -3846,8 +4135,8 @@ namespace Clara {
                 m_arg->description = description;
                 return *this;
             }
-            ArgBuilder& detail( std::string const& detail ) {
-                m_arg->detail = detail;
+            ArgBuilder& detail( std::string const& _detail ) {
+                m_arg->detail = _detail;
                 return *this;
             }
 
@@ -3930,14 +4219,14 @@ namespace Clara {
                 maxWidth = (std::max)( maxWidth, it->commands().size() );
 
             for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                Detail::Text usageText( it->commands(), Detail::TextAttributes()
                                                         .setWidth( maxWidth+indent )
                                                         .setIndent( indent ) );
                 Detail::Text desc( it->description, Detail::TextAttributes()
                                                         .setWidth( width - maxWidth - 3 ) );
 
-                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usageText.size() ? usageText[i] : "";
                     os << usageCol;
 
                     if( i < desc.size() && !desc[i].empty() )
@@ -4143,6 +4432,7 @@ namespace Catch {
         config.abortAfter = x;
     }
     inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
 
     inline void addWarning( ConfigData& config, std::string const& _warning ) {
         if( _warning == "NoAssertions" )
@@ -4236,7 +4526,7 @@ namespace Catch {
         cli["-r"]["--reporter"]
 //            .placeholder( "name[:filename]" )
             .describe( "reporter to use (defaults to console)" )
-            .bind( &ConfigData::reporterName, "name" );
+            .bind( &addReporterName, "name" );
 
         cli["-n"]["--name"]
             .describe( "suite name" )
@@ -4273,6 +4563,10 @@ namespace Catch {
             .describe( "load test names to run from a file" )
             .bind( &loadTestNamesFromFile, "filename" );
 
+        cli["-#"]["--filenames-as-tags"]
+            .describe( "adds a tag for the filename" )
+            .bind( &ConfigData::filenamesAsTags );
+
         // Less common commands which don't have a short form
         cli["--list-test-names-only"]
             .describe( "list all/matching test cases names only" )
@@ -4530,18 +4824,18 @@ namespace Catch {
 namespace Catch
 {
     struct ReporterConfig {
-        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
         :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
 
-        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
         :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
 
         std::ostream& stream() const    { return *m_stream; }
-        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
 
     private:
         std::ostream* m_stream;
-        Ptr<IConfig> m_fullConfig;
+        Ptr<IConfig const> m_fullConfig;
     };
 
     struct ReporterPreferences {
@@ -4605,7 +4899,7 @@ namespace Catch
         }
         virtual ~AssertionStats();
 
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         AssertionStats( AssertionStats const& )              = default;
         AssertionStats( AssertionStats && )                  = default;
         AssertionStats& operator = ( AssertionStats const& ) = default;
@@ -4628,7 +4922,7 @@ namespace Catch
             missingAssertions( _missingAssertions )
         {}
         virtual ~SectionStats();
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         SectionStats( SectionStats const& )              = default;
         SectionStats( SectionStats && )                  = default;
         SectionStats& operator = ( SectionStats const& ) = default;
@@ -4655,7 +4949,7 @@ namespace Catch
         {}
         virtual ~TestCaseStats();
 
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         TestCaseStats( TestCaseStats const& )              = default;
         TestCaseStats( TestCaseStats && )                  = default;
         TestCaseStats& operator = ( TestCaseStats const& ) = default;
@@ -4683,7 +4977,7 @@ namespace Catch
         {}
         virtual ~TestGroupStats();
 
-#  ifdef CATCH_CPP11_OR_GREATER
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         TestGroupStats( TestGroupStats const& )              = default;
         TestGroupStats( TestGroupStats && )                  = default;
         TestGroupStats& operator = ( TestGroupStats const& ) = default;
@@ -4705,7 +4999,7 @@ namespace Catch
         {}
         virtual ~TestRunStats();
 
-#  ifndef CATCH_CPP11_OR_GREATER
+#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
         TestRunStats( TestRunStats const& _other )
         :   runInfo( _other.runInfo ),
             totals( _other.totals ),
@@ -4743,6 +5037,7 @@ namespace Catch
 
         // The return value indicates if the messages buffer should be cleared:
         virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
         virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
@@ -4751,20 +5046,24 @@ namespace Catch
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
     };
 
-    struct IReporterFactory {
+    struct IReporterFactory : IShared {
         virtual ~IReporterFactory();
         virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
         virtual std::string getDescription() const = 0;
     };
 
     struct IReporterRegistry {
-        typedef std::map<std::string, IReporterFactory*> FactoryMap;
+        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+        typedef std::vector<Ptr<IReporterFactory> > Listeners;
 
         virtual ~IReporterRegistry();
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
         virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
     };
 
+    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
 }
 
 #include <limits>
@@ -4787,8 +5086,7 @@ namespace Catch {
         nameAttr.setInitialIndent( 2 ).setIndent( 4 );
         tagsAttr.setIndent( 6 );
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4816,8 +5114,7 @@ namespace Catch {
         if( !config.testSpec().hasFilters() )
             testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
         std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4857,8 +5154,7 @@ namespace Catch {
 
         std::map<std::string, TagInfo> tagCounts;
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4929,7 +5225,7 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: internal/catch_runner_impl.hpp
+// #included from: internal/catch_run_context.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
 
 // #included from: catch_test_case_tracker.hpp
@@ -4938,132 +5234,300 @@ namespace Catch {
 #include <map>
 #include <string>
 #include <assert.h>
+#include <vector>
 
 namespace Catch {
-namespace SectionTracking {
+namespace TestCaseTracking {
+
+    struct ITracker : SharedImpl<> {
+        virtual ~ITracker();
 
-    class TrackedSection {
+        // static queries
+        virtual std::string name() const = 0;
 
-        typedef std::map<std::string, TrackedSection> TrackedSections;
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( Ptr<ITracker> const& child ) = 0;
+        virtual ITracker* findChild( std::string const& name ) = 0;
+        virtual void openChild() = 0;
+    };
+
+    class TrackerContext {
 
-    public:
         enum RunState {
             NotStarted,
             Executing,
-            ExecutingChildren,
-            Completed
+            CompletedCycle
         };
 
-        TrackedSection( std::string const& name, TrackedSection* parent )
-        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
+        Ptr<ITracker> m_rootTracker;
+        ITracker* m_currentTracker;
+        RunState m_runState;
+
+    public:
+
+        static TrackerContext& instance() {
+            static TrackerContext s_instance;
+            return s_instance;
+        }
+
+        TrackerContext()
+        :   m_currentTracker( CATCH_NULL ),
+            m_runState( NotStarted )
         {}
 
-        RunState runState() const { return m_runState; }
+        ITracker& startRun();
 
-        TrackedSection* findChild( std::string const& childName ) {
-            TrackedSections::iterator it = m_children.find( childName );
-            return it != m_children.end()
-                ? &it->second
-                : NULL;
+        void endRun() {
+            m_rootTracker.reset();
+            m_currentTracker = CATCH_NULL;
+            m_runState = NotStarted;
         }
-        TrackedSection* acquireChild( std::string const& childName ) {
-            if( TrackedSection* child = findChild( childName ) )
-                return child;
-            m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
-            return findChild( childName );
+
+        void startCycle() {
+            m_currentTracker = m_rootTracker.get();
+            m_runState = Executing;
         }
-        void enter() {
-            if( m_runState == NotStarted )
-                m_runState = Executing;
+        void completeCycle() {
+            m_runState = CompletedCycle;
         }
-        void leave() {
-            for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
-                    it != itEnd;
-                    ++it )
-                if( it->second.runState() != Completed ) {
-                    m_runState = ExecutingChildren;
-                    return;
-                }
-            m_runState = Completed;
+
+        bool completedCycle() const {
+            return m_runState == CompletedCycle;
         }
-        TrackedSection* getParent() {
-            return m_parent;
+        ITracker& currentTracker() {
+            return *m_currentTracker;
         }
-        bool hasChildren() const {
-            return !m_children.empty();
+        void setCurrentTracker( ITracker* tracker ) {
+            m_currentTracker = tracker;
         }
-
-    private:
-        std::string m_name;
-        RunState m_runState;
-        TrackedSections m_children;
-        TrackedSection* m_parent;
-
     };
 
-    class TestCaseTracker {
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+        class TrackerHasName {
+            std::string m_name;
+        public:
+            TrackerHasName( std::string const& name ) : m_name( name ) {}
+            bool operator ()( Ptr<ITracker> const& tracker ) {
+                return tracker->name() == m_name;
+            }
+        };
+        typedef std::vector<Ptr<ITracker> > Children;
+        std::string m_name;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState;
     public:
-        TestCaseTracker( std::string const& testCaseName )
-        :   m_testCase( testCaseName, NULL ),
-            m_currentSection( &m_testCase ),
-            m_completedASectionThisRun( false )
+        TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   m_name( name ),
+            m_ctx( ctx ),
+            m_parent( parent ),
+            m_runState( NotStarted )
         {}
+        virtual ~TrackerBase();
 
-        bool enterSection( std::string const& name ) {
-            TrackedSection* child = m_currentSection->acquireChild( name );
-            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
-                return false;
+        virtual std::string name() const CATCH_OVERRIDE {
+            return m_name;
+        }
+        virtual bool isComplete() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        }
+        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully;
+        }
+        virtual bool isOpen() const CATCH_OVERRIDE {
+            return m_runState != NotStarted && !isComplete();
+        }
+        virtual bool hasChildren() const CATCH_OVERRIDE {
+            return !m_children.empty();
+        }
 
-            m_currentSection = child;
-            m_currentSection->enter();
-            return true;
+        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+            m_children.push_back( child );
+        }
+
+        virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE {
+            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
+            return( it != m_children.end() )
+                ? it->get()
+                : CATCH_NULL;
         }
-        void leaveSection() {
-            m_currentSection->leave();
-            m_currentSection = m_currentSection->getParent();
-            assert( m_currentSection != NULL );
-            m_completedASectionThisRun = true;
+        virtual ITracker& parent() CATCH_OVERRIDE {
+            assert( m_parent ); // Should always be non-null except for root
+            return *m_parent;
         }
 
-        bool currentSectionHasChildren() const {
-            return m_currentSection->hasChildren();
+        virtual void openChild() CATCH_OVERRIDE {
+            if( m_runState != ExecutingChildren ) {
+                m_runState = ExecutingChildren;
+                if( m_parent )
+                    m_parent->openChild();
+            }
         }
-        bool isCompleted() const {
-            return m_testCase.runState() == TrackedSection::Completed;
+        void open() {
+            m_runState = Executing;
+            moveToThis();
+            if( m_parent )
+                m_parent->openChild();
         }
 
-        class Guard {
-        public:
-            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
-                m_tracker.enterTestCase();
+        virtual void close() CATCH_OVERRIDE {
+
+            // Close any still open children (e.g. generators)
+            while( &m_ctx.currentTracker() != this )
+                m_ctx.currentTracker().close();
+
+            switch( m_runState ) {
+                case NotStarted:
+                case CompletedSuccessfully:
+                case Failed:
+                    throw std::logic_error( "Illogical state" );
+
+                case NeedsAnotherRun:
+                    break;;
+
+                case Executing:
+                    m_runState = CompletedSuccessfully;
+                    break;
+                case ExecutingChildren:
+                    if( m_children.empty() || m_children.back()->isComplete() )
+                        m_runState = CompletedSuccessfully;
+                    break;
+
+                default:
+                    throw std::logic_error( "Unexpected state" );
+            }
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void fail() CATCH_OVERRIDE {
+            m_runState = Failed;
+            if( m_parent )
+                m_parent->markAsNeedingAnotherRun();
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+            m_runState = NeedsAnotherRun;
+        }
+    private:
+        void moveToParent() {
+            assert( m_parent );
+            m_ctx.setCurrentTracker( m_parent );
+        }
+        void moveToThis() {
+            m_ctx.setCurrentTracker( this );
+        }
+    };
+
+    class SectionTracker : public TrackerBase {
+    public:
+        SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   TrackerBase( name, ctx, parent )
+        {}
+        virtual ~SectionTracker();
+
+        static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
+            SectionTracker* section = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                section = dynamic_cast<SectionTracker*>( childTracker );
+                assert( section );
             }
-            ~Guard() {
-                m_tracker.leaveTestCase();
+            else {
+                section = new SectionTracker( name, ctx, &currentTracker );
+                currentTracker.addChild( section );
             }
-        private:
-            Guard( Guard const& );
-            void operator = ( Guard const& );
-            TestCaseTracker& m_tracker;
-        };
+            if( !ctx.completedCycle() && !section->isComplete() ) {
 
-    private:
-        void enterTestCase() {
-            m_currentSection = &m_testCase;
-            m_completedASectionThisRun = false;
-            m_testCase.enter();
+                section->open();
+            }
+            return *section;
+        }
+    };
+
+    class IndexTracker : public TrackerBase {
+        int m_size;
+        int m_index;
+    public:
+        IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size )
+        :   TrackerBase( name, ctx, parent ),
+            m_size( size ),
+            m_index( -1 )
+        {}
+        virtual ~IndexTracker();
+
+        static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
+            IndexTracker* tracker = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                tracker = dynamic_cast<IndexTracker*>( childTracker );
+                assert( tracker );
+            }
+            else {
+                tracker = new IndexTracker( name, ctx, &currentTracker, size );
+                currentTracker.addChild( tracker );
+            }
+
+            if( !ctx.completedCycle() && !tracker->isComplete() ) {
+                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+                    tracker->moveNext();
+                tracker->open();
+            }
+
+            return *tracker;
         }
-        void leaveTestCase() {
-            m_testCase.leave();
+
+        int index() const { return m_index; }
+
+        void moveNext() {
+            m_index++;
+            m_children.clear();
         }
 
-        TrackedSection m_testCase;
-        TrackedSection* m_currentSection;
-        bool m_completedASectionThisRun;
+        virtual void close() CATCH_OVERRIDE {
+            TrackerBase::close();
+            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+                m_runState = Executing;
+        }
     };
 
-} // namespace SectionTracking
+    inline ITracker& TrackerContext::startRun() {
+        m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL );
+        m_currentTracker = CATCH_NULL;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+} // namespace TestCaseTracking
 
-using SectionTracking::TestCaseTracker;
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
 
 } // namespace Catch
 
@@ -5179,15 +5643,12 @@ namespace Catch {
 
     public:
 
-        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
-        :   m_runInfo( config->name() ),
+        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( _config->name() ),
             m_context( getCurrentMutableContext() ),
-            m_activeTestCase( NULL ),
-            m_config( config ),
-            m_reporter( reporter ),
-            m_prevRunner( m_context.getRunner() ),
-            m_prevResultCapture( m_context.getResultCapture() ),
-            m_prevConfig( m_context.getConfig() )
+            m_activeTestCase( CATCH_NULL ),
+            m_config( _config ),
+            m_reporter( reporter )
         {
             m_context.setRunner( this );
             m_context.setConfig( m_config );
@@ -5197,10 +5658,6 @@ namespace Catch {
 
         virtual ~RunContext() {
             m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
-            m_context.setRunner( m_prevRunner );
-            m_context.setConfig( NULL );
-            m_context.setResultCapture( m_prevResultCapture );
-            m_context.setConfig( m_prevConfig );
         }
 
         void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
@@ -5221,14 +5678,17 @@ namespace Catch {
             m_reporter->testCaseStarting( testInfo );
 
             m_activeTestCase = &testCase;
-            m_testCaseTracker = TestCaseTracker( testInfo.name );
 
             do {
+                m_trackerContext.startRun();
                 do {
+                    m_trackerContext.startCycle();
+                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name );
                     runCurrentTest( redirectedCout, redirectedCerr );
                 }
-                while( !m_testCaseTracker->isCompleted() && !aborting() );
+                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
             }
+            // !TBD: deprecated - this will be replaced by indexed trackers
             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
 
             Totals deltaTotals = m_totals.delta( prevTotals );
@@ -5239,8 +5699,8 @@ namespace Catch {
                                                         redirectedCerr,
                                                         aborting() ) );
 
-            m_activeTestCase = NULL;
-            m_testCaseTracker.reset();
+            m_activeTestCase = CATCH_NULL;
+            m_testCaseTracker = CATCH_NULL;
 
             return deltaTotals;
         }
@@ -5275,8 +5735,10 @@ namespace Catch {
             std::ostringstream oss;
             oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
 
-            if( !m_testCaseTracker->enterSection( oss.str() ) )
+            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() );
+            if( !sectionTracker.isOpen() )
                 return false;
+            m_activeSections.push_back( &sectionTracker );
 
             m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
 
@@ -5287,30 +5749,40 @@ namespace Catch {
             return true;
         }
         bool testForMissingAssertions( Counts& assertions ) {
-            if( assertions.total() != 0 ||
-                    !m_config->warnAboutMissingAssertions() ||
-                    m_testCaseTracker->currentSectionHasChildren() )
+            if( assertions.total() != 0 )
+                return false;
+            if( !m_config->warnAboutMissingAssertions() )
+                return false;
+            if( m_trackerContext.currentTracker().hasChildren() )
                 return false;
             m_totals.assertions.failed++;
             assertions.failed++;
             return true;
         }
 
-        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
-            if( std::uncaught_exception() ) {
-                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
-                return;
-            }
-
-            Counts assertions = m_totals.assertions - prevAssertions;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
             bool missingAssertions = testForMissingAssertions( assertions );
 
-            m_testCaseTracker->leaveSection();
+            if( !m_activeSections.empty() ) {
+                m_activeSections.back()->close();
+                m_activeSections.pop_back();
+            }
 
-            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
             m_messages.clear();
         }
 
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+            if( m_unfinishedSections.empty() )
+                m_activeSections.back()->fail();
+            else
+                m_activeSections.back()->close();
+            m_activeSections.pop_back();
+
+            m_unfinishedSections.push_back( endInfo );
+        }
+
         virtual void pushScopedMessage( MessageInfo const& message ) {
             m_messages.push_back( message );
         }
@@ -5376,7 +5848,8 @@ namespace Catch {
             double duration = 0;
             try {
                 m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
-                TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+                seedRng( *m_config );
 
                 Timer timer;
                 timer.start();
@@ -5396,6 +5869,7 @@ namespace Catch {
             catch(...) {
                 makeUnexpectedResultBuilder().useActiveException();
             }
+            m_testCaseTracker->close();
             handleUnfinishedSections();
             m_messages.clear();
 
@@ -5430,39 +5904,29 @@ namespace Catch {
         void handleUnfinishedSections() {
             // If sections ended prematurely due to an exception we stored their
             // infos here so we can tear them down outside the unwind process.
-            for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
                         itEnd = m_unfinishedSections.rend();
                     it != itEnd;
                     ++it )
-                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+                sectionEnded( *it );
             m_unfinishedSections.clear();
         }
 
-        struct UnfinishedSections {
-            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
-            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
-            {}
-
-            SectionInfo info;
-            Counts prevAssertions;
-            double durationInSeconds;
-        };
-
         TestRunInfo m_runInfo;
         IMutableContext& m_context;
         TestCase const* m_activeTestCase;
-        Option<TestCaseTracker> m_testCaseTracker;
+        ITracker* m_testCaseTracker;
+        ITracker* m_currentSectionTracker;
         AssertionResult m_lastResult;
 
         Ptr<IConfig const> m_config;
         Totals m_totals;
         Ptr<IStreamingReporter> m_reporter;
         std::vector<MessageInfo> m_messages;
-        IRunner* m_prevRunner;
-        IResultCapture* m_prevResultCapture;
-        Ptr<IConfig const> m_prevConfig;
         AssertionInfo m_lastAssertionInfo;
-        std::vector<UnfinishedSections> m_unfinishedSections;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
     };
 
     IResultCapture& getResultCapture() {
@@ -5483,18 +5947,19 @@ namespace Catch {
     struct Version {
         Version(    unsigned int _majorVersion,
                     unsigned int _minorVersion,
-                    unsigned int _buildNumber,
-                    char const* const _branchName )
-        :   majorVersion( _majorVersion ),
-            minorVersion( _minorVersion ),
-            buildNumber( _buildNumber ),
-            branchName( _branchName )
-        {}
+                    unsigned int _patchNumber,
+                    std::string const& _branchName,
+                    unsigned int _buildNumber );
 
         unsigned int const majorVersion;
         unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        std::string const branchName;
         unsigned int const buildNumber;
-        char const* const branchName;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
 
     private:
         void operator=( Version const& );
@@ -5509,89 +5974,87 @@ namespace Catch {
 
 namespace Catch {
 
-    class Runner {
-
-    public:
-        Runner( Ptr<Config> const& config )
-        :   m_config( config )
-        {
-            openStream();
-            makeReporter();
+    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+        if( !reporter ) {
+            std::ostringstream oss;
+            oss << "No reporter registered with name: '" << reporterName << "'";
+            throw std::domain_error( oss.str() );
         }
+        return reporter;
+    }
 
-        Totals runTests() {
-
-            RunContext context( m_config.get(), m_reporter );
+    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+        std::vector<std::string> reporters = config->getReporterNames();
+        if( reporters.empty() )
+            reporters.push_back( "console" );
 
-            Totals totals;
+        Ptr<IStreamingReporter> reporter;
+        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+                it != itEnd;
+                ++it )
+            reporter = addReporter( reporter, createReporter( *it, config ) );
+        return reporter;
+    }
+    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+                it != itEnd;
+                ++it )
+            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+        return reporters;
+    }
 
-            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+    Totals runTests( Ptr<Config> const& config ) {
 
-            TestSpec testSpec = m_config->testSpec();
-            if( !testSpec.hasFilters() )
-                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+        Ptr<IConfig const> iconfig = config.get();
 
-            std::vector<TestCase> testCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+        Ptr<IStreamingReporter> reporter = makeReporter( config );
+        reporter = addListeners( iconfig, reporter );
 
-            int testsRunForGroup = 0;
-            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
-                    it != itEnd;
-                    ++it ) {
-                testsRunForGroup++;
-                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+        RunContext context( iconfig, reporter );
 
-                    if( context.aborting() )
-                        break;
+        Totals totals;
 
-                    totals += context.runTest( *it );
-                    m_testsAlreadyRun.insert( *it );
-                }
-            }
-            std::vector<TestCase> skippedTestCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+        context.testGroupStarting( config->name(), 1, 1 );
 
-            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
-                    it != itEnd;
-                    ++it )
-                m_reporter->skipTest( *it );
+        TestSpec testSpec = config->testSpec();
+        if( !testSpec.hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
 
-            context.testGroupEnded( "all tests", totals, 1, 1 );
-            return totals;
+        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+                it != itEnd;
+                ++it ) {
+            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+                totals += context.runTest( *it );
+            else
+                reporter->skipTest( *it );
         }
 
-    private:
-        void openStream() {
-            // Open output file, if specified
-            if( !m_config->getFilename().empty() ) {
-                m_ofs.open( m_config->getFilename().c_str() );
-                if( m_ofs.fail() ) {
-                    std::ostringstream oss;
-                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
-                    throw std::domain_error( oss.str() );
-                }
-                m_config->setStreamBuf( m_ofs.rdbuf() );
-            }
-        }
-        void makeReporter() {
-            std::string reporterName = m_config->getReporterName().empty()
-                ? "console"
-                : m_config->getReporterName();
+        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+        return totals;
+    }
 
-            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
-            if( !m_reporter ) {
-                std::ostringstream oss;
-                oss << "No reporter registered with name: '" << reporterName << "'";
-                throw std::domain_error( oss.str() );
-            }
-        }
+    void applyFilenamesAsTags( IConfig const& config ) {
+        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+        for(std::size_t i = 0; i < tests.size(); ++i ) {
+            TestCase& test = const_cast<TestCase&>( tests[i] );
+            std::set<std::string> tags = test.tags;
 
-    private:
-        Ptr<Config> m_config;
-        std::ofstream m_ofs;
-        Ptr<IStreamingReporter> m_reporter;
-        std::set<TestCase> m_testsAlreadyRun;
-    };
+            std::string filename = test.lineInfo.file;
+            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+            if( lastSlash != std::string::npos )
+                filename = filename.substr( lastSlash+1 );
+
+            std::string::size_type lastDot = filename.find_last_of( "." );
+            if( lastDot != std::string::npos )
+                filename = filename.substr( 0, lastDot );
+
+            tags.insert( "#" + filename );
+            setTags( test, tags );
+        }
+    }
 
     class Session : NonCopyable {
         static bool alreadyInstantiated;
@@ -5614,18 +6077,13 @@ namespace Catch {
         }
 
         void showHelp( std::string const& processName ) {
-            Catch::cout() << "\nCatch v"    << libraryVersion.majorVersion << "."
-                                        << libraryVersion.minorVersion << " build "
-                                        << libraryVersion.buildNumber;
-            if( libraryVersion.branchName != std::string( "master" ) )
-                Catch::cout() << " (" << libraryVersion.branchName << " branch)";
-            Catch::cout() << "\n";
+            Catch::cout() << "\nCatch v" << libraryVersion << "\n";
 
             m_cli.usage( Catch::cout(), processName );
             Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
         }
 
-        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+        int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
             try {
                 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
                 m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
@@ -5636,9 +6094,10 @@ namespace Catch {
             catch( std::exception& ex ) {
                 {
                     Colour colourGuard( Colour::Red );
-                    Catch::cerr()   << "\nError(s) in input:\n"
-                                << Text( ex.what(), TextAttributes().setIndent(2) )
-                                << "\n\n";
+                    Catch::cerr()
+                        << "\nError(s) in input:\n"
+                        << Text( ex.what(), TextAttributes().setIndent(2) )
+                        << "\n\n";
                 }
                 m_cli.usage( Catch::cout(), m_configData.processName );
                 return (std::numeric_limits<int>::max)();
@@ -5651,7 +6110,7 @@ namespace Catch {
             m_config.reset();
         }
 
-        int run( int argc, char* const argv[] ) {
+        int run( int argc, char const* const argv[] ) {
 
             int returnCode = applyCommandLine( argc, argv );
             if( returnCode == 0 )
@@ -5667,15 +6126,16 @@ namespace Catch {
             {
                 config(); // Force config to be constructed
 
-                std::srand( m_configData.rngSeed );
+                seedRng( *m_config );
 
-                Runner runner( m_config );
+                if( m_configData.filenamesAsTags )
+                    applyFilenamesAsTags( *m_config );
 
                 // Handle list request
                 if( Option<std::size_t> listed = list( config() ) )
                     return static_cast<int>( *listed );
 
-                return static_cast<int>( runner.runTests().assertions.failed );
+                return static_cast<int>( runTests( m_config ).assertions.failed );
             }
             catch( std::exception& ex ) {
                 Catch::cerr() << ex.what() << std::endl;
@@ -5683,7 +6143,7 @@ namespace Catch {
             }
         }
 
-        Clara::CommandLine<ConfigData> const& cli() const {
+        Clara::CommandLine<ConfigData> & cli() {
             return m_cli;
         }
         std::vector<Clara::Parser::Token> const& unusedTokens() const {
@@ -5697,7 +6157,6 @@ namespace Catch {
                 m_config = new Config( m_configData );
             return *m_config;
         }
-
     private:
         Clara::CommandLine<ConfigData> m_cli;
         std::vector<Clara::Parser::Token> m_unusedTokens;
@@ -5723,16 +6182,76 @@ namespace Catch {
 
 namespace Catch {
 
-    class TestRegistry : public ITestCaseRegistry {
-        struct LexSort {
-            bool operator() (TestCase i,TestCase j) const { return (i<j);}
-        };
-        struct RandomNumberGenerator {
-            int operator()( int n ) const { return std::rand() % n; }
-        };
+    struct LexSort {
+        bool operator() (TestCase i,TestCase j) const { return (i<j);}
+    };
+    struct RandomNumberGenerator {
+        int operator()( int n ) const { return std::rand() % n; }
+    };
+
+    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+        std::vector<TestCase> sorted = unsortedTestCases;
+
+        switch( config.runOrder() ) {
+            case RunTests::InLexicographicalOrder:
+                std::sort( sorted.begin(), sorted.end(), LexSort() );
+                break;
+            case RunTests::InRandomOrder:
+                {
+                    seedRng( config );
+
+                    RandomNumberGenerator rng;
+                    std::random_shuffle( sorted.begin(), sorted.end(), rng );
+                }
+                break;
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+        }
+        return sorted;
+    }
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+            it != itEnd;
+            ++it ) {
+            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+            if( !prev.second ){
+                Catch::cerr()
+                << Colour( Colour::Red )
+                << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+                exit(1);
+            }
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                it != itEnd;
+                ++it )
+            if( matchTest( *it, testSpec, config ) )
+                filtered.push_back( *it );
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
 
+    class TestRegistry : public ITestCaseRegistry {
     public:
-        TestRegistry() : m_unnamedCount( 0 ) {}
+        TestRegistry()
+        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
+            m_unnamedCount( 0 )
+        {}
         virtual ~TestRegistry();
 
         virtual void registerTest( TestCase const& testCase ) {
@@ -5742,69 +6261,29 @@ namespace Catch {
                 oss << "Anonymous test case " << ++m_unnamedCount;
                 return registerTest( testCase.withName( oss.str() ) );
             }
-
-            if( m_functions.find( testCase ) == m_functions.end() ) {
-                m_functions.insert( testCase );
-                m_functionsInOrder.push_back( testCase );
-                if( !testCase.isHidden() )
-                    m_nonHiddenFunctions.push_back( testCase );
-            }
-            else {
-                TestCase const& prev = *m_functions.find( testCase );
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
-                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
-                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
-                }
-                exit(1);
-            }
+            m_functions.push_back( testCase );
         }
 
         virtual std::vector<TestCase> const& getAllTests() const {
-            return m_functionsInOrder;
-        }
-
-        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
-            return m_nonHiddenFunctions;
+            return m_functions;
         }
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+            if( m_sortedFunctions.empty() )
+                enforceNoDuplicateTestCases( m_functions );
 
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
-
-            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
-                                                        itEnd = m_functionsInOrder.end();
-                    it != itEnd;
-                    ++it ) {
-                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
-                if( includeTest != negated )
-                    matchingTestCases.push_back( *it );
+            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+                m_sortedFunctions = sortTests( config, m_functions );
+                m_currentSortOrder = config.runOrder();
             }
-            sortTests( config, matchingTestCases );
+            return m_sortedFunctions;
         }
 
     private:
-
-        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
-
-            switch( config.runOrder() ) {
-                case RunTests::InLexicographicalOrder:
-                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
-                    break;
-                case RunTests::InRandomOrder:
-                {
-                    RandomNumberGenerator rng;
-                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
-                }
-                    break;
-                case RunTests::InDeclarationOrder:
-                    // already in declaration order
-                    break;
-            }
-        }
-        std::set<TestCase> m_functions;
-        std::vector<TestCase> m_functionsInOrder;
-        std::vector<TestCase> m_nonHiddenFunctions;
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
         size_t m_unnamedCount;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
     };
 
     ///////////////////////////////////////////////////////////////////////////
@@ -5837,29 +6316,38 @@ namespace Catch {
         return className;
     }
 
-    ///////////////////////////////////////////////////////////////////////////
+    void registerTestCase
+        (   ITestCase* testCase,
+            char const* classOrQualifiedMethodName,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
 
-    AutoReg::AutoReg(   TestFunction function,
-                        SourceLineInfo const& lineInfo,
-                        NameAndDesc const& nameAndDesc ) {
+        getMutableRegistryHub().registerTest
+            ( makeTestCase
+                (   testCase,
+                    extractClassName( classOrQualifiedMethodName ),
+                    nameAndDesc.name,
+                    nameAndDesc.description,
+                    lineInfo ) );
+    }
+    void registerTestCaseFunction
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
     }
 
-    AutoReg::~AutoReg() {}
-
-    void AutoReg::registerTestCase( ITestCase* testCase,
-                                    char const* classOrQualifiedMethodName,
-                                    NameAndDesc const& nameAndDesc,
-                                    SourceLineInfo const& lineInfo ) {
+    ///////////////////////////////////////////////////////////////////////////
 
-        getMutableRegistryHub().registerTest
-            ( makeTestCase( testCase,
-                            extractClassName( classOrQualifiedMethodName ),
-                            nameAndDesc.name,
-                            nameAndDesc.description,
-                            lineInfo ) );
+    AutoReg::AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCaseFunction( function, lineInfo, nameAndDesc );
     }
 
+    AutoReg::~AutoReg() {}
+
 } // end namespace Catch
 
 // #included from: catch_reporter_registry.hpp
@@ -5873,27 +6361,32 @@ namespace Catch {
 
     public:
 
-        virtual ~ReporterRegistry() {
-            deleteAllValues( m_factories );
-        }
+        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
 
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
             FactoryMap::const_iterator it =  m_factories.find( name );
             if( it == m_factories.end() )
-                return NULL;
+                return CATCH_NULL;
             return it->second->create( ReporterConfig( config ) );
         }
 
-        void registerReporter( std::string const& name, IReporterFactory* factory ) {
+        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
             m_factories.insert( std::make_pair( name, factory ) );
         }
+        void registerListener( Ptr<IReporterFactory> const& factory ) {
+            m_listeners.push_back( factory );
+        }
 
-        FactoryMap const& getFactories() const {
+        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
             return m_factories;
         }
+        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+            return m_listeners;
+        }
 
     private:
         FactoryMap m_factories;
+        Listeners m_listeners;
     };
 }
 
@@ -5921,13 +6414,13 @@ namespace Catch {
 #ifdef __OBJC__
                 // In Objective-C try objective-c exceptions first
                 @try {
-                    throw;
+                    return tryTranslators();
                 }
                 @catch (NSException *exception) {
                     return Catch::toString( [exception description] );
                 }
 #else
-                throw;
+                return tryTranslators();
 #endif
             }
             catch( TestFailureException& ) {
@@ -5943,20 +6436,15 @@ namespace Catch {
                 return msg;
             }
             catch(...) {
-                return tryTranslators( m_translators.begin() );
+                return "Unknown exception";
             }
         }
 
-        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
-            if( it == m_translators.end() )
-                return "Unknown exception";
-
-            try {
-                return (*it)->translate();
-            }
-            catch(...) {
-                return tryTranslators( it+1 );
-            }
+        std::string tryTranslators() const {
+            if( m_translators.empty() )
+                throw;
+            else
+                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
         }
 
     private:
@@ -5976,24 +6464,27 @@ namespace Catch {
         public: // IRegistryHub
             RegistryHub() {
             }
-            virtual IReporterRegistry const& getReporterRegistry() const {
+            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
                 return m_reporterRegistry;
             }
-            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
                 return m_testCaseRegistry;
             }
-            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
                 return m_exceptionTranslatorRegistry;
             }
 
         public: // IMutableRegistryHub
-            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
                 m_reporterRegistry.registerReporter( name, factory );
             }
-            virtual void registerTest( TestCase const& testInfo ) {
+            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerListener( factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
                 m_testCaseRegistry.registerTest( testInfo );
             }
-            virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
                 m_exceptionTranslatorRegistry.registerTranslator( translator );
             }
 
@@ -6005,7 +6496,7 @@ namespace Catch {
 
         // Single, global, instance
         inline RegistryHub*& getTheRegistryHub() {
-            static RegistryHub* theRegistryHub = NULL;
+            static RegistryHub* theRegistryHub = CATCH_NULL;
             if( !theRegistryHub )
                 theRegistryHub = new RegistryHub();
             return theRegistryHub;
@@ -6020,7 +6511,7 @@ namespace Catch {
     }
     void cleanUp() {
         delete getTheRegistryHub();
-        getTheRegistryHub() = NULL;
+        getTheRegistryHub() = CATCH_NULL;
         cleanUpContext();
     }
     std::string translateActiveException() {
@@ -6051,23 +6542,10 @@ namespace Catch {
 } // end namespace Catch
 
 // #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
-
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
-
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
 
-    class StreamBufBase : public std::streambuf {
-    public:
-        virtual ~StreamBufBase() CATCH_NOEXCEPT;
-    };
-}
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
 
 #include <stdexcept>
 #include <cstdio>
@@ -6113,6 +6591,19 @@ namespace Catch {
 
     ///////////////////////////////////////////////////////////////////////////
 
+    FileStream::FileStream( std::string const& filename ) {
+        m_ofs.open( filename.c_str() );
+        if( m_ofs.fail() ) {
+            std::ostringstream oss;
+            oss << "Unable to open file: '" << filename << "'";
+            throw std::domain_error( oss.str() );
+        }
+    }
+
+    std::ostream& FileStream::stream() const {
+        return m_ofs;
+    }
+
     struct OutputDebugWriter {
 
         void operator()( std::string const&str ) {
@@ -6120,23 +6611,26 @@ namespace Catch {
         }
     };
 
-    Stream::Stream()
-    : streamBuf( NULL ), isOwned( false )
+    DebugOutStream::DebugOutStream()
+    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+        m_os( m_streamBuf.get() )
     {}
 
-    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
-    : streamBuf( _streamBuf ), isOwned( _isOwned )
+    std::ostream& DebugOutStream::stream() const {
+        return m_os;
+    }
+
+    // Store the streambuf from cout up-front because
+    // cout may get redirected when running tests
+    CoutStream::CoutStream()
+    :   m_os( Catch::cout().rdbuf() )
     {}
 
-    void Stream::release() {
-        if( isOwned ) {
-            delete streamBuf;
-            streamBuf = NULL;
-            isOwned = false;
-        }
+    std::ostream& CoutStream::stream() const {
+        return m_os;
     }
 
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
     std::ostream& cout() {
         return std::cout;
     }
@@ -6150,7 +6644,7 @@ namespace Catch {
 
     class Context : public IMutableContext {
 
-        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
         Context( Context const& );
         void operator=( Context const& );
 
@@ -6196,7 +6690,7 @@ namespace Catch {
                 m_generatorsByTestName.find( testName );
             return it != m_generatorsByTestName.end()
                 ? it->second
-                : NULL;
+                : CATCH_NULL;
         }
 
         IGeneratorsForTest& getGeneratorsForCurrentTest() {
@@ -6217,7 +6711,7 @@ namespace Catch {
     };
 
     namespace {
-        Context* currentContext = NULL;
+        Context* currentContext = CATCH_NULL;
     }
     IMutableContext& getCurrentMutableContext() {
         if( !currentContext )
@@ -6228,17 +6722,9 @@ namespace Catch {
         return getCurrentMutableContext();
     }
 
-    Stream createStream( std::string const& streamName ) {
-        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
-        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
-        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
-
-        throw std::domain_error( "Unknown stream: " + streamName );
-    }
-
     void cleanUpContext() {
         delete currentContext;
-        currentContext = NULL;
+        currentContext = CATCH_NULL;
     }
 }
 
@@ -6294,12 +6780,13 @@ namespace {
         {
             CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalAttributes = csbiInfo.wAttributes;
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
         }
 
         virtual void use( Colour::Code _colourCode ) {
             switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalAttributes );
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
@@ -6319,10 +6806,11 @@ namespace {
 
     private:
         void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
         }
         HANDLE stdoutHandle;
-        WORD originalAttributes;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
     };
 
     IColourImpl* platformColourInstance() {
@@ -6648,6 +7136,21 @@ namespace Catch {
         return TestCase( _testCase, info );
     }
 
+    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+    {
+        testCaseInfo.tags = tags;
+        testCaseInfo.lcaseTags.clear();
+
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+            oss << "[" << *it << "]";
+            std::string lcaseTag = toLower( *it );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.insert( lcaseTag );
+        }
+        testCaseInfo.tagsAsString = oss.str();
+    }
+
     TestCaseInfo::TestCaseInfo( std::string const& _name,
                                 std::string const& _className,
                                 std::string const& _description,
@@ -6656,18 +7159,10 @@ namespace Catch {
     :   name( _name ),
         className( _className ),
         description( _description ),
-        tags( _tags ),
         lineInfo( _lineInfo ),
         properties( None )
     {
-        std::ostringstream oss;
-        for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
-            oss << "[" << *it << "]";
-            std::string lcaseTag = toLower( *it );
-            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
-            lcaseTags.insert( lcaseTag );
-        }
-        tagsAsString = oss.str();
+        setTags( *this, _tags );
     }
 
     TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
@@ -6750,8 +7245,33 @@ namespace Catch {
 
 namespace Catch {
 
-    // These numbers are maintained by a script
-    Version libraryVersion( 1, 1, 1, "master" );
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            std::string const& _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << "."
+            << version.minorVersion << "."
+            << version.patchNumber;
+
+        if( !version.branchName.empty() ) {
+            os  << "-" << version.branchName
+                << "." << version.buildNumber;
+        }
+        return os;
+    }
+
+    Version libraryVersion( 1, 3, 2, "", 0 );
+
 }
 
 // #included from: catch_message.hpp
@@ -6943,7 +7463,7 @@ namespace Catch {
 #else
         uint64_t getCurrentTicks() {
             timeval t;
-            gettimeofday(&t,NULL);
+            gettimeofday(&t,CATCH_NULL);
             return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
         }
 #endif
@@ -7042,6 +7562,14 @@ namespace Catch {
         return line < other.line || ( line == other.line  && file < other.file );
     }
 
+    void seedRng( IConfig const& config ) {
+        if( config.rngSeed() != 0 )
+            std::srand( config.rngSeed() );
+    }
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
     std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
 #ifndef __GNUG__
         os << info.file << "(" << info.line << ")";
@@ -7081,8 +7609,13 @@ namespace Catch {
     }
 
     Section::~Section() {
-        if( m_sectionIncluded )
-            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+            if( std::uncaught_exception() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
     }
 
     // This indicates whether the section should be executed or not
@@ -7134,7 +7667,7 @@ namespace Catch {
             // Call sysctl.
 
             size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
                 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
                 return false;
             }
@@ -7188,9 +7721,11 @@ namespace Catch {
 
 namespace Detail {
 
-    std::string unprintableString = "{?}";
+    const std::string unprintableString = "{?}";
 
     namespace {
+        const int hexThreshold = 255;
+
         struct Endianness {
             enum Arch { Big, Little };
 
@@ -7271,19 +7806,17 @@ std::string toString( wchar_t* const value )
 
 std::string toString( int value ) {
     std::ostringstream oss;
-    if( value > 8192 )
-        oss << "0x" << std::hex << value;
-    else
-        oss << value;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
 
 std::string toString( unsigned long value ) {
     std::ostringstream oss;
-    if( value > 8192 )
-        oss << "0x" << std::hex << value;
-    else
-        oss << value;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
 
@@ -7332,6 +7865,23 @@ std::string toString( unsigned char value ) {
     return toString( static_cast<char>( value ) );
 }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+std::string toString( unsigned long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t ) {
     return "nullptr";
@@ -7361,11 +7911,17 @@ std::string toString( std::nullptr_t ) {
 
 namespace Catch {
 
+    std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) {
+        return secondArg.empty() || secondArg == "\"\""
+            ? capturedExpression
+            : capturedExpression + ", " + secondArg;
+    }
     ResultBuilder::ResultBuilder(   char const* macroName,
                                     SourceLineInfo const& lineInfo,
                                     char const* capturedExpression,
-                                    ResultDisposition::Flags resultDisposition )
-    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+                                    ResultDisposition::Flags resultDisposition,
+                                    char const* secondArg )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ),
         m_shouldDebugBreak( false ),
         m_shouldThrow( false )
     {}
@@ -7406,15 +7962,41 @@ namespace Catch {
         setResultType( resultType );
         captureExpression();
     }
+    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+        if( expectedMessage.empty() )
+            captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
+        else
+            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    }
+
+    void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
+
+        assert( m_exprComponents.testFalse == false );
+        AssertionResultData data = m_data;
+        data.resultType = ResultWas::Ok;
+        data.reconstructedExpression = m_assertionInfo.capturedExpression;
+
+        std::string actualMessage = Catch::translateActiveException();
+        if( !matcher.match( actualMessage ) ) {
+            data.resultType = ResultWas::ExpressionFailed;
+            data.reconstructedExpression = actualMessage;
+        }
+        AssertionResult result( m_assertionInfo, data );
+        handleResult( result );
+    }
 
     void ResultBuilder::captureExpression() {
         AssertionResult result = build();
+        handleResult( result );
+    }
+    void ResultBuilder::handleResult( AssertionResult const& result )
+    {
         getResultCapture().assertionEnded( result );
 
         if( !result.isOk() ) {
             if( getCurrentContext().getConfig()->shouldDebugBreak() )
                 m_shouldDebugBreak = true;
-            if( getCurrentContext().getRunner()->aborting() || m_assertionInfo.resultDisposition == ResultDisposition::Normal )
+            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
                 m_shouldThrow = true;
         }
     }
@@ -7561,6 +8143,137 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+    Reporters m_reporters;
+
+public:
+    void add( Ptr<IStreamingReporter> const& reporter ) {
+        m_reporters.push_back( reporter );
+    }
+
+public: // IStreamingReporter
+
+    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+        return m_reporters[0]->getPreferences();
+    }
+
+    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->noMatchingTestCases( spec );
+    }
+
+    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunStarting( testRunInfo );
+    }
+
+    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupStarting( groupInfo );
+    }
+
+    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseStarting( testInfo );
+    }
+
+    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionStarting( sectionInfo );
+    }
+
+    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+        bool clearBuffer = false;
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            clearBuffer |= (*it)->assertionEnded( assertionStats );
+        return clearBuffer;
+    }
+
+    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionEnded( sectionStats );
+    }
+
+    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseEnded( testCaseStats );
+    }
+
+    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupEnded( testGroupStats );
+    }
+
+    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunEnded( testRunStats );
+    }
+
+    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->skipTest( testInfo );
+    }
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+    Ptr<IStreamingReporter> resultingReporter;
+
+    if( existingReporter ) {
+        MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
+        if( !multi ) {
+            multi = new MultipleReporters;
+            resultingReporter = Ptr<IStreamingReporter>( multi );
+            if( existingReporter )
+                multi->add( existingReporter );
+        }
+        else
+            resultingReporter = existingReporter;
+        multi->add( additionalReporter );
+    }
+    else
+        resultingReporter = additionalReporter;
+
+    return resultingReporter;
+}
+
+} // end namespace Catch
+
 // #included from: ../reporters/catch_reporter_xml.hpp
 #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
 
@@ -7576,47 +8289,53 @@ namespace Catch {
         StreamingReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
 
-        virtual ~StreamingReporterBase();
+        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
 
-        virtual void noMatchingTestCases( std::string const& ) {}
+        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
             currentTestRunInfo = _testRunInfo;
         }
-        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
             currentGroupInfo = _groupInfo;
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
             currentTestCaseInfo = _testInfo;
         }
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_sectionStack.push_back( _sectionInfo );
         }
 
-        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
         }
-        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
             currentGroupInfo.reset();
         }
-        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
             currentGroupInfo.reset();
             currentTestRunInfo.reset();
         }
 
-        virtual void skipTest( TestCaseInfo const& ) {
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
             // Don't do anything with this by default.
             // It can optionally be overridden in the derived class.
         }
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
 
         LazyStat<TestRunInfo> currentTestRunInfo;
@@ -7624,6 +8343,7 @@ namespace Catch {
         LazyStat<TestCaseInfo> currentTestCaseInfo;
 
         std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
     };
 
     struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
@@ -7674,15 +8394,21 @@ namespace Catch {
         CumulativeReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
         ~CumulativeReporterBase();
 
-        virtual void testRunStarting( TestRunInfo const& ) {}
-        virtual void testGroupStarting( GroupInfo const& ) {}
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void testCaseStarting( TestCaseInfo const& ) {}
+        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
             Ptr<SectionNode> node;
             if( m_sectionStack.empty() ) {
@@ -7707,7 +8433,7 @@ namespace Catch {
             m_deepestSection = node;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {}
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
 
         virtual bool assertionEnded( AssertionStats const& assertionStats ) {
             assert( !m_sectionStack.empty() );
@@ -7715,13 +8441,13 @@ namespace Catch {
             sectionNode.assertions.push_back( assertionStats );
             return true;
         }
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             assert( !m_sectionStack.empty() );
             SectionNode& node = *m_sectionStack.back();
             node.stats = sectionStats;
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
             assert( m_sectionStack.size() == 0 );
             node->children.push_back( m_rootSection );
@@ -7732,12 +8458,12 @@ namespace Catch {
             m_deepestSection->stdOut = testCaseStats.stdOut;
             m_deepestSection->stdErr = testCaseStats.stdErr;
         }
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
             node->children.swap( m_testCases );
             m_testGroups.push_back( node );
         }
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             Ptr<TestRunNode> node = new TestRunNode( testRunStats );
             node->children.swap( m_testGroups );
             m_testRuns.push_back( node );
@@ -7745,9 +8471,9 @@ namespace Catch {
         }
         virtual void testRunEndedCumulative() = 0;
 
-        virtual void skipTest( TestCaseInfo const& ) {}
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
         std::vector<AssertionStats> m_assertions;
         std::vector<std::vector<Ptr<SectionNode> > > m_sections;
@@ -7759,6 +8485,7 @@ namespace Catch {
         Ptr<SectionNode> m_rootSection;
         Ptr<SectionNode> m_deepestSection;
         std::vector<Ptr<SectionNode> > m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
 
     };
 
@@ -7772,6 +8499,17 @@ namespace Catch {
         return line;
     }
 
+    struct TestEventListenerBase : StreamingReporterBase {
+        TestEventListenerBase( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+            return false;
+        }
+    };
+
 } // end namespace Catch
 
 // #included from: ../internal/catch_reporter_registrars.hpp
@@ -7802,7 +8540,7 @@ namespace Catch {
     template<typename T>
     class ReporterRegistrar {
 
-        class ReporterFactory : public IReporterFactory {
+        class ReporterFactory : public SharedImpl<IReporterFactory> {
 
             // *** Please Note ***:
             // - If you end up here looking at a compiler error because it's trying to register
@@ -7830,22 +8568,102 @@ namespace Catch {
             getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
         }
     };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+            virtual std::string getDescription() const {
+                return "";
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        }
+    };
 }
 
 #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
     namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
 #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
     namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
 
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
 // #included from: ../internal/catch_xmlwriter.hpp
 #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
 
 #include <sstream>
 #include <string>
 #include <vector>
+#include <iomanip>
 
 namespace Catch {
 
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+        :   m_str( str ),
+            m_forWhat( forWhat )
+        {}
+
+        void encodeTo( std::ostream& os ) const {
+
+            // Apostrophe escaping not necessary if we always use " to write attributes
+            // (see: http://www.w3.org/TR/xml/#syntax)
+
+            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+                char c = m_str[i];
+                switch( c ) {
+                    case '<':   os << "<"; break;
+                    case '&':   os << "&"; break;
+
+                    case '>':
+                        // See: http://www.w3.org/TR/xml/#syntax
+                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+                            os << ">";
+                        else
+                            os << c;
+                        break;
+
+                    case '\"':
+                        if( m_forWhat == ForAttributes )
+                            os << """;
+                        else
+                            os << c;
+                        break;
+
+                    default:
+                        // Escape control chars - based on contribution by @espenalb in PR #465
+                        if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
+                            os << "&#x" << std::uppercase << std::hex << static_cast<int>( c );
+                        else
+                            os << c;
+                }
+            }
+        }
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+            xmlEncode.encodeTo( os );
+            return os;
+        }
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
     class XmlWriter {
     public:
 
@@ -7857,7 +8675,7 @@ namespace Catch {
 
             ScopedElement( ScopedElement const& other )
             :   m_writer( other.m_writer ){
-                other.m_writer = NULL;
+                other.m_writer = CATCH_NULL;
             }
 
             ~ScopedElement() {
@@ -7897,27 +8715,6 @@ namespace Catch {
                 endElement();
         }
 
-//#  ifndef CATCH_CPP11_OR_GREATER
-//        XmlWriter& operator = ( XmlWriter const& other ) {
-//            XmlWriter temp( other );
-//            swap( temp );
-//            return *this;
-//        }
-//#  else
-//        XmlWriter( XmlWriter const& )              = default;
-//        XmlWriter( XmlWriter && )                  = default;
-//        XmlWriter& operator = ( XmlWriter const& ) = default;
-//        XmlWriter& operator = ( XmlWriter && )     = default;
-//#  endif
-//
-//        void swap( XmlWriter& other ) {
-//            std::swap( m_tagIsOpen, other.m_tagIsOpen );
-//            std::swap( m_needsNewline, other.m_needsNewline );
-//            std::swap( m_tags, other.m_tags );
-//            std::swap( m_indent, other.m_indent );
-//            std::swap( m_os, other.m_os );
-//        }
-
         XmlWriter& startElement( std::string const& name ) {
             ensureTagClosed();
             newlineIfNecessary();
@@ -7949,11 +8746,8 @@ namespace Catch {
         }
 
         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
-            if( !name.empty() && !attribute.empty() ) {
-                stream() << " " << name << "=\"";
-                writeEncodedText( attribute );
-                stream() << "\"";
-            }
+            if( !name.empty() && !attribute.empty() )
+                stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\"";
             return *this;
         }
 
@@ -7964,9 +8758,9 @@ namespace Catch {
 
         template<typename T>
         XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            if( !name.empty() )
-                stream() << " " << name << "=\"" << attribute << "\"";
-            return *this;
+            std::ostringstream oss;
+            oss << attribute;
+            return writeAttribute( name, oss.str() );
         }
 
         XmlWriter& writeText( std::string const& text, bool indent = true ) {
@@ -7975,7 +8769,7 @@ namespace Catch {
                 ensureTagClosed();
                 if( tagWasOpen && indent )
                     stream() << m_indent;
-                writeEncodedText( text );
+                stream() << XmlEncode( text );
                 m_needsNewline = true;
             }
             return *this;
@@ -8020,30 +8814,6 @@ namespace Catch {
             }
         }
 
-        void writeEncodedText( std::string const& text ) {
-            static const char* charsToEncode = "<&\"";
-            std::string mtext = text;
-            std::string::size_type pos = mtext.find_first_of( charsToEncode );
-            while( pos != std::string::npos ) {
-                stream() << mtext.substr( 0, pos );
-
-                switch( mtext[pos] ) {
-                    case '<':
-                        stream() << "<";
-                        break;
-                    case '&':
-                        stream() << "&";
-                        break;
-                    case '\"':
-                        stream() << """;
-                        break;
-                }
-                mtext = mtext.substr( pos+1 );
-                pos = mtext.find_first_of( charsToEncode );
-            }
-            stream() << mtext;
-        }
-
         bool m_tagIsOpen;
         bool m_needsNewline;
         std::vector<std::string> m_tags;
@@ -8052,32 +8822,44 @@ namespace Catch {
     };
 
 }
+// #included from: catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+
 namespace Catch {
     class XmlReporter : public StreamingReporterBase {
     public:
         XmlReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
             m_sectionDepth( 0 )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        virtual ~XmlReporter();
+        virtual ~XmlReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results as an XML document";
         }
 
     public: // StreamingReporterBase
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& s ) {
+        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
             StreamingReporterBase::noMatchingTestCases( s );
         }
 
-        virtual void testRunStarting( TestRunInfo const& testInfo ) {
+        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunStarting( testInfo );
             m_xml.setStream( stream );
             m_xml.startElement( "Catch" );
@@ -8085,13 +8867,13 @@ namespace Catch {
                 m_xml.writeAttribute( "name", m_config->name() );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupStarting( groupInfo );
             m_xml.startElement( "Group" )
                 .writeAttribute( "name", groupInfo.name );
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseStarting(testInfo);
             m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
 
@@ -8099,7 +8881,7 @@ namespace Catch {
                 m_testCaseTimer.start();
         }
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionStarting( sectionInfo );
             if( m_sectionDepth++ > 0 ) {
                 m_xml.startElement( "Section" )
@@ -8108,9 +8890,9 @@ namespace Catch {
             }
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) { }
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             const AssertionResult& assertionResult = assertionStats.assertionResult;
 
             // Print any info messages in <Info> tags.
@@ -8181,7 +8963,7 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionEnded( sectionStats );
             if( --m_sectionDepth > 0 ) {
                 XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
@@ -8196,7 +8978,7 @@ namespace Catch {
             }
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( testCaseStats );
             XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
             e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
@@ -8207,7 +8989,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupEnded( testGroupStats );
             // TODO: Check testGroupStats.aborting and act accordingly.
             m_xml.scopedElement( "OverallResults" )
@@ -8217,7 +8999,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunEnded( testRunStats );
             m_xml.scopedElement( "OverallResults" )
                 .writeAttribute( "successes", testRunStats.totals.assertions.passed )
@@ -8248,28 +9030,24 @@ namespace Catch {
         JunitReporter( ReporterConfig const& _config )
         :   CumulativeReporterBase( _config ),
             xml( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        ~JunitReporter();
+        virtual ~JunitReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results in an XML format that looks like Ant's junitreport target";
         }
 
-        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
-
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& runInfo ) {
+        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
             CumulativeReporterBase::testRunStarting( runInfo );
             xml.startElement( "testsuites" );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             suiteTimer.start();
             stdOutForSuite.str("");
             stdErrForSuite.str("");
@@ -8277,25 +9055,25 @@ namespace Catch {
             CumulativeReporterBase::testGroupStarting( groupInfo );
         }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
                 unexpectedExceptions++;
             return CumulativeReporterBase::assertionEnded( assertionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             stdOutForSuite << testCaseStats.stdOut;
             stdErrForSuite << testCaseStats.stdErr;
             CumulativeReporterBase::testCaseEnded( testCaseStats );
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             double suiteTime = suiteTimer.getElapsedSeconds();
             CumulativeReporterBase::testGroupEnded( testGroupStats );
             writeGroup( *m_testGroups.back(), suiteTime );
         }
 
-        virtual void testRunEndedCumulative() {
+        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
             xml.endElement();
         }
 
@@ -8460,24 +9238,19 @@ namespace Catch {
             m_headerPrinted( false )
         {}
 
-        virtual ~ConsoleReporter();
+        virtual ~ConsoleReporter() CATCH_OVERRIDE;
         static std::string getDescription() {
             return "Reports test results as plain lines of text";
         }
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& spec ) {
+        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
             stream << "No test cases matched '" << spec << "'" << std::endl;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
         }
 
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
             AssertionResult const& result = _assertionStats.assertionResult;
 
             bool printInfoMessages = true;
@@ -8497,11 +9270,11 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_headerPrinted = false;
             StreamingReporterBase::sectionStarting( _sectionInfo );
         }
-        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
             if( _sectionStats.missingAssertions ) {
                 lazyPrint();
                 Colour colour( Colour::ResultError );
@@ -8523,11 +9296,11 @@ namespace Catch {
             StreamingReporterBase::sectionEnded( _sectionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( _testCaseStats );
             m_headerPrinted = false;
         }
-        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
             if( currentGroupInfo.used ) {
                 printSummaryDivider();
                 stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
@@ -8536,7 +9309,7 @@ namespace Catch {
             }
             StreamingReporterBase::testGroupEnded( _testGroupStats );
         }
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
             printTotalsDivider( _testRunStats.totals );
             printTotals( _testRunStats.totals );
             stream << std::endl;
@@ -8700,12 +9473,7 @@ namespace Catch {
             stream  << "\n" << getLineOfChars<'~'>() << "\n";
             Colour colour( Colour::SecondaryText );
             stream  << currentTestRunInfo->name
-                    << " is a Catch v"  << libraryVersion.majorVersion << "."
-                    << libraryVersion.minorVersion << " b"
-                    << libraryVersion.buildNumber;
-            if( libraryVersion.branchName != std::string( "master" ) )
-                stream << " (" << libraryVersion.branchName << ")";
-            stream  << " host application.\n"
+                    << " is a Catch v"  << libraryVersion << " host application.\n"
                     << "Run with -? for options\n\n";
 
             if( m_config->rngSeed() != 0 )
@@ -9180,8 +9948,14 @@ namespace Catch {
 } // end namespace Catch
 
 namespace Catch {
+    // These are all here to avoid warnings about not having any out of line
+    // virtual methods
     NonCopyable::~NonCopyable() {}
     IShared::~IShared() {}
+    IStream::~IStream() CATCH_NOEXCEPT {}
+    FileStream::~FileStream() CATCH_NOEXCEPT {}
+    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
     StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
     IContext::~IContext() {}
     IResultCapture::~IResultCapture() {}
@@ -9215,6 +9989,7 @@ namespace Catch {
     FreeFunctionTestCase::~FreeFunctionTestCase() {}
     IGeneratorInfo::~IGeneratorInfo() {}
     IGeneratorsForTest::~IGeneratorsForTest() {}
+    WildcardPattern::~WildcardPattern() {}
     TestSpec::Pattern::~Pattern() {}
     TestSpec::NamePattern::~NamePattern() {}
     TestSpec::TagPattern::~TagPattern() {}
@@ -9226,6 +10001,13 @@ namespace Catch {
     Matchers::Impl::StdString::EndsWith::~EndsWith() {}
 
     void Config::dummy() {}
+
+    namespace TestCaseTracking {
+        ITracker::~ITracker() {}
+        TrackerBase::~TrackerBase() {}
+        SectionTracker::~SectionTracker() {}
+        IndexTracker::~IndexTracker() {}
+    }
 }
 
 #ifdef __clang__
@@ -9241,7 +10023,7 @@ namespace Catch {
 #ifndef __OBJC__
 
 // Standard C/C++ main entry point
-int main (int argc, char * const argv[]) {
+int main (int argc, char * argv[]) {
     return Catch::Session().run( argc, argv );
 }
 
@@ -9279,8 +10061,9 @@ int main (int argc, char * const argv[]) {
 #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
 #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
 
-#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" )
 #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" )
 #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
 
 #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
@@ -9291,6 +10074,7 @@ int main (int argc, char * const argv[]) {
 
 #define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
 #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
 #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
@@ -9306,6 +10090,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_REGISTER_TEST_CASE( ... ) INTERNAL_CATCH_REGISTER_TESTCASE( __VA_ARGS__ )
     #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
     #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
@@ -9313,6 +10098,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
     #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
     #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
@@ -9332,11 +10118,11 @@ int main (int argc, char * const argv[]) {
 #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
 #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define CATCH_GIVEN( desc )    CATCH_SECTION( "Given: " desc, "" )
-#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
-#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
 
 // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
 #else
@@ -9344,8 +10130,9 @@ int main (int argc, char * const argv[]) {
 #define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
 #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
 
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" )
 #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" )
 #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
 
 #define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
@@ -9354,8 +10141,9 @@ int main (int argc, char * const argv[]) {
 #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
 #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
 
-#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" )
 #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" )
 #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
@@ -9371,6 +10159,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define REGISTER_TEST_CASE( ... ) INTERNAL_CATCH_REGISTER_TESTCASE( __VA_ARGS__ )
     #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
     #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
@@ -9378,6 +10167,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
     #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
     #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
@@ -9401,27 +10191,13 @@ int main (int argc, char * const argv[]) {
 #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
 #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
-#define WHEN( desc )     SECTION( "    When: " desc, "" )
-#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
-#define THEN( desc )     SECTION( "    Then: " desc, "" )
-#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
+#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
+#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
 
 using Catch::Detail::Approx;
 
-// #included from: internal/catch_reenable_warnings.h
-
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-#    ifdef __ICC // icpc defines the __clang__ macro
-#        pragma warning(pop)
-#    else
-#        pragma clang diagnostic pop
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic pop
-#endif
-
 #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
 
diff --git a/test/catch_ext.hpp b/test/catch_ext.hpp
new file mode 100644
index 0000000..3168ecf
--- /dev/null
+++ b/test/catch_ext.hpp
@@ -0,0 +1,22 @@
+#ifndef TEST_CATCH_EXT_HPP
+#define TEST_CATCH_EXT_HPP
+
+#include "catch.hpp"
+
+#define TRY_CHECK( expr )                                   \
+    try {                                                   \
+        CHECK( expr );                                      \
+    }                                                       \
+    catch ( Catch::TestFailureException & ) {               \
+        /* thrown by CHECK after it catches and reports */  \
+        /* an exception from expr => swallow this       */  \
+    }
+
+#define TRY_CHECK_FALSE( expr )                             \
+    try {                                                   \
+        CHECK_FALSE( expr );                                \
+    }                                                       \
+    catch ( Catch::TestFailureException & ) {               \
+    }
+
+#endif // TEST_CATCH_EXT_HPP
diff --git a/test/unit/color/css_color.cpp b/test/unit/color/css_color.cpp
index 7ff6e3f..54c90e7 100644
--- a/test/unit/color/css_color.cpp
+++ b/test/unit/color/css_color.cpp
@@ -2,6 +2,7 @@
 #include <mapnik/css_color_grammar.hpp>
 #include <mapnik/css_color_grammar_impl.hpp>
 #include <mapnik/safe_cast.hpp>
+#include <sstream>
 
 TEST_CASE("css color") {
 
@@ -164,4 +165,27 @@ TEST_CASE("css color") {
             CHECK( !boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), color_grammar, space) );
         }
     }
+    SECTION("operator<< / to_string()")
+    {
+        mapnik::color c("salmon");
+        std::ostringstream ss;
+        ss << c ;
+        CHECK(ss.str() == "rgb(250,128,114)");
+        c.set_alpha(127);
+        ss.seekp(0);
+        ss << c ;
+        CHECK(ss.str() == "rgba(250,128,114,0.498)");
+    }
+    SECTION("premultiply/demultiply")
+    {
+        mapnik::color c("cornflowerblue");
+        c.set_alpha(127);
+        c.premultiply();
+        CHECK(int(c.red()) == 50);
+        CHECK(int(c.green()) == 74);
+        CHECK(int(c.blue()) == 118);
+        CHECK(int(c.alpha()) == 127);
+        c.demultiply();
+        CHECK(c == mapnik::color(100, 148, 236, 127));
+    }
 }
diff --git a/test/unit/core/box2d_test.cpp b/test/unit/core/box2d_test.cpp
index 87f2e14..14cbb48 100644
--- a/test/unit/core/box2d_test.cpp
+++ b/test/unit/core/box2d_test.cpp
@@ -2,6 +2,7 @@
 
 #include <mapnik/coord.hpp>
 #include <mapnik/box2d.hpp>
+#include "agg_trans_affine.h"
 
 TEST_CASE("box2d") {
 SECTION("coord init") {
@@ -159,4 +160,62 @@ SECTION("envelope clipping") {
   REQUIRE(e1 == e2);
 }
 
+SECTION("mapnik::box2d intersects")
+{
+    mapnik::box2d<double> b0(0,0,100,100);
+    // another box2d
+    mapnik::box2d<double> b1(100,100,200,200);
+    CHECK(b0.intersects(b1));
+    CHECK(b1.intersects(b0));
+    mapnik::box2d<double> b2(100.001,100,200,200);
+    CHECK(!b0.intersects(b2));
+    CHECK(!b2.intersects(b0));
+    // coord
+    CHECK(b0.intersects(mapnik::coord<double,2>(100,100)));
+    CHECK(!b0.intersects(mapnik::coord<double,2>(100.001,100)));
+}
+
+SECTION("mapnik::box2d intersect")
+{
+    mapnik::box2d<double> b0(0,0,100,100);
+    mapnik::box2d<double> b1(100,100,200,200);
+    CHECK(b0.intersect(b1) == mapnik::box2d<double>(100,100,100,100));
+    CHECK(b1.intersect(b0) == mapnik::box2d<double>(100,100,100,100));
+    mapnik::box2d<double> b2(100.001,100,200,200);
+    CHECK(b0.intersect(b2) == mapnik::box2d<double>());
+    CHECK(b2.intersect(b0) == mapnik::box2d<double>());
+}
+
+SECTION("mapnik::box2d re_center")
+{
+    mapnik::box2d<double> b(0, 0, 100, 100);
+    b.re_center(0, 0);
+    CHECK(b == mapnik::box2d<double>(-50, -50, 50, 50));
+    b.re_center(mapnik::coord2d(50,50));
+    CHECK(b == mapnik::box2d<double>(0, 0, 100, 100));
+}
+
+SECTION("mapnik::box2d operator+=")
+{
+    mapnik::box2d<double> b(0, 0, 50, 50);
+    b += mapnik::box2d<double>(100, 100, 200, 200);
+    CHECK(b == mapnik::box2d<double>(0, 0, 200, 200));
+    b += 100;
+    CHECK(b == mapnik::box2d<double>(-100, -100, 300, 300));
+}
+
+SECTION("mapnik::box2d operator*=  operator=/ ")
+{
+    mapnik::box2d<double> b(0, 0, 100, 100);
+    b *= 2.0;
+    CHECK(b == mapnik::box2d<double>(-50, -50, 150, 150));
+    b /= 2.0;
+    CHECK(b == mapnik::box2d<double>(0, 0, 100, 100));
+
+    agg::trans_affine tr;
+    tr.translate(-50,-50);
+    tr.scale(2.0);
+    b *= tr;
+    CHECK(b == mapnik::box2d<double>(-100, -100, 100, 100));
+}
 } // TEST_CASE
diff --git a/test/unit/core/expressions_test.cpp b/test/unit/core/expressions_test.cpp
new file mode 100644
index 0000000..72490a2
--- /dev/null
+++ b/test/unit/core/expressions_test.cpp
@@ -0,0 +1,183 @@
+#include "catch_ext.hpp"
+
+#include <mapnik/expression.hpp>
+#include <mapnik/expression_evaluator.hpp>
+#include <mapnik/expression_string.hpp>
+#include <mapnik/wkt/wkt_factory.hpp>
+#include <mapnik/feature.hpp>
+#include <mapnik/feature_factory.hpp>
+#include <mapnik/unicode.hpp>
+
+#include <functional>
+#include <vector>
+
+namespace {
+
+template <typename Properties>
+mapnik::feature_ptr make_test_feature(mapnik::value_integer id, std::string const& wkt, Properties const& prop)
+{
+    auto ctx = std::make_shared<mapnik::context_type>();
+    mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx, id));
+    mapnik::geometry::geometry<double> geom;
+    if (mapnik::from_wkt(wkt, geom))
+    {
+        feature->set_geometry(std::move(geom));
+    }
+
+    for (auto const& kv : prop)
+    {
+        feature->put_new(kv.first, kv.second);
+    }
+    return feature;
+}
+
+template <typename Feature, typename Expression>
+mapnik::value_type evaluate(Feature const& feature, Expression const& expr)
+{
+    auto value = mapnik::util::apply_visitor(
+        mapnik::evaluate<Feature, mapnik::value_type, mapnik::attributes>(
+            feature, mapnik::attributes()), expr);
+    return value;
+}
+
+mapnik::value evaluate_string(mapnik::feature_ptr const& feature, std::string const& str)
+{
+    auto expr = mapnik::parse_expression(str);
+    return evaluate(*feature, *expr);
+}
+
+std::string parse_and_dump(std::string const& str)
+{
+    auto expr = mapnik::parse_expression(str);
+    return mapnik::to_expression_string(*expr);
+}
+
+} // namespace
+
+TEST_CASE("expressions")
+{
+    using namespace std::placeholders;
+    using properties_type = std::vector<std::pair<std::string, mapnik::value> > ;
+    mapnik::transcoder tr("utf8");
+
+    properties_type prop = {{ "foo"   , tr.transcode("bar") },
+                            { "name"  , tr.transcode("Québec")},
+                            { "grass" , tr.transcode("grow")},
+                            { "wind"  , tr.transcode("blow")},
+                            { "sky"   , tr.transcode("is blue")},
+                            { "double", mapnik::value_double(1.23456)},
+                            { "int"   , mapnik::value_integer(123)},
+                            { "bool"  , mapnik::value_bool(true)},
+                            { "null"  , mapnik::value_null()}};
+
+    auto feature = make_test_feature(1, "POINT(100 200)", prop);
+    auto eval = std::bind(evaluate_string, feature, _1);
+    auto approx = Approx::custom().epsilon(1e-6);
+
+    TRY_CHECK(eval(" [foo]='bar' ") == true);
+
+    // primary expressions
+    // null
+    TRY_CHECK(parse_and_dump("null") == "null");
+    // boolean
+    TRY_CHECK(parse_and_dump("true") == "true");
+    TRY_CHECK(parse_and_dump("false") == "false");
+    // floating point
+    TRY_CHECK(parse_and_dump("3.14159") == "3.14159");
+    // integer
+    TRY_CHECK(parse_and_dump("123") == "123");
+    // unicode
+    TRY_CHECK(parse_and_dump("'single-quoted string'") == "'single-quoted string'");
+    TRY_CHECK(parse_and_dump("\"double-quoted string\"") == "'double-quoted string'");
+    TRY_CHECK(parse_and_dump("'escaped \\' apostrophe'") == "'escaped \\' apostrophe'");
+    TRY_CHECK(parse_and_dump("'escaped \\\\ backslash'") == "'escaped \\\\ backslash'");
+
+    // floating point constants
+    TRY_CHECK(parse_and_dump("pi") == "3.14159");
+    TRY_CHECK(parse_and_dump("deg_to_rad") == "0.0174533");
+    TRY_CHECK(parse_and_dump("rad_to_deg") == "57.2958");
+
+    // unary functions
+    // sin / cos
+    TRY_CHECK(eval(" sin(0.25 * pi) / cos(0.25 * pi) ").to_double() == approx(1.0));
+    // tan
+    TRY_CHECK(eval(" tan(0.25 * pi) ").to_double() == approx(1.0));
+    // atan
+    TRY_CHECK(eval(" rad_to_deg * atan(1.0) ").to_double() == approx(45.0));
+    // exp
+    TRY_CHECK(eval(" exp(0.0) ") == 1.0);
+    // abs
+    TRY_CHECK(eval(" abs(cos(-pi)) ") == 1.0);
+    // length (string)
+    TRY_CHECK(eval(" length('1234567890') ").to_int() == 10);
+
+    // binary functions
+    // min
+    TRY_CHECK(eval(" min(-0.01, 0.001) ") == -0.01);
+    // max
+    TRY_CHECK(eval(" max(0.01, -0.1) ") == 0.01);
+    // pow
+    TRY_CHECK(eval(" pow(2, 32) ") == 4294967296.0);
+
+    // geometry types
+    TRY_CHECK(eval(" [mapnik::geometry_type] = point ") == true);
+    TRY_CHECK(eval(" [mapnik::geometry_type] <> linestring ") == true);
+    TRY_CHECK(eval(" [mapnik::geometry_type] != polygon ") == true);
+    TRY_CHECK(eval(" [mapnik::geometry_type] neq collection ") == true);
+    TRY_CHECK(eval(" [mapnik::geometry_type] eq collection ") == false);
+
+    //unary expression
+    TRY_CHECK(eval(" -123.456 ") == -123.456);
+    TRY_CHECK(eval(" +123.456 ") == 123.456);
+
+    // multiplicative/additive
+    auto expr = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/(2.0 * 2.0 - 3.0 * 3.0)");
+    TRY_CHECK(evaluate(*feature, *expr) == -2.6);
+    auto expr2 = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/((2.0 - 3.0) * (2.0 + 3.0))");
+    TRY_CHECK(evaluate(*feature, *expr) == evaluate(*feature, *expr2));
+
+    // logical
+    TRY_CHECK(eval(" [int] = 123 and [double] = 1.23456 && [bool] = true and [null] = null && [foo] = 'bar' ") == true);
+    TRY_CHECK(eval(" [int] = 456 or [foo].match('foo') || length([foo]) = 3 ") == true);
+    TRY_CHECK(eval(" not true  and not true  ") == eval(" (not true ) and (not true ) "));
+    TRY_CHECK(eval(" not true  or  not true  ") == eval(" (not true ) or  (not true ) "));
+    TRY_CHECK(eval(" not false and not false ") == eval(" (not false) and (not false) "));
+    TRY_CHECK(eval(" not false or  not false ") == eval(" (not false) or  (not false) "));
+
+    // test not/and/or precedence using combinations of "not EQ1 OP1 not EQ2 OP2 not EQ3"
+    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
+    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
+    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
+    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
+    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
+    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);
+    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
+    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);
+
+    // relational
+    TRY_CHECK(eval(" [int] > 100 and [int] gt 100.0 and [double] < 2 and [double] lt 2.0 ") == true);
+    TRY_CHECK(eval(" [int] >= 123 and [int] ge 123.0 and [double] <= 1.23456 and [double] le 1.23456 ") == true);
+
+    // empty string/null equality
+    TRY_CHECK(eval("[null] = null") == true);
+    TRY_CHECK(eval("[null] != null") == false);
+    TRY_CHECK(eval("[null] = ''") == false);
+    ///////////////////// ref: https://github.com/mapnik/mapnik/issues/1859
+    TRY_CHECK(eval("[null] != ''") == false); // back compatible - will be changed in 3.1.x
+    //////////////////////
+    TRY_CHECK(eval("'' = [null]") == false);
+    TRY_CHECK(eval("'' != [null]") == true);
+
+    // regex
+    // replace
+    TRY_CHECK(eval(" [foo].replace('(\\B)|( )','$1 ') ") == tr.transcode("b a r"));
+    // 'foo' =~ s:(\w)\1:$1x:r
+    TRY_CHECK(eval(" 'foo'.replace('(\\w)\\1', '$1x') ") == tr.transcode("fox"));
+    TRY_CHECK(parse_and_dump(" 'foo'.replace('(\\w)\\1', '$1x') ") == "'foo'.replace('(\\w)\\1','$1x')");
+
+    // match
+    TRY_CHECK(eval(" [name].match('Québec') ") == true);
+    // 'Québec' =~ m:^Q\S*$:
+    TRY_CHECK(eval(" [name].match('^Q\\S*$') ") == true);
+    TRY_CHECK(parse_and_dump(" [name].match('^Q\\S*$') ") == "[name].match('^Q\\S*$')");
+}
diff --git a/test/unit/core/value_test.cpp b/test/unit/core/value_test.cpp
new file mode 100644
index 0000000..0480154
--- /dev/null
+++ b/test/unit/core/value_test.cpp
@@ -0,0 +1,109 @@
+#include "catch.hpp"
+
+#include <mapnik/value_types.hpp>
+#include <mapnik/value.hpp>
+
+TEST_CASE("mapnik::value")
+{
+    SECTION("add/sub/mult/div")
+    {
+        mapnik::value v0 = 1; // mapnik::value_integer
+        mapnik::value v1 = 1.001; // mapnik::value_double
+        mapnik::value v2 = true; // mapnik::value_boolean
+
+        CHECK(v0.is<mapnik::value_integer>());
+        CHECK(v1.is<mapnik::value_double>());
+        CHECK(v2.is<mapnik::value_bool>());
+
+        // add
+        auto add0 = v0 + v1; // result value_double
+        auto add1 = v1 + v0;
+        auto add2 = v1 + v2; // result value_double
+        auto add3 = v2 + v1;
+        auto add4 = v0 + v2; // result value_integer
+        auto add5 = v2 + v0;
+        auto add6 = v2 + v2; // result_integer
+        // check type promotion
+        CHECK(add0.is<mapnik::value_double>());
+        CHECK(add1.is<mapnik::value_double>());
+        CHECK(add2.is<mapnik::value_double>());
+        CHECK(add3.is<mapnik::value_double>());
+        CHECK(add4.is<mapnik::value_integer>());
+        CHECK(add5.is<mapnik::value_integer>());
+        CHECK(add6.is<mapnik::value_integer>());
+        //
+        CHECK(add6 == v0 + v0);
+        // check commutative rules
+        CHECK(add0 == add1);
+        CHECK(add2 == add3);
+        CHECK(add4 == add5);
+
+        // sub
+        auto sub0 = v0 - v1; // result value_double
+        auto sub1 = v1 - v0;
+        auto sub2 = v1 - v2; // result value_double
+        auto sub3 = v2 - v1;
+        auto sub4 = v0 - v2; // result value_integer
+        auto sub5 = v2 - v0;
+        auto sub6 = v2 - v2; // result value_integer
+
+        CHECK(sub0.is<mapnik::value_double>());
+        CHECK(sub1.is<mapnik::value_double>());
+        CHECK(sub2.is<mapnik::value_double>());
+        CHECK(sub3.is<mapnik::value_double>());
+        CHECK(sub4.is<mapnik::value_integer>());
+        CHECK(sub5.is<mapnik::value_integer>());
+        CHECK(sub6.is<mapnik::value_integer>());
+
+        // check commutative rules
+        CHECK(sub0 == -sub1);
+        CHECK(sub2 == -sub3);
+        CHECK(sub4 == -sub5);
+        CHECK(sub6 == v0 - v0);
+
+        // multl
+        auto mult0 = v0 * v1; // result value_double
+        auto mult1 = v1 * v0;
+        auto mult2 = v1 * v2; // result value_double
+        auto mult3 = v2 * v1;
+        auto mult4 = v0 * v2; // result value_integer
+        auto mult5 = v2 * v0;
+        auto mult6 = v2 * v2; // result value_integer
+
+        CHECK(mult0.is<mapnik::value_double>());
+        CHECK(mult1.is<mapnik::value_double>());
+        CHECK(mult2.is<mapnik::value_double>());
+        CHECK(mult3.is<mapnik::value_double>());
+        CHECK(mult4.is<mapnik::value_integer>());
+        CHECK(mult5.is<mapnik::value_integer>());
+        CHECK(mult6.is<mapnik::value_integer>());
+        // check commutative rules
+        CHECK(mult0 == mult1);
+        CHECK(mult2 == mult3);
+        CHECK(mult4 == mult5);
+        //
+        CHECK(mult6 == v0 * v0);
+
+        // div
+        auto div0 = v0 / v1; // result value_double
+        auto div1 = v1 / v0;
+        auto div2 = v1 / v2; // result value_double
+        auto div3 = v2 / v1;
+        auto div4 = v0 / v2; // result value_integer
+        auto div5 = v2 / v0;
+        auto div6 = v2 / v2; // result value_interger
+
+        CHECK(div0.is<mapnik::value_double>());
+        CHECK(div1.is<mapnik::value_double>());
+        CHECK(div2.is<mapnik::value_double>());
+        CHECK(div3.is<mapnik::value_double>());
+        CHECK(div4.is<mapnik::value_integer>());
+        CHECK(div5.is<mapnik::value_integer>());
+        CHECK(div6.is<mapnik::value_integer>());
+
+        CHECK(div0 == 1.0/div1);
+        CHECK(div2 == 1.0/div3);
+        CHECK(div4 == 1.0/div5);
+        CHECK(div6 == v0/v0);
+    }
+}
diff --git a/test/unit/datasource/csv.cpp b/test/unit/datasource/csv.cpp
index 72e8719..3f466ff 100644
--- a/test/unit/datasource/csv.cpp
+++ b/test/unit/datasource/csv.cpp
@@ -68,6 +68,10 @@ mapnik::datasource_ptr get_csv_ds(std::string const& file_name, bool strict = tr
     mapnik::parameters params;
     params["type"] = std::string("csv");
     params["file"] = file_name;
+    if (!base.empty())
+    {
+        params["base"] = base;
+    }
     params["strict"] = mapnik::value_bool(strict);
     auto ds = mapnik::datasource_cache::instance().create(params);
     // require a non-null pointer returned
@@ -80,7 +84,7 @@ int create_disk_index(std::string const& filename, bool silent = true)
     std::string cmd;
     if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
     {
-        cmd += std::string("export DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
+        cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " ";
     }
     cmd += "mapnik-index " + filename;
     if (silent)
@@ -288,7 +292,7 @@ TEST_CASE("csv") {
                     INFO(ret_posix);
                     CHECK(mapnik::util::exists(filepath + ".index"));
                 }
-                auto ds = get_csv_ds(filepath,true,base);
+                auto ds = get_csv_ds(filename,true,base);
                 CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
                 auto fields = ds->get_descriptor().get_descriptors();
                 require_field_names(fields, {"Precinct", "Phone", "Address", "City", "geo_longitude", "geo_latitude", "geo_accuracy"});
@@ -983,7 +987,7 @@ TEST_CASE("csv") {
             using ustring = mapnik::value_unicode_string;
             using row = std::pair<std::string, std::size_t>;
 
-            for (auto const &r : {
+            for (auto const& r : {
                     row{"test/data/csv/fails/needs_headers_two_lines.csv", 2},
                         row{"test/data/csv/fails/needs_headers_one_line.csv", 1},
                             row{"test/data/csv/fails/needs_headers_one_line_no_newline.csv", 1}})
diff --git a/test/unit/datasource/ds_test_util.hpp b/test/unit/datasource/ds_test_util.hpp
index e334318..bfa5b17 100644
--- a/test/unit/datasource/ds_test_util.hpp
+++ b/test/unit/datasource/ds_test_util.hpp
@@ -36,7 +36,7 @@ std::string vector_to_string(T const& vec)
     std::stringstream s;
     for (auto const& item : vec)
     {
-        s << item << "\n";
+        s << "  " << item << "\n";
     }
     return s.str();
 }
@@ -47,34 +47,41 @@ std::string vector_to_string(std::vector<mapnik::attribute_descriptor> const& ve
     std::stringstream s;
     for (auto const& item : vec)
     {
-        s << item.get_name() << "\n";
+        s << "  " << item.get_name() << "\n";
     }
     return s.str();
 }
 
+#define REQUIRE_FIELD_NAMES(fields, names) \
+    INFO("fields:\n" + vector_to_string(fields) + "names:\n" +  vector_to_string(names)); \
+    REQUIRE(fields.size() == names.size()); \
+    auto itr_a = fields.begin(); \
+    auto const end_a = fields.end(); \
+    auto itr_b = names.begin(); \
+    for (; itr_a != end_a; ++itr_a, ++itr_b) \
+    { \
+        CHECK(itr_a->get_name() == *itr_b); \
+    } \
+
 inline void require_field_names(std::vector<mapnik::attribute_descriptor> const &fields,
                          std::initializer_list<std::string> const &names)
 {
-    INFO("fields: " + vector_to_string(fields) + " names: " +  vector_to_string(names));
-    REQUIRE(fields.size() == names.size());
-    auto itr_a = fields.begin();
-    auto const end_a = fields.end();
-    auto itr_b = names.begin();
-    for (; itr_a != end_a; ++itr_a, ++itr_b)
-    {
-        CHECK(itr_a->get_name() == *itr_b);
-    }
+    REQUIRE_FIELD_NAMES(fields,names);
 }
 
+#define REQUIRE_FIELD_TYPES(fields, types) \
+    REQUIRE(fields.size() == types.size()); \
+    auto itr_a = fields.begin(); \
+    auto const end_a = fields.end(); \
+    auto itr_b = types.begin(); \
+    for (; itr_a != end_a; ++itr_a, ++itr_b) { \
+        CHECK(itr_a->get_type() == *itr_b); \
+    } \
+
 inline void require_field_types(std::vector<mapnik::attribute_descriptor> const &fields,
-                         std::initializer_list<mapnik::eAttributeType> const &types) {
-    REQUIRE(fields.size() == types.size());
-    auto itr_a = fields.begin();
-    auto const end_a = fields.end();
-    auto itr_b = types.begin();
-    for (; itr_a != end_a; ++itr_a, ++itr_b) {
-        CHECK(itr_a->get_type() == *itr_b);
-    }
+                         std::initializer_list<mapnik::eAttributeType> const &types)
+{
+    REQUIRE_FIELD_TYPES(fields, types);
 }
 
 inline mapnik::featureset_ptr all_features(mapnik::datasource_ptr ds) {
diff --git a/test/unit/datasource/geojson.cpp b/test/unit/datasource/geojson.cpp
index d27347c..e8c0a90 100644
--- a/test/unit/datasource/geojson.cpp
+++ b/test/unit/datasource/geojson.cpp
@@ -21,6 +21,7 @@
  *****************************************************************************/
 
 #include "catch.hpp"
+#include "ds_test_util.hpp"
 
 #include <mapnik/datasource.hpp>
 #include <mapnik/datasource_cache.hpp>
@@ -66,7 +67,7 @@ int create_disk_index(std::string const& filename, bool silent = true)
     std::string cmd;
     if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
     {
-        cmd += std::string("export DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
+        cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " ";
     }
     cmd += "mapnik-index " + filename;
     if (silent)
@@ -629,5 +630,48 @@ TEST_CASE("geojson") {
                 }
             }
         }
+
+        SECTION("GeoJSON descriptor returns all field names")
+        {
+            mapnik::parameters params;
+            params["type"] = "geojson";
+
+            std::string filename("./test/data/json/featurecollection-multipleprops.geojson");
+            params["file"] = filename;
+
+            // cleanup in the case of a failed previous run
+            if (mapnik::util::exists(filename + ".index"))
+            {
+                mapnik::util::remove(filename + ".index");
+            }
+
+            for (auto create_index : { true, false })
+            {
+                if (create_index)
+                {
+                    CHECK(!mapnik::util::exists(filename + ".index"));
+                    int ret = create_disk_index(filename);
+                    int ret_posix = (ret >> 8) & 0x000000ff;
+                    INFO(ret);
+                    INFO(ret_posix);
+                    CHECK(mapnik::util::exists(filename + ".index"));
+                }
+
+                for (auto cache_features : {true, false})
+                {
+                    params["cache_features"] = cache_features;
+                    auto ds = mapnik::datasource_cache::instance().create(params);
+                    REQUIRE(bool(ds));
+                    auto fields = ds->get_descriptor().get_descriptors();
+                    std::initializer_list<std::string> names = {"one", "two"};
+                    REQUIRE_FIELD_NAMES(fields, names);
+                }
+                // cleanup
+                if (create_index && mapnik::util::exists(filename + ".index"))
+                {
+                    mapnik::util::remove(filename + ".index");
+                }
+            }
+        }
     }
 }
diff --git a/test/unit/datasource/postgis.cpp b/test/unit/datasource/postgis.cpp
index 4aaa76f..2689952 100644
--- a/test/unit/datasource/postgis.cpp
+++ b/test/unit/datasource/postgis.cpp
@@ -29,24 +29,22 @@
 #include <mapnik/util/fs.hpp>
 
 /*
-
 Compile and run just this test:
-
 clang++ -o test-postgis -g -I./test/ test/unit/run.cpp test/unit/datasource/postgis.cpp `mapnik-config --all-flags` && ./test-postgis -d yes
-
 */
 
-namespace {
+#include <boost/optional/optional_io.hpp>
 
-int run(std::string const& command, bool silent = false)
+int run(std::string const& command, bool okay_to_fail = false)
 {
     std::string cmd;
     if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
     {
-        cmd += std::string("export DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
+        cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
     }
     cmd += command;
-    if (silent)
+    // silence output unless MAPNIK_TEST_DEBUG is defined
+    if (std::getenv("MAPNIK_TEST_DEBUG") == nullptr)
     {
 #ifndef _WINDOWS
         cmd += " 2>/dev/null";
@@ -54,41 +52,260 @@ int run(std::string const& command, bool silent = false)
         cmd += " 2> nul";
 #endif
     }
+    else
+    {
+        std::clog << "Running " << cmd << "\n";
+    }
     bool worked = (std::system(cmd.c_str()) == 0);
-    if (silent == true) return true;
+    if (okay_to_fail == true) return true;
     return worked;
 }
 
+std::string dbname("mapnik-tmp-postgis-test-db");
 
 TEST_CASE("postgis") {
 
     SECTION("Postgis data initialization")
     {
-        REQUIRE(run("dropdb mapnik-tmp-postgis-test-db",true));
-        REQUIRE(run("createdb -T template_postgis mapnik-tmp-postgis-test-db"));
-        std::stringstream cmd;
-        cmd << "psql -q mapnik-tmp-postgis-test-db -f ./test/data/sql/table1.sql";
-        REQUIRE(run(cmd.str()));
-    }
-
-    std::string datasource_plugin("./plugins/input/postgis.input");
-    if (mapnik::util::exists(datasource_plugin))
-    {
-        SECTION("Postgis plugin initialization")
-        {
-            mapnik::parameters params;
-            params["type"] = "postgis";
-            params["dbname"] = "mapnik-tmp-postgis-test-db";
-            params["table"] = "test";
-            auto ds = mapnik::datasource_cache::instance().create(params);
-            REQUIRE(ds != nullptr);
-            CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
-            auto fields = ds->get_descriptor().get_descriptors();
-            require_field_names(fields, {"gid"});
-            require_field_types(fields, {mapnik::Integer});
+        //don't add 'true' here, to get error message, when drop fails. If it works nothing is output
+        REQUIRE(run("dropdb --if-exists " + dbname));
+        REQUIRE(run("createdb -T template_postgis " + dbname));
+        //REQUIRE(run("createdb " + dbname));
+        // Breaks when raster support is missing (unfortunately this is common)
+        //REQUIRE(run("psql -c 'CREATE EXTENSION postgis;' " + dbname, true));
+        REQUIRE(run("psql -q -f ./test/data/sql/postgis-create-db-and-tables.sql " + dbname));
+    }
+
+    mapnik::parameters base_params;
+    base_params["type"] = "postgis";
+    base_params["dbname"] = dbname;
+
+    SECTION("Postgis should throw without 'table' parameter")
+    {
+        mapnik::parameters params(base_params);
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis should throw with 'max_async_connection' greater than 'max_size'")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test";
+        params["max_async_connection"] = "2";
+        params["max_size"] = "1";
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis should throw with invalid metadata query")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "does_not_exist";
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis should throw with invalid key field")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test_invalid_id";
+        params["key_field"] = "id";
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis should throw with multicolumn primary key")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test_invalid_multi_col_pk";
+        params["autodetect_key_field"] = "true";
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis should throw without geom column")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test_no_geom_col";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        CHECK_THROWS(all_features(ds));
+    }
+
+    SECTION("Postgis should throw with invalid credentials")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test";
+        params["user"] = "not_a_valid_user";
+        params["password"] = "not_a_valid_pwd";
+        CHECK_THROWS(mapnik::datasource_cache::instance().create(params));
+    }
+
+    SECTION("Postgis initialize dataset with persist_connection, schema, extent, geometry field, autodectect key field, simplify_geometries, row_limit")
+    {
+        mapnik::parameters params(base_params);
+        params["persist_connection"] = "false";
+        params["table"] = "public.test";
+        params["geometry_field"] = "geom";
+        params["autodetect_key_field"] = "true";
+        params["extent"] = "-1 -1, -1 2, 4 3, 3 -1, -1 -1";
+        params["simplify_geometries"] = "true";
+        params["row_limit"] = "1";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+    }
+
+    SECTION("Postgis dataset geometry type")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM test WHERE gid=1) as data";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        CHECK(ds->get_geometry_type() == mapnik::datasource_geometry_t::Point);
+    }
+
+    SECTION("Postgis query field names")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        REQUIRE(ds->type() == mapnik::datasource::datasource_t::Vector);
+        auto fields = ds->get_descriptor().get_descriptors();
+        require_field_names(fields, { "gid", "colbigint", "col_text", "col-char", "col+bool", "colnumeric", "colsmallint", "colfloat4", "colfloat8", "colcharacter" });
+        require_field_types(fields, { mapnik::Integer, mapnik::Integer, mapnik::String, mapnik::String, mapnik::Boolean, mapnik::Double, mapnik::Integer, mapnik::Double, mapnik::Double, mapnik::String });
+    }
+
+    SECTION("Postgis iterate features")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "test";
+        params["key_field"] = "gid";
+        params["max_async_connection"] = "2";
+        //params["cursor_size"] = "2";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+
+        auto featureset = ds->features_at_point(mapnik::coord2d(1, 1));
+        mapnik::feature_ptr feature;
+        while ((bool(feature = featureset->next()))) {
+            REQUIRE(feature->get(2).to_string() == feature->get("col_text").to_string());
+            REQUIRE(feature->get(4).to_bool() == feature->get("col+bool").to_bool());
+            REQUIRE(feature->get(5).to_double() == feature->get("colnumeric").to_double());
+            REQUIRE(feature->get(5).to_string() == feature->get("colnumeric").to_string());
         }
+
+        featureset = all_features(ds);
+        feature = featureset->next();
+        //deactivate char tests for now: not yet implemented.
+        //add at postgis_datasource.cpp:423
+        //case 18:    // char
+        //REQUIRE("A" == feature->get("col-char").to_string());
+        feature = featureset->next();
+        //REQUIRE("B" == feature->get("col-char").to_string());
+        feature = featureset->next();
+        REQUIRE(false == feature->get("col+bool").to_bool());
+    }
+
+    SECTION("Postgis cursorresultest")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM test) as data";
+        params["cursor_size"] = "2";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        auto featureset = all_features(ds);
+        CHECK(count_features(featureset) == 8);
+
+        featureset = all_features(ds);
+        mapnik::feature_ptr feature;
+        while (bool(feature = featureset->next())) {
+            CHECK(feature->size() == 10);
+        }
+
+        featureset = all_features(ds);
+        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
+        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Point);
+        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPoint);
+        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::LineString);
+        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiLineString);
+        require_geometry(featureset->next(), 1, mapnik::geometry::geometry_types::Polygon);
+        require_geometry(featureset->next(), 2, mapnik::geometry::geometry_types::MultiPolygon);
+        require_geometry(featureset->next(), 3, mapnik::geometry::geometry_types::GeometryCollection);
     }
-}
 
+    SECTION("Postgis bbox query")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM public.test) as data WHERE geom && !bbox!";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        mapnik::box2d<double> ext = ds->envelope();
+        CAPTURE(ext);
+        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+        REQUIRE(ext.minx() == -2);
+        REQUIRE(ext.miny() == -2);
+        REQUIRE(ext.maxx() == 5);
+        REQUIRE(ext.maxy() == 4);
+    }
 
+    SECTION("Postgis query extent: full dataset")
+    {
+        //include schema to increase coverage
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM public.test) as data";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        mapnik::box2d<double> ext = ds->envelope();
+        CAPTURE(ext);
+        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+        REQUIRE(ext.minx() == -2);
+        REQUIRE(ext.miny() == -2);
+        REQUIRE(ext.maxx() == 5);
+        REQUIRE(ext.maxy() == 4);
+    }
+/* deactivated for merging: still investigating a proper fix
+    SECTION("Postgis query extent from subquery")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM test where gid=4) as data";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        mapnik::box2d<double> ext = ds->envelope();
+        CAPTURE(ext);
+        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+        REQUIRE(ext.minx() == 0);
+        REQUIRE(ext.miny() == 0);
+        REQUIRE(ext.maxx() == 1);
+        REQUIRE(ext.maxy() == 2);
+    }
+*/
+    SECTION("Postgis query extent: from subquery with 'extent_from_subquery=true'")
+    {
+        mapnik::parameters params(base_params);
+        params["table"] = "(SELECT * FROM test where gid=4) as data";
+        params["extent_from_subquery"] = "true";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        mapnik::box2d<double> ext = ds->envelope();
+        CAPTURE(ext);
+        INFO(std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+        REQUIRE(ext.minx() == 0);
+        REQUIRE(ext.miny() == 0);
+        REQUIRE(ext.maxx() == 1);
+        REQUIRE(ext.maxy() == 2);
+    }
+/* deactivated for merging: still investigating a proper fix
+    SECTION("Postgis query extent: subset with 'extent_from_subquery=true' and 'scale_denominator'")
+    {
+        mapnik::parameters params(base_params);
+        // !!!! postgis-vt-util::z() returns 'null' when 'scale_denominator > 600000000'
+        // https://github.com/mapbox/postgis-vt-util/blob/559f073877696a6bfea41baf3e1065f9cf4d18d1/postgis-vt-util.sql#L615-L617
+        params["table"] = "(SELECT * FROM test where gid=4 AND z(!scale_denominator!) BETWEEN 0 AND 22) as data";
+        params["extent_from_subquery"] = "true";
+        auto ds = mapnik::datasource_cache::instance().create(params);
+        REQUIRE(ds != nullptr);
+        mapnik::box2d<double> ext = ds->envelope();
+        CAPTURE(ext);
+        INFO("" << std::setprecision(6) << std::fixed << ext.minx() << "/" << ext.miny() << " " << ext.maxx() << "/" << ext.maxy());
+        REQUIRE(ext.minx() == 0);
+        REQUIRE(ext.miny() == 0);
+        REQUIRE(ext.maxx() == 1);
+        REQUIRE(ext.maxy() == 2);
+    }
+*/
 }
\ No newline at end of file
diff --git a/test/unit/datasource/shapeindex.cpp b/test/unit/datasource/shapeindex.cpp
index e169678..51f47ac 100644
--- a/test/unit/datasource/shapeindex.cpp
+++ b/test/unit/datasource/shapeindex.cpp
@@ -24,8 +24,10 @@
 
 #include <mapnik/datasource.hpp>
 #include <mapnik/datasource_cache.hpp>
+#include <mapnik/mapped_memory_cache.hpp>
 #include <mapnik/util/fs.hpp>
 #include <cstdlib>
+#include <fstream>
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/algorithm/string.hpp>
@@ -35,10 +37,14 @@ namespace {
 
 std::size_t count_shapefile_features(std::string const& filename)
 {
+#if defined(MAPNIK_MEMORY_MAPPED_FILE)
+    mapnik::mapped_memory_cache::instance().clear();
+#endif
     mapnik::parameters params;
     params["type"] = "shape";
     params["file"] = filename;
     auto ds = mapnik::datasource_cache::instance().create(params);
+    REQUIRE(ds != nullptr);
     CHECK(ds->type() == mapnik::datasource::datasource_t::Vector);
     auto fields = ds->get_descriptor().get_descriptors();
     mapnik::query query(ds->envelope());
@@ -47,6 +53,7 @@ std::size_t count_shapefile_features(std::string const& filename)
         query.add_property_name(field.get_name());
     }
     auto features = ds->features(query);
+    REQUIRE(features != nullptr);
 
     std::size_t feature_count = 0;
     auto feature = features->next();
@@ -55,17 +62,21 @@ std::size_t count_shapefile_features(std::string const& filename)
         ++feature_count;
         feature = features->next();
     }
+
     return feature_count;
 }
 
-int create_shapefile_index(std::string const& filename, bool silent = true)
+int create_shapefile_index(std::string const& filename, bool index_parts, bool silent = true)
 {
     std::string cmd;
     if (std::getenv("DYLD_LIBRARY_PATH") != nullptr)
     {
-        cmd += std::string("export DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " && ";
+        cmd += std::string("DYLD_LIBRARY_PATH=") + std::getenv("DYLD_LIBRARY_PATH") + " ";
     }
-    cmd += "shapeindex " + filename;
+
+    cmd += "shapeindex ";
+    if (index_parts) cmd+= "--index-parts ";
+    cmd += filename;
     if (silent)
     {
 #ifndef _WINDOWS
@@ -79,39 +90,92 @@ int create_shapefile_index(std::string const& filename, bool silent = true)
 
 }
 
+TEST_CASE("invalid shapeindex")
+{
+    std::string shape_plugin("./plugins/input/shape.input");
+    if (mapnik::util::exists(shape_plugin))
+    {
+        SECTION("Invalid index")
+        {
+            for (auto val : {std::make_tuple(true, std::string("mapnik-invalid-index.................")), // invalid header
+                           std::make_tuple(false, std::string("mapnik-index................."))})       // valid header + invalid index
+            {
+                std::string path = "test/data/shp/boundaries.shp";
+                std::string index_path = path.substr(0, path.rfind(".")) + ".index";
+                // remove *.index if present
+                if (mapnik::util::exists(index_path))
+                {
+                    mapnik::util::remove(index_path);
+                }
+                // count features
+
+                std::size_t feature_count = count_shapefile_features(path);
+
+                // create index
+                std::ofstream index(index_path.c_str(), std::ios::binary);
+                index.write(std::get<1>(val).c_str(), std::get<1>(val).size());
+                index.close();
+
+                // count features
+                std::size_t feature_count_indexed = count_shapefile_features(path);
+                if (std::get<0>(val)) // fallback to un-indexed access
+                {
+                    // ensure number of features are the same
+                    CHECK(feature_count == feature_count_indexed);
+                }
+                else // the header is valid but index file itself is not - expect datasource to fail and return 0 features.
+                {
+                    CHECK(feature_count_indexed == 0);
+                }
+                // remove *.index if present
+                if (mapnik::util::exists(index_path))
+                {
+                    mapnik::util::remove(index_path);
+                }
+            }
+        }
+    }
+}
+
 TEST_CASE("shapeindex")
 {
     std::string shape_plugin("./plugins/input/shape.input");
     if (mapnik::util::exists(shape_plugin))
     {
-        SECTION("Shapefile index")
+        SECTION("Index")
         {
             for (auto const& path : mapnik::util::list_directory("test/data/shp/"))
             {
                 if (boost::iends_with(path,".shp"))
                 {
-                    std::string index_path = path.substr(0, path.rfind(".")) + ".index";
-                    // remove *.index if present
-                    if (mapnik::util::exists(index_path))
-                    {
-                        mapnik::util::remove(index_path);
-                    }
-                    // count features
-                    std::size_t feature_count = count_shapefile_features(path);
-                    // create *.index
-                    create_shapefile_index(path);
-                    if (feature_count == 0)
+                    for (bool index_parts : {false, true} )
                     {
-                        REQUIRE(!mapnik::util::exists(index_path)); // index won't be created if there's no features
-                    }
-                    // count features
-                    std::size_t feature_count_indexed = count_shapefile_features(path);
-                    // ensure number of features are the same
-                    REQUIRE(feature_count == feature_count_indexed);
-                    // remove *.index if present
-                    if (mapnik::util::exists(index_path))
-                    {
-                        mapnik::util::remove(index_path);
+                        CAPTURE(path);
+                        CAPTURE(index_parts);
+
+                        std::string index_path = path.substr(0, path.rfind(".")) + ".index";
+                        // remove *.index if present
+                        if (mapnik::util::exists(index_path))
+                        {
+                            mapnik::util::remove(index_path);
+                        }
+                        // count features
+                        std::size_t feature_count = count_shapefile_features(path);
+                        // create *.index
+                        REQUIRE(create_shapefile_index(path, index_parts) == 0);
+                        if (feature_count == 0)
+                        {
+                            REQUIRE(!mapnik::util::exists(index_path)); // index won't be created if there's no features
+                        }
+                        // count features
+                        std::size_t feature_count_indexed = count_shapefile_features(path);
+                        // ensure number of features are the same
+                        REQUIRE(feature_count == feature_count_indexed);
+                        // remove *.index if present
+                        if (mapnik::util::exists(index_path))
+                        {
+                            mapnik::util::remove(index_path);
+                        }
                     }
                 }
             }
diff --git a/test/unit/geometry/geometry.cpp b/test/unit/geometry/geometry.cpp
index d42f0ab..556f6ca 100644
--- a/test/unit/geometry/geometry.cpp
+++ b/test/unit/geometry/geometry.cpp
@@ -35,4 +35,17 @@ SECTION("json point reversed") {
     REQUIRE( point.y == 10 );
 }
 
+SECTION("json point reversed + extra attributes") {
+    mapnik::util::file input("./test/data/json/point3.json");
+    REQUIRE( input.open() );
+    mapnik::geometry::geometry<double> geom;
+    REQUIRE( input.data() );
+    std::string json_string(input.data().get(), input.size());
+    REQUIRE( mapnik::json::from_geojson(json_string,geom) );
+    REQUIRE( geom.is<mapnik::geometry::point<double> >() );
+    auto const& point = mapnik::util::get<mapnik::geometry::point<double> >(geom);
+    REQUIRE( point.x == 30 );
+    REQUIRE( point.y == 10 );
+}
+
 }
diff --git a/test/unit/geometry/geometry_equal.hpp b/test/unit/geometry/geometry_equal.hpp
index cb4a30d..3907149 100644
--- a/test/unit/geometry/geometry_equal.hpp
+++ b/test/unit/geometry/geometry_equal.hpp
@@ -89,26 +89,26 @@ void assert_g_equal(geometry<T> const& g1, geometry<T> const& g2);
 struct geometry_equal_visitor
 {
     template <typename T1, typename T2>
-    void operator() (T1 const&, T2 const&)
+    void operator() (T1 const&, T2 const&) const
     {
         // comparing two different types!
         REQUIRE(false);
     }
 
-    void operator() (geometry_empty const&, geometry_empty const&)
+    void operator() (geometry_empty const&, geometry_empty const&) const
     {
         REQUIRE(true);
     }
 
     template <typename T>
-    void operator() (point<T> const& p1, point<T> const& p2)
+    void operator() (point<T> const& p1, point<T> const& p2) const
     {
         REQUIRE(p1.x == Approx(p2.x));
         REQUIRE(p1.y == Approx(p2.y));
     }
 
     template <typename T>
-    void operator() (line_string<T> const& ls1, line_string<T> const& ls2)
+    void operator() (line_string<T> const& ls1, line_string<T> const& ls2) const
     {
         if (ls1.size() != ls2.size())
         {
@@ -123,7 +123,7 @@ struct geometry_equal_visitor
     }
 
     template <typename T>
-    void operator() (polygon<T> const& p1, polygon<T> const& p2)
+    void operator() (polygon<T> const& p1, polygon<T> const& p2) const
     {
         (*this)(static_cast<line_string<T> const&>(p1.exterior_ring), static_cast<line_string<T> const&>(p2.exterior_ring));
 
@@ -139,13 +139,13 @@ struct geometry_equal_visitor
     }
 
     template <typename T>
-    void operator() (multi_point<T> const& mp1, multi_point<T> const& mp2)
+    void operator() (multi_point<T> const& mp1, multi_point<T> const& mp2) const
     {
         (*this)(static_cast<line_string<T> const&>(mp1), static_cast<line_string<T> const&>(mp2));
     }
 
     template <typename T>
-    void operator() (multi_line_string<T> const& mls1, multi_line_string<T> const& mls2)
+    void operator() (multi_line_string<T> const& mls1, multi_line_string<T> const& mls2) const
     {
         if (mls1.size() != mls2.size())
         {
@@ -159,7 +159,7 @@ struct geometry_equal_visitor
     }
 
     template <typename T>
-    void operator() (multi_polygon<T> const& mpoly1, multi_polygon<T> const& mpoly2)
+    void operator() (multi_polygon<T> const& mpoly1, multi_polygon<T> const& mpoly2) const
     {
         if (mpoly1.size() != mpoly2.size())
         {
@@ -173,7 +173,7 @@ struct geometry_equal_visitor
     }
 
     template <typename T>
-    void operator() (mapnik::util::recursive_wrapper<geometry_collection<T> > const& c1_, mapnik::util::recursive_wrapper<geometry_collection<T> > const& c2_)
+    void operator() (mapnik::util::recursive_wrapper<geometry_collection<T> > const& c1_, mapnik::util::recursive_wrapper<geometry_collection<T> > const& c2_) const
     {
         geometry_collection<T> const& c1 = static_cast<geometry_collection<T> const&>(c1_);
         geometry_collection<T> const& c2 = static_cast<geometry_collection<T> const&>(c2_);
@@ -189,7 +189,7 @@ struct geometry_equal_visitor
     }
 
     template <typename T>
-    void operator() (geometry_collection<T> const& c1, geometry_collection<T> const& c2)
+    void operator() (geometry_collection<T> const& c1, geometry_collection<T> const& c2) const
     {
         if (c1.size() != c2.size())
         {
diff --git a/test/unit/geometry/geometry_is_simple.cpp b/test/unit/geometry/geometry_is_simple.cpp
index dbed04a..1170350 100644
--- a/test/unit/geometry/geometry_is_simple.cpp
+++ b/test/unit/geometry/geometry_is_simple.cpp
@@ -163,6 +163,36 @@ SECTION("polygon 3 repeated points") {
     CHECK( !mapnik::geometry::is_simple(poly) );
 }
 
+#if BOOST_VERSION >= 106000
+
+SECTION("polygon that is empty") {
+    mapnik::geometry::polygon<double> poly;
+    CHECK( !mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon that has empty exterior ring") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    poly.set_exterior_ring(std::move(ring));
+    CHECK( !mapnik::geometry::is_simple(poly) );
+}
+
+SECTION("polygon that has empty interior ring") {
+    mapnik::geometry::polygon<double> poly;
+    mapnik::geometry::linear_ring<double> ring;
+    ring.add_coord(0,0);    
+    ring.add_coord(1,0);
+    ring.add_coord(1,1);
+    ring.add_coord(0,1);
+    ring.add_coord(0,0);
+    poly.set_exterior_ring(std::move(ring));
+    mapnik::geometry::linear_ring<double> ring2;
+    poly.add_hole(std::move(ring2));
+    CHECK( !mapnik::geometry::is_simple(poly) );
+}
+
+#else // BOOST_VERSION >= 1.60
+
 SECTION("polygon that is empty") {
     mapnik::geometry::polygon<double> poly;
     CHECK( mapnik::geometry::is_simple(poly) );
@@ -189,6 +219,8 @@ SECTION("polygon that has empty interior ring") {
     CHECK( mapnik::geometry::is_simple(poly) );
 }
 
+#endif // BOOST_VERSION >= 1.60
+
 // A polygon with a spike can still be simple
 SECTION("polygon with spike") {
     mapnik::geometry::polygon<double> poly;
diff --git a/test/unit/geometry/geometry_is_valid.cpp b/test/unit/geometry/geometry_is_valid.cpp
index 6d8b922..c36549b 100644
--- a/test/unit/geometry/geometry_is_valid.cpp
+++ b/test/unit/geometry/geometry_is_valid.cpp
@@ -43,6 +43,8 @@ SECTION("point -- geometry object") {
     CHECK( failure == boost::geometry::no_failure );
 }
 
+#if BOOST_VERSION < 106000
+
 SECTION("point unitialized") {
     mapnik::geometry::point<double> pt2;
     CHECK( mapnik::geometry::is_valid(pt2) );
@@ -53,6 +55,37 @@ SECTION("point unitialized") {
     CHECK( mapnik::geometry::is_valid(pt2, failure2) );
     CHECK( failure2 == boost::geometry::no_failure );
 }
+#endif
+
+#if BOOST_VERSION >= 106000
+
+SECTION("point NaN") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::quiet_NaN(),std::numeric_limits<double>::quiet_NaN());
+    CHECK( std::isnan(pt.x) );
+    CHECK( std::isnan(pt.y) );
+    CHECK( !mapnik::geometry::is_valid(pt) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(pt, message) );
+    CHECK( message == "Geometry has point(s) with invalid coordinate(s)");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(pt, failure) );
+    CHECK( failure == boost::geometry::failure_invalid_coordinate );
+}
+
+SECTION("point Infinity") {
+    mapnik::geometry::point<double> pt(std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity());
+    CHECK( std::isinf(pt.x) );
+    CHECK( std::isinf(pt.y) );
+    CHECK( !mapnik::geometry::is_valid(pt) );
+    std::string message;
+    CHECK( !mapnik::geometry::is_valid(pt, message) );
+    CHECK( message == "Geometry has point(s) with invalid coordinate(s)");
+    boost::geometry::validity_failure_type failure;
+    CHECK( !mapnik::geometry::is_valid(pt, failure) );
+    CHECK( failure == boost::geometry::failure_invalid_coordinate );
+}
+
+#else // BOOST_VERSION >= 1.60
 
 // This is funky that boost geometry is_valid does not check for NAN when dealing with a point
 // this test is here in case the logic ever changes
@@ -86,9 +119,11 @@ SECTION("point Infinity") {
     CHECK( failure == boost::geometry::no_failure );
 }
 
+#endif // BOOST_VERSION >= 1.60
+
 SECTION("multi point") {
     mapnik::geometry::multi_point<double> mpt;
-    mpt.add_coord(0,0);    
+    mpt.add_coord(0,0);
     mpt.add_coord(1,1);
     CHECK( mapnik::geometry::is_valid(mpt) );
     std::string message;
@@ -113,7 +148,7 @@ SECTION("multi point empty") {
 
 SECTION("line_string") {
     mapnik::geometry::line_string<double> line;
-    line.add_coord(0,0);    
+    line.add_coord(0,0);
     line.add_coord(1,1);
     CHECK( mapnik::geometry::is_valid(line) );
     std::string message;
@@ -127,7 +162,7 @@ SECTION("line_string") {
 // This shouldn't fail -- test added in case logic ever changes
 SECTION("line_string repeated points") {
     mapnik::geometry::line_string<double> line;
-    line.add_coord(0,0);    
+    line.add_coord(0,0);
     line.add_coord(1,1);
     line.add_coord(1,1);
     line.add_coord(2,2);
@@ -153,10 +188,10 @@ SECTION("line_string empty") {
 
 SECTION("multi_line_string") {
     mapnik::geometry::line_string<double> line1;
-    line1.add_coord(0,0);    
+    line1.add_coord(0,0);
     line1.add_coord(1,1);
     mapnik::geometry::line_string<double> line2;
-    line2.add_coord(0,1);    
+    line2.add_coord(0,1);
     line2.add_coord(1,2);
     mapnik::geometry::multi_line_string<double> lines;
     lines.emplace_back(line1);
@@ -184,7 +219,7 @@ SECTION("multi_line_string empty") {
 SECTION("polygon") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(1,0);
     ring.add_coord(1,1);
     ring.add_coord(0,1);
@@ -202,7 +237,7 @@ SECTION("polygon") {
 SECTION("polygon invalid winding order") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(0,1);
     ring.add_coord(1,1);
     ring.add_coord(1,0);
@@ -221,7 +256,7 @@ SECTION("polygon invalid winding order") {
 SECTION("polygon 2 repeated points") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(1,0);
     ring.add_coord(1,1);
     ring.add_coord(1,1);
@@ -240,7 +275,7 @@ SECTION("polygon 2 repeated points") {
 SECTION("polygon 3 repeated points") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(1,0);
     ring.add_coord(1,1);
     ring.add_coord(1,1);
@@ -271,7 +306,7 @@ SECTION("polygon that is empty") {
 SECTION("polygon with spike") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(1,0);
     ring.add_coord(1,1);
     ring.add_coord(2,2);
@@ -291,14 +326,14 @@ SECTION("polygon with spike") {
 SECTION("polygon with hole") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(3,0);
     ring.add_coord(3,3);
     ring.add_coord(0,3);
     ring.add_coord(0,0);
     poly.set_exterior_ring(std::move(ring));
     mapnik::geometry::linear_ring<double> hole;
-    hole.add_coord(1,1);    
+    hole.add_coord(1,1);
     hole.add_coord(1,2);
     hole.add_coord(2,2);
     hole.add_coord(2,1);
@@ -316,7 +351,7 @@ SECTION("polygon with hole") {
 SECTION("polygon with empty hole") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(3,0);
     ring.add_coord(3,3);
     ring.add_coord(0,3);
@@ -337,14 +372,14 @@ SECTION("polygon with empty hole") {
 SECTION("polygon with hole with invalid winding order") {
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(3,0);
     ring.add_coord(3,3);
     ring.add_coord(0,3);
     ring.add_coord(0,0);
     poly.set_exterior_ring(std::move(ring));
     mapnik::geometry::linear_ring<double> hole;
-    hole.add_coord(1,1);    
+    hole.add_coord(1,1);
     hole.add_coord(2,1);
     hole.add_coord(2,2);
     hole.add_coord(1,2);
@@ -363,7 +398,7 @@ SECTION("multi polygon") {
     mapnik::geometry::multi_polygon<double> mp;
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(1,0);
     ring.add_coord(1,1);
     ring.add_coord(0,1);
@@ -371,7 +406,7 @@ SECTION("multi polygon") {
     poly.set_exterior_ring(std::move(ring));
     mapnik::geometry::polygon<double> poly2;
     mapnik::geometry::linear_ring<double> ring2;
-    ring2.add_coord(0,0);    
+    ring2.add_coord(0,0);
     ring2.add_coord(-1,0);
     ring2.add_coord(-1,-1);
     ring2.add_coord(0,-1);
@@ -392,14 +427,14 @@ SECTION("multi polygon with hole") {
     mapnik::geometry::multi_polygon<double> mp;
     mapnik::geometry::polygon<double> poly;
     mapnik::geometry::linear_ring<double> ring;
-    ring.add_coord(0,0);    
+    ring.add_coord(0,0);
     ring.add_coord(3,0);
     ring.add_coord(3,3);
     ring.add_coord(0,3);
     ring.add_coord(0,0);
     poly.set_exterior_ring(std::move(ring));
     mapnik::geometry::linear_ring<double> hole;
-    hole.add_coord(1,1);    
+    hole.add_coord(1,1);
     hole.add_coord(1,2);
     hole.add_coord(2,2);
     hole.add_coord(2,1);
@@ -407,14 +442,14 @@ SECTION("multi polygon with hole") {
     poly.add_hole(std::move(hole));
     mapnik::geometry::polygon<double> poly2;
     mapnik::geometry::linear_ring<double> ring2;
-    ring2.add_coord(0,0);    
+    ring2.add_coord(0,0);
     ring2.add_coord(-3,0);
     ring2.add_coord(-3,-3);
     ring2.add_coord(0,-3);
     ring2.add_coord(0,0);
     poly2.set_exterior_ring(std::move(ring2));
     mapnik::geometry::linear_ring<double> hole2;
-    hole2.add_coord(-1,-1);    
+    hole2.add_coord(-1,-1);
     hole2.add_coord(-1,-2);
     hole2.add_coord(-2,-2);
     hole2.add_coord(-2,-1);
diff --git a/test/unit/imaging/image_filter.cpp b/test/unit/imaging/image_filter.cpp
index 8d41ccb..2b43c4d 100644
--- a/test/unit/imaging/image_filter.cpp
+++ b/test/unit/imaging/image_filter.cpp
@@ -6,29 +6,30 @@
 #include <mapnik/color.hpp>
 #include <mapnik/image_filter.hpp>
 #include <mapnik/image_util.hpp>
-#include <mapnik/image_filter_grammar.hpp>
-#include <mapnik/image_filter_grammar_impl.hpp>
-#include <mapnik/css_color_grammar_impl.hpp>
+#include <mapnik/image_filter_types.hpp>
+// stl
+#include <sstream>
+#include <array>
 
 TEST_CASE("image filter") {
 
 SECTION("test bad filter input") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("red"));
-    
+
     REQUIRE_THROWS( mapnik::filter::filter_image(im, "foo,asdfasdf()"); );
     REQUIRE_THROWS( mapnik::filter::filter_image(im, "colorize-alpha("); );
     REQUIRE_THROWS( mapnik::filter::filter_image(im, "color-to-alpha(blue"); );
     REQUIRE_THROWS( mapnik::filter::filter_image(im, "color-to-alpha(,blue)"); );
     REQUIRE_THROWS( mapnik::filter::filter_image(im, "colorize-alpha()"); );
 
-    REQUIRE_THROWS( 
+    REQUIRE_THROWS(
         mapnik::image_rgba8 const& im2 = im;
         mapnik::image_rgba8 new_im = mapnik::filter::filter_image(im2, "foo");
     );
-    
+
     CHECK(im(0,0) == 0xffff0000);
     CHECK(im(0,1) == 0xffff0000);
     CHECK(im(0,2) == 0xffff0000);
@@ -38,11 +39,11 @@ SECTION("test bad filter input") {
     CHECK(im(2,0) == 0xffff0000);
     CHECK(im(2,1) == 0xffff0000);
     CHECK(im(2,2) == 0xffff0000);
-    
+
 } // END SECTION
 
 SECTION("test blur") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("red"));
@@ -58,11 +59,11 @@ SECTION("test blur") {
     CHECK(im(2,0) == 0xffc60038);
     CHECK(im(2,1) == 0xffe2001c);
     CHECK(im(2,2) == 0xffc60038);
-    
+
 } // END SECTION
 
 SECTION("test blur constant") {
-    
+
     mapnik::image_rgba8 im_orig(3,3);
     mapnik::fill(im_orig,mapnik::color("blue"));
     mapnik::set_pixel(im_orig, 1, 1, mapnik::color("red"));
@@ -79,11 +80,11 @@ SECTION("test blur constant") {
     CHECK(im(2,0) == 0xffc60038);
     CHECK(im(2,1) == 0xffe2001c);
     CHECK(im(2,2) == 0xffc60038);
-    
+
 } // END SECTION
 
 SECTION("test gray") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("red"));
@@ -99,11 +100,11 @@ SECTION("test gray") {
     CHECK(im(2,0) == 0xff1c1c1c);
     CHECK(im(2,1) == 0xff1c1c1c);
     CHECK(im(2,2) == 0xff1c1c1c);
-    
+
 } // END SECTION
 
 SECTION("test agg stack blur") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("red"));
@@ -123,7 +124,7 @@ SECTION("test agg stack blur") {
 } // END SECTION
 
 SECTION("test scale-hsla 1") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("red"));
@@ -139,7 +140,7 @@ SECTION("test scale-hsla 1") {
     CHECK(im(2,0) == 0x80004000);
     CHECK(im(2,1) == 0x80004000);
     CHECK(im(2,2) == 0x80004000);
-    
+
 } // END SECTION
 
 SECTION("test scale-hsla 2") {
@@ -172,7 +173,7 @@ SECTION("test scale-hsla 2") {
 } // END SECTION
 
 SECTION("test emboss") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("white"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("orange"));
@@ -188,11 +189,11 @@ SECTION("test emboss") {
     CHECK(im(2,0) == 0xffffffff);
     CHECK(im(2,1) == 0xffffffff);
     CHECK(im(2,2) == 0xffffffff);
-    
+
 } // END SECTION
 
 SECTION("test sharpen") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -208,11 +209,11 @@ SECTION("test sharpen") {
     CHECK(im(2,0) == 0xffff0000);
     CHECK(im(2,1) == 0xffff0000);
     CHECK(im(2,2) == 0xffff0000);
-    
+
 } // END SECTION
 
 SECTION("test edge detect") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -228,11 +229,11 @@ SECTION("test edge detect") {
     CHECK(im(2,0) == 0xff000000);
     CHECK(im(2,1) == 0xff008080);
     CHECK(im(2,2) == 0xff000000);
-    
+
 } // END SECTION
 
 SECTION("test sobel") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -248,11 +249,11 @@ SECTION("test sobel") {
     CHECK(im(2,0) == 0xfffeffff);
     CHECK(im(2,1) == 0xfffeffff);
     CHECK(im(2,2) == 0xfffeffff);
-    
+
 } // END SECTION
 
 SECTION("test x-gradient") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -268,11 +269,11 @@ SECTION("test x-gradient") {
     CHECK(im(2,0) == 0xff808080);
     CHECK(im(2,1) == 0xff41c0c0);
     CHECK(im(2,2) == 0xff808080);
-    
+
 } // END SECTION
 
 SECTION("test y-gradient") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -288,11 +289,11 @@ SECTION("test y-gradient") {
     CHECK(im(2,0) == 0xff808080);
     CHECK(im(2,1) == 0xff808080);
     CHECK(im(2,2) == 0xff808080);
-    
+
 } // END SECTION
 
 SECTION("test invert") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -308,11 +309,11 @@ SECTION("test invert") {
     CHECK(im(2,0) == 0xff00ffff);
     CHECK(im(2,1) == 0xff00ffff);
     CHECK(im(2,2) == 0xff00ffff);
-    
+
 } // END SECTION
 
 SECTION("test colorize-alpha - one color") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -328,11 +329,11 @@ SECTION("test colorize-alpha - one color") {
     CHECK(im(2,0) == 0xffff0000);
     CHECK(im(2,1) == 0xffff0000);
     CHECK(im(2,2) == 0xffff0000);
-    
+
 } // END SECTION
 
 SECTION("test colorize-alpha - two color") {
-    
+
     mapnik::image_rgba8 im(3,3);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 1, 1, mapnik::color("gray"));
@@ -348,7 +349,7 @@ SECTION("test colorize-alpha - two color") {
     CHECK(im(2,0) == 0xfffd0000);
     CHECK(im(2,1) == 0xfffd0000);
     CHECK(im(2,2) == 0xfffd0000);
-    
+
 } // END SECTION
 
 SECTION("test colorize-alpha - one color with transparency") {
@@ -391,15 +392,53 @@ SECTION("test colorize-alpha - two color with transparency") {
 
 } // END SECTION
 
+SECTION("test parsing image-filters") {
+
+    std::string str = ""; // empty string
+    std::vector<mapnik::filter::filter_type> filters;
+    CHECK(parse_image_filters(str, filters));
+    CHECK(filters.size() == 0);
+
+    std::array<std::string,17> expected = {{ "emboss",
+                                             "emboss",
+                                             "blur",
+                                             "gray",
+                                             "edge-detect",
+                                             "sobel",
+                                             "sharpen",
+                                             "x-gradient",
+                                             "y-gradient",
+                                             "invert",
+                                             "color-blind-protanope",
+                                             "color-blind-deuteranope",
+                                             "color-blind-tritanope",
+                                             "agg-stack-blur(1,1)",
+                                             "agg-stack-blur(1,1)",
+                                             "agg-stack-blur(2,2)",
+                                             "agg-stack-blur(2,3)"}};
+
+    str += "emboss emboss() blur,gray ,edge-detect, sobel , , sharpen,,,x-gradient y-gradientinvert";
+    str += "color-blind-protanope color-blind-deuteranope color-blind-tritanope agg-stack-blur,agg-stack-blur(),agg-stack-blur(2),agg-stack-blur(2,3)" ;
+    CHECK(parse_image_filters(str, filters));
+    CHECK(filters.size() == expected.size());
+    std::size_t count = 0;
+    for (auto const& filter : filters)
+    {
+        std::stringstream ss;
+        ss << filter;
+        CHECK (expected[count++] == ss.str());
+    }
+}
+
 SECTION("test colorize-alpha - parsing correct input") {
 
-    mapnik::image_filter_grammar<std::string::const_iterator, std::vector<mapnik::filter::filter_type>> filter_grammar;
-    boost::spirit::qi::ascii::space_type space;
-    std::vector<mapnik::filter::filter_type> f;
     std::string s("colorize-alpha(#0000ff 0%, #00ff00 100%)");
-    CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), filter_grammar, space, f) );
+    std::vector<mapnik::filter::filter_type> f;
+    REQUIRE(parse_image_filters(s, f));
     mapnik::filter::colorize_alpha const & ca = mapnik::util::get<mapnik::filter::colorize_alpha>(f.front());
+    CHECK(ca.size() == 2);
 
+    CHECKED_IF(ca.size() > 0)
     {
         mapnik::filter::color_stop const & s2 = ca[0];
         CHECK( s2.color.alpha() == 0xff );
@@ -409,6 +448,7 @@ SECTION("test colorize-alpha - parsing correct input") {
         CHECK( s2.offset == 0.0 );
     }
 
+    CHECKED_IF(ca.size() > 1)
     {
         mapnik::filter::color_stop const & s2 = ca[1];
         CHECK( s2.color.alpha() == 0xff );
@@ -422,20 +462,15 @@ SECTION("test colorize-alpha - parsing correct input") {
 
 SECTION("test colorize-alpha - parsing incorrect input") {
 
-    mapnik::image_filter_grammar<std::string::const_iterator, std::vector<mapnik::filter::filter_type>> filter_grammar;
-    boost::spirit::qi::ascii::space_type space;
     std::string s("colorize-alpha(#0000ff 0%, #00ff00 00 100%)");
-    std::string::const_iterator itr = s.cbegin();
-    std::string::const_iterator end = s.cend();
     std::vector<mapnik::filter::filter_type> f;
-    CHECK( boost::spirit::qi::phrase_parse(s.cbegin(), s.cend(), filter_grammar, space, f) );
+    CHECK(!parse_image_filters(s, f));
     CHECK( f.empty() );
-    CHECK( itr != end );
 
 } // END SECTION
 
 SECTION("test color-blind-protanope") {
-    
+
     mapnik::image_rgba8 im(2,2);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 0, 1, mapnik::color("green"));
@@ -448,11 +483,11 @@ SECTION("test color-blind-protanope") {
     CHECK(im(0,1) == 0xff006e7c);
     CHECK(im(1,0) == 0xffd9f6ff);
     CHECK(im(1,1) == 0xff1d7e8e);
-    
+
 } // END SECTION
 
 SECTION("test color-blind-deuteranope") {
-    
+
     mapnik::image_rgba8 im(2,2);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 0, 1, mapnik::color("green"));
@@ -465,11 +500,11 @@ SECTION("test color-blind-deuteranope") {
     CHECK(im(0,1) == 0xff1c688b);
     CHECK(im(1,0) == 0xffe9f5ff);
     CHECK(im(1,1) == 0xff0077a0);
-    
+
 } // END SECTION
 
 SECTION("test color-blind-tritanope") {
-    
+
     mapnik::image_rgba8 im(2,2);
     mapnik::fill(im,mapnik::color("blue"));
     mapnik::set_pixel(im, 0, 1, mapnik::color("green"));
@@ -482,8 +517,7 @@ SECTION("test color-blind-tritanope") {
     CHECK(im(0,1) == 0xff80763a);
     CHECK(im(1,0) == 0xfff8f3ff);
     CHECK(im(1,1) == 0xff0017fd);
-    
+
 } // END SECTION
 
 } // END TEST CASE
-
diff --git a/test/unit/imaging/image_io_test.cpp b/test/unit/imaging/image_io_test.cpp
index 37f8fee..f2463f8 100644
--- a/test/unit/imaging/image_io_test.cpp
+++ b/test/unit/imaging/image_io_test.cpp
@@ -1,6 +1,8 @@
 #include "catch.hpp"
 
 #include <iostream>
+#include <cstring>
+#include <mapnik/color.hpp>
 #include <mapnik/image.hpp>
 #include <mapnik/image_reader.hpp>
 #include <mapnik/image_util.hpp>
@@ -11,6 +13,8 @@
 #include <mapnik/cairo/cairo_image_util.hpp>
 #endif
 
+#include <boost/format.hpp>
+
 TEST_CASE("image io") {
 
 SECTION("readers") {
@@ -110,7 +114,59 @@ SECTION("writers options")
     int q1 = mapnik::detail::parse_jpeg_quality("jpeg:quality=50");
     REQUIRE(q0 == q1);
 #endif
-
 } // END SECTION
 
+
+SECTION("image_util : save_to_file/save_to_stream/save_to_string")
+{
+    mapnik::image_rgba8 im(256,256);
+    std::string named_color = "lightblue";
+    mapnik::fill(im, mapnik::color(named_color).rgba());
+    ////////////////////////////////////////////////////
+    std::vector<std::tuple<std::string, std::string> > supported_types;
+#if defined(HAVE_PNG)
+    supported_types.push_back(std::make_tuple("png","png"));
+    supported_types.push_back(std::make_tuple("png","png24"));
+    supported_types.push_back(std::make_tuple("png","png32"));
+    supported_types.push_back(std::make_tuple("png","png8"));
+    supported_types.push_back(std::make_tuple("png","png256"));
+#endif
+#if defined(HAVE_JPEG)
+    supported_types.push_back(std::make_tuple("jpeg","jpeg"));
+    supported_types.push_back(std::make_tuple("jpeg","jpeg80"));
+    supported_types.push_back(std::make_tuple("jpeg","jpeg90"));
+#endif
+#if defined(HAVE_TIFF)
+    supported_types.push_back(std::make_tuple("tiff","tiff"));
+#endif
+#if defined(HAVE_WEBP)
+    supported_types.push_back(std::make_tuple("webp","webp"));
+#endif
+
+    for (auto const& info : supported_types)
+    {
+        std::string extension;
+        std::string format;
+        std::tie(extension, format) = info;
+        std::string filename = (boost::format("/tmp/mapnik-%1%.%2%") % named_color % extension).str();
+        mapnik::save_to_file(im, filename);
+        std::string str = mapnik::save_to_string(im, format);
+        std::ostringstream ss;
+        mapnik::save_to_stream(im, ss, format);
+        CHECK(str.length() == ss.str().length());
+        std::unique_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(filename, extension));
+        unsigned w = reader->width();
+        unsigned h = reader->height();
+        auto im2 = reader->read(0, 0, w, h);
+        CHECK(im2.size() == im.size());
+        if (extension == "png" || extension == "tiff")
+        {
+            CHECK(0 == std::memcmp(im2.bytes(), im.bytes(), im.width() * im.height()));
+        }
+        if (mapnik::util::exists(filename))
+        {
+            mapnik::util::remove(filename);
+        }
+    }
+}
 } // END TEST_CASE
diff --git a/test/unit/numerics/enumeration.cpp b/test/unit/numerics/enumeration.cpp
new file mode 100644
index 0000000..3c15993
--- /dev/null
+++ b/test/unit/numerics/enumeration.cpp
@@ -0,0 +1,16 @@
+#include "catch.hpp"
+#include <mapnik/enumeration.hpp>
+#include <mapnik/symbolizer_enumerations.hpp>
+#include <sstream>
+
+TEST_CASE("enumeration") {
+
+        mapnik::line_cap_e e(mapnik::ROUND_CAP);
+        CHECK( e.as_string() == "round" );
+        // note: test the << operator, which calls `as_string` internally
+        // is not used in mapnik, but kept for back compat
+        std::stringstream s;
+        s << e;
+        CHECK( s.str() == "round" );
+
+}
\ No newline at end of file
diff --git a/test/unit/run.cpp b/test/unit/run.cpp
index 8ae4ddc..caaab46 100644
--- a/test/unit/run.cpp
+++ b/test/unit/run.cpp
@@ -1,17 +1,73 @@
 #define CATCH_CONFIG_RUNNER
 #include "catch.hpp"
 
+#include <string>
+#include <mapnik/util/fs.hpp>
 #include <mapnik/datasource_cache.hpp>
+#include <boost/filesystem/convenience.hpp>
 
 #include "cleanup.hpp" // run_cleanup()
 
+std::string plugin_path;
+
+inline void set_plugin_path(Catch::ConfigData&, std::string const& _plugin_path ) {
+    plugin_path = _plugin_path;
+}
+
+std::string working_dir;
+
+inline void set_working_dir(Catch::ConfigData&, std::string const& _working_dir ) {
+    working_dir = _working_dir;
+}
+
+
 int main (int argc, char* const argv[])
 {
-    mapnik::datasource_cache::instance().register_datasources("plugins/input/");
+    Catch::Session session;
 
-    int result = Catch::Session().run( argc, argv );
+    auto & cli = session.cli();
+
+    cli["-p"]["--plugins"]
+        .describe("path to mapnik plugins")
+        .bind(&set_plugin_path, "plugins");
+
+    cli["-C"]["--working-directory"]
+        .describe("change working directory")
+        .bind(&set_working_dir, "working directory");
+
+    int result = session.applyCommandLine(argc, argv);
+
+    if (!plugin_path.empty())
+    {
+        if (!mapnik::util::exists(plugin_path))
+        {
+            std::clog << "Could not find " << plugin_path << "\n";
+            return -1;
+        }
+        mapnik::datasource_cache::instance().register_datasources(plugin_path);
+    }
+    else
+    {
+        mapnik::datasource_cache::instance().register_datasources("plugins/input/");
+    }
+
+    if (!working_dir.empty())
+    {
+        if (!mapnik::util::exists(working_dir))
+        {
+            std::clog << "Could not find " << working_dir << "\n";
+            return -1;
+        }
+        boost::filesystem::current_path(working_dir);
+    }
+
+    if (result == 0)
+    {
+        result = session.run();
+    }
 
     testing::run_cleanup();
 
     return result;
+
 }
diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp
index c92b797..3fee6a4 100644
--- a/test/unit/svg/svg_parser_test.cpp
+++ b/test/unit/svg/svg_parser_test.cpp
@@ -30,25 +30,44 @@
 #include <mapnik/svg/svg_converter.hpp>
 #include <mapnik/svg/svg_path_adapter.hpp>
 #include <mapnik/svg/svg_path_attributes.hpp>
-
-#include <cmath>
+#include "util.hpp"
 #include <fstream>
 #include <iterator>
 
-namespace detail {
-
-template <int N = 6>
-struct vertex_equal
+namespace // internal
 {
-    template <typename T>
-    bool operator() (T const& lhs, T const& rhs) const
+    struct test_parser
+    {
+        mapnik::svg_storage_type path;
+        mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage;
+        mapnik::svg::svg_path_adapter svg_path;
+        mapnik::svg::svg_converter_type svg;
+        mapnik::svg::svg_parser p;
+
+        test_parser()
+            : stl_storage(path.source())
+            , svg_path(stl_storage)
+            , svg(svg_path, path.attributes())
+            , p(svg)
+        {}
+
+        mapnik::svg::svg_parser* operator->()
+        {
+            return &p;
+        }
+    };
+
+    template <typename C>
+    std::string join(C const& container)
     {
-        static const double eps = 1.0 / std::pow(10,N);
-        return (std::fabs(std::get<0>(lhs) - std::get<0>(rhs)) < eps)
-            && (std::fabs(std::get<1>(lhs) - std::get<1>(rhs)) < eps)
-            && std::get<2>(lhs) == std::get<2>(rhs);
+        std::string result;
+        for (auto const& str : container)
+        {
+            if (!result.empty()) result += "\n ";
+            result += str;
+        }
+        return result;
     }
-};
 }
 
 TEST_CASE("SVG parser") {
@@ -66,142 +85,112 @@ TEST_CASE("SVG parser") {
     SECTION("SVG::parse i/o")
     {
         std::string svg_name("FAIL");
-
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse(svg_name))
+        char const* expected_errors[] =
         {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 1);
-            REQUIRE(errors[0] ==  "Unable to open 'FAIL'");
-        }
+            "Unable to open 'FAIL'"
+        };
+
+        test_parser p;
+        REQUIRE(!p->parse(svg_name));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG::parse_from_string syntax error")
     {
         std::string svg_name("./test/data/svg/invalid.svg");
+        char const* expected_errors[] =
+        {
+            "Unable to parse '<?xml version=\"1.0\"?>\n<svg width=\"12cm\" height=\"4cm\" viewBox=\"0 0 1200 400\"\nxmlns=\"http://www.w3.org/2000/svg\" version=\"1.2\" baseProfile=\"tiny\">\n'"
+        };
+
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
 
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse_from_string(svg_str))
-        {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 1);
-            REQUIRE(errors[0] ==  "Unable to parse '<?xml version=\"1.0\"?>\n<svg width=\"12cm\" height=\"4cm\" viewBox=\"0 0 1200 400\"\nxmlns=\"http://www.w3.org/2000/svg\" version=\"1.2\" baseProfile=\"tiny\">\n'");
-        }
+        test_parser p;
+        REQUIRE(!p->parse_from_string(svg_str));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG::parse_from_string syntax error")
     {
         std::string svg_name("./test/data/svg/invalid.svg");
-
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse(svg_name))
+        char const* expected_errors[] =
         {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 1);
-            REQUIRE(errors[0] ==  "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'");
-        }
+            "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'"
+        };
+
+        test_parser p;
+        REQUIRE(!p->parse(svg_name));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG parser color <fail>")
     {
 
         std::string svg_name("./test/data/svg/color_fail.svg");
+        char const* expected_errors[] =
+        {
+            "Failed to parse color: \"fail\"",
+            "Failed to parse SVG value: 'fail'",
+            "Failed to parse color: \"fail\"",
+        };
+
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
 
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse_from_string(svg_str))
-        {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 3);
-            REQUIRE(errors[0] ==  "Failed to parse color: \"fail\"");
-            REQUIRE(errors[1] ==  "Failed to parse double: \"fail\"");
-            REQUIRE(errors[2] ==  "Failed to parse color: \"fail\"");
-        }
+        test_parser p;
+        REQUIRE(!p->parse_from_string(svg_str));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG - cope with erroneous geometries")
     {
         std::string svg_name("./test/data/svg/errors.svg");
+        char const* expected_errors[] =
+        {
+            "parse_rect: Invalid width",
+            "Failed to parse SVG value: 'FAIL'",
+            "parse_rect: Invalid height",
+            "parse_rect: Invalid rx",
+            "parse_rect: Invalid ry",
+            "Failed to parse SVG value: '100invalidunit', trailing garbage: 'validunit'",
+            "unable to parse invalid svg <path>",
+            "unable to parse invalid svg <path> with id 'fail-path'",
+            "unable to parse invalid svg <path> with id 'fail-path'",
+            "parse_circle: Invalid radius",
+            "Failed to parse <polygon> 'points'",
+            "Failed to parse <polyline> 'points'",
+            "parse_ellipse: Invalid rx",
+            "parse_ellipse: Invalid ry",
+        };
+
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
 
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-        if (!p.parse_from_string(svg_str))
-        {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 13);
-            REQUIRE(errors[0] == "parse_rect: Invalid width");
-            REQUIRE(errors[1] == "Failed to parse double: \"FAIL\"");
-            REQUIRE(errors[2] == "parse_rect: Invalid height");
-            REQUIRE(errors[3] == "parse_rect: Invalid rx");
-            REQUIRE(errors[4] == "parse_rect: Invalid ry");
-            REQUIRE(errors[5] == "unable to parse invalid svg <path>");
-            REQUIRE(errors[6] == "unable to parse invalid svg <path> with id 'fail-path'");
-            REQUIRE(errors[7] == "unable to parse invalid svg <path> with id 'fail-path'");
-            REQUIRE(errors[8] == "parse_circle: Invalid radius");
-            REQUIRE(errors[9] == "Failed to parse <polygon> 'points'");
-            REQUIRE(errors[10] == "Failed to parse <polyline> 'points'");
-            REQUIRE(errors[11] == "parse_ellipse: Invalid rx");
-            REQUIRE(errors[12] == "parse_ellipse: Invalid ry");
-        }
+        test_parser p;
+        REQUIRE(!p->parse_from_string(svg_str));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG parser double % <fail>")
     {
 
         std::string svg_name("./test/data/svg/gradient-radial-error.svg");
+        char const* expected_errors[] =
+        {
+            "Failed to parse SVG value: 'FAIL'"
+        };
+
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
 
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse_from_string(svg_str))
-        {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 1);
-            REQUIRE(errors[0] ==  "Failed to parse double (optional %) from FAIL");
-        }
+        test_parser p;
+        REQUIRE(!p->parse_from_string(svg_str));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG parser display=none")
@@ -337,16 +326,10 @@ TEST_CASE("SVG parser") {
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
-
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-        p.parse_from_string(svg_str);
-        auto width = svg.width();
-        auto height = svg.height();
+        test_parser p;
+        REQUIRE(p->parse_from_string(svg_str));
+        auto width = p.svg.width();
+        auto height = p.svg.height();
         REQUIRE(width == 100);
         REQUIRE(height == 100);
     }
@@ -621,41 +604,33 @@ TEST_CASE("SVG parser") {
     SECTION("SVG missing <gradient> def")
     {
         std::string svg_name("./test/data/svg/gradient-nodef.svg");
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-        REQUIRE(!p.parse(svg_name));
-        auto const& errors = p.error_messages();
-        REQUIRE(errors.size() == 2);
-        REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient");
-        REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient");
+        char const* expected_errors[] =
+        {
+            "Failed to find gradient fill: MyGradient",
+            "Failed to find gradient stroke: MyGradient",
+        };
+
+        test_parser p;
+        REQUIRE(!p->parse(svg_name));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG missing <gradient> id")
     {
-
         std::string svg_name("./test/data/svg/gradient-no-id.svg");
+        char const* expected_errors[] =
+        {
+            "Failed to find gradient fill: MyGradient",
+            "Failed to find gradient stroke: MyGradient",
+        };
+
         std::ifstream in(svg_name.c_str());
         std::string svg_str((std::istreambuf_iterator<char>(in)),
                         std::istreambuf_iterator<char>());
 
-        using namespace mapnik::svg;
-        mapnik::svg_storage_type path;
-        vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
-        svg_path_adapter svg_path(stl_storage);
-        svg_converter_type svg(svg_path, path.attributes());
-        svg_parser p(svg);
-
-        if (!p.parse_from_string(svg_str))
-        {
-            auto const& errors = p.error_messages();
-            REQUIRE(errors.size() == 2);
-            REQUIRE(errors[0] ==  "Failed to find gradient fill: MyGradient");
-            REQUIRE(errors[1] ==  "Failed to find gradient stroke: MyGradient");
-        }
+        test_parser p;
+        REQUIRE(!p->parse_from_string(svg_str));
+        REQUIRE(join(p->error_messages()) == join(expected_errors));
     }
 
     SECTION("SVG missing <gradient> inheritance")
diff --git a/test/unit/svg/svg_path_parser_test.cpp b/test/unit/svg/svg_path_parser_test.cpp
new file mode 100644
index 0000000..db13d2b
--- /dev/null
+++ b/test/unit/svg/svg_path_parser_test.cpp
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ *
+ * This file is part of Mapnik (c++ mapping toolkit)
+ *
+ * Copyright (C) 2015 Artem Pavlenko
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *****************************************************************************/
+
+#include "catch.hpp"
+
+#include <mapnik/debug.hpp>
+#include <mapnik/vertex.hpp>
+#include <mapnik/svg/svg_path_parser.hpp>
+#include <mapnik/svg/svg_converter.hpp>
+#include <mapnik/marker.hpp>
+#include "util.hpp"
+
+namespace {
+
+template <typename Expected>
+void test_path_parser(std::string const& str, Expected const& expected)
+{
+    using namespace mapnik::svg;
+    mapnik::svg_path_ptr marker_path(std::make_shared<mapnik::svg_storage_type>());
+    vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
+    svg_path_adapter svg_path(stl_storage);
+    svg_converter_type svg(svg_path, marker_path->attributes());
+
+    CHECK(mapnik::svg::parse_path(str.c_str(), svg));
+    double x,y;
+    unsigned cmd;
+    auto & p = svg.storage();
+    std::vector<std::tuple<double,double,unsigned>> vec;
+    while ((cmd = p.vertex(&x,&y)) != mapnik::SEG_END)
+    {
+        vec.emplace_back(x, y, cmd);
+        //std::cerr << "std::make_tuple(" << x << ", " << y << ", " << cmd  << ")," << std::endl;
+    }
+    REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(), detail::vertex_equal<3>()));
+}
+} // anonymous ns
+
+TEST_CASE("SVG path parser") {
+
+    SECTION("MoveTo/LineTo")
+    {
+        std::string str = "M 100 100 L 300 100 L 200 300 z";
+        std::string str2 = "M100,100L300,100L200,300z";
+        std::string str3 = "M100,100l200 0L200,300z";
+        std::vector<std::tuple<double,double,unsigned>> expected = {
+            std::make_tuple(100, 100, 1),
+            std::make_tuple(300, 100, 2),
+            std::make_tuple(200, 300, 2),
+            std::make_tuple(100, 100, 79) };
+        test_path_parser(str, expected);
+        test_path_parser(str2, expected);
+        test_path_parser(str3, expected);
+    }
+
+    SECTION("MoveTo/HLine/VLine")
+    {
+        std::string str = "M100 100H300V200z";
+        std::string str2 = "M100,100h200v100z";
+        std::vector<std::tuple<double,double,unsigned>> expected = {
+            std::make_tuple(100, 100, 1),
+            std::make_tuple(300, 100, 2),
+            std::make_tuple(300, 200, 2),
+            std::make_tuple(100, 100, 79)
+        };
+        test_path_parser(str, expected);
+        test_path_parser(str2, expected);
+    }
+
+    SECTION("Arcs")
+    {
+        std::string str = "M300,200 h-150 a150,150 0 1,0 150,-150 z";
+
+        std::vector<std::tuple<double,double,unsigned>> expected = {
+            std::make_tuple(300, 200, 1),
+            std::make_tuple(150, 200, 2),
+            std::make_tuple(150, 282.843, 4),
+            std::make_tuple(217.157, 350, 4),
+            std::make_tuple(300, 350, 4),
+            std::make_tuple(382.843, 350, 4),
+            std::make_tuple(450, 282.843, 4),
+            std::make_tuple(450, 200, 4),
+            std::make_tuple(450, 117.157, 4),
+            std::make_tuple(382.843, 50, 4),
+            std::make_tuple(300, 50, 4),
+            std::make_tuple(300, 200, 79)};
+        test_path_parser(str, expected);
+    }
+
+
+    SECTION("Arcs 2")
+    {
+        std::string str = "M275,175 v-150 a150,150 0 0,0 -150,150 z";
+
+        std::vector<std::tuple<double,double,unsigned>> expected = {
+            std::make_tuple(275, 175, 1),
+            std::make_tuple(275, 25, 2),
+            std::make_tuple(192.157, 25, 4),
+            std::make_tuple(125, 92.1573, 4),
+            std::make_tuple(125, 175, 4),
+            std::make_tuple(275, 175, 79)};
+        test_path_parser(str, expected);
+    }
+
+    SECTION("Arcs 3")
+    {
+        std::string str = "M600,350 l 50,-25"
+            "a25,25 -30 0,1 50,-25 l 50,-25"
+            "a25,50 -30 0,1 50,-25 l 50,-25"
+            "a25,75 -30 0,1 50,-25 l 50,-25"
+            "a25,100 -30 0,1 50,-25 l 50,-25";
+
+        std::vector<std::tuple<double,double,unsigned>> expected = {
+            std::make_tuple(600, 350, 1),
+            std::make_tuple(650, 325, 2),
+            std::make_tuple(643.096, 311.193, 4),
+            std::make_tuple(648.693, 294.404, 4),
+            std::make_tuple(662.5, 287.5, 4),
+            std::make_tuple(676.307, 280.596, 4),
+            std::make_tuple(693.096, 286.193, 4),
+            std::make_tuple(700, 300, 4),
+            std::make_tuple(750, 275, 2),
+            std::make_tuple(734.991, 248.079, 4),
+            std::make_tuple(734.017, 220.66, 4),
+            std::make_tuple(747.825, 213.756, 4),
+            std::make_tuple(761.632, 206.852, 4),
+            std::make_tuple(784.991, 223.079, 4),
+            std::make_tuple(800, 250, 4),
+            std::make_tuple(850, 225, 2),
+            std::make_tuple(827.153, 184.812, 4),
+            std::make_tuple(819.825, 146.636, 4),
+            std::make_tuple(833.632, 139.733, 4),
+            std::make_tuple(847.44, 132.829, 4),
+            std::make_tuple(877.153, 159.812, 4),
+            std::make_tuple(900, 200, 4),
+            std::make_tuple(950, 175, 2),
+            std::make_tuple(919.382, 121.506, 4),
+            std::make_tuple(905.754, 72.5436, 4),
+            std::make_tuple(919.561, 65.64, 4),
+            std::make_tuple(933.368, 58.7365, 4),
+            std::make_tuple(969.382, 96.5057, 4),
+            std::make_tuple(1000, 150, 4),
+            std::make_tuple(1050, 125, 2)};
+        test_path_parser(str, expected);
+    }
+}
diff --git a/include/mapnik/image_util_jpeg.hpp b/test/unit/svg/util.hpp
similarity index 69%
copy from include/mapnik/image_util_jpeg.hpp
copy to test/unit/svg/util.hpp
index b8b213a..f8d9b5f 100644
--- a/include/mapnik/image_util_jpeg.hpp
+++ b/test/unit/svg/util.hpp
@@ -20,27 +20,25 @@
  *
  *****************************************************************************/
 
-#ifndef MAPNIK_IMAGE_UTIL_JPEG_HPP
-#define MAPNIK_IMAGE_UTIL_JPEG_HPP
+#ifndef TEST_UNIT_SVG_UTIL_HPP
+#define TEST_UNIT_SVG_UTIL_HPP
 
-// stl
-#include <string>
-#include <iostream>
+#include <cmath>
 
-namespace mapnik {
 namespace detail {
-MAPNIK_DECL int parse_jpeg_quality(std::string const& params);
-}
-struct jpeg_saver
+
+template <int N = 6>
+struct vertex_equal
 {
-    jpeg_saver(std::ostream &, std::string const&);
     template <typename T>
-    void operator() (T const&) const;
-  private:
-    std::ostream & stream_;
-    std::string const& t_;
+    bool operator() (T const& lhs, T const& rhs) const
+    {
+        static const double eps = 1.0 / std::pow(10,N);
+        return (std::fabs(std::get<0>(lhs) - std::get<0>(rhs)) < eps)
+            && (std::fabs(std::get<1>(lhs) - std::get<1>(rhs)) < eps)
+            && std::get<2>(lhs) == std::get<2>(rhs);
+    }
 };
+}
 
-} // end ns
-
-#endif // MAPNIK_IMAGE_UTIL_JPEG_HPP
+#endif // TEST_UNIT_SVG_UTIL_HPP
diff --git a/test/visual/runner.cpp b/test/visual/runner.cpp
index 06ade77..d0c7222 100644
--- a/test/visual/runner.cpp
+++ b/test/visual/runner.cpp
@@ -57,13 +57,13 @@ public:
     }
 
     template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
-    void operator()(T const & renderer)
+    void operator()(T const& renderer) const
     {
         test(renderer);
     }
 
     template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
-    void operator()(T const & renderer)
+    void operator()(T const & renderer) const
     {
         if (tiles_.width == 1 && tiles_.height == 1)
         {
@@ -73,7 +73,7 @@ public:
 
 private:
     template <typename T>
-    void test(T const & renderer)
+    void test(T const & renderer) const
     {
         map_size size { map_.width(), map_.height() };
         std::chrono::high_resolution_clock::time_point start(std::chrono::high_resolution_clock::now());
@@ -96,7 +96,7 @@ private:
     }
 
     template <typename T, typename std::enable_if<T::renderer_type::support_tiles>::type* = nullptr>
-    typename T::image_type render(T const & renderer)
+    typename T::image_type render(T const& renderer) const
     {
         if (tiles_.width == 1 && tiles_.height == 1)
         {
@@ -109,7 +109,7 @@ private:
     }
 
     template <typename T, typename std::enable_if<!T::renderer_type::support_tiles>::type* = nullptr>
-    typename T::image_type render(T const & renderer)
+    typename T::image_type render(T const & renderer) const
     {
         return renderer.render(map_, scale_factor_);
     }
@@ -150,7 +150,8 @@ result_list runner::test_all(report_type & report) const
 
 result_list runner::test(std::vector<std::string> const & style_names, report_type & report) const
 {
-    std::vector<runner::path_type> files(style_names.size());
+    std::vector<runner::path_type> files;
+    files.reserve(style_names.size());
     std::transform(style_names.begin(), style_names.end(), std::back_inserter(files),
         [&](runner::path_type const & name)
         {
@@ -363,4 +364,3 @@ void runner::parse_map_sizes(std::string const & str, std::vector<map_size> & si
 }
 
 }
-
diff --git a/utils/mapnik-config/build.py b/utils/mapnik-config/build.py
index e74684e..bf3e5b3 100644
--- a/utils/mapnik-config/build.py
+++ b/utils/mapnik-config/build.py
@@ -63,7 +63,7 @@ CONFIG_MAPNIK_LIBNAME='%(mapnik_libname)s'
 CONFIG_MAPNIK_LIBPATH="%(mapnik_libpath)s"
 CONFIG_DEP_LIBS='%(dep_libs)s'
 CONFIG_MAPNIK_LDFLAGS="%(ldflags)s"
-CONFIG_MAPNIK_INCLUDE="${CONFIG_PREFIX}/include -I${CONFIG_PREFIX}/include/mapnik/agg"
+CONFIG_MAPNIK_INCLUDE="${CONFIG_PREFIX}/include -I${CONFIG_PREFIX}/include/mapnik/agg -I${CONFIG_PREFIX}/include/mapnik"
 CONFIG_DEP_INCLUDES="%(dep_includes)s"
 CONFIG_CXXFLAGS="%(cxxflags)s"
 CONFIG_CXX='%(cxx)s'
diff --git a/utils/mapnik-index/mapnik-index.cpp b/utils/mapnik-index/mapnik-index.cpp
index 7361c05..f6f72be 100644
--- a/utils/mapnik-index/mapnik-index.cpp
+++ b/utils/mapnik-index/mapnik-index.cpp
@@ -186,7 +186,7 @@ int main (int argc, char** argv)
         else if (mapnik::detail::is_geojson(filename))
         {
             std::clog << "processing '" << filename << "' as GeoJSON\n";
-            auto result = mapnik::detail::process_geojson_file(boxes, filename, validate_features);
+            auto result = mapnik::detail::process_geojson_file(boxes, filename, validate_features, verbose);
             if (!result.first)
             {
                 std::clog << "Error: failed to process " << filename << std::endl;
diff --git a/utils/mapnik-index/process_csv_file.cpp b/utils/mapnik-index/process_csv_file.cpp
index 908ec9a..92c0728 100644
--- a/utils/mapnik-index/process_csv_file.cpp
+++ b/utils/mapnik-index/process_csv_file.cpp
@@ -76,16 +76,17 @@ std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& fil
     char newline;
     bool has_newline;
     char detected_quote;
-    std::tie(newline, has_newline, detected_quote) = ::detail::autodect_newline_and_quote(csv_file, file_length);
+    char detected_separator;
+    std::tie(newline, has_newline, detected_separator, detected_quote) = ::detail::autodect_csv_flavour(csv_file, file_length);
     if (quote == 0) quote = detected_quote;
+    if (separator == 0) separator = detected_separator;
     // set back to start
     csv_file.seekg(0, std::ios::beg);
-    // get first line
     std::string csv_line;
     csv_utils::getline_csv(csv_file, csv_line, newline, quote);
-    if (separator == 0) separator = ::detail::detect_separator(csv_line);
     csv_file.seekg(0, std::ios::beg);
     int line_number = 0;
+
     ::detail::geometry_column_locator locator;
     std::vector<std::string> headers;
     std::clog << "Parsing CSV using SEPARATOR=" << separator << " QUOTE=" << quote << std::endl;
@@ -186,15 +187,23 @@ std::pair<bool,box2d<double>> process_csv_file(T & boxes, std::string const& fil
         }
         try
         {
-            auto values = csv_utils::parse_line(csv_line, separator, quote);
+            auto const* start_line = csv_line.data();
+            auto const* end_line = start_line + csv_line.size();
+            auto values = csv_utils::parse_line(start_line, end_line, separator, quote, num_headers);
             unsigned num_fields = values.size();
-            if (num_fields > num_headers || num_fields < num_headers)
+            if (num_fields != num_headers)
             {
-                // skip this row
                 std::ostringstream s;
-                s << "CSV Index: # of columns("
-                  << num_fields << ") > # of headers("
-                  << num_headers << ") parsed for row " << line_number;
+                s << "CSV Plugin: # of columns(" << num_fields << ")";
+                if (num_fields > num_headers)
+                {
+                    s << " > ";
+                }
+                else
+                {
+                    s << " < ";
+                }
+                s << "# of headers(" << num_headers << ") parsed";
                 throw mapnik::datasource_exception(s.str());
             }
 
diff --git a/utils/mapnik-index/process_geojson_file.cpp b/utils/mapnik-index/process_geojson_file.cpp
index af9656b..ac0fa92 100644
--- a/utils/mapnik-index/process_geojson_file.cpp
+++ b/utils/mapnik-index/process_geojson_file.cpp
@@ -66,7 +66,7 @@ const mapnik::json::feature_grammar_callback<base_iterator_type, mapnik::feature
 namespace mapnik { namespace detail {
 
 template <typename T>
-std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features)
+std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features, bool verbose)
 {
     mapnik::box2d<double> extent;
 #if defined(MAPNIK_MEMORY_MAPPED_FILE)
@@ -131,6 +131,7 @@ std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const&
                                                               space);
                 if (!result || feat_itr != feat_end)
                 {
+                    if (verbose) std::clog << std::string(start + item.second.first, feat_end ) << std::endl;
                     return std::make_pair(false, extent);
                 }
             }
@@ -142,6 +143,6 @@ std::pair<bool,box2d<double>> process_geojson_file(T & boxes, std::string const&
 using box_type = mapnik::box2d<double>;
 using item_type = std::pair<box_type, std::pair<std::size_t, std::size_t>>;
 using boxes_type = std::vector<item_type>;
-template std::pair<bool,box2d<double>> process_geojson_file(boxes_type&, std::string const&, bool);
+template std::pair<bool,box2d<double>> process_geojson_file(boxes_type&, std::string const&, bool, bool);
 
 }}
diff --git a/utils/mapnik-index/process_geojson_file.hpp b/utils/mapnik-index/process_geojson_file.hpp
index a4d468a..aba4911 100644
--- a/utils/mapnik-index/process_geojson_file.hpp
+++ b/utils/mapnik-index/process_geojson_file.hpp
@@ -29,7 +29,7 @@
 namespace mapnik { namespace detail {
 
 template <typename T>
-std::pair<bool, box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features);
+std::pair<bool, box2d<double>> process_geojson_file(T & boxes, std::string const& filename, bool validate_features, bool verbose);
 
 }}
 
diff --git a/utils/mapnik-render/mapnik-render.cpp b/utils/mapnik-render/mapnik-render.cpp
index 0ed950c..c399226 100644
--- a/utils/mapnik-render/mapnik-render.cpp
+++ b/utils/mapnik-render/mapnik-render.cpp
@@ -32,7 +32,7 @@ int main (int argc,char** argv)
 
     try
     {
-        po::options_description desc("nik2img utility");
+        po::options_description desc("mapnik-render utility");
         desc.add_options()
             ("help,h", "produce usage message")
             ("version,V","print version string")
diff --git a/utils/shapeindex/shapeindex.cpp b/utils/shapeindex/shapeindex.cpp
index a9c5242..f54508c 100644
--- a/utils/shapeindex/shapeindex.cpp
+++ b/utils/shapeindex/shapeindex.cpp
@@ -26,9 +26,10 @@
 #include <string>
 #include <mapnik/util/fs.hpp>
 #include <mapnik/quad_tree.hpp>
+#include <mapnik/geometry_envelope.hpp>
 #include "shapefile.hpp"
 #include "shape_io.hpp"
-
+#include "shape_index_featureset.hpp"
 #pragma GCC diagnostic push
 #include <mapnik/warning_ignore.hpp>
 #include <boost/algorithm/string.hpp>
@@ -44,8 +45,9 @@ int main (int argc,char** argv)
     namespace po = boost::program_options;
 
     bool verbose=false;
-    unsigned int depth=DEFAULT_DEPTH;
-    double ratio=DEFAULT_RATIO;
+    bool index_parts = false;
+    unsigned int depth = DEFAULT_DEPTH;
+    double ratio = DEFAULT_RATIO;
     std::vector<std::string> shape_files;
 
     try
@@ -54,6 +56,7 @@ int main (int argc,char** argv)
         desc.add_options()
             ("help,h", "produce usage message")
             ("version,V","print version string")
+            ("index-parts","index individual shape parts (default: no)")
             ("verbose,v","verbose output")
             ("depth,d", po::value<unsigned int>(), "max tree depth\n(default 8)")
             ("ratio,r",po::value<double>(),"split ratio (default 0.55)")
@@ -81,6 +84,10 @@ int main (int argc,char** argv)
         {
             verbose = true;
         }
+        if (vm.count("index-parts"))
+        {
+            index_parts = true;
+        }
         if (vm.count("depth"))
         {
             depth = vm["depth"].as<unsigned int>();
@@ -159,52 +166,96 @@ int main (int argc,char** argv)
 
         int pos = 50;
         shx.seek(pos * 2);
-        mapnik::quad_tree<int> tree(extent, depth, ratio);
+        mapnik::quad_tree<mapnik::detail::node> tree(extent, depth, ratio);
         int count = 0;
 
-        while (shx.is_good() && pos <= file_length - 4)
+        if (shape_type != shape_io::shape_null)
         {
-            int offset = shx.read_xdr_integer();
-            int content_length = shx.read_xdr_integer();
-            pos += 4;
-            box2d<double> item_ext;
-            shp.seek(offset * 2);
-            int record_number = shp.read_xdr_integer();
-            if (content_length != shp.read_xdr_integer())
+            while (shx.is_good() && pos <= file_length - 4)
             {
-                std::clog << "Content length mismatch for record number " << record_number << std::endl;
-                continue;
-            }
-            shape_type = shp.read_ndr_integer();
+                int offset = shx.read_xdr_integer();
+                int shx_content_length = shx.read_xdr_integer();
+                pos += 4;
+                box2d<double> item_ext;
+                shp.seek(offset * 2);
+                int record_number = shp.read_xdr_integer();
+                int shp_content_length = shp.read_xdr_integer();
+                if (shx_content_length != shp_content_length)
+                {
+                    std::clog << "Content length mismatch for record number " << record_number << std::endl;
+                    continue;
+                }
+                shape_type = shp.read_ndr_integer();
 
-            if (shape_type==shape_io::shape_point
-                || shape_type==shape_io::shape_pointm
-                || shape_type == shape_io::shape_pointz)
-            {
-                double x=shp.read_double();
-                double y=shp.read_double();
-                item_ext=box2d<double>(x,y,x,y);
-            }
-            else
-            {
-                shp.read_envelope(item_ext);
-            }
-            if (verbose)
-            {
-                std::clog << "record number " << record_number << " box=" << item_ext << std::endl;
-            }
-            if (item_ext.valid())
-            {
-                tree.insert(offset * 2,item_ext);
-                ++count;
+                if (shape_type == shape_io::shape_null) continue;
+
+                if (shape_type==shape_io::shape_point
+                    || shape_type==shape_io::shape_pointm
+                    || shape_type == shape_io::shape_pointz)
+                {
+                    double x=shp.read_double();
+                    double y=shp.read_double();
+                    item_ext=box2d<double>(x,y,x,y);
+                }
+                else if (index_parts &&
+                         (shape_type == shape_io::shape_polygon || shape_type == shape_io::shape_polygonm || shape_type == shape_io::shape_polygonz
+                          || shape_type == shape_io::shape_polyline || shape_type == shape_io::shape_polylinem || shape_type == shape_io::shape_polylinez))
+                {
+                    shp.read_envelope(item_ext);
+                    int num_parts = shp.read_ndr_integer();
+                    int num_points = shp.read_ndr_integer();
+                    std::vector<int> parts;
+                    parts.resize(num_parts);
+                    std::for_each(parts.begin(), parts.end(), [&](int & part) { part = shp.read_ndr_integer();});
+                    for (int k = 0; k < num_parts; ++k)
+                    {
+                        int start = parts[k];
+                        int end;
+                        if (k == num_parts - 1) end = num_points;
+                        else end = parts[k + 1];
+
+                        mapnik::geometry::linear_ring<double> ring;
+                        ring.reserve(end - start);
+                        for (int j = start; j < end; ++j)
+                        {
+                            double x = shp.read_double();
+                            double y = shp.read_double();
+                            ring.emplace_back(x, y);
+                        }
+                        item_ext = mapnik::geometry::envelope(ring);
+                        if (item_ext.valid())
+                        {
+                            if (verbose)
+                            {
+                                std::clog << "record number " << record_number << " box=" << item_ext << std::endl;
+                            }
+                            tree.insert(mapnik::detail::node(offset * 2, start, end),item_ext);
+                            ++count;
+                        }
+                    }
+                    item_ext = mapnik::box2d<double>(); //invalid
+                }
+                else
+                {
+                    shp.read_envelope(item_ext);
+                }
+
+                if (item_ext.valid())
+                {
+                    if (verbose)
+                    {
+                        std::clog << "record number " << record_number << " box=" << item_ext << std::endl;
+                    }
+                    tree.insert(mapnik::detail::node(offset * 2,-1,0),item_ext);
+                    ++count;
+                }
             }
         }
 
         if (count > 0)
         {
             std::clog << " number shapes=" << count << std::endl;
-            std::fstream file((shapename+".index").c_str(),
-                              std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
+            std::ofstream file((shapename+".index").c_str(), std::ios::trunc | std::ios::binary);
             if (!file)
             {
                 std::clog << "cannot open index file for writing file \""
diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp
index 8e855fd..c7fe797 100644
--- a/utils/svg2png/svg2png.cpp
+++ b/utils/svg2png/svg2png.cpp
@@ -56,7 +56,7 @@ struct main_marker_visitor
           verbose_(verbose),
           auto_open_(auto_open) {}
 
-    int operator() (mapnik::marker_svg const& marker)
+    int operator() (mapnik::marker_svg const& marker) const
     {
         using pixfmt = agg::pixfmt_rgba32_pre;
         using renderer_base = agg::renderer_base<pixfmt>;
@@ -67,6 +67,11 @@ struct main_marker_visitor
         double opacity = 1;
         int w = marker.width();
         int h = marker.height();
+        if (w == 0 || h == 0)
+        {
+            // fallback to svg width/height or viewBox
+            std::tie(w, h) = marker.dimensions();
+        }
         if (verbose_)
         {
             std::clog << "found width of '" << w << "' and height of '" << h << "'\n";
@@ -117,14 +122,14 @@ struct main_marker_visitor
 
     // default
     template <typename T>
-    int operator() (T const&)
+    int operator() (T const&) const
     {
         std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n";
         return -1;
     }
 
   private:
-    std::string const& svg_name_;
+    std::string svg_name_;
     bool verbose_;
     bool auto_open_;
 };

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapnik.git



More information about the Pkg-grass-devel mailing list