[libosmium] 01/05: New upstream version 2.13.1

Bas Couwenberg sebastic at debian.org
Sat Aug 26 07:18:03 UTC 2017


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

sebastic pushed a commit to branch master
in repository libosmium.

commit 919222e216666fef283b8f7fe537b7b143b18acb
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Aug 26 08:55:42 2017 +0200

    New upstream version 2.13.1
---
 CHANGELOG.md                                     |  32 ++++-
 CMakeLists.txt                                   |   2 +-
 NOTES_FOR_DEVELOPERS.md                          |  34 ++++-
 appveyor.yml                                     |   8 ++
 build-appveyor.bat                               |   6 +-
 examples/osmium_dump_internal.cpp                |  11 ++
 examples/osmium_index_lookup.cpp                 |  43 ++++--
 examples/osmium_location_cache_create.cpp        |   9 +-
 examples/osmium_location_cache_use.cpp           |   9 +-
 include/osmium/index/detail/mmap_vector_file.hpp |   2 +-
 include/osmium/io/detail/input_format.hpp        |  16 +--
 include/osmium/io/detail/opl_input_format.hpp    |  98 +++++++------
 include/osmium/io/detail/output_format.hpp       |  30 ++++
 include/osmium/io/file.hpp                       |   3 +
 include/osmium/io/file_format.hpp                |  17 ++-
 include/osmium/thread/pool.hpp                   |   2 +-
 include/osmium/util/file.hpp                     |  46 ++++++-
 include/osmium/util/memory_mapping.hpp           |   2 +-
 include/osmium/version.hpp                       |   4 +-
 test/examples/CMakeLists.txt                     |  33 ++++-
 test/examples/t/change_tags/.gitattributes       |   1 +
 test/examples/t/dump_internal/CMakeLists.txt     |  45 +++---
 test/t/io/data-cr.opl                            |   1 +
 test/t/io/test_file_formats.cpp                  |  40 ++++++
 test/t/io/test_opl_parser.cpp                    | 166 +++++++++++++++++++++++
 test/t/memory/test_item.cpp                      |  16 ++-
 test/t/osm/test_location.cpp                     |  20 +--
 test/t/thread/test_pool.cpp                      |   4 +-
 test/t/util/.gitattributes                       |   1 +
 test/t/util/known_file_size                      |   1 +
 test/t/util/test_file.cpp                        |  92 +++++--------
 test/t/util/test_memory_mapping.cpp              |  20 ++-
 32 files changed, 620 insertions(+), 194 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d90ae3..2493e0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,35 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [2.13.1] - 2017-08-25
+
+### Added
+
+- New "blackhole" file format which throws away all data written into it.
+  Used for benchmarking.
+
+### Changed
+
+- When reading OPL files, CRLF file endings are now handled correctly.
+- Reduce the max number of threads allowed for the `Pool` to 32. This should
+  still be plenty and might help with test failures on some architectures.
+
+### Fixed
+
+- Tests now run correctly independent of git `core.autocrlf` setting.
+- Set binary mode for all files on Windows in example code.
+- Low-level file functions now set an invalid parameter handler on Windows
+  to properly handle errors.
+- Restore earlier behaviour allowing zero-length mmap. It is important to
+  allow zero-length memory mapping, because it is possible that such an index
+  is empty, for instance when one type of object is missing from an input
+  file as in https://github.com/osmcode/osmium-tool/issues/65. Drawback is
+  that files must be opened read-write for this to work, even if we only
+  want to read from them.
+- Use Approx() to compare floating point values in tests.
+- Fix broken `Item` test on 32 bit platforms.
+
+
 ## [2.13.0] - 2017-08-15
 
 ### Added
@@ -710,7 +739,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   Doxygen (up to version 1.8.8). This version contains a workaround to fix
   this.
 
-[unreleased]: https://github.com/osmcode/libosmium/compare/v2.13.0...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.13.1...HEAD
+[2.13.1]: https://github.com/osmcode/libosmium/compare/v2.13.0...v2.13.1
 [2.13.0]: https://github.com/osmcode/libosmium/compare/v2.12.2...v2.13.0
 [2.12.2]: https://github.com/osmcode/libosmium/compare/v2.12.1...v2.12.2
 [2.12.1]: https://github.com/osmcode/libosmium/compare/v2.12.0...v2.12.1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c5e62f..e278875 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@ project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
 set(LIBOSMIUM_VERSION_MINOR 13)
-set(LIBOSMIUM_VERSION_PATCH 0)
+set(LIBOSMIUM_VERSION_PATCH 1)
 
 set(LIBOSMIUM_VERSION
     "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
diff --git a/NOTES_FOR_DEVELOPERS.md b/NOTES_FOR_DEVELOPERS.md
index 5f84494..4bd9d68 100644
--- a/NOTES_FOR_DEVELOPERS.md
+++ b/NOTES_FOR_DEVELOPERS.md
@@ -12,7 +12,7 @@ All Osmium code MUST be in the `osmium` namespace or one of its sub-namespaces.
 ## Include-Only
 
 Osmium is a include-only library. You can't compile the library itself. There
-is no libosmium.so.
+is no `libosmium.so` or `libosmium.dll`.
 
 One drawback ist that you can't have static data in classes, because there
 is no place to put this data.
@@ -69,7 +69,7 @@ different.
 * Template parameters are single uppercase letters or start with uppercase `T`
   and use CamelCase.
 * Always use `typename` in templates, not `class`: `template <typename T>`.
-* The ellipsis in variadic template never has a space to the left of it and
+* The ellipsis in a variadic template never has a space to the left of it and
   always has a space to the right: `template <typename... TArgs>` etc.
 
 Keep to the indentation and other styles used in the code.
@@ -92,6 +92,19 @@ between compilers due to different C++11 support.
 Usually all code must work on Linux, OSX, and Windows. Execptions are allowed
 for some minor functionality, but please discuss this first.
 
+When writing code and tests, care must be taken that everything works with the
+CR line ending convention used on Linux and OSX and the CRLF line ending used
+on Windows. Note that `git` can be run with different settings regarding line
+ending rewritings on different machines making things more difficult. Some
+files have been "forced" to LF line endings using `.gitattributes` files.
+
+
+## 32bit systems
+
+Libosmium tries to work on 32bit systems whereever possible. But there are
+some parts which will not work on 32bit systems, mainly because the amount
+of main memory available is not enough for it to work anyway.
+
 
 ## Checking your code
 
@@ -118,9 +131,20 @@ used to define mappings for iwyu. See the IWYU tool at
 
 ## Testing
 
-There are a unit tests using the Catch Unit Test Framework in the `test`
-directory and some data tests in `test/osm-testdata`. They are built by the
-default cmake config. Run `ctest` to run them. Many more tests are needed.
+There are unit tests using the Catch Unit Test Framework in the `test`
+directory, some data tests in `test/osm-testdata` and tests of the examples in
+`test/examples`. They are built by the default cmake config. Run `ctest` to
+run them. We can always use more tests.
+
+Tests are run automatically using the Travis (Linux/Mac) and Appveyor (Windows)
+services. We automatically create coverage reports on Codevoc.io. Note that
+the coverage percentages reported are not always accurate, because code that
+is not used in tests at all will not necessarily end up in the binary and
+the code coverage tool will not know it is there.
+
+[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium)
+[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
+[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium)
 
 
 ## Documenting the code
diff --git a/appveyor.yml b/appveyor.yml
index d1eea30..ca77ae6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,7 +7,13 @@
 environment:
   matrix:
   - config: Dev
+    autocrlf: true
+  - config: Dev
+    autocrlf: false
+  - config: RelWithDebInfo
+    autocrlf: true
   - config: RelWithDebInfo
+    autocrlf: false
 
 shallow_clone: true
 
@@ -16,6 +22,8 @@ os: Visual Studio 2015
 
 # scripts that are called at very beginning, before repo cloning
 init:
+  - git config --global core.autocrlf %autocrlf%
+  - git config --get core.autocrlf
 
 # clone directory
 clone_folder: c:\projects\libosmium
diff --git a/build-appveyor.bat b/build-appveyor.bat
index b4f45f3..ef9da5f 100644
--- a/build-appveyor.bat
+++ b/build-appveyor.bat
@@ -97,12 +97,14 @@ ECHO calling^: %CMAKE_CMD%
 %CMAKE_CMD%
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
+SET avlogger=
+IF /I "%APPVEYOR%"=="True" SET avlogger=/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
+
 msbuild libosmium.sln ^
 /p:Configuration=%config% ^
 /toolsversion:14.0 ^
 /p:Platform=x64 ^
-/p:PlatformToolset=v140 ^
-/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
+/p:PlatformToolset=v140 %avlogger%
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 ctest --output-on-failure ^
diff --git a/examples/osmium_dump_internal.cpp b/examples/osmium_dump_internal.cpp
index 58f49b3..78229da 100644
--- a/examples/osmium_dump_internal.cpp
+++ b/examples/osmium_dump_internal.cpp
@@ -36,6 +36,10 @@
 #include <sys/stat.h>  // for open
 #include <sys/types.h> // for open
 
+#ifdef _WIN32
+# include <io.h>       // for _setmode
+#endif
+
 #ifdef _MSC_VER
 # include <direct.h>
 #endif
@@ -72,6 +76,9 @@ public:
             std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n";
             std::exit(2);
         }
+#ifdef _WIN32
+        _setmode(m_fd, _O_BINARY);
+#endif
     }
 
     ~IndexFile() {
@@ -114,6 +121,10 @@ int main(int argc, char* argv[]) {
         std::exit(2);
     }
 
+#ifdef _WIN32
+    _setmode(data_fd, _O_BINARY);
+#endif
+
     // These indexes store the offset in the data file where each node, way,
     // or relation is stored.
     offset_index_type node_index;
diff --git a/examples/osmium_index_lookup.cpp b/examples/osmium_index_lookup.cpp
index 4449e70..2af2378 100644
--- a/examples/osmium_index_lookup.cpp
+++ b/examples/osmium_index_lookup.cpp
@@ -33,6 +33,10 @@
 #include <sys/types.h> // for open
 #include <vector>      // for std::vector
 
+#ifdef _WIN32
+# include <io.h>       // for _setmode
+#endif
+
 // Disk-based indexes
 #include <osmium/index/map/dense_file_array.hpp>
 #include <osmium/index/map/sparse_file_array.hpp>
@@ -243,7 +247,7 @@ public:
             std::exit(2);
         }
 
-        if (m_dump && !m_ids.empty()) {
+        if (m_dump == !m_ids.empty()) {
             std::cerr << "Need option --dump or --search, but not both\n";
             std::exit(2);
         }
@@ -308,26 +312,35 @@ int main(int argc, char* argv[]) {
     Options options{argc, argv};
 
     // Open the index file.
-    const int fd = open(options.filename(), O_RDWR);
+    const int fd = ::open(options.filename(), O_RDWR);
     if (fd < 0) {
         std::cerr << "Can not open file '" << options.filename()
                   << "': " << std::strerror(errno) << '\n';
         std::exit(2);
     }
 
-    // Depending on the type of index, we have different implementations.
-    if (options.type_is("location")) {
-        // index id -> location
-        const auto index = create<osmium::Location>(options.dense_format(), fd);
-        return run(*index, options);
-    } else if (options.type_is("id")) {
-        // index id -> id
-        const auto index = create<osmium::unsigned_object_id_type>(options.dense_format(), fd);
-        return run(*index, options);
-    } else {
-        // index id -> offset
-        const auto index = create<std::size_t>(options.dense_format(), fd);
-        return run(*index, options);
+#ifdef _WIN32
+    _setmode(fd, _O_BINARY);
+#endif
+
+    try {
+        // Depending on the type of index, we have different implementations.
+        if (options.type_is("location")) {
+            // index id -> location
+            const auto index = create<osmium::Location>(options.dense_format(), fd);
+            return run(*index, options);
+        } else if (options.type_is("id")) {
+            // index id -> id
+            const auto index = create<osmium::unsigned_object_id_type>(options.dense_format(), fd);
+            return run(*index, options);
+        } else {
+            // index id -> offset
+            const auto index = create<std::size_t>(options.dense_format(), fd);
+            return run(*index, options);
+        }
+    } catch(const std::exception& e) {
+        std::cerr << "Error: " << e.what() << '\n';
+        std::exit(1);
     }
 }
 
diff --git a/examples/osmium_location_cache_create.cpp b/examples/osmium_location_cache_create.cpp
index c01dd97..a7119df 100644
--- a/examples/osmium_location_cache_create.cpp
+++ b/examples/osmium_location_cache_create.cpp
@@ -33,6 +33,10 @@
 #include <sys/stat.h>  // for open
 #include <sys/types.h> // for open
 
+#ifdef _WIN32
+# include <io.h>       // for _setmode
+#endif
+
 // Allow any format of input files (XML, PBF, ...)
 #include <osmium/io/any_input.hpp>
 
@@ -68,11 +72,14 @@ int main(int argc, char* argv[]) {
     osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node};
 
     // Initialize location index on disk creating a new file.
-    const int fd = open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
+    const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
     if (fd == -1) {
         std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n";
         std::exit(1);
     }
+#ifdef _WIN32
+    _setmode(fd, _O_BINARY);
+#endif
     index_type index{fd};
 
     // The handler that stores all node locations in the index.
diff --git a/examples/osmium_location_cache_use.cpp b/examples/osmium_location_cache_use.cpp
index f5db0be..6af0fe6 100644
--- a/examples/osmium_location_cache_use.cpp
+++ b/examples/osmium_location_cache_use.cpp
@@ -33,6 +33,10 @@
 #include <sys/stat.h>  // for open
 #include <sys/types.h> // for open
 
+#ifdef _WIN32
+# include <io.h>       // for _setmode
+#endif
+
 // Allow any format of input files (XML, PBF, ...)
 #include <osmium/io/any_input.hpp>
 
@@ -81,11 +85,14 @@ int main(int argc, char* argv[]) {
     osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way};
 
     // Initialize location index on disk using an existing file
-    const int fd = open(cache_filename.c_str(), O_RDWR);
+    const int fd = ::open(cache_filename.c_str(), O_RDWR);
     if (fd == -1) {
         std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n";
         return 1;
     }
+#ifdef _WIN32
+    _setmode(fd, _O_BINARY);
+#endif
     index_type index{fd};
 
     // The handler that adds node locations from the index to the ways.
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
index ff0ca54..d853992 100644
--- a/include/osmium/index/detail/mmap_vector_file.hpp
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -53,7 +53,7 @@ namespace osmium {
         template <typename T>
         class mmap_vector_file : public mmap_vector_base<T> {
 
-            std::size_t filesize(int fd) const {
+            static std::size_t filesize(int fd) {
                 const auto size = osmium::util::file_size(fd);
 
                 if (size % sizeof(T) != 0) {
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
index 442c717..7ba6677 100644
--- a/include/osmium/io/detail/input_format.hpp
+++ b/include/osmium/io/detail/input_format.hpp
@@ -81,14 +81,6 @@ namespace osmium {
                     return m_pool;
                 }
 
-                std::string get_input() {
-                    return m_input_queue.pop();
-                }
-
-                bool input_done() const {
-                    return m_input_queue.has_reached_end_of_data();
-                }
-
                 osmium::osm_entity_bits::type read_types() const noexcept {
                     return m_read_which_entities;
                 }
@@ -148,6 +140,14 @@ namespace osmium {
 
                 virtual void run() = 0;
 
+                std::string get_input() {
+                    return m_input_queue.pop();
+                }
+
+                bool input_done() const {
+                    return m_input_queue.has_reached_end_of_data();
+                }
+
                 void parse() {
                     try {
                         run();
diff --git a/include/osmium/io/detail/opl_input_format.hpp b/include/osmium/io/detail/opl_input_format.hpp
index 0d7a62d..f5fcd00 100644
--- a/include/osmium/io/detail/opl_input_format.hpp
+++ b/include/osmium/io/detail/opl_input_format.hpp
@@ -51,10 +51,56 @@ namespace osmium {
 
         namespace detail {
 
+            // Feed data coming in blocks line by line to the OPL parser
+            // function. This has been broken out of the OPLParser class
+            // where it belongs into a standalone template function to be
+            // better testable.
+            template <typename T>
+            void line_by_line(T& worker) {
+                std::string rest;
+
+                while (!worker.input_done()) {
+                    std::string input{worker.get_input()};
+                    std::string::size_type ppos = 0;
+
+                    if (!rest.empty()) {
+                        ppos = input.find_first_of("\n\r");
+                        if (ppos == std::string::npos) {
+                            rest.append(input);
+                            continue;
+                        }
+                        rest.append(input, 0, ppos);
+                        if (!rest.empty()) {
+                            worker.parse_line(rest.data());
+                            rest.clear();
+                        }
+                        ++ppos;
+                    }
+
+                    for (auto pos = input.find_first_of("\n\r", ppos);
+                         pos != std::string::npos;
+                         pos = input.find_first_of("\n\r", ppos)) {
+                        const char* data = &input[ppos];
+                        input[pos] = '\0';
+                        if (data[0] != '\0') {
+                            worker.parse_line(data);
+                        }
+                        ppos = pos + 1;
+                        if (ppos >= input.size()) {
+                            break;
+                        }
+                    }
+                    rest.assign(input, ppos, std::string::npos);
+                }
+
+                if (!rest.empty()) {
+                    worker.parse_line(rest.data());
+                }
+            }
+
             class OPLParser : public Parser {
 
                 osmium::memory::Buffer m_buffer{1024*1024};
-                const char* m_data = nullptr;
                 uint64_t m_line_count = 0;
 
                 void maybe_flush() {
@@ -67,13 +113,6 @@ namespace osmium {
                     }
                 }
 
-                void parse_line() {
-                    if (opl_parse_line(m_line_count, m_data, m_buffer, read_types())) {
-                        maybe_flush();
-                    }
-                    ++m_line_count;
-                }
-
             public:
 
                 explicit OPLParser(parser_arguments& args) :
@@ -83,44 +122,17 @@ namespace osmium {
 
                 ~OPLParser() noexcept final = default;
 
+                void parse_line(const char* data) {
+                    if (opl_parse_line(m_line_count, data, m_buffer, read_types())) {
+                        maybe_flush();
+                    }
+                    ++m_line_count;
+                }
+
                 void run() final {
                     osmium::thread::set_thread_name("_osmium_opl_in");
 
-                    std::string rest;
-                    while (!input_done()) {
-                        std::string input{get_input()};
-                        std::string::size_type ppos = 0;
-
-                        if (!rest.empty()) {
-                            ppos = input.find('\n');
-                            if (ppos == std::string::npos) {
-                                rest.append(input);
-                                continue;
-                            }
-                            rest.append(input.substr(0, ppos));
-                            m_data = rest.data();
-                            parse_line();
-                            rest.clear();
-                        }
-
-                        std::string::size_type pos = input.find('\n', ppos);
-                        while (pos != std::string::npos) {
-                            m_data = &input[ppos];
-                            input[pos] = '\0';
-                            parse_line();
-                            ppos = pos + 1;
-                            if (ppos >= input.size()) {
-                                break;
-                            }
-                            pos = input.find('\n', ppos);
-                        }
-                        rest = input.substr(ppos);
-                    }
-
-                    if (!rest.empty()) {
-                        m_data = rest.data();
-                        parse_line();
-                    }
+                    line_by_line(*this);
 
                     if (m_buffer.committed() > 0) {
                         send_to_output_queue(std::move(m_buffer));
diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp
index 5fe8bd9..21d5e86 100644
--- a/include/osmium/io/detail/output_format.hpp
+++ b/include/osmium/io/detail/output_format.hpp
@@ -197,6 +197,36 @@ namespace osmium {
 
             }; // class OutputFormatFactory
 
+            class BlackholeOutputFormat : public osmium::io::detail::OutputFormat {
+
+            public:
+
+                BlackholeOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& /*file*/, future_string_queue_type& output_queue) :
+                    OutputFormat(pool, output_queue) {
+                }
+
+                BlackholeOutputFormat(const BlackholeOutputFormat&) = delete;
+                BlackholeOutputFormat& operator=(const BlackholeOutputFormat&) = delete;
+
+                ~BlackholeOutputFormat() noexcept final = default;
+
+                void write_buffer(osmium::memory::Buffer&& /*buffer*/) final {
+                }
+
+            }; // class BlackholeOutputFormat
+
+            // we want the register_output_format() function to run, setting
+            // the variable is only a side-effect, it will never be used
+            const bool registered_blackhole_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::blackhole,
+                [](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
+                    return new osmium::io::detail::BlackholeOutputFormat(pool, file, output_queue);
+            });
+
+            // dummy function to silence the unused variable warning from above
+            inline bool get_registered_blackhole_output() noexcept {
+                return registered_blackhole_output;
+            }
+
         } // namespace detail
 
     } // namespace io
diff --git a/include/osmium/io/file.hpp b/include/osmium/io/file.hpp
index bf7c874..4b40924 100644
--- a/include/osmium/io/file.hpp
+++ b/include/osmium/io/file.hpp
@@ -235,6 +235,9 @@ namespace osmium {
                 } else if (suffixes.back() == "debug") {
                     m_file_format = file_format::debug;
                     suffixes.pop_back();
+                } else if (suffixes.back() == "blackhole") {
+                    m_file_format = file_format::blackhole;
+                    suffixes.pop_back();
                 }
 
                 if (suffixes.empty()) {
diff --git a/include/osmium/io/file_format.hpp b/include/osmium/io/file_format.hpp
index 620082c..cde1299 100644
--- a/include/osmium/io/file_format.hpp
+++ b/include/osmium/io/file_format.hpp
@@ -40,13 +40,14 @@ namespace osmium {
     namespace io {
 
         enum class file_format {
-            unknown = 0,
-            xml     = 1,
-            pbf     = 2,
-            opl     = 3,
-            json    = 4,
-            o5m     = 5,
-            debug   = 6
+            unknown   = 0,
+            xml       = 1,
+            pbf       = 2,
+            opl       = 3,
+            json      = 4,
+            o5m       = 5,
+            debug     = 6,
+            blackhole = 7
         };
 
         enum class read_meta {
@@ -68,6 +69,8 @@ namespace osmium {
                     return "O5M";
                 case file_format::debug:
                     return "DEBUG";
+                case file_format::blackhole:
+                    return "BLACKHOLE";
                 default: // file_format::unknown
                     break;
             }
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index 5539e7a..ef64954 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -56,7 +56,7 @@ namespace osmium {
 
             // Maximum number of allowed pool threads (just to keep the user
             // from setting something silly).
-            constexpr const int max_pool_threads = 256;
+            constexpr const int max_pool_threads = 32;
 
             inline int get_pool_size(int num_threads, int user_setting, unsigned hardware_concurrency) {
                 if (num_threads == 0) {
diff --git a/include/osmium/util/file.hpp b/include/osmium/util/file.hpp
index 431445b..4af6d77 100644
--- a/include/osmium/util/file.hpp
+++ b/include/osmium/util/file.hpp
@@ -47,6 +47,7 @@ DEALINGS IN THE SOFTWARE.
 #  endif
 # include <io.h>
 # include <windows.h>
+# include <crtdbg.h>
 #endif
 
 #ifndef _MSC_VER
@@ -59,6 +60,44 @@ namespace osmium {
 
     namespace util {
 
+#ifdef _MSC_VER
+        namespace detail {
+
+            // Disable parameter validation on Windows and reenable it
+            // automatically when scope closes.
+            // https://docs.microsoft.com/en-us/cpp/c-runtime-library/parameter-validation
+            class disable_invalid_parameter_handler {
+
+                static void invalid_parameter_handler(
+                        const wchar_t* expression,
+                        const wchar_t* function,
+                        const wchar_t* file,
+                        unsigned int line,
+                        uintptr_t pReserved
+                        ) {
+                    // do nothing
+                }
+
+                _invalid_parameter_handler old_handler;
+                int old_report_mode;
+
+            public:
+
+                disable_invalid_parameter_handler() :
+                    old_handler(_set_thread_local_invalid_parameter_handler(invalid_parameter_handler)),
+                    old_report_mode(_CrtSetReportMode(_CRT_ASSERT, 0)) {
+                }
+
+                ~disable_invalid_parameter_handler() {
+                    _CrtSetReportMode(_CRT_ASSERT, old_report_mode);
+                    _set_thread_local_invalid_parameter_handler(old_handler);
+                }
+
+            }; // class disable_invalid_parameter_handler
+
+        }
+#endif
+
         /**
          * Get file size.
          * This is a small wrapper around a system call.
@@ -70,9 +109,10 @@ namespace osmium {
         inline std::size_t file_size(int fd) {
 #ifdef _MSC_VER
             // Windows implementation
+            detail::disable_invalid_parameter_handler diph;
             // https://msdn.microsoft.com/en-us/library/dfbc2kec.aspx
             const auto size = ::_filelengthi64(fd);
-            if (size == -1L) {
+            if (size < 0) {
                 throw std::system_error{errno, std::system_category(), "Could not get file size"};
             }
             return static_cast<std::size_t>(size);
@@ -93,6 +133,7 @@ namespace osmium {
          * @param name File name
          * @returns file size
          * @throws std::system_error If system call failed
+         * @pre name must not be nullptr
          */
         inline std::size_t file_size(const char* name) {
 #ifdef _MSC_VER
@@ -134,6 +175,7 @@ namespace osmium {
          */
         inline void resize_file(int fd, std::size_t new_size) {
 #ifdef _WIN32
+            detail::disable_invalid_parameter_handler diph;
             // https://msdn.microsoft.com/en-us/library/whx354w1.aspx
             if (::_chsize_s(fd, static_cast_with_assert<__int64>(new_size)) != 0) {
 #else
@@ -166,6 +208,7 @@ namespace osmium {
          */
         inline std::size_t file_offset(int fd) {
 #ifdef _MSC_VER
+            detail::disable_invalid_parameter_handler diph;
             // https://msdn.microsoft.com/en-us/library/1yee101t.aspx
             const auto offset = _lseeki64(fd, 0, SEEK_CUR);
 #else
@@ -182,6 +225,7 @@ namespace osmium {
          */
         inline bool isatty(int fd) {
 #ifdef _MSC_VER
+            detail::disable_invalid_parameter_handler diph;
             // https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
             return _isatty(fd) != 0;
 #else
diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp
index a26e372..c405979 100644
--- a/include/osmium/util/memory_mapping.hpp
+++ b/include/osmium/util/memory_mapping.hpp
@@ -139,7 +139,7 @@ namespace osmium {
 
             static std::size_t check_size(std::size_t size) {
                 if (size == 0) {
-                    throw std::runtime_error{"Zero-sized mapping is not allowed."};
+                    return osmium::util::get_pagesize();
                 }
                 return size;
             }
diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp
index eb1aa36..004ad0e 100644
--- a/include/osmium/version.hpp
+++ b/include/osmium/version.hpp
@@ -35,8 +35,8 @@ DEALINGS IN THE SOFTWARE.
 
 #define LIBOSMIUM_VERSION_MAJOR 2
 #define LIBOSMIUM_VERSION_MINOR 13
-#define LIBOSMIUM_VERSION_PATCH 0
+#define LIBOSMIUM_VERSION_PATCH 1
 
-#define LIBOSMIUM_VERSION_STRING "2.13.0"
+#define LIBOSMIUM_VERSION_STRING "2.13.1"
 
 #endif // OSMIUM_VERSION_HPP
diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt
index 90bf76e..f6d427d 100644
--- a/test/examples/CMakeLists.txt
+++ b/test/examples/CMakeLists.txt
@@ -8,13 +8,40 @@
 
 message(STATUS "Configuring example tests")
 
-file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/t/*)
+set(EXAMPLE_TESTS
+    amenity_list
+    area_test
+    change_tags
+    convert
+    count
+    create_pois
+    debug
+    dump_internal
+    filter_discussions
+    index_lookup
+    location_cache
+    pub_names
+    read
+    read_with_progress
+    road_length
+    tiles
+)
 
-foreach(_dir ${_dirs})
+# This is the list of all tests that are in the repository. It should be the
+# same as the configured list above, if not we'll find out about that below
+# and create a fatal error.
+file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/t ${CMAKE_CURRENT_SOURCE_DIR}/t/*)
+
+foreach(_dir ${EXAMPLE_TESTS})
     message(STATUS "  adding test: ${_dir}")
-    add_subdirectory("${_dir}")
+    add_subdirectory("t/${_dir}")
+    list(REMOVE_ITEM _dirs ${_dir})
 endforeach()
 
+if(NOT ${_dirs} STREQUAL "")
+    message(FATAL_ERROR "Found example tests that are not configured: ${_dirs}")
+endif()
+
 message(STATUS "Configuring example tests - done")
 
 
diff --git a/test/examples/t/change_tags/.gitattributes b/test/examples/t/change_tags/.gitattributes
new file mode 100644
index 0000000..f4111dd
--- /dev/null
+++ b/test/examples/t/change_tags/.gitattributes
@@ -0,0 +1 @@
+result.osm text eol=lf
diff --git a/test/examples/t/dump_internal/CMakeLists.txt b/test/examples/t/dump_internal/CMakeLists.txt
index 6938563..fa942ef 100644
--- a/test/examples/t/dump_internal/CMakeLists.txt
+++ b/test/examples/t/dump_internal/CMakeLists.txt
@@ -2,40 +2,35 @@
 add_test(NAME examples_dump_internal
          COMMAND osmium_dump_internal ${CMAKE_CURRENT_SOURCE_DIR}/data.osm ${CMAKE_CURRENT_BINARY_DIR}/out)
 
-# XXX Disable failing tests on Windows until we figure out what's wrong
-# See https://github.com/osmcode/libosmium/issues/220
-if(NOT MSVC)
 
-    add_test(NAME examples_dump_internal_index_nodes
-            COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/nodes.idx --type=offset --dump)
+add_test(NAME examples_dump_internal_index_nodes
+         COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/nodes.idx --type=offset --dump)
 
-    set_tests_properties(examples_dump_internal_index_nodes PROPERTIES
-                        DEPENDS examples_dump_internal
-                        PASS_REGULAR_EXPRESSION "^701000 .*\n701001 .*\n")
+set_tests_properties(examples_dump_internal_index_nodes PROPERTIES
+                     DEPENDS examples_dump_internal
+                     PASS_REGULAR_EXPRESSION "^701000 .*\n701001 .*\n")
 
 
-    add_test(NAME examples_dump_internal_index_ways
-            COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/ways.idx --type=offset --dump)
+add_test(NAME examples_dump_internal_index_ways
+         COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/ways.idx --type=offset --dump)
 
-    set_tests_properties(examples_dump_internal_index_ways PROPERTIES
-                        DEPENDS examples_dump_internal
-                        PASS_REGULAR_EXPRESSION "^701800 .*\n701801 .*\n")
+set_tests_properties(examples_dump_internal_index_ways PROPERTIES
+                     DEPENDS examples_dump_internal
+                     PASS_REGULAR_EXPRESSION "^701800 .*\n701801 .*\n")
 
 
-    add_test(NAME examples_dump_internal_map_node2way_dump
-            COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --dump)
+add_test(NAME examples_dump_internal_map_node2way_dump
+         COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --dump)
 
-    set_tests_properties(examples_dump_internal_map_node2way_dump PROPERTIES
-                        DEPENDS examples_dump_internal
-                        PASS_REGULAR_EXPRESSION "^701000 701800\n701000 701801\n701001 701800\n")
+set_tests_properties(examples_dump_internal_map_node2way_dump PROPERTIES
+                     DEPENDS examples_dump_internal
+                     PASS_REGULAR_EXPRESSION "^701000 701800\n701000 701801\n701001 701800\n")
 
 
-    add_test(NAME examples_dump_internal_map_node2way_search
-            COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --search=701002)
+add_test(NAME examples_dump_internal_map_node2way_search
+         COMMAND osmium_index_lookup --list=${CMAKE_CURRENT_BINARY_DIR}/out/node2way.map --type=id --search=701002)
 
-    set_tests_properties(examples_dump_internal_map_node2way_search PROPERTIES
-                        DEPENDS examples_dump_internal
-                        PASS_REGULAR_EXPRESSION "^701002 701800\n701002 701801\n$")
-
-endif()
+set_tests_properties(examples_dump_internal_map_node2way_search PROPERTIES
+                     DEPENDS examples_dump_internal
+                     PASS_REGULAR_EXPRESSION "^701002 701800\n701002 701801\n$")
 
diff --git a/test/t/io/data-cr.opl b/test/t/io/data-cr.opl
new file mode 100644
index 0000000..aef666e
--- /dev/null
+++ b/test/t/io/data-cr.opl
@@ -0,0 +1 @@
+n1 v1 dV c1 t2014-01-01T00:00:00Z i1 utest T x1.02 y1.02
diff --git a/test/t/io/test_file_formats.cpp b/test/t/io/test_file_formats.cpp
index 8b63f94..284d477 100644
--- a/test/t/io/test_file_formats.cpp
+++ b/test/t/io/test_file_formats.cpp
@@ -189,6 +189,46 @@ TEST_CASE("Override file format by suffix 'osh.opl.gz'") {
     f.check();
 }
 
+TEST_CASE("File format by suffix 'blackhole'") {
+    const osmium::io::File f{"test.blackhole"};
+    REQUIRE(osmium::io::file_format::blackhole == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'blackhole'") {
+    const osmium::io::File f{"test", "blackhole"};
+    REQUIRE(osmium::io::file_format::blackhole == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.blackhole'") {
+    const osmium::io::File f{"test", "osm.blackhole"};
+    REQUIRE(osmium::io::file_format::blackhole == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.blackhole.bz2'") {
+    const osmium::io::File f{"test", "osm.blackhole.bz2"};
+    REQUIRE(osmium::io::file_format::blackhole == f.format());
+    REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osh.blackhole.gz'") {
+    const osmium::io::File f{"test", "osh.blackhole.gz"};
+    REQUIRE(osmium::io::file_format::blackhole == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
 TEST_CASE("Override file format by suffix 'osh.pbf'") {
     const osmium::io::File f{"test", "osh.pbf"};
     REQUIRE(osmium::io::file_format::pbf == f.format());
diff --git a/test/t/io/test_opl_parser.cpp b/test/t/io/test_opl_parser.cpp
index 7ecf2b8..88cd570 100644
--- a/test/t/io/test_opl_parser.cpp
+++ b/test/t/io/test_opl_parser.cpp
@@ -1052,6 +1052,16 @@ TEST_CASE("Parse OPL using Reader") {
     REQUIRE(node.id() == 1);
 }
 
+TEST_CASE("Parse OPL with CRLF line ending using Reader") {
+    osmium::io::File file{with_data_dir("t/io/data-cr.opl")};
+    osmium::io::Reader reader{file};
+
+    const auto buffer = reader.read();
+    REQUIRE(buffer);
+    const auto& node = buffer.get<osmium::Node>(0);
+    REQUIRE(node.id() == 1);
+}
+
 TEST_CASE("Parse OPL with missing newline using Reader") {
     osmium::io::File file{with_data_dir("t/io/data-nonl.opl")};
     osmium::io::Reader reader{file};
@@ -1062,3 +1072,159 @@ TEST_CASE("Parse OPL with missing newline using Reader") {
     REQUIRE(node.id() == 1);
 }
 
+class lbl_tester {
+
+    std::vector<std::string> m_inputs;
+    std::vector<std::string> m_outputs;
+
+public:
+
+    lbl_tester(const std::initializer_list<std::string>& inputs,
+               const std::initializer_list<std::string>& outputs) :
+        m_inputs(inputs),
+        m_outputs(outputs) {
+    }
+
+    bool input_done() {
+        return m_inputs.empty();
+    }
+
+    std::string get_input() {
+        REQUIRE_FALSE(m_inputs.empty());
+        std::string data = std::move(m_inputs.front());
+        m_inputs.erase(m_inputs.begin());
+        return data;
+    }
+
+    void parse_line(const char *data) {
+        REQUIRE_FALSE(m_outputs.empty());
+        REQUIRE(m_outputs.front() == data);
+        m_outputs.erase(m_outputs.begin());
+    }
+
+    void check() {
+        REQUIRE(m_inputs.empty());
+        REQUIRE(m_outputs.empty());
+    }
+
+}; // class lbl_tester
+
+void check_lbl(const std::initializer_list<std::string>& in,
+               const std::initializer_list<std::string>& out) {
+    lbl_tester tester{in, out};
+    osmium::io::detail::line_by_line(tester);
+    tester.check();
+}
+
+TEST_CASE("line_by_line for OPL parser 1") {
+    check_lbl({""}, {});
+}
+
+TEST_CASE("line_by_line for OPL parser 2") {
+    check_lbl({"\n"}, {});
+}
+
+TEST_CASE("line_by_line for OPL parser 3") {
+    check_lbl({"foo\n"}, {"foo"});
+}
+
+TEST_CASE("line_by_line for OPL parser 4") {
+    check_lbl({"foo"}, {"foo"});
+}
+
+TEST_CASE("line_by_line for OPL parser 5") {
+    check_lbl({"foo\nbar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 6") {
+    check_lbl({"foo\nbar"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 7") {
+    check_lbl({"foo\nbar\nbaz\n"}, {"foo", "bar", "baz"});
+}
+
+TEST_CASE("line_by_line for OPL parser 8") {
+    check_lbl({"foo\n", "bar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 9") {
+    check_lbl({"foo\nb", "ar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 10") {
+    check_lbl({"foo\nb", "ar\n", "baz\n"}, {"foo", "bar", "baz"});
+}
+
+TEST_CASE("line_by_line for OPL parser 11") {
+    check_lbl({"foo", "\nbar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 12") {
+    check_lbl({"foo", "\nbar"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 13") {
+    check_lbl({"foo", "\nbar", "\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 14") {
+    check_lbl({"foo\n", "b", "ar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 15") {
+    check_lbl({"foo\n", "ba", "r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 16") {
+    check_lbl({"foo", "\n", "bar\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 17") {
+    check_lbl({"foo\r\nbar\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 18") {
+    check_lbl({"foo\r\nb", "ar\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 19") {
+    check_lbl({"foo\r\nb", "ar\n", "baz\r\n"}, {"foo", "bar", "baz"});
+}
+
+TEST_CASE("line_by_line for OPL parser 20") {
+    check_lbl({"foo", "\r\nbar\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 21") {
+    check_lbl({"foo\r", "\nbar"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 22") {
+    check_lbl({"foo", "\r\nbar\r", "\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 23") {
+    check_lbl({"foo\r\n", "b", "ar\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 24") {
+    check_lbl({"foo\n", "ba", "r\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 25") {
+    check_lbl({"foo", "\n", "bar\r\n"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 26") {
+    check_lbl({"foo", "\n\r", "bar\r"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 27") {
+    check_lbl({"foo\r", "bar\n\r"}, {"foo", "bar"});
+}
+
+TEST_CASE("line_by_line for OPL parser 28") {
+    check_lbl({"foo\nb", "ar"}, {"foo", "bar"});
+}
+
diff --git a/test/t/memory/test_item.cpp b/test/t/memory/test_item.cpp
index a81003d..81e18a9 100644
--- a/test/t/memory/test_item.cpp
+++ b/test/t/memory/test_item.cpp
@@ -14,12 +14,16 @@ TEST_CASE("padded length") {
     REQUIRE(osmium::memory::padded_length(2147483648) == 2147483648);
     REQUIRE(osmium::memory::padded_length(2147483650) == 2147483656);
 
-    REQUIRE(osmium::memory::padded_length(4294967295) == 4294967296);
-    REQUIRE(osmium::memory::padded_length(4294967296) == 4294967296);
-    REQUIRE(osmium::memory::padded_length(4294967297) == 4294967304);
+    // The following checks only make sense on a 64 bit system (with
+    // sizeof(size_t) == 8), because the numbers are too large for 32 bit.
+    // The casts to size_t do nothing on a 64 bit system, on a 32 bit system
+    // they bring the numbers into the right range and everything still works.
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(4294967295)) == static_cast<std::size_t>(4294967296));
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(4294967296)) == static_cast<std::size_t>(4294967296));
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(4294967297)) == static_cast<std::size_t>(4294967304));
 
-    REQUIRE(osmium::memory::padded_length(7999999999) == 8000000000);
-    REQUIRE(osmium::memory::padded_length(8000000000) == 8000000000);
-    REQUIRE(osmium::memory::padded_length(8000000001) == 8000000008);
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(7999999999)) == static_cast<std::size_t>(8000000000));
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(8000000000)) == static_cast<std::size_t>(8000000000));
+    REQUIRE(osmium::memory::padded_length(static_cast<std::size_t>(8000000001)) == static_cast<std::size_t>(8000000008));
 }
 
diff --git a/test/t/osm/test_location.cpp b/test/t/osm/test_location.cpp
index 28cf16e..a47a49c 100644
--- a/test/t/osm/test_location.cpp
+++ b/test/t/osm/test_location.cpp
@@ -25,32 +25,32 @@ TEST_CASE("Location instantiation with double parameters") {
     REQUIRE_FALSE(loc1.is_undefined());
     REQUIRE(12000000 == loc1.x());
     REQUIRE(45000000 == loc1.y());
-    REQUIRE(1.2 == loc1.lon());
-    REQUIRE(4.5 == loc1.lat());
+    REQUIRE(1.2 == Approx(loc1.lon()));
+    REQUIRE(4.5 == Approx(loc1.lat()));
 
     const osmium::Location loc2{loc1};
-    REQUIRE(4.5 == loc2.lat());
+    REQUIRE(4.5 == Approx(loc2.lat()));
 
     const osmium::Location loc3 = loc1;
-    REQUIRE(4.5 == loc3.lat());
+    REQUIRE(4.5 == Approx(loc3.lat()));
 }
 
 TEST_CASE("Location instantiation with double parameters constructor with universal initializer") {
     const osmium::Location loc{2.2, 3.3};
-    REQUIRE(2.2 == loc.lon());
-    REQUIRE(3.3 == loc.lat());
+    REQUIRE(2.2 == Approx(loc.lon()));
+    REQUIRE(3.3 == Approx(loc.lat()));
 }
 
 TEST_CASE("Location instantiation with double parameters constructor with initializer list") {
     const osmium::Location loc({4.4, 5.5});
-    REQUIRE(4.4 == loc.lon());
-    REQUIRE(5.5 == loc.lat());
+    REQUIRE(4.4 == Approx(loc.lon()));
+    REQUIRE(5.5 == Approx(loc.lat()));
 }
 
 TEST_CASE("Location instantiation with double parameters operator equal") {
     const osmium::Location loc = {5.5, 6.6};
-    REQUIRE(5.5 == loc.lon());
-    REQUIRE(6.6 == loc.lat());
+    REQUIRE(5.5 == Approx(loc.lon()));
+    REQUIRE(6.6 == Approx(loc.lat()));
 }
 
 TEST_CASE("Location equality") {
diff --git a/test/t/thread/test_pool.cpp b/test/t/thread/test_pool.cpp
index 5f0e5b6..567d8de 100644
--- a/test/t/thread/test_pool.cpp
+++ b/test/t/thread/test_pool.cpp
@@ -43,8 +43,8 @@ TEST_CASE("number of threads in pool") {
     REQUIRE(osmium::thread::detail::get_pool_size( 0,  8, 16) ==  8);
 
     // outliers
-    REQUIRE(osmium::thread::detail::get_pool_size(-100, 0, 16) ==   1);
-    REQUIRE(osmium::thread::detail::get_pool_size(1000, 0, 16) == 256);
+    REQUIRE(osmium::thread::detail::get_pool_size(-100, 0, 16) ==  1);
+    REQUIRE(osmium::thread::detail::get_pool_size(1000, 0, 16) == 32);
 
 }
 
diff --git a/test/t/util/.gitattributes b/test/t/util/.gitattributes
new file mode 100644
index 0000000..a22ace4
--- /dev/null
+++ b/test/t/util/.gitattributes
@@ -0,0 +1 @@
+known_file_size text eol=lf
diff --git a/test/t/util/known_file_size b/test/t/util/known_file_size
new file mode 100644
index 0000000..1369e1e
--- /dev/null
+++ b/test/t/util/known_file_size
@@ -0,0 +1 @@
+this file has size 22
diff --git a/test/t/util/test_file.cpp b/test/t/util/test_file.cpp
index 17afd07..a5d65ce 100644
--- a/test/t/util/test_file.cpp
+++ b/test/t/util/test_file.cpp
@@ -1,71 +1,51 @@
 #include "catch.hpp"
+#include "utils.hpp"
 
+#include <osmium/io/detail/read_write.hpp>
 #include <osmium/util/file.hpp>
 
-#ifdef _WIN32
-#include <crtdbg.h>
-// https://msdn.microsoft.com/en-us/library/ksazx244.aspx
-// https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
-class DoNothingInvalidParameterHandler {
-
-    static void invalid_parameter_handler(
-                    const wchar_t* expression,
-                    const wchar_t* function,
-                    const wchar_t* file,
-                    unsigned int line,
-                    uintptr_t pReserved
-                ) {
-        // do nothing
-    }
-
-    _invalid_parameter_handler old_handler;
-
-public:
-
-    DoNothingInvalidParameterHandler() :
-        old_handler(_set_invalid_parameter_handler(invalid_parameter_handler)) {
-        _CrtSetReportMode(_CRT_ASSERT, 0);
-    }
-
-    ~DoNothingInvalidParameterHandler() {
-        _set_invalid_parameter_handler(old_handler);
-    }
-
-}; // class InvalidParameterHandler
-#endif
-
-
-TEST_CASE("file_size") {
-
-#ifdef _WIN32
-    DoNothingInvalidParameterHandler handler;
-#endif
+TEST_CASE("file_size(int) and file_offset() of known file") {
+    std::string file_name{with_data_dir("t/util/known_file_size")};
+    const int fd = osmium::io::detail::open_for_reading(file_name);
+    REQUIRE(fd > 0);
+    REQUIRE(osmium::util::file_size(fd) == 22);
+    REQUIRE(osmium::util::file_offset(fd) == 0);
+    REQUIRE_FALSE(osmium::util::isatty(fd));
+}
 
-    SECTION("illegal fd should throw") {
-        REQUIRE_THROWS_AS(osmium::util::file_size(-1), const std::system_error&);
-    }
+TEST_CASE("file_size(std::string) of known file") {
+    std::string file_name{with_data_dir("t/util/known_file_size")};
+    REQUIRE(osmium::util::file_size(file_name) == 22);
+}
 
-    SECTION("unused fd should throw") {
-        // its unlikely that fd 1000 is open...
-        REQUIRE_THROWS_AS(osmium::util::file_size(1000), const std::system_error&);
-    }
+TEST_CASE("file_size(const char*) of known file") {
+    std::string file_name{with_data_dir("t/util/known_file_size")};
+    REQUIRE(osmium::util::file_size(file_name.c_str()) == 22);
+}
 
+TEST_CASE("file_size() with illegal fd should throw") {
+    REQUIRE_THROWS_AS(osmium::util::file_size(-1), const std::system_error&);
 }
 
-TEST_CASE("resize_file") {
+TEST_CASE("file_size() with unused fd should throw") {
+    // its unlikely that fd 1000 is open...
+    REQUIRE_THROWS_AS(osmium::util::file_size(1000), const std::system_error&);
+}
 
-#ifdef _WIN32
-    DoNothingInvalidParameterHandler handler;
-#endif
+TEST_CASE("file_size() of unknown file should throw") {
+    REQUIRE_THROWS_AS(osmium::util::file_size("unknown file"), const std::system_error&);
+}
 
-    SECTION("illegal fd should throw") {
-        REQUIRE_THROWS_AS(osmium::util::resize_file(-1, 10), const std::system_error&);
-    }
+TEST_CASE("resize_file() with illegal fd should throw") {
+    REQUIRE_THROWS_AS(osmium::util::resize_file(-1, 10), const std::system_error&);
+}
 
-    SECTION("unused fd should throw") {
-        // its unlikely that fd 1000 is open...
-        REQUIRE_THROWS_AS(osmium::util::resize_file(1000, 10), const std::system_error&);
-    }
+TEST_CASE("resize_file() with unused fd should throw") {
+    // its unlikely that fd 1000 is open...
+    REQUIRE_THROWS_AS(osmium::util::resize_file(1000, 10), const std::system_error&);
+}
 
+TEST_CASE("get_pagesize()") {
+    REQUIRE(osmium::util::get_pagesize() > 0);
 }
 
diff --git a/test/t/util/test_memory_mapping.cpp b/test/t/util/test_memory_mapping.cpp
index 966f670..7330aaf 100644
--- a/test/t/util/test_memory_mapping.cpp
+++ b/test/t/util/test_memory_mapping.cpp
@@ -32,8 +32,9 @@ TEST_CASE("Anonymous mapping: simple memory mapping should work") {
     mapping.unmap(); // second unmap is okay
 }
 
-TEST_CASE("Anonymous mapping: memory mapping of zero length should fail") {
-    REQUIRE_THROWS(osmium::util::MemoryMapping(0, osmium::util::MemoryMapping::mapping_mode::write_private));
+TEST_CASE("Anonymous mapping: memory mapping of zero length should result in memory mapping of pagesize length") {
+    osmium::util::MemoryMapping mapping{0, osmium::util::MemoryMapping::mapping_mode::write_private};
+    REQUIRE(mapping.size() == osmium::util::get_pagesize());
 }
 
 TEST_CASE("Anonymous mapping: moving a memory mapping should work") {
@@ -145,6 +146,21 @@ TEST_CASE("File-based mapping: writing to a mapped file should work") {
     REQUIRE(0 == unlink(filename));
 }
 
+TEST_CASE("File-based mapping: Reading from a zero-sized mapped file should work") {
+    char filename[] = "test_mmap_read_zero_XXXXXX";
+    const int fd = mkstemp(filename);
+    REQUIRE(fd > 0);
+
+    {
+        osmium::util::MemoryMapping mapping{0, osmium::util::MemoryMapping::mapping_mode::readonly, fd};
+        REQUIRE(mapping.size() > 0);
+        mapping.unmap();
+    }
+
+    REQUIRE(0 == close(fd));
+    REQUIRE(0 == unlink(filename));
+}
+
 TEST_CASE("File-based mapping: writing to a privately mapped file should work") {
     char filename[] = "test_mmap_write_XXXXXX";
     const int fd = mkstemp(filename);

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



More information about the Pkg-grass-devel mailing list