[libosmium] 01/05: Imported Upstream version 2.8.0
Bas Couwenberg
sebastic at debian.org
Thu Aug 4 09:01:46 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository libosmium.
commit b90fb1a5efa55bf3eeb98aae0f02114ceae12595
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Thu Aug 4 10:21:26 2016 +0200
Imported Upstream version 2.8.0
---
.travis.yml | 83 +--
CHANGELOG.md | 39 +-
CMakeLists.txt | 25 +-
appveyor.yml | 2 -
examples/CMakeLists.txt | 32 +-
examples/osmium_index.cpp | 131 ++--
include/osmium/area/assembler.hpp | 4 +-
include/osmium/geom/factory.hpp | 4 +-
include/osmium/geom/geojson.hpp | 2 +-
include/osmium/geom/geos.hpp | 15 +-
include/osmium/geom/ogr.hpp | 3 +-
include/osmium/geom/rapid_geojson.hpp | 2 +-
include/osmium/geom/wkb.hpp | 9 +-
include/osmium/geom/wkt.hpp | 30 +-
include/osmium/io/detail/debug_output_format.hpp | 2 +-
include/osmium/io/detail/pbf_decoder.hpp | 203 +++---
include/osmium/io/detail/pbf_input_format.hpp | 6 +-
include/osmium/io/detail/pbf_output_format.hpp | 61 +-
include/osmium/io/detail/string_table.hpp | 57 +-
include/osmium/io/detail/zlib.hpp | 6 +-
include/osmium/io/reader.hpp | 22 +-
include/osmium/io/writer.hpp | 12 +-
include/osmium/osm/location.hpp | 41 +-
include/osmium/relations/collector.hpp | 7 -
include/osmium/thread/pool.hpp | 21 +-
include/osmium/thread/queue.hpp | 60 +-
include/osmium/util/config.hpp | 17 +-
include/osmium/util/delta.hpp | 55 +-
include/osmium/version.hpp | 6 +-
include/protozero/byteswap.hpp | 6 +-
include/protozero/exception.hpp | 8 +-
include/protozero/iterators.hpp | 373 +++++++++++
include/protozero/pbf_builder.hpp | 20 +-
include/protozero/pbf_message.hpp | 10 +-
include/protozero/pbf_reader.hpp | 763 +++++++++--------------
include/protozero/pbf_writer.hpp | 240 ++++---
include/protozero/types.hpp | 170 ++++-
include/protozero/varint.hpp | 125 ++--
include/protozero/version.hpp | 17 +-
test/CMakeLists.txt | 41 +-
test/include/catch.hpp | 280 +++++----
test/t/basic/test_location.cpp | 33 +-
test/t/geom/test_geos.cpp | 13 +-
test/t/geom/test_wkb.cpp | 17 +-
test/t/geom/test_wkt.cpp | 15 +
test/t/io/test_string_table.cpp | 40 +-
test/t/util/test_delta.cpp | 22 -
47 files changed, 1966 insertions(+), 1184 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 9da37fa..9918112 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,24 +4,34 @@
#
#-----------------------------------------------------------------------------
-language: cpp
+language: generic
sudo: false
+cache:
+ directories:
+ - $HOME/.ccache
+
+env:
+ global:
+ - CCACHE_TEMPDIR=/tmp/.ccache-temp
+ - CCACHE_COMPRESS=1
+ - CASHER_TIME_OUT=1000
+
matrix:
include:
# 1/ Linux Clang Builds
- os: linux
- compiler: clang
+ compiler: linux-clang35-release
addons:
apt:
sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
- packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
+ packages: ['clang-3.5', 'cmake', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.5' BUILD_TYPE='Release'
- os: linux
- compiler: clang
+ compiler: linux-clang35-dev
addons:
apt:
sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
@@ -30,42 +40,42 @@ matrix:
- os: linux
- compiler: clang
+ compiler: linux-clang37-release
addons:
apt:
- sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest']
- packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
- env: COMPILER='clang++-3.6' BUILD_TYPE='Release'
+ sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
+ packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
+ env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
- os: linux
- compiler: clang
+ compiler: linux-clang37-dev
addons:
apt:
- sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest']
- packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
- env: COMPILER='clang++-3.6' BUILD_TYPE='Dev'
+ sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
+ packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
+ env: COMPILER='clang++-3.7' BUILD_TYPE='Dev'
- os: linux
- compiler: clang
+ compiler: linux-clang38-release
addons:
apt:
- sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
- packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
- env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
+ sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest']
+ packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
+ env: COMPILER='clang++-3.8' BUILD_TYPE='Release'
- os: linux
- compiler: clang
+ compiler: linux-clang38-dev
addons:
apt:
- sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
- packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
- env: COMPILER='clang++-3.7' BUILD_TYPE='Dev'
+ sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest']
+ packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
+ env: COMPILER='clang++-3.8' BUILD_TYPE='Dev'
# 2/ Linux GCC Builds
- os: linux
- compiler: gcc
+ compiler: linux-gcc48-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -73,7 +83,7 @@ matrix:
env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release'
- os: linux
- compiler: gcc
+ compiler: linux-gcc48-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -82,7 +92,7 @@ matrix:
- os: linux
- compiler: gcc
+ compiler: linux-gcc49-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -90,7 +100,7 @@ matrix:
env: COMPILER='g++-4.9' BUILD_TYPE='Release'
- os: linux
- compiler: gcc
+ compiler: linux-gcc49-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -99,7 +109,7 @@ matrix:
- os: linux
- compiler: gcc
+ compiler: linux-gcc50-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -107,7 +117,7 @@ matrix:
env: COMPILER='g++-5' BUILD_TYPE='Release'
- os: linux
- compiler: gcc
+ compiler: linux-gcc50-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
@@ -118,24 +128,24 @@ matrix:
# 3/ OSX Clang Builds
- os: osx
osx_image: xcode6.4
- compiler: clang
- env: COMPILER='clang++' BUILD_TYPE='Dev'
+ compiler: xcode64-clang-release
+ env: COMPILER='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode6.4
- compiler: clang
- env: COMPILER='clang++' BUILD_TYPE='Release'
+ compiler: xcode64-clang-dev
+ env: COMPILER='clang++' BUILD_TYPE='Dev'
- os: osx
osx_image: xcode7
- compiler: clang
- env: COMPILER='clang++' BUILD_TYPE='Dev'
+ compiler: xcode7-clang-release
+ env: COMPILER='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode7
- compiler: clang
- env: COMPILER='clang++' BUILD_TYPE='Release'
+ compiler: xcode7-clang-dev
+ env: COMPILER='clang++' BUILD_TYPE='Dev'
install:
@@ -145,13 +155,14 @@ install:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew remove gdal
- brew install cmake boost google-sparsehash gdal
+ brew install cmake boost google-sparsehash gdal || true
fi
+ - cmake --version
before_script:
- cd ${TRAVIS_BUILD_DIR}
- mkdir build && cd build
- - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata"
+ - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_WITH_CCACHE=1 -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata"
script:
- make VERBOSE=1 && ctest --output-on-failure
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b8a6a9..bb192ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,42 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
+## [2.8.0] - 2016-08-04
+
+### Added
+
+- EWKT support.
+- Track `pop` type calls and queue underruns when `OSMIUM_DEBUG_QUEUE_SIZE`
+ environment variable is set.
+
+### Changed
+
+- Switched to newest protozero v1.4.0. This should deliver some speedups
+ when parsing PBF files. This also removes the DeltaEncodeIterator class,
+ which isn't needed any more.
+- Uses `std::unordered_map` instead of `std::map` in PBF string table code
+ speeding up writing of PBF files considerably.
+- Uses less memory when writing PBF files (smaller string table by default).
+- Removes dependency on sparsehash and boost program options libraries for
+ examples.
+- Cleaned up threaded queue code.
+
+### Fixed
+
+- A potentially very bad bug was fixed: When there are many and/or long strings
+ in tag keys and values and/or user names and/or relation roles, the string
+ table inside a PBF block would overflow. I have never seen this happen for
+ normal OSM data, but that doesn't mean it can't happen. The result is that
+ the strings will all be mixed up, keys for values, values for user names or
+ whatever.
+- Automatically set correct SRID when creating WKB and GEOS geometries.
+ Note that this changes the behaviour of libosmium when creating GEOS
+ geometries. Before we created them with -1 as SRID unless set otherwise.
+ Manual setting of the SRID on the GEOSGeometryFactory is now deprecated.
+- Allow coordinates of nodes in scientific notation when reading XML files.
+ This shouldn't be used really, but sometimes you can find them.
+
+
## [2.7.2] - 2016-06-08
### Changed
@@ -341,7 +377,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.7.2...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.8.0...HEAD
+[2.8.0]: https://github.com/osmcode/libosmium/compare/v2.7.2...v2.8.0
[2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2
[2.7.1]: https://github.com/osmcode/libosmium/compare/v2.7.0...v2.7.1
[2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53251d0..c82cdd0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover
project(libosmium)
set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 7)
-set(LIBOSMIUM_VERSION_PATCH 2)
+set(LIBOSMIUM_VERSION_MINOR 8)
+set(LIBOSMIUM_VERSION_PATCH 0)
set(LIBOSMIUM_VERSION
"${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
@@ -63,6 +63,27 @@ option(WITH_PROFILING "add flags needed for profiling" OFF)
#-----------------------------------------------------------------------------
#
+# CCache support
+#
+#-----------------------------------------------------------------------------
+
+option(BUILD_WITH_CCACHE "build using ccache" OFF)
+
+if(BUILD_WITH_CCACHE)
+ find_program(CCACHE_PROGRAM ccache)
+ if(CCACHE_PROGRAM)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "CCACHE_CPP2=1 ${CCACHE_PROGRAM}")
+
+ # workaround for some clang versions
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ add_definitions(-Qunused-arguments)
+ endif()
+ endif()
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
# Coverage support
#
#-----------------------------------------------------------------------------
diff --git a/appveyor.yml b/appveyor.yml
index 8244d98..e33b08c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -72,7 +72,6 @@ build_script:
-DCMAKE_BUILD_TYPE=%config%
-DBUILD_HEADERS=OFF
-DBOOST_ROOT=%LODEPSDIR%\boost
- -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_58.lib
-DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib
-DBZIP2_LIBRARY_RELEASE=%LIBBZIP2%
-DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt
@@ -82,7 +81,6 @@ build_script:
# -DOsmium_DEBUG=TRUE
# -DCMAKE_BUILD_TYPE=%config%
# -DBOOST_ROOT=%LODEPSDIR%\boost
- # -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib
# -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib
# -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include
# -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index a04a843..eff3363 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -28,7 +28,7 @@ set(EXAMPLES
# Examples depending on wingetopt
#
#-----------------------------------------------------------------------------
-set(GETOPT_EXAMPLES area_test convert serdump)
+set(GETOPT_EXAMPLES area_test convert index serdump)
if(NOT GETOPT_MISSING)
foreach(example ${GETOPT_EXAMPLES})
list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY})
@@ -44,36 +44,6 @@ endif()
#-----------------------------------------------------------------------------
#
-# Examples depending on SparseHash
-#
-#-----------------------------------------------------------------------------
-if(NOT SPARSEHASH_FOUND)
- list(REMOVE_ITEM EXAMPLES area_test)
- message(STATUS "Configuring examples - Skipping examples because Google SparseHash not found:")
- message(STATUS " - osmium_area_test")
-endif()
-
-
-#-----------------------------------------------------------------------------
-#
-# Examples depending on Boost Program Options
-#
-#-----------------------------------------------------------------------------
-unset(Boost_LIBRARIES)
-unset(Boost_FOUND)
-find_package(Boost 1.38 COMPONENTS program_options)
-
-if(Boost_PROGRAM_OPTIONS_FOUND)
- list(APPEND EXAMPLE_LIBS_index ${Boost_PROGRAM_OPTIONS_LIBRARY})
-else()
- list(REMOVE_ITEM EXAMPLES index)
- message(STATUS "Configuring examples - Skipping examples because Boost program_options not found:")
- message(STATUS " - osmium_index")
-endif()
-
-
-#-----------------------------------------------------------------------------
-#
# Configure examples
#
#-----------------------------------------------------------------------------
diff --git a/examples/osmium_index.cpp b/examples/osmium_index.cpp
index b612140..8d5c5d9 100644
--- a/examples/osmium_index.cpp
+++ b/examples/osmium_index.cpp
@@ -12,7 +12,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <boost/program_options.hpp>
+#include <getopt.h>
#include <osmium/index/map/dense_file_array.hpp>
#include <osmium/index/map/sparse_file_array.hpp>
@@ -124,82 +124,105 @@ enum return_code : int {
fatal = 3
};
-namespace po = boost::program_options;
-
class Options {
- po::variables_map vm;
+ std::vector<osmium::unsigned_object_id_type> m_ids;
+ std::string m_type;
+ std::string m_filename;
+ bool m_dump = false;
+ bool m_array_format = false;
+ bool m_list_format = false;
+
+ void print_help() {
+ std::cout << "Usage: osmium_index [OPTIONS]\n\n"
+ << "-h, --help Print this help message\n"
+ << "-a, --array=FILE Read given index file in array format\n"
+ << "-l, --list=FILE Read given index file in list format\n"
+ << "-d, --dump Dump contents of index file to STDOUT\n"
+ << "-s, --search=ID Search for given id (Option can appear multiple times)\n"
+ << "-t, --type=TYPE Type of value ('location' or 'offset')\n"
+ ;
+ }
public:
Options(int argc, char* argv[]) {
- try {
- po::options_description desc("Allowed options");
- desc.add_options()
- ("help,h", "Print this help message")
- ("array,a", po::value<std::string>(), "Read given index file in array format")
- ("list,l", po::value<std::string>(), "Read given index file in list format")
- ("dump,d", "Dump contents of index file to STDOUT")
- ("search,s", po::value<std::vector<osmium::unsigned_object_id_type>>(), "Search for given id (Option can appear multiple times)")
- ("type,t", po::value<std::string>(), "Type of value ('location' or 'offset')")
- ;
-
- po::store(po::parse_command_line(argc, argv, desc), vm);
- po::notify(vm);
-
- if (vm.count("help")) {
- std::cout << desc << "\n";
- exit(return_code::okay);
+ static struct option long_options[] = {
+ {"array", required_argument, 0, 'a'},
+ {"dump", no_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"list", required_argument, 0, 'l'},
+ {"search", required_argument, 0, 's'},
+ {"type", required_argument, 0, 't'},
+ {0, 0, 0, 0}
+ };
+
+ while (true) {
+ int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0);
+ if (c == -1) {
+ break;
}
- if (vm.count("array") && vm.count("list")) {
- std::cerr << "Only option --array or --list allowed." << std::endl;
- exit(return_code::fatal);
- }
-
- if (!vm.count("array") && !vm.count("list")) {
- std::cerr << "Need one of option --array or --list." << std::endl;
- exit(return_code::fatal);
+ switch (c) {
+ case 'a':
+ m_array_format = true;
+ m_filename = optarg;
+ break;
+ case 'd':
+ m_dump = true;
+ break;
+ case 'h':
+ print_help();
+ exit(return_code::okay);
+ case 'l':
+ m_list_format = true;
+ m_filename = optarg;
+ break;
+ case 's':
+ m_ids.push_back(std::atoll(optarg));
+ break;
+ case 't':
+ m_type = optarg;
+ if (m_type != "location" && m_type != "offset") {
+ std::cerr << "Unknown type '" << m_type << "'. Must be 'location' or 'offset'.\n";
+ exit(return_code::fatal);
+ }
+ break;
+ default:
+ exit(return_code::fatal);
}
+ }
- if (!vm.count("type")) {
- std::cerr << "Need --type argument." << std::endl;
- exit(return_code::fatal);
- }
+ if (m_array_format == m_list_format) {
+ std::cerr << "Need option --array or --list, but not both\n";
+ exit(return_code::fatal);
+ }
- const std::string& type = vm["type"].as<std::string>();
- if (type != "location" && type != "offset") {
- std::cerr << "Unknown type '" << type << "'. Must be 'location' or 'offset'." << std::endl;
- exit(return_code::fatal);
- }
- } catch (boost::program_options::error& e) {
- std::cerr << "Error parsing command line: " << e.what() << std::endl;
+ if (m_type.empty()) {
+ std::cerr << "Need --type argument.\n";
exit(return_code::fatal);
}
+
}
- const std::string& filename() const {
- if (vm.count("array")) {
- return vm["array"].as<std::string>();
- } else {
- return vm["list"].as<std::string>();
- }
+ const std::string& filename() const noexcept {
+ return m_filename;
}
- bool dense_format() const {
- return vm.count("array") != 0;
+ bool dense_format() const noexcept {
+ return m_array_format;
}
- bool do_dump() const {
- return vm.count("dump") != 0;
+ bool do_dump() const noexcept {
+ return m_dump;
}
- std::vector<osmium::unsigned_object_id_type> search_keys() const {
- return vm["search"].as<std::vector<osmium::unsigned_object_id_type>>();
+ const std::vector<osmium::unsigned_object_id_type>& search_keys() const noexcept {
+ return m_ids;
}
- bool type_is(const char* type) const {
- return vm["type"].as<std::string>() == type;
+ bool type_is(const char* type) const noexcept {
+ return m_type == type;
}
}; // class Options
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index f964d4b..9095302 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -153,7 +153,7 @@ namespace osmium {
*
* @deprecated Set debug_level directly.
*/
- void enable_debug_output(bool d = true) {
+ OSMIUM_DEPRECATED void enable_debug_output(bool d = true) {
debug_level = d;
}
@@ -573,8 +573,6 @@ namespace osmium {
const int64_t ly = location.y();
const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax);
-// std::cerr << "z=" << z << "\n";
-
if (z >= 0) {
nesting += segment->is_reverse() ? -1 : 1;
if (debug()) {
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index 03c1015..f03eec5 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -169,7 +169,7 @@ namespace osmium {
template <typename... TArgs>
explicit GeometryFactory<TGeomImpl, TProjection>(TArgs&&... args) :
m_projection(),
- m_impl(std::forward<TArgs>(args)...) {
+ m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
}
/**
@@ -179,7 +179,7 @@ namespace osmium {
template <typename... TArgs>
explicit GeometryFactory<TGeomImpl, TProjection>(TProjection&& projection, TArgs&&... args) :
m_projection(std::move(projection)),
- m_impl(std::forward<TArgs>(args)...) {
+ m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
}
using projection_type = TProjection;
diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp
index 5291257..ffa9440 100644
--- a/include/osmium/geom/geojson.hpp
+++ b/include/osmium/geom/geojson.hpp
@@ -59,7 +59,7 @@ namespace osmium {
using multipolygon_type = std::string;
using ring_type = std::string;
- GeoJSONFactoryImpl(int precision = 7) :
+ GeoJSONFactoryImpl(int /* srid */, int precision = 7) :
m_precision(precision) {
}
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index 187c048..4a097e9 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -59,6 +59,7 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/geom/factory.hpp>
#include <osmium/geom/coordinates.hpp>
+#include <osmium/util/compatibility.hpp>
// MSVC doesn't support throw_with_nested yet
#ifdef _MSC_VER
@@ -99,13 +100,23 @@ namespace osmium {
using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
using ring_type = std::unique_ptr<geos::geom::LinearRing>;
- explicit GEOSFactoryImpl(geos::geom::GeometryFactory& geos_factory) :
+ explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) :
m_precision_model(nullptr),
m_our_geos_factory(nullptr),
m_geos_factory(&geos_factory) {
}
- explicit GEOSFactoryImpl(int srid = -1) :
+ /**
+ * @deprecated Do not set SRID explicitly. It will be set to the
+ * correct value automatically.
+ */
+ OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) :
+ m_precision_model(new geos::geom::PrecisionModel),
+ m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)),
+ m_geos_factory(m_our_geos_factory.get()) {
+ }
+
+ explicit GEOSFactoryImpl(int srid) :
m_precision_model(new geos::geom::PrecisionModel),
m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)),
m_geos_factory(m_our_geos_factory.get()) {
diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp
index 45586e2..a457d66 100644
--- a/include/osmium/geom/ogr.hpp
+++ b/include/osmium/geom/ogr.hpp
@@ -77,7 +77,8 @@ namespace osmium {
public:
- OGRFactoryImpl() = default;
+ OGRFactoryImpl(int /* srid */) {
+ }
/* Point */
diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp
index b54809a..340f0c1 100644
--- a/include/osmium/geom/rapid_geojson.hpp
+++ b/include/osmium/geom/rapid_geojson.hpp
@@ -59,7 +59,7 @@ namespace osmium {
using multipolygon_type = void;
using ring_type = void;
- RapidGeoJSONFactoryImpl(TWriter& writer) :
+ RapidGeoJSONFactoryImpl(int /* srid */, TWriter& writer) :
m_writer(&writer) {
}
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
index 9df1823..efb07c6 100644
--- a/include/osmium/geom/wkb.hpp
+++ b/include/osmium/geom/wkb.hpp
@@ -79,9 +79,6 @@ namespace osmium {
class WKBFactoryImpl {
- /// OSM data always uses SRID 4326 (WGS84).
- static constexpr uint32_t srid = 4326;
-
/**
* Type of WKB geometry.
* These definitions are from
@@ -112,6 +109,7 @@ namespace osmium {
std::string m_data;
uint32_t m_points {0};
+ int m_srid;
wkb_type m_wkb_type;
out_type m_out_type;
@@ -130,7 +128,7 @@ namespace osmium {
#endif
if (m_wkb_type == wkb_type::ewkb) {
str_push(str, type | wkbSRID);
- str_push(str, srid);
+ str_push(str, m_srid);
} else {
str_push(str, type);
}
@@ -154,7 +152,8 @@ namespace osmium {
using multipolygon_type = std::string;
using ring_type = std::string;
- explicit WKBFactoryImpl(wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
+ explicit WKBFactoryImpl(int srid, wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
+ m_srid(srid),
m_wkb_type(wtype),
m_out_type(otype) {
}
diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp
index 57a6625..6113674 100644
--- a/include/osmium/geom/wkt.hpp
+++ b/include/osmium/geom/wkt.hpp
@@ -45,12 +45,22 @@ namespace osmium {
namespace geom {
+ enum class wkt_type : bool {
+ wkt = false,
+ ewkt = true
+ }; // enum class wkt_type
+
namespace detail {
class WKTFactoryImpl {
+ std::string m_srid_prefix;
std::string m_str;
int m_precision;
+ wkt_type m_wkt_type;
+
+ void init_srid() {
+ }
public:
@@ -60,14 +70,22 @@ namespace osmium {
using multipolygon_type = std::string;
using ring_type = std::string;
- WKTFactoryImpl(int precision = 7) :
- m_precision(precision) {
+ WKTFactoryImpl(int srid, int precision = 7, wkt_type wtype = wkt_type::wkt) :
+ m_srid_prefix(),
+ m_precision(precision),
+ m_wkt_type(wtype) {
+ if (m_wkt_type == wkt_type::ewkt) {
+ m_srid_prefix = "SRID=";
+ m_srid_prefix += std::to_string(srid);
+ m_srid_prefix += ';';
+ }
}
/* Point */
point_type make_point(const osmium::geom::Coordinates& xy) const {
- std::string str {"POINT"};
+ std::string str {m_srid_prefix};
+ str += "POINT";
xy.append_to_string(str, '(', ' ', ')', m_precision);
return str;
}
@@ -75,7 +93,8 @@ namespace osmium {
/* LineString */
void linestring_start() {
- m_str = "LINESTRING(";
+ m_str = m_srid_prefix;
+ m_str += "LINESTRING(";
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
@@ -97,7 +116,8 @@ namespace osmium {
/* MultiPolygon */
void multipolygon_start() {
- m_str = "MULTIPOLYGON(";
+ m_str = m_srid_prefix;
+ m_str += "MULTIPOLYGON(";
}
void multipolygon_polygon_start() {
diff --git a/include/osmium/io/detail/debug_output_format.hpp b/include/osmium/io/detail/debug_output_format.hpp
index 691dc0c..7ae0807 100644
--- a/include/osmium/io/detail/debug_output_format.hpp
+++ b/include/osmium/io/detail/debug_output_format.hpp
@@ -236,7 +236,7 @@ namespace osmium {
void write_location(const osmium::Location& location) {
write_fieldname("lon/lat");
*m_out += " ";
- location.as_string(std::back_inserter(*m_out));
+ location.as_string_without_check(std::back_inserter(*m_out));
if (!location.valid()) {
write_error(" INVALID LOCATION!");
}
diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp
index 1c0d2eb..6980c48 100644
--- a/include/osmium/io/detail/pbf_decoder.hpp
+++ b/include/osmium/io/detail/pbf_decoder.hpp
@@ -65,14 +65,14 @@ namespace osmium {
namespace detail {
- using ptr_len_type = std::pair<const char*, size_t>;
+ using protozero::data_view;
using osm_string_len_type = std::pair<const char*, osmium::string_size_type>;
class PBFPrimitiveBlockDecoder {
static constexpr size_t initial_buffer_size = 2 * 1024 * 1024;
- ptr_len_type m_data;
+ data_view m_data;
std::vector<osm_string_len_type> m_stringtable;
int64_t m_lon_offset = 0;
@@ -84,18 +84,18 @@ namespace osmium {
osmium::memory::Buffer m_buffer { initial_buffer_size };
- void decode_stringtable(const ptr_len_type& data) {
+ void decode_stringtable(const data_view& data) {
if (!m_stringtable.empty()) {
throw osmium::pbf_error("more than one stringtable in pbf file");
}
protozero::pbf_message<OSMFormat::StringTable> pbf_string_table(data);
while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) {
- auto str_len = pbf_string_table.get_data();
- if (str_len.second > osmium::max_osm_string_length) {
+ auto str_view = pbf_string_table.get_view();
+ if (str_view.size() > osmium::max_osm_string_length) {
throw osmium::pbf_error("overlong string in string table");
}
- m_stringtable.emplace_back(str_len.first, osmium::string_size_type(str_len.second));
+ m_stringtable.emplace_back(str_view.data(), osmium::string_size_type(str_view.size()));
}
}
@@ -104,7 +104,7 @@ namespace osmium {
while (pbf_primitive_block.next()) {
switch (pbf_primitive_block.tag()) {
case OSMFormat::PrimitiveBlock::required_StringTable_stringtable:
- decode_stringtable(pbf_primitive_block.get_data());
+ decode_stringtable(pbf_primitive_block.get_view());
break;
case OSMFormat::PrimitiveBlock::optional_int32_granularity:
m_granularity = pbf_primitive_block.get_int32();
@@ -132,28 +132,28 @@ namespace osmium {
switch (pbf_primitive_group.tag()) {
case OSMFormat::PrimitiveGroup::repeated_Node_nodes:
if (m_read_types & osmium::osm_entity_bits::node) {
- decode_node(pbf_primitive_group.get_data());
+ decode_node(pbf_primitive_group.get_view());
} else {
pbf_primitive_group.skip();
}
break;
case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense:
if (m_read_types & osmium::osm_entity_bits::node) {
- decode_dense_nodes(pbf_primitive_group.get_data());
+ decode_dense_nodes(pbf_primitive_group.get_view());
} else {
pbf_primitive_group.skip();
}
break;
case OSMFormat::PrimitiveGroup::repeated_Way_ways:
if (m_read_types & osmium::osm_entity_bits::way) {
- decode_way(pbf_primitive_group.get_data());
+ decode_way(pbf_primitive_group.get_view());
} else {
pbf_primitive_group.skip();
}
break;
case OSMFormat::PrimitiveGroup::repeated_Relation_relations:
if (m_read_types & osmium::osm_entity_bits::relation) {
- decode_relation(pbf_primitive_group.get_data());
+ decode_relation(pbf_primitive_group.get_view());
} else {
pbf_primitive_group.skip();
}
@@ -165,7 +165,7 @@ namespace osmium {
}
}
- osm_string_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) {
+ osm_string_len_type decode_info(const data_view& data, osmium::OSMObject& object) {
osm_string_len_type user = std::make_pair("", 0);
protozero::pbf_message<OSMFormat::Info> pbf_info(data);
@@ -209,15 +209,15 @@ namespace osmium {
return user;
}
- using kv_type = std::pair<protozero::pbf_reader::const_uint32_iterator, protozero::pbf_reader::const_uint32_iterator>;
+ using kv_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) {
- if (keys.first != keys.second) {
+ if (!keys.empty()) {
osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
- auto kit = keys.first;
- auto vit = vals.first;
- while (kit != keys.second) {
- if (vit == vals.second) {
+ auto kit = keys.begin();
+ auto vit = vals.begin();
+ while (kit != keys.end()) {
+ if (vit == vals.end()) {
// this is against the spec, must have same number of elements
throw osmium::pbf_error("PBF format error");
}
@@ -232,7 +232,7 @@ namespace osmium {
return int32_t((c * m_granularity + m_lon_offset) / resolution_convert);
}
- void decode_node(const ptr_len_type& data) {
+ void decode_node(const data_view& data) {
osmium::builder::NodeBuilder builder(m_buffer);
osmium::Node& node = builder.object();
@@ -256,7 +256,7 @@ namespace osmium {
vals = pbf_node.get_packed_uint32();
break;
case OSMFormat::Node::optional_Info_info:
- user = decode_info(pbf_node.get_data(), builder.object());
+ user = decode_info(pbf_node.get_view(), builder.object());
break;
case OSMFormat::Node::required_sint64_lat:
lat = pbf_node.get_sint64();
@@ -287,14 +287,14 @@ namespace osmium {
m_buffer.commit();
}
- void decode_way(const ptr_len_type& data) {
+ void decode_way(const data_view& data) {
osmium::builder::WayBuilder builder(m_buffer);
kv_type keys;
kv_type vals;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lats;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lons;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> refs;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
osm_string_len_type user = { "", 0 };
@@ -311,7 +311,7 @@ namespace osmium {
vals = pbf_way.get_packed_uint32();
break;
case OSMFormat::Way::optional_Info_info:
- user = decode_info(pbf_way.get_data(), builder.object());
+ user = decode_info(pbf_way.get_view(), builder.object());
break;
case OSMFormat::Way::packed_sint64_refs:
refs = pbf_way.get_packed_sint64();
@@ -329,22 +329,25 @@ namespace osmium {
builder.add_user(user.first, user.second);
- if (refs.first != refs.second) {
+ if (!refs.empty()) {
osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
osmium::util::DeltaDecode<int64_t> ref;
- if (lats.first == lats.second) {
- while (refs.first != refs.second) {
- wnl_builder.add_node_ref(ref.update(*refs.first++));
+ if (lats.empty()) {
+ for (const auto& ref_value : refs) {
+ wnl_builder.add_node_ref(ref.update(ref_value));
}
} else {
osmium::util::DeltaDecode<int64_t> lon;
osmium::util::DeltaDecode<int64_t> lat;
- while (refs.first != refs.second && lons.first != lons.second && lats.first != lats.second) {
+ while (!refs.empty() && !lons.empty() && !lats.empty()) {
wnl_builder.add_node_ref(
- ref.update(*refs.first++),
- osmium::Location{convert_pbf_coordinate(lon.update(*lons.first++)),
- convert_pbf_coordinate(lat.update(*lats.first++))}
+ ref.update(refs.front()),
+ osmium::Location{convert_pbf_coordinate(lon.update(lons.front())),
+ convert_pbf_coordinate(lat.update(lats.front()))}
);
+ refs.drop_front();
+ lons.drop_front();
+ lats.drop_front();
}
}
}
@@ -354,14 +357,14 @@ namespace osmium {
m_buffer.commit();
}
- void decode_relation(const ptr_len_type& data) {
+ void decode_relation(const data_view& data) {
osmium::builder::RelationBuilder builder(m_buffer);
kv_type keys;
kv_type vals;
- std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> roles;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
- std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> types;
+ protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> roles;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> refs;
+ protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> types;
osm_string_len_type user = { "", 0 };
@@ -378,7 +381,7 @@ namespace osmium {
vals = pbf_relation.get_packed_uint32();
break;
case OSMFormat::Relation::optional_Info_info:
- user = decode_info(pbf_relation.get_data(), builder.object());
+ user = decode_info(pbf_relation.get_view(), builder.object());
break;
case OSMFormat::Relation::packed_int32_roles_sid:
roles = pbf_relation.get_packed_int32();
@@ -396,21 +399,24 @@ namespace osmium {
builder.add_user(user.first, user.second);
- if (refs.first != refs.second) {
+ if (!refs.empty()) {
osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
osmium::util::DeltaDecode<int64_t> ref;
- while (roles.first != roles.second && refs.first != refs.second && types.first != types.second) {
- const auto& r = m_stringtable.at(*roles.first++);
- int type = *types.first++;
+ while (!roles.empty() && !refs.empty() && !types.empty()) {
+ const auto& r = m_stringtable.at(roles.front());
+ int type = types.front();
if (type < 0 || type > 2) {
throw osmium::pbf_error("unknown relation member type");
}
rml_builder.add_member(
osmium::item_type(type + 1),
- ref.update(*refs.first++),
+ ref.update(refs.front()),
r.first,
r.second
);
+ roles.drop_front();
+ refs.drop_front();
+ types.drop_front();
}
}
@@ -419,22 +425,22 @@ namespace osmium {
m_buffer.commit();
}
- void decode_dense_nodes(const ptr_len_type& data) {
+ void decode_dense_nodes(const data_view& data) {
bool has_info = false;
bool has_visibles = false;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> ids;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lats;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lons;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> ids;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
- std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> tags;
+ protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> tags;
- std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> versions;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> timestamps;
- std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> changesets;
- std::pair<protozero::pbf_reader::const_sint32_iterator, protozero::pbf_reader::const_sint32_iterator> uids;
- std::pair<protozero::pbf_reader::const_sint32_iterator, protozero::pbf_reader::const_sint32_iterator> user_sids;
- std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> visibles;
+ protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> versions;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> timestamps;
+ protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> changesets;
+ protozero::iterator_range<protozero::pbf_reader::const_sint32_iterator> uids;
+ protozero::iterator_range<protozero::pbf_reader::const_sint32_iterator> user_sids;
+ protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> visibles;
protozero::pbf_message<OSMFormat::DenseNodes> pbf_dense_nodes(data);
while (pbf_dense_nodes.next()) {
@@ -495,11 +501,11 @@ namespace osmium {
osmium::util::DeltaDecode<int64_t> dense_changeset;
osmium::util::DeltaDecode<int64_t> dense_timestamp;
- auto tag_it = tags.first;
+ auto tag_it = tags.begin();
- while (ids.first != ids.second) {
- if (lons.first == lons.second ||
- lats.first == lats.second) {
+ while (!ids.empty()) {
+ if (lons.empty() ||
+ lats.empty()) {
// this is against the spec, must have same number of elements
throw osmium::pbf_error("PBF format error");
}
@@ -509,43 +515,50 @@ namespace osmium {
osmium::builder::NodeBuilder builder(m_buffer);
osmium::Node& node = builder.object();
- node.set_id(dense_id.update(*ids.first++));
+ node.set_id(dense_id.update(ids.front()));
+ ids.drop_front();
if (has_info) {
- if (versions.first == versions.second ||
- changesets.first == changesets.second ||
- timestamps.first == timestamps.second ||
- uids.first == uids.second ||
- user_sids.first == user_sids.second) {
+ if (versions.empty() ||
+ changesets.empty() ||
+ timestamps.empty() ||
+ uids.empty() ||
+ user_sids.empty()) {
// this is against the spec, must have same number of elements
throw osmium::pbf_error("PBF format error");
}
- auto version = *versions.first++;
+ auto version = versions.front();
+ versions.drop_front();
if (version < 0) {
throw osmium::pbf_error("object version must not be negative");
}
node.set_version(static_cast<osmium::object_version_type>(version));
- auto changeset_id = dense_changeset.update(*changesets.first++);
+ auto changeset_id = dense_changeset.update(changesets.front());
+ changesets.drop_front();
if (changeset_id < 0) {
throw osmium::pbf_error("object changeset_id must not be negative");
}
node.set_changeset(static_cast<osmium::changeset_id_type>(changeset_id));
- node.set_timestamp(dense_timestamp.update(*timestamps.first++) * m_date_factor / 1000);
- node.set_uid_from_signed(static_cast<osmium::signed_user_id_type>(dense_uid.update(*uids.first++)));
+ node.set_timestamp(dense_timestamp.update(timestamps.front()) * m_date_factor / 1000);
+ timestamps.drop_front();
+ node.set_uid_from_signed(static_cast<osmium::signed_user_id_type>(dense_uid.update(uids.front())));
+ uids.drop_front();
if (has_visibles) {
- if (visibles.first == visibles.second) {
+ if (visibles.empty()) {
// this is against the spec, must have same number of elements
throw osmium::pbf_error("PBF format error");
}
- visible = (*visibles.first++) != 0;
+ visible = (visibles.front() != 0);
+ visibles.drop_front();
}
node.set_visible(visible);
- const auto& u = m_stringtable.at(dense_user_sid.update(*user_sids.first++));
+ const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front()));
+ user_sids.drop_front();
builder.add_user(u.first, u.second);
} else {
builder.add_user("");
@@ -553,8 +566,10 @@ namespace osmium {
// even if the node isn't visible, there's still a record
// of its lat/lon in the dense arrays.
- const auto lon = dense_longitude.update(*lons.first++);
- const auto lat = dense_latitude.update(*lats.first++);
+ const auto lon = dense_longitude.update(lons.front());
+ lons.drop_front();
+ const auto lat = dense_latitude.update(lats.front());
+ lats.drop_front();
if (visible) {
builder.object().set_location(osmium::Location(
convert_pbf_coordinate(lon),
@@ -562,18 +577,18 @@ namespace osmium {
));
}
- if (tag_it != tags.second) {
+ if (tag_it != tags.end()) {
osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
- while (tag_it != tags.second && *tag_it != 0) {
+ while (tag_it != tags.end() && *tag_it != 0) {
const auto& k = m_stringtable.at(*tag_it++);
- if (tag_it == tags.second) {
+ if (tag_it == tags.end()) {
throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs
}
const auto& v = m_stringtable.at(*tag_it++);
tl_builder.add_tag(k.first, k.second, v.first, v.second);
}
- if (tag_it != tags.second) {
+ if (tag_it != tags.end()) {
++tag_it;
}
}
@@ -585,7 +600,7 @@ namespace osmium {
public:
- PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) :
+ PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types) :
m_data(data),
m_read_types(read_types) {
}
@@ -611,17 +626,17 @@ namespace osmium {
}; // class PBFPrimitiveBlockDecoder
- inline ptr_len_type decode_blob(const std::string& blob_data, std::string& output) {
+ inline data_view decode_blob(const std::string& blob_data, std::string& output) {
int32_t raw_size = 0;
- std::pair<const char*, protozero::pbf_length_type> zlib_data = {nullptr, 0};
+ protozero::data_view zlib_data;
protozero::pbf_message<FileFormat::Blob> pbf_blob(blob_data);
while (pbf_blob.next()) {
switch (pbf_blob.tag()) {
case FileFormat::Blob::optional_bytes_raw:
{
- auto data_len = pbf_blob.get_data();
- if (data_len.second > max_uncompressed_blob_size) {
+ auto data_len = pbf_blob.get_view();
+ if (data_len.size() > max_uncompressed_blob_size) {
throw osmium::pbf_error("illegal blob size");
}
return data_len;
@@ -633,7 +648,7 @@ namespace osmium {
}
break;
case FileFormat::Blob::optional_bytes_zlib_data:
- zlib_data = pbf_blob.get_data();
+ zlib_data = pbf_blob.get_view();
break;
case FileFormat::Blob::optional_bytes_lzma_data:
throw osmium::pbf_error("lzma blobs not implemented");
@@ -642,10 +657,10 @@ namespace osmium {
}
}
- if (zlib_data.second != 0 && raw_size != 0) {
+ if (zlib_data.size() != 0 && raw_size != 0) {
return osmium::io::detail::zlib_uncompress_string(
- zlib_data.first,
- static_cast<unsigned long>(zlib_data.second),
+ zlib_data.data(),
+ static_cast<unsigned long>(zlib_data.size()),
static_cast<unsigned long>(raw_size),
output
);
@@ -654,7 +669,7 @@ namespace osmium {
throw osmium::pbf_error("blob contains no data");
}
- inline osmium::Box decode_header_bbox(const ptr_len_type& data) {
+ inline osmium::Box decode_header_bbox(const data_view& data) {
int64_t left = std::numeric_limits<int64_t>::max();
int64_t right = std::numeric_limits<int64_t>::max();
int64_t top = std::numeric_limits<int64_t>::max();
@@ -694,7 +709,7 @@ namespace osmium {
return box;
}
- inline osmium::io::Header decode_header_block(const ptr_len_type& data) {
+ inline osmium::io::Header decode_header_block(const data_view& data) {
osmium::io::Header header;
int i = 0;
@@ -702,20 +717,20 @@ namespace osmium {
while (pbf_header_block.next()) {
switch (pbf_header_block.tag()) {
case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox:
- header.add_box(decode_header_bbox(pbf_header_block.get_data()));
+ header.add_box(decode_header_bbox(pbf_header_block.get_view()));
break;
case OSMFormat::HeaderBlock::repeated_string_required_features:
{
- auto feature = pbf_header_block.get_data();
- if (!strncmp("OsmSchema-V0.6", feature.first, feature.second)) {
+ auto feature = pbf_header_block.get_view();
+ if (!std::strncmp("OsmSchema-V0.6", feature.data(), feature.size())) {
// intentionally left blank
- } else if (!strncmp("DenseNodes", feature.first, feature.second)) {
+ } else if (!std::strncmp("DenseNodes", feature.data(), feature.size())) {
header.set("pbf_dense_nodes", true);
- } else if (!strncmp("HistoricalInformation", feature.first, feature.second)) {
+ } else if (!std::strncmp("HistoricalInformation", feature.data(), feature.size())) {
header.set_has_multiple_object_versions(true);
} else {
std::string msg("required feature not supported: ");
- msg.append(feature.first, feature.second);
+ msg.append(feature.data(), feature.size());
throw osmium::pbf_error(msg);
}
}
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
index 0c6a9ef..8361c72 100644
--- a/include/osmium/io/detail/pbf_input_format.hpp
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -123,13 +123,13 @@ namespace osmium {
* type. Return the size of the following Blob.
*/
size_t decode_blob_header(protozero::pbf_message<FileFormat::BlobHeader>&& pbf_blob_header, const char* expected_type) {
- std::pair<const char*, size_t> blob_header_type;
+ protozero::data_view blob_header_type;
size_t blob_header_datasize = 0;
while (pbf_blob_header.next()) {
switch (pbf_blob_header.tag()) {
case FileFormat::BlobHeader::required_string_type:
- blob_header_type = pbf_blob_header.get_data();
+ blob_header_type = pbf_blob_header.get_view();
break;
case FileFormat::BlobHeader::required_int32_datasize:
blob_header_datasize = pbf_blob_header.get_int32();
@@ -143,7 +143,7 @@ namespace osmium {
throw osmium::pbf_error("PBF format error: BlobHeader.datasize missing or zero.");
}
- if (strncmp(expected_type, blob_header_type.first, blob_header_type.second)) {
+ if (std::strncmp(expected_type, blob_header_type.data(), blob_header_type.size())) {
throw osmium::pbf_error("blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)");
}
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 5046087..d5f8cd4 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -579,33 +579,29 @@ namespace osmium {
pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id());
add_meta(way, pbf_way);
- const auto& nodes = way.nodes();
-
- static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type {
- return node_ref->ref();
- };
- using it_type = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type>;
- it_type first { nodes.cbegin(), nodes.cend(), map_node_ref };
- it_type last { nodes.cend(), nodes.cend(), map_node_ref };
- pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_refs, first, last);
+ {
+ osmium::util::DeltaEncode<object_id_type, int64_t> delta_id;
+ protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_refs)};
+ for (const auto& node_ref : way.nodes()) {
+ field.add_element(delta_id.update(node_ref.ref()));
+ }
+ }
if (m_options.locations_on_ways) {
- static auto map_node_x = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
- return lonlat2int(node_ref->location().lon_without_check());
- };
- static auto map_node_y = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
- return lonlat2int(node_ref->location().lat_without_check());
- };
- using it_type_x = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_x), int64_t>;
- using it_type_y = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_y), int64_t>;
-
- it_type_x first_x { nodes.cbegin(), nodes.cend(), map_node_x };
- it_type_x last_x { nodes.cend(), nodes.cend(), map_node_x };
- pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lon, first_x, last_x);
-
- it_type_y first_y { nodes.cbegin(), nodes.cend(), map_node_y };
- it_type_y last_y { nodes.cend(), nodes.cend(), map_node_y };
- pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lat, first_y, last_y);
+ {
+ osmium::util::DeltaEncode<int64_t, int64_t> delta_id;
+ protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lon)};
+ for (const auto& node_ref : way.nodes()) {
+ field.add_element(delta_id.update(lonlat2int(node_ref.location().lon_without_check())));
+ }
+ }
+ {
+ osmium::util::DeltaEncode<int64_t, int64_t> delta_id;
+ protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lat)};
+ for (const auto& node_ref : way.nodes()) {
+ field.add_element(delta_id.update(lonlat2int(node_ref.location().lat_without_check())));
+ }
+ }
}
}
@@ -623,14 +619,13 @@ namespace osmium {
}
}
- static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type {
- return member->ref();
- };
- using it_type = osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type>;
- const auto& members = relation.members();
- it_type first { members.cbegin(), members.cend(), map_member_ref };
- it_type last { members.cend(), members.cend(), map_member_ref };
- pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last);
+ {
+ osmium::util::DeltaEncode<object_id_type, int64_t> delta_id;
+ protozero::packed_field_sint64 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_sint64_memids)};
+ for (const auto& member : relation.members()) {
+ field.add_element(delta_id.update(member.ref()));
+ }
+ }
{
protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)};
diff --git a/include/osmium/io/detail/string_table.hpp b/include/osmium/io/detail/string_table.hpp
index b989530..995e2cf 100644
--- a/include/osmium/io/detail/string_table.hpp
+++ b/include/osmium/io/detail/string_table.hpp
@@ -39,8 +39,8 @@ DEALINGS IN THE SOFTWARE.
#include <cstring>
#include <iterator>
#include <list>
-#include <map>
#include <string>
+#include <unordered_map>
#include <osmium/io/detail/pbf.hpp>
@@ -69,8 +69,8 @@ namespace osmium {
std::list<std::string> m_chunks;
void add_chunk() {
- m_chunks.push_front(std::string());
- m_chunks.front().reserve(m_chunk_size);
+ m_chunks.emplace_back();
+ m_chunks.back().reserve(m_chunk_size);
}
public:
@@ -82,6 +82,7 @@ namespace osmium {
}
void clear() noexcept {
+ assert(!m_chunks.empty());
m_chunks.erase(std::next(m_chunks.begin()), m_chunks.end());
m_chunks.front().clear();
}
@@ -97,16 +98,16 @@ namespace osmium {
assert(len <= m_chunk_size);
- size_t chunk_len = m_chunks.front().size();
- if (chunk_len + len > m_chunks.front().capacity()) {
+ size_t chunk_len = m_chunks.back().size();
+ if (chunk_len + len > m_chunks.back().capacity()) {
add_chunk();
chunk_len = 0;
}
- m_chunks.front().append(string);
- m_chunks.front().append(1, '\0');
+ m_chunks.back().append(string);
+ m_chunks.back().append(1, '\0');
- return m_chunks.front().c_str() + chunk_len;
+ return m_chunks.back().c_str() + chunk_len;
}
class const_iterator {
@@ -191,18 +192,33 @@ namespace osmium {
}
size_t get_used_bytes_in_last_chunk() const noexcept {
- return m_chunks.front().size();
+ return m_chunks.back().size();
}
}; // class StringStore
- struct StrComp {
+ struct str_equal {
- bool operator()(const char* lhs, const char* rhs) const {
- return std::strcmp(lhs, rhs) < 0;
+ bool operator()(const char* lhs, const char* rhs) const noexcept {
+ return lhs == rhs || std::strcmp(lhs, rhs) == 0;
}
- }; // struct StrComp
+ }; // struct str_equal
+
+ struct djb2_hash {
+
+ size_t operator()(const char* str) const noexcept {
+ size_t hash = 5381;
+ int c;
+
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ }
+
+ return hash;
+ }
+
+ }; // struct djb2_hash
class StringTable {
@@ -213,14 +229,23 @@ namespace osmium {
// Blob.
static constexpr const uint32_t max_entries = max_uncompressed_blob_size;
+ // There is one string table per PBF primitive block. Most of
+ // them are really small, because most blocks are full of nodes
+ // with no tags. But string tables can get really large for
+ // ways with many tags or for large relations.
+ // The chosen size is enough so that 99% of all string tables
+ // in typical OSM files will only need a single memory
+ // allocation.
+ static constexpr const size_t default_stringtable_chunk_size = 100 * 1024;
+
StringStore m_strings;
- std::map<const char*, size_t, StrComp> m_index;
+ std::unordered_map<const char*, size_t, djb2_hash, str_equal> m_index;
uint32_t m_size;
public:
- StringTable() :
- m_strings(1024 * 1024),
+ explicit StringTable(size_t size = default_stringtable_chunk_size) :
+ m_strings(size),
m_index(),
m_size(0) {
m_strings.add("");
diff --git a/include/osmium/io/detail/zlib.hpp b/include/osmium/io/detail/zlib.hpp
index 2b6dddb..57b456b 100644
--- a/include/osmium/io/detail/zlib.hpp
+++ b/include/osmium/io/detail/zlib.hpp
@@ -39,6 +39,8 @@ DEALINGS IN THE SOFTWARE.
#include <zlib.h>
+#include <protozero/types.hpp>
+
#include <osmium/io/error.hpp>
#include <osmium/util/cast.hpp>
@@ -89,7 +91,7 @@ namespace osmium {
* @param output Uncompressed result data.
* @returns Pointer and size to incompressed data.
*/
- inline std::pair<const char*, size_t> zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) {
+ inline protozero::data_view zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) {
output.resize(raw_size);
auto result = ::uncompress(
@@ -103,7 +105,7 @@ namespace osmium {
throw io_error(std::string("failed to uncompress data: ") + zError(result));
}
- return std::make_pair(output.data(), output.size());
+ return protozero::data_view{output.data(), output.size()};
}
} // namespace detail
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
index 7c60511..b267db6 100644
--- a/include/osmium/io/reader.hpp
+++ b/include/osmium/io/reader.hpp
@@ -62,11 +62,26 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/entity_bits.hpp>
#include <osmium/thread/util.hpp>
+#include <osmium/util/config.hpp>
namespace osmium {
namespace io {
+ namespace detail {
+
+ inline size_t get_input_queue_size() noexcept {
+ size_t n = osmium::config::get_max_queue_size("INPUT", 20);
+ return n > 2 ? n : 2;
+ }
+
+ inline size_t get_osmdata_queue_size() noexcept {
+ size_t n = osmium::config::get_max_queue_size("OSMDATA", 20);
+ return n > 2 ? n : 2;
+ }
+
+ } // namespace detail
+
/**
* This is the user-facing interface for reading OSM files. Instantiate
* an object of this class with a file name or osmium::io::File object
@@ -75,9 +90,6 @@ namespace osmium {
*/
class Reader {
- static constexpr size_t max_input_queue_size = 20; // XXX
- static constexpr size_t max_osmdata_queue_size = 20; // XXX
-
osmium::io::File m_file;
osmium::osm_entity_bits::type m_read_which_entities;
@@ -202,12 +214,12 @@ namespace osmium {
m_read_which_entities(read_which_entities),
m_status(status::okay),
m_childpid(0),
- m_input_queue(max_input_queue_size, "raw_input"),
+ m_input_queue(detail::get_input_queue_size(), "raw_input"),
m_decompressor(m_file.buffer() ?
osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), m_file.buffer(), m_file.buffer_size()) :
osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), open_input_file_or_url(m_file.filename(), &m_childpid))),
m_read_thread_manager(*m_decompressor, m_input_queue),
- m_osmdata_queue(max_osmdata_queue_size, "parser_results"),
+ m_osmdata_queue(detail::get_osmdata_queue_size(), "parser_results"),
m_osmdata_queue_wrapper(m_osmdata_queue),
m_header_future(),
m_header(),
diff --git a/include/osmium/io/writer.hpp b/include/osmium/io/writer.hpp
index b389698..13ac575 100644
--- a/include/osmium/io/writer.hpp
+++ b/include/osmium/io/writer.hpp
@@ -52,11 +52,21 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/io/writer_options.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/thread/util.hpp>
+#include <osmium/util/config.hpp>
namespace osmium {
namespace io {
+ namespace detail {
+
+ inline size_t get_output_queue_size() noexcept {
+ size_t n = osmium::config::get_max_queue_size("OUTPUT", 20);
+ return n > 2 ? n : 2;
+ }
+
+ } // namespace detail
+
/**
* This is the user-facing interface for writing OSM files. Instantiate
* an object of this class with a file name or osmium::io::File object
@@ -194,7 +204,7 @@ namespace osmium {
template <typename... TArgs>
explicit Writer(const osmium::io::File& file, TArgs&&... args) :
m_file(file.check()),
- m_output_queue(20, "raw_output"), // XXX
+ m_output_queue(detail::get_output_queue_size(), "raw_output"),
m_output(osmium::io::detail::OutputFormatFactory::instance().create_output(m_file, m_output_queue)),
m_buffer(),
m_buffer_size(default_buffer_size),
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
index a9d8cb3..b8a4660 100644
--- a/include/osmium/osm/location.hpp
+++ b/include/osmium/osm/location.hpp
@@ -36,7 +36,10 @@ DEALINGS IN THE SOFTWARE.
#include <cmath>
#include <cstdint>
#include <functional>
+#include <iomanip>
#include <iosfwd>
+#include <locale>
+#include <sstream>
#include <stdexcept>
#include <string>
@@ -67,11 +70,38 @@ namespace osmium {
constexpr const int coordinate_precision = 10000000;
+ // Fallback function used when a coordinate is written in scientific
+ // notation. This function uses stringstream and is much more expensive
+ // than the handcrafted one. But coordinates in scientific notations
+ // shouldn't be used anyway.
+ inline int32_t string_to_location_coordinate_fallback(const char* str) {
+ double value;
+ std::istringstream ss{str};
+ ss.imbue(std::locale("C"));
+ ss >> std::noskipws >> value;
+
+ if (ss.fail() || !ss.eof() || ss.bad() || value > 215.0 || value < -215.0) {
+ throw invalid_location{std::string{"wrong format for coordinate: '"} + str + "'"};
+ }
+
+ return std::round(value * coordinate_precision);
+ }
+
// Convert string with a floating point number into integer suitable
// for use as coordinate in a Location.
inline int32_t string_to_location_coordinate(const char* str) {
const char* full = str;
+ // call fallback if scientific notation is used
+ while (*str) {
+ if (*str == 'e' || *str == 'E') {
+ return string_to_location_coordinate_fallback(full);
+ }
+ ++str;
+ }
+
+ str = full;
+
int32_t result = 0;
int sign = 1;
int scale = 7;
@@ -372,13 +402,18 @@ namespace osmium {
}
template <typename T>
+ T as_string_without_check(T iterator, const char separator = ',') const {
+ iterator = detail::append_location_coordinate_to_string(iterator, x());
+ *iterator++ = separator;
+ return detail::append_location_coordinate_to_string(iterator, y());
+ }
+
+ template <typename T>
T as_string(T iterator, const char separator = ',') const {
if (!valid()) {
throw osmium::invalid_location("invalid location");
}
- iterator = detail::append_location_coordinate_to_string(iterator, x());
- *iterator++ = separator;
- return detail::append_location_coordinate_to_string(iterator, y());
+ return as_string_without_check(iterator, separator);
}
}; // class Location
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index 4d2b523..166b00d 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -362,7 +362,6 @@ namespace osmium {
} else {
m_relations_buffer.commit();
m_relations.push_back(std::move(relation_meta));
-// std::cerr << "added relation id=" << relation.id() << "\n";
}
}
@@ -371,10 +370,6 @@ namespace osmium {
* search on them.
*/
void sort_member_meta() {
-/* std::cerr << "relations: " << m_relations.size() << "\n";
- std::cerr << "node members: " << m_member_meta[0].size() << "\n";
- std::cerr << "way members: " << m_member_meta[1].size() << "\n";
- std::cerr << "relation members: " << m_member_meta[2].size() << "\n";*/
std::sort(m_member_meta[0].begin(), m_member_meta[0].end());
std::sort(m_member_meta[1].begin(), m_member_meta[1].end());
std::sort(m_member_meta[2].begin(), m_member_meta[2].end());
@@ -417,9 +412,7 @@ namespace osmium {
assert(member_meta.member_id() == object.id());
assert(member_meta.relation_pos() < m_relations.size());
RelationMeta& relation_meta = m_relations[member_meta.relation_pos()];
-// std::cerr << " => " << member_meta.member_pos() << " < " << get_relation(relation_meta).members().size() << " (id=" << get_relation(relation_meta).id() << ")\n";
assert(member_meta.member_pos() < get_relation(relation_meta).members().size());
-// std::cerr << " add way " << member_meta.member_id() << " to rel " << get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n";
relation_meta.got_one_member();
if (relation_meta.has_all_members()) {
const size_t relation_offset = member_meta.relation_pos();
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index 44b6d15..d1b74c1 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -78,6 +78,11 @@ namespace osmium {
return num_threads;
}
+ inline size_t get_work_queue_size() noexcept {
+ size_t n = osmium::config::get_max_queue_size("WORK", 10);
+ return n > 2 ? n : 2;
+ }
+
} // namespace detail
/**
@@ -118,13 +123,11 @@ namespace osmium {
osmium::thread::set_thread_name("_osmium_worker");
while (true) {
function_wrapper task;
- m_work_queue.wait_and_pop_with_timeout(task);
- if (task) {
- if (task()) {
- // The called tasks returns true only when the
- // worker thread should shut down.
- return;
- }
+ m_work_queue.wait_and_pop(task);
+ if (task && task()) {
+ // The called tasks returns true only when the
+ // worker thread should shut down.
+ return;
}
}
}
@@ -160,10 +163,9 @@ namespace osmium {
public:
static constexpr int default_num_threads = 0;
- static constexpr size_t max_work_queue_size = 10;
static Pool& instance() {
- static Pool pool(default_num_threads, max_work_queue_size);
+ static Pool pool(default_num_threads, detail::get_work_queue_size());
return pool;
}
@@ -176,7 +178,6 @@ namespace osmium {
~Pool() {
shutdown_all_workers();
- m_work_queue.shutdown();
}
size_t queue_size() const {
diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp
index 7717358..3124611 100644
--- a/include/osmium/thread/queue.hpp
+++ b/include/osmium/thread/queue.hpp
@@ -43,6 +43,10 @@ DEALINGS IN THE SOFTWARE.
#include <thread>
#include <utility> // IWYU pragma: keep (for std::move)
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+# include <iostream>
+#endif
+
namespace osmium {
namespace thread {
@@ -69,8 +73,6 @@ namespace osmium {
/// Used to signal readers when data is available in the queue.
std::condition_variable m_data_available;
- std::atomic<bool> m_done;
-
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
/// The largest size the queue has been so far.
size_t m_largest_size;
@@ -81,6 +83,16 @@ namespace osmium {
/// The number of times the queue was full and a thread pushing
/// to the queue was blocked.
std::atomic<int> m_full_counter;
+
+ /**
+ * The number of times wait_and_pop(with_timeout)() was called
+ * on the queue.
+ */
+ std::atomic<int> m_pop_counter;
+
+ /// The number of times the queue was full and a thread pushing
+ /// to the queue was blocked.
+ std::atomic<int> m_empty_counter;
#endif
public:
@@ -97,21 +109,21 @@ namespace osmium {
m_name(name),
m_mutex(),
m_queue(),
- m_data_available(),
- m_done(false)
+ m_data_available()
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
,
m_largest_size(0),
m_push_counter(0),
- m_full_counter(0)
+ m_full_counter(0),
+ m_pop_counter(0),
+ m_empty_counter(0)
#endif
{
}
~Queue() {
- shutdown();
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
- std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls\n";
+ std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls and was empty " << m_empty_counter << " times in " << m_pop_counter << " pop() calls\n";
#endif
}
@@ -141,15 +153,18 @@ namespace osmium {
m_data_available.notify_one();
}
- void shutdown() {
- m_done = true;
- m_data_available.notify_all();
- }
-
void wait_and_pop(T& value) {
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ ++m_pop_counter;
+#endif
std::unique_lock<std::mutex> lock(m_mutex);
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ if (m_queue.empty()) {
+ ++m_empty_counter;
+ }
+#endif
m_data_available.wait(lock, [this] {
- return !m_queue.empty() || m_done;
+ return !m_queue.empty();
});
if (!m_queue.empty()) {
value = std::move(m_queue.front());
@@ -157,22 +172,15 @@ namespace osmium {
}
}
- void wait_and_pop_with_timeout(T& value) {
- std::unique_lock<std::mutex> lock(m_mutex);
- if (!m_data_available.wait_for(lock, std::chrono::seconds(1), [this] {
- return !m_queue.empty() || m_done;
- })) {
- return;
- }
- if (!m_queue.empty()) {
- value = std::move(m_queue.front());
- m_queue.pop();
- }
- }
-
bool try_pop(T& value) {
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ ++m_pop_counter;
+#endif
std::lock_guard<std::mutex> lock(m_mutex);
if (m_queue.empty()) {
+#ifdef OSMIUM_DEBUG_QUEUE_SIZE
+ ++m_empty_counter;
+#endif
return false;
}
value = std::move(m_queue.front());
diff --git a/include/osmium/util/config.hpp b/include/osmium/util/config.hpp
index c405123..e041235 100644
--- a/include/osmium/util/config.hpp
+++ b/include/osmium/util/config.hpp
@@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE.
#include <cstdlib>
#include <cstring>
+#include <string>
#ifdef _MSC_VER
# define strcasecmp _stricmp
@@ -44,7 +45,7 @@ namespace osmium {
namespace config {
- inline int get_pool_threads() {
+ inline int get_pool_threads() noexcept {
const char* env = getenv("OSMIUM_POOL_THREADS");
if (env) {
return std::atoi(env);
@@ -52,7 +53,7 @@ namespace osmium {
return 0;
}
- inline bool use_pool_threads_for_pbf_parsing() {
+ inline bool use_pool_threads_for_pbf_parsing() noexcept {
const char* env = getenv("OSMIUM_USE_POOL_THREADS_FOR_PBF_PARSING");
if (env) {
if (!strcasecmp(env, "off") ||
@@ -65,6 +66,18 @@ namespace osmium {
return true;
}
+ inline size_t get_max_queue_size(const char* queue_name, size_t default_value) noexcept {
+ std::string name {"OSMIUM_MAX_"};
+ name += queue_name;
+ name += "_QUEUE_SIZE";
+ const char* env = getenv(name.c_str());
+ if (env) {
+ auto value = std::atoi(env);
+ return value == 0 ? default_value : value;
+ }
+ return default_value;
+ }
+
} // namespace config
} // namespace osmium
diff --git a/include/osmium/util/delta.hpp b/include/osmium/util/delta.hpp
index 8fd63a4..51c5c7b 100644
--- a/include/osmium/util/delta.hpp
+++ b/include/osmium/util/delta.hpp
@@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE.
#include <utility>
#include <osmium/util/cast.hpp>
+#include <osmium/util/compatibility.hpp>
namespace osmium {
@@ -118,60 +119,6 @@ namespace osmium {
}; // class DeltaDecode
- template <typename TBaseIterator, typename TTransform, typename TValue, typename TDelta = int64_t>
- class DeltaEncodeIterator {
-
- TBaseIterator m_it;
- TBaseIterator m_end;
- TTransform m_trans;
- DeltaEncode<TValue, TDelta> m_value;
- TDelta m_delta;
-
- public:
-
- using iterator_category = std::input_iterator_tag;
- using value_type = TValue;
- using difference_type = std::ptrdiff_t;
- using pointer = value_type*;
- using reference = value_type&;
-
- using delta_type = TDelta;
-
- DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) :
- m_it(first),
- m_end(last),
- m_trans(trans),
- m_value(m_it != m_end ? m_trans(m_it) : 0),
- m_delta(static_cast_with_assert<TDelta>(m_value.value())) {
- }
-
- DeltaEncodeIterator& operator++() {
- if (++m_it != m_end) {
- m_delta = m_value.update(m_trans(m_it));
- }
- return *this;
- }
-
- DeltaEncodeIterator operator++(int) {
- DeltaEncodeIterator tmp(*this);
- operator++();
- return tmp;
- }
-
- TDelta operator*() {
- return m_delta;
- }
-
- bool operator==(const DeltaEncodeIterator& rhs) const {
- return m_it == rhs.m_it && m_end == rhs.m_end;
- }
-
- bool operator!=(const DeltaEncodeIterator& rhs) const {
- return !(*this == rhs);
- }
-
- }; // class DeltaEncodeIterator
-
} // namespace util
} // namespace osmium
diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp
index 7ff931e..ac7a94f 100644
--- a/include/osmium/version.hpp
+++ b/include/osmium/version.hpp
@@ -34,9 +34,9 @@ DEALINGS IN THE SOFTWARE.
*/
#define LIBOSMIUM_VERSION_MAJOR 2
-#define LIBOSMIUM_VERSION_MINOR 7
-#define LIBOSMIUM_VERSION_PATCH 2
+#define LIBOSMIUM_VERSION_MINOR 8
+#define LIBOSMIUM_VERSION_PATCH 0
-#define LIBOSMIUM_VERSION_STRING "2.7.2"
+#define LIBOSMIUM_VERSION_STRING "2.8.0"
#endif // OSMIUM_VERSION_HPP
diff --git a/include/protozero/byteswap.hpp b/include/protozero/byteswap.hpp
index a018c1c..06ba6de 100644
--- a/include/protozero/byteswap.hpp
+++ b/include/protozero/byteswap.hpp
@@ -28,7 +28,7 @@ namespace protozero {
* be specialized to actually work.
*/
template <int N>
-inline void byteswap(const char* /*data*/, char* /*result*/) {
+inline void byteswap(const char* /*data*/, char* /*result*/) noexcept {
static_assert(N == 1, "Can only swap 4 or 8 byte values");
}
@@ -36,7 +36,7 @@ inline void byteswap(const char* /*data*/, char* /*result*/) {
* Swap 4 byte value (int32_t, uint32_t, float) between endianness formats.
*/
template <>
-inline void byteswap<4>(const char* data, char* result) {
+inline void byteswap<4>(const char* data, char* result) noexcept {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint32_t*>(result) = __builtin_bswap32(*reinterpret_cast<const uint32_t*>(data));
#else
@@ -51,7 +51,7 @@ inline void byteswap<4>(const char* data, char* result) {
* Swap 8 byte value (int64_t, uint64_t, double) between endianness formats.
*/
template <>
-inline void byteswap<8>(const char* data, char* result) {
+inline void byteswap<8>(const char* data, char* result) noexcept {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint64_t*>(result) = __builtin_bswap64(*reinterpret_cast<const uint64_t*>(data));
#else
diff --git a/include/protozero/exception.hpp b/include/protozero/exception.hpp
index 5c7ab54..ca4340e 100644
--- a/include/protozero/exception.hpp
+++ b/include/protozero/exception.hpp
@@ -29,7 +29,7 @@ namespace protozero {
*/
struct exception : std::exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "pbf exception"; }
+ const char* what() const noexcept override { return "pbf exception"; }
};
/**
@@ -38,7 +38,7 @@ struct exception : std::exception {
*/
struct varint_too_long_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "varint too long exception"; }
+ const char* what() const noexcept override { return "varint too long exception"; }
};
/**
@@ -47,7 +47,7 @@ struct varint_too_long_exception : exception {
*/
struct unknown_pbf_wire_type_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "unknown pbf field type exception"; }
+ const char* what() const noexcept override { return "unknown pbf field type exception"; }
};
/**
@@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception {
*/
struct end_of_buffer_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "end of buffer exception"; }
+ const char* what() const noexcept override { return "end of buffer exception"; }
};
} // end namespace protozero
diff --git a/include/protozero/iterators.hpp b/include/protozero/iterators.hpp
new file mode 100644
index 0000000..813d96b
--- /dev/null
+++ b/include/protozero/iterators.hpp
@@ -0,0 +1,373 @@
+#ifndef PROTOZERO_ITERATORS_HPP
+#define PROTOZERO_ITERATORS_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file iterators.hpp
+ *
+ * @brief Contains the iterators for access to packed repeated fields.
+ */
+
+#include <cstring>
+#include <iterator>
+#include <utility>
+
+#include <protozero/config.hpp>
+#include <protozero/varint.hpp>
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <protozero/byteswap.hpp>
+#endif
+
+namespace protozero {
+
+namespace detail {
+
+ // Copy N bytes from src to dest on little endian machines, on big
+ // endian swap the bytes in the process.
+ template <int N>
+ inline void copy_or_byteswap(const char* src, void* dest) noexcept {
+#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
+ std::memcpy(dest, src, N);
+#else
+ byteswap<N>(src, reinterpret_cast<char*>(dest));
+#endif
+ }
+
+} // end namespace detail
+
+/**
+ * A range of iterators based on std::pair. Created from beginning and
+ * end iterators. Used as a return type from some pbf_reader methods
+ * that is easy to use with range-based for loops.
+ */
+template <typename T, typename P = std::pair<T, T>>
+class iterator_range :
+#ifdef PROTOZERO_STRICT_API
+ protected
+#else
+ public
+#endif
+ P {
+
+public:
+
+ /// The type of the iterators in this range.
+ using iterator = T;
+
+ /// The value type of the underlying iterator.
+ using value_type = typename std::iterator_traits<T>::value_type;
+
+ /**
+ * Default constructor. Create empty iterator_range.
+ */
+ constexpr iterator_range() :
+ P(iterator{}, iterator{}) {
+ }
+
+ /**
+ * Create iterator range from two iterators.
+ *
+ * @param first Iterator to beginning or range.
+ * @param last Iterator to end or range.
+ */
+ constexpr iterator_range(iterator&& first, iterator&& last) :
+ P(std::forward<iterator>(first),
+ std::forward<iterator>(last)) {
+ }
+
+ /// Return iterator to beginning of range.
+ constexpr iterator begin() const noexcept {
+ return this->first;
+ }
+
+ /// Return iterator to end of range.
+ constexpr iterator end() const noexcept {
+ return this->second;
+ }
+
+ /// Return iterator to beginning of range.
+ constexpr iterator cbegin() const noexcept {
+ return this->first;
+ }
+
+ /// Return iterator to end of range.
+ constexpr iterator cend() const noexcept {
+ return this->second;
+ }
+
+ /// Return true if this range is empty.
+ constexpr std::size_t empty() const noexcept {
+ return begin() == end();
+ }
+
+ /**
+ * Get element at the beginning of the range.
+ *
+ * @pre Range must not be empty.
+ */
+ value_type front() const {
+ protozero_assert(!empty());
+ return *(this->first);
+ }
+
+ /**
+ * Advance beginning of range by one.
+ *
+ * @pre Range must not be empty.
+ */
+ void drop_front() {
+ protozero_assert(!empty());
+ ++this->first;
+ }
+
+ /**
+ * Swap the contents of this range with the other.
+ *
+ * @param other Other range to swap data with.
+ */
+ void swap(iterator_range& other) noexcept {
+ using std::swap;
+ swap(this->first, other.first);
+ swap(this->second, other.second);
+ }
+
+}; // struct iterator_range
+
+/**
+ * Swap two iterator_ranges.
+ *
+ * @param lhs First range.
+ * @param rhs Second range.
+ */
+template <typename T>
+inline void swap(iterator_range<T>& lhs, iterator_range<T>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+
+template <typename T>
+using const_fixed_iterator = const T*;
+
+/**
+ * Create iterator_range from char pointers to beginning and end of range.
+ *
+ * @param first Beginning of range.
+ * @param last End of range.
+ */
+template <typename T>
+inline iterator_range<const_fixed_iterator<T>> create_fixed_iterator_range(const char* first, const char* last) {
+ return iterator_range<const_fixed_iterator<T>>{reinterpret_cast<const T*>(first),
+ reinterpret_cast<const T*>(last)};
+}
+
+#else
+
+/**
+ * A forward iterator used for accessing packed repeated fields of fixed
+ * length (fixed32, sfixed32, float, double).
+ */
+template <typename T>
+class const_fixed_iterator {
+
+ /// Pointer to current iterator position
+ const char* m_data;
+
+ /// Pointer to end iterator position
+ const char* m_end;
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_fixed_iterator() noexcept :
+ m_data(nullptr),
+ m_end(nullptr) {
+ }
+
+ const_fixed_iterator(const char* data, const char* end) noexcept :
+ m_data(data),
+ m_end(end) {
+ }
+
+ const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
+ const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
+
+ const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
+ const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
+
+ ~const_fixed_iterator() noexcept = default;
+
+ value_type operator*() const {
+ value_type result;
+ detail::copy_or_byteswap<sizeof(value_type)>(m_data , &result);
+ return result;
+ }
+
+ const_fixed_iterator& operator++() {
+ m_data += sizeof(value_type);
+ return *this;
+ }
+
+ const_fixed_iterator operator++(int) {
+ const const_fixed_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+ bool operator==(const const_fixed_iterator& rhs) const noexcept {
+ return m_data == rhs.m_data && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const const_fixed_iterator& rhs) const noexcept {
+ return !(*this == rhs);
+ }
+
+}; // class const_fixed_iterator
+
+/**
+ * Create iterator_range from char pointers to beginning and end of range.
+ *
+ * @param first Beginning of range.
+ * @param last End of range.
+ */
+template <typename T>
+inline iterator_range<const_fixed_iterator<T>> create_fixed_iterator_range(const char* first, const char* last) {
+ return iterator_range<const_fixed_iterator<T>>{const_fixed_iterator<T>(first, last),
+ const_fixed_iterator<T>(last, last)};
+}
+
+#endif
+
+/**
+ * A forward iterator used for accessing packed repeated varint fields
+ * (int32, uint32, int64, uint64, bool, enum).
+ */
+template <typename T>
+class const_varint_iterator {
+
+protected:
+
+ /// Pointer to current iterator position
+ const char* m_data;
+
+ /// Pointer to end iterator position
+ const char* m_end;
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_varint_iterator() noexcept :
+ m_data(nullptr),
+ m_end(nullptr) {
+ }
+
+ const_varint_iterator(const char* data, const char* end) noexcept :
+ m_data(data),
+ m_end(end) {
+ }
+
+ const_varint_iterator(const const_varint_iterator&) noexcept = default;
+ const_varint_iterator(const_varint_iterator&&) noexcept = default;
+
+ const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
+ const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
+
+ ~const_varint_iterator() noexcept = default;
+
+ value_type operator*() const {
+ const char* d = m_data; // will be thrown away
+ return static_cast<value_type>(decode_varint(&d, m_end));
+ }
+
+ const_varint_iterator& operator++() {
+ skip_varint(&m_data, m_end);
+ return *this;
+ }
+
+ const_varint_iterator operator++(int) {
+ const const_varint_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+ bool operator==(const const_varint_iterator& rhs) const noexcept {
+ return m_data == rhs.m_data && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const const_varint_iterator& rhs) const noexcept {
+ return !(*this == rhs);
+ }
+
+}; // class const_varint_iterator
+
+/**
+ * A forward iterator used for accessing packed repeated svarint fields
+ * (sint32, sint64).
+ */
+template <typename T>
+class const_svarint_iterator : public const_varint_iterator<T> {
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_svarint_iterator() noexcept :
+ const_varint_iterator<T>() {
+ }
+
+ const_svarint_iterator(const char* data, const char* end) noexcept :
+ const_varint_iterator<T>(data, end) {
+ }
+
+ const_svarint_iterator(const const_svarint_iterator&) = default;
+ const_svarint_iterator(const_svarint_iterator&&) = default;
+
+ const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
+ const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
+
+ ~const_svarint_iterator() = default;
+
+ value_type operator*() const {
+ const char* d = this->m_data; // will be thrown away
+ return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
+ }
+
+ const_svarint_iterator& operator++() {
+ skip_varint(&this->m_data, this->m_end);
+ return *this;
+ }
+
+ const_svarint_iterator operator++(int) {
+ const const_svarint_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+}; // class const_svarint_iterator
+
+} // end namespace protozero
+
+#endif // PROTOZERO_ITERATORS_HPP
diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp
index 548f4ce..39af53f 100644
--- a/include/protozero/pbf_builder.hpp
+++ b/include/protozero/pbf_builder.hpp
@@ -57,7 +57,7 @@ public:
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
- inline void add_##name(T tag, type value) { \
+ void add_##name(T tag, type value) { \
pbf_writer::add_##name(pbf_tag_type(tag), value); \
}
@@ -79,38 +79,38 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
/// @endcond
- inline void add_bytes(T tag, const char* value, std::size_t size) {
+ void add_bytes(T tag, const char* value, std::size_t size) {
pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
}
- inline void add_bytes(T tag, const std::string& value) {
+ void add_bytes(T tag, const std::string& value) {
pbf_writer::add_bytes(pbf_tag_type(tag), value);
}
- inline void add_string(T tag, const char* value, std::size_t size) {
+ void add_string(T tag, const char* value, std::size_t size) {
pbf_writer::add_string(pbf_tag_type(tag), value, size);
}
- inline void add_string(T tag, const std::string& value) {
+ void add_string(T tag, const std::string& value) {
pbf_writer::add_string(pbf_tag_type(tag), value);
}
- inline void add_string(T tag, const char* value) {
+ void add_string(T tag, const char* value) {
pbf_writer::add_string(pbf_tag_type(tag), value);
}
- inline void add_message(T tag, const char* value, std::size_t size) {
+ void add_message(T tag, const char* value, std::size_t size) {
pbf_writer::add_message(pbf_tag_type(tag), value, size);
}
- inline void add_message(T tag, const std::string& value) {
+ void add_message(T tag, const std::string& value) {
pbf_writer::add_message(pbf_tag_type(tag), value);
}
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \
template <typename InputIterator> \
- inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
+ void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \
}
@@ -132,7 +132,7 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_PACKED
/// @endcond
-};
+}; // class pbf_builder
} // end namespace protozero
diff --git a/include/protozero/pbf_message.hpp b/include/protozero/pbf_message.hpp
index 45f01c1..0557734 100644
--- a/include/protozero/pbf_message.hpp
+++ b/include/protozero/pbf_message.hpp
@@ -13,7 +13,7 @@ documentation.
/**
* @file pbf_message.hpp
*
- * @brief Contains the pbf_message class.
+ * @brief Contains the pbf_message template class.
*/
#include <type_traits>
@@ -75,19 +75,19 @@ public:
pbf_reader(std::forward<Args>(args)...) {
}
- inline bool next() {
+ bool next() {
return pbf_reader::next();
}
- inline bool next(T tag) {
+ bool next(T tag) {
return pbf_reader::next(pbf_tag_type(tag));
}
- inline T tag() const noexcept {
+ T tag() const noexcept {
return T(pbf_reader::tag());
}
-};
+}; // class pbf_message
} // end namespace protozero
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
index 58b3884..2f4054d 100644
--- a/include/protozero/pbf_reader.hpp
+++ b/include/protozero/pbf_reader.hpp
@@ -18,13 +18,12 @@ documentation.
#include <cstddef>
#include <cstdint>
-#include <cstring>
-#include <iterator>
#include <string>
#include <utility>
#include <protozero/config.hpp>
#include <protozero/exception.hpp>
+#include <protozero/iterators.hpp>
#include <protozero/types.hpp>
#include <protozero/varint.hpp>
@@ -55,16 +54,16 @@ namespace protozero {
*
* All methods of the pbf_reader class except get_bytes() and get_string()
* provide the strong exception guarantee, ie they either succeed or do not
- * change the pbf_reader object they are called on. Use the get_data() method
+ * change the pbf_reader object they are called on. Use the get_view() method
* instead of get_bytes() or get_string(), if you need this guarantee.
*/
class pbf_reader {
// A pointer to the next unread data.
- const char *m_data = nullptr;
+ const char* m_data = nullptr;
// A pointer to one past the end of data.
- const char *m_end = nullptr;
+ const char* m_end = nullptr;
// The wire type of the current field.
pbf_wire_type m_wire_type = pbf_wire_type::unknown;
@@ -72,119 +71,84 @@ class pbf_reader {
// The tag of the current field.
pbf_tag_type m_tag = 0;
- // Copy N bytes from src to dest on little endian machines, on big endian
- // swap the bytes in the process.
- template <int N>
- static void copy_or_byteswap(const char* src, void* dest) noexcept {
-#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
- memcpy(dest, src, N);
-#else
- byteswap<N>(src, reinterpret_cast<char*>(dest));
-#endif
- }
-
template <typename T>
- inline T get_fixed() {
+ T get_fixed() {
T result;
skip_bytes(sizeof(T));
- copy_or_byteswap<sizeof(T)>(m_data - sizeof(T), &result);
+ detail::copy_or_byteswap<sizeof(T)>(m_data - sizeof(T), &result);
return result;
}
-#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
-
template <typename T>
- using const_fixed_iterator = const T*;
+ iterator_range<const_fixed_iterator<T>> packed_fixed() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ const auto len = get_len_and_skip();
+ protozero_assert(len % sizeof(T) == 0);
+ return create_fixed_iterator_range<T>(m_data - len, m_data);
+ }
template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
- return std::make_pair(reinterpret_cast<const T*>(first),
- reinterpret_cast<const T*>(last));
+ T get_varint() {
+ return static_cast<T>(decode_varint(&m_data, m_end));
}
-#else
-
template <typename T>
- class const_fixed_iterator : public std::iterator<std::forward_iterator_tag, T> {
-
- const char* m_data;
- const char* m_end;
-
- public:
-
- const_fixed_iterator() noexcept :
- m_data(nullptr),
- m_end(nullptr) {
- }
-
- const_fixed_iterator(const char *data, const char* end) noexcept :
- m_data(data),
- m_end(end) {
- }
-
- const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
- const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
-
- const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
- const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
-
- ~const_fixed_iterator() noexcept = default;
-
- T operator*() {
- T result;
- copy_or_byteswap<sizeof(T)>(m_data , &result);
- return result;
- }
-
- const_fixed_iterator& operator++() {
- m_data += sizeof(T);
- return *this;
- }
-
- const_fixed_iterator operator++(int) {
- const const_fixed_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
+ T get_svarint() {
+ protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
+ return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
+ }
- bool operator==(const const_fixed_iterator& rhs) const noexcept {
- return m_data == rhs.m_data && m_end == rhs.m_end;
- }
+ pbf_length_type get_length() {
+ return get_varint<pbf_length_type>();
+ }
- bool operator!=(const const_fixed_iterator& rhs) const noexcept {
- return !(*this == rhs);
+ void skip_bytes(pbf_length_type len) {
+ if (m_data + len > m_end) {
+ throw end_of_buffer_exception();
}
+ m_data += len;
- }; // class const_fixed_iterator
-
- template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
- return std::make_pair(const_fixed_iterator<T>(first, last),
- const_fixed_iterator<T>(last, last));
+ // In debug builds reset the tag to zero so that we can detect (some)
+ // wrong code.
+#ifndef NDEBUG
+ m_tag = 0;
+#endif
}
-#endif
+ pbf_length_type get_len_and_skip() {
+ const auto len = get_length();
+ skip_bytes(len);
+ return len;
+ }
template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> packed_fixed() {
+ iterator_range<T> get_packed() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- protozero_assert(len % sizeof(T) == 0);
- return create_fixed_iterator_pair<T>(m_data-len, m_data);
+ const auto len = get_len_and_skip();
+ return iterator_range<T>{T{m_data - len, m_data},
+ T{m_data, m_data}};
}
- template <typename T> inline T get_varint();
- template <typename T> inline T get_svarint();
-
- inline pbf_length_type get_length() { return get_varint<pbf_length_type>(); }
-
- inline void skip_bytes(pbf_length_type len);
-
- inline pbf_length_type get_len_and_skip();
-
public:
/**
+ * Construct a pbf_reader message from a data_view. The pointer from the
+ * data_view will be stored inside the pbf_reader object, no data is
+ * copied. So you must* make sure the view stays valid as long as the
+ * pbf_reader object is used.
+ *
+ * The buffer must contain a complete protobuf message.
+ *
+ * @post There is no current field.
+ */
+ explicit pbf_reader(const data_view& view) noexcept
+ : m_data(view.data()),
+ m_end(view.data() + view.size()),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
+
+ /**
* Construct a pbf_reader message from a data pointer and a length. The pointer
* will be stored inside the pbf_reader object, no data is copied. So you must
* make sure the buffer stays valid as long as the pbf_reader object is used.
@@ -193,7 +157,12 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(const char *data, std::size_t length) noexcept;
+ pbf_reader(const char* data, std::size_t length) noexcept
+ : m_data(data),
+ m_end(data + length),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* Construct a pbf_reader message from a data pointer and a length. The pointer
@@ -204,7 +173,12 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(std::pair<const char *, std::size_t> data) noexcept;
+ pbf_reader(std::pair<const char*, std::size_t> data) noexcept
+ : m_data(data.first),
+ m_end(data.first + data.second),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* Construct a pbf_reader message from a std::string. A pointer to the string
@@ -216,33 +190,53 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(const std::string& data) noexcept;
+ pbf_reader(const std::string& data) noexcept
+ : m_data(data.data()),
+ m_end(data.data() + data.size()),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* pbf_reader can be default constructed and behaves like it has an empty
* buffer.
*/
- inline pbf_reader() noexcept = default;
+ pbf_reader() noexcept = default;
/// pbf_reader messages can be copied trivially.
- inline pbf_reader(const pbf_reader&) noexcept = default;
+ pbf_reader(const pbf_reader&) noexcept = default;
/// pbf_reader messages can be moved trivially.
- inline pbf_reader(pbf_reader&&) noexcept = default;
+ pbf_reader(pbf_reader&&) noexcept = default;
/// pbf_reader messages can be copied trivially.
- inline pbf_reader& operator=(const pbf_reader& other) noexcept = default;
+ pbf_reader& operator=(const pbf_reader& other) noexcept = default;
/// pbf_reader messages can be moved trivially.
- inline pbf_reader& operator=(pbf_reader&& other) noexcept = default;
+ pbf_reader& operator=(pbf_reader&& other) noexcept = default;
+
+ ~pbf_reader() = default;
- inline ~pbf_reader() = default;
+ /**
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
+ */
+ void swap(pbf_reader& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_end, other.m_end);
+ swap(m_wire_type, other.m_wire_type);
+ swap(m_tag, other.m_tag);
+ }
/**
* In a boolean context the pbf_reader class evaluates to `true` if there are
* still fields available and to `false` if the last field has been read.
*/
- inline operator bool() const noexcept;
+ operator bool() const noexcept {
+ return m_data < m_end;
+ }
/**
* Return the length in bytes of the current message. If you have
@@ -272,7 +266,31 @@ public:
* @pre There must be no current field.
* @post If it returns `true` there is a current field now.
*/
- inline bool next();
+ bool next() {
+ if (m_data == m_end) {
+ return false;
+ }
+
+ const auto value = get_varint<uint32_t>();
+ m_tag = pbf_tag_type(value >> 3);
+
+ // tags 0 and 19000 to 19999 are not allowed as per
+ // https://developers.google.com/protocol-buffers/docs/proto
+ protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
+
+ m_wire_type = pbf_wire_type(value & 0x07);
+ switch (m_wire_type) {
+ case pbf_wire_type::varint:
+ case pbf_wire_type::fixed64:
+ case pbf_wire_type::length_delimited:
+ case pbf_wire_type::fixed32:
+ break;
+ default:
+ throw unknown_pbf_wire_type_exception();
+ }
+
+ return true;
+ }
/**
* Set next field with given tag in the message as the current field.
@@ -299,7 +317,16 @@ public:
* @pre There must be no current field.
* @post If it returns `true` there is a current field now with the given tag.
*/
- inline bool next(pbf_tag_type tag);
+ bool next(pbf_tag_type tag) {
+ while (next()) {
+ if (m_tag == tag) {
+ return true;
+ } else {
+ skip();
+ }
+ }
+ return false;
+ }
/**
* The tag of the current field. The tag is the field number from the
@@ -310,7 +337,9 @@ public:
* @returns tag of the current field.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline pbf_tag_type tag() const noexcept;
+ pbf_tag_type tag() const noexcept {
+ return m_tag;
+ }
/**
* Get the wire type of the current field. The wire types are:
@@ -327,7 +356,9 @@ public:
* @returns wire type of the current field.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline pbf_wire_type wire_type() const noexcept;
+ pbf_wire_type wire_type() const noexcept {
+ return m_wire_type;
+ }
/**
* Check the wire type of the current field.
@@ -335,7 +366,9 @@ public:
* @returns `true` if the current field has the given wire type.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline bool has_wire_type(pbf_wire_type type) const noexcept;
+ bool has_wire_type(pbf_wire_type type) const noexcept {
+ return wire_type() == type;
+ }
/**
* Consume the current field.
@@ -343,7 +376,25 @@ public:
* @pre There must be a current field (ie. next() must have returned `true`).
* @post The current field was consumed and there is no current field now.
*/
- inline void skip();
+ void skip() {
+ protozero_assert(tag() != 0 && "call next() before calling skip()");
+ switch (wire_type()) {
+ case pbf_wire_type::varint:
+ skip_varint(&m_data, m_end);
+ break;
+ case pbf_wire_type::fixed64:
+ skip_bytes(8);
+ break;
+ case pbf_wire_type::length_delimited:
+ skip_bytes(get_length());
+ break;
+ case pbf_wire_type::fixed32:
+ skip_bytes(4);
+ break;
+ default:
+ protozero_assert(false && "can not be here because next() should have thrown already");
+ }
+ }
///@{
/**
@@ -357,7 +408,13 @@ public:
* @pre The current field must be of type "bool".
* @post The current field was consumed and there is no current field now.
*/
- inline bool get_bool();
+ bool get_bool() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+ protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
+ skip_bytes(1);
+ return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
+ }
/**
* Consume and return value of current "enum" field.
@@ -366,7 +423,7 @@ public:
* @pre The current field must be of type "enum".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_enum() {
+ int32_t get_enum() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
@@ -378,7 +435,7 @@ public:
* @pre The current field must be of type "int32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_int32() {
+ int32_t get_int32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
@@ -390,7 +447,7 @@ public:
* @pre The current field must be of type "sint32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_sint32() {
+ int32_t get_sint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int32_t>();
}
@@ -402,7 +459,7 @@ public:
* @pre The current field must be of type "uint32".
* @post The current field was consumed and there is no current field now.
*/
- inline uint32_t get_uint32() {
+ uint32_t get_uint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint32_t>();
}
@@ -414,7 +471,7 @@ public:
* @pre The current field must be of type "int64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_int64() {
+ int64_t get_int64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int64_t>();
}
@@ -426,7 +483,7 @@ public:
* @pre The current field must be of type "sint64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_sint64() {
+ int64_t get_sint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int64_t>();
}
@@ -438,7 +495,7 @@ public:
* @pre The current field must be of type "uint64".
* @post The current field was consumed and there is no current field now.
*/
- inline uint64_t get_uint64() {
+ uint64_t get_uint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint64_t>();
}
@@ -450,7 +507,11 @@ public:
* @pre The current field must be of type "fixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline uint32_t get_fixed32();
+ uint32_t get_fixed32() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<uint32_t>();
+ }
/**
* Consume and return value of current "sfixed32" field.
@@ -459,7 +520,11 @@ public:
* @pre The current field must be of type "sfixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_sfixed32();
+ int32_t get_sfixed32() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<int32_t>();
+ }
/**
* Consume and return value of current "fixed64" field.
@@ -468,7 +533,11 @@ public:
* @pre The current field must be of type "fixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline uint64_t get_fixed64();
+ uint64_t get_fixed64() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<uint64_t>();
+ }
/**
* Consume and return value of current "sfixed64" field.
@@ -477,7 +546,11 @@ public:
* @pre The current field must be of type "sfixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_sfixed64();
+ int64_t get_sfixed64() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<int64_t>();
+ }
/**
* Consume and return value of current "float" field.
@@ -486,7 +559,11 @@ public:
* @pre The current field must be of type "float".
* @post The current field was consumed and there is no current field now.
*/
- inline float get_float();
+ float get_float() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<float>();
+ }
/**
* Consume and return value of current "double" field.
@@ -495,8 +572,29 @@ public:
* @pre The current field must be of type "double".
* @post The current field was consumed and there is no current field now.
*/
- inline double get_double();
+ double get_double() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<double>();
+ }
+
+ /**
+ * Consume and return value of current "bytes", "string", or "message"
+ * field.
+ *
+ * @returns A data_view object.
+ * @pre There must be a current field (ie. next() must have returned `true`).
+ * @pre The current field must be of type "bytes", "string", or "message".
+ * @post The current field was consumed and there is no current field now.
+ */
+ data_view get_view() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
+ const auto len = get_len_and_skip();
+ return data_view{m_data-len, len};
+ }
+#ifndef PROTOZERO_STRICT_API
/**
* Consume and return value of current "bytes" or "string" field.
*
@@ -505,7 +603,13 @@ public:
* @pre The current field must be of type "bytes" or "string".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<const char*, pbf_length_type> get_data();
+ std::pair<const char*, pbf_length_type> get_data() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
+ const auto len = get_len_and_skip();
+ return std::make_pair(m_data-len, len);
+ }
+#endif
/**
* Consume and return value of current "bytes" field.
@@ -514,7 +618,9 @@ public:
* @pre The current field must be of type "bytes".
* @post The current field was consumed and there is no current field now.
*/
- inline std::string get_bytes();
+ std::string get_bytes() {
+ return std::string(get_view());
+ }
/**
* Consume and return value of current "string" field.
@@ -523,7 +629,9 @@ public:
* @pre The current field must be of type "string".
* @post The current field was consumed and there is no current field now.
*/
- inline std::string get_string();
+ std::string get_string() {
+ return std::string(get_view());
+ }
/**
* Consume and return value of current "message" field.
@@ -532,136 +640,35 @@ public:
* @pre The current field must be of type "message".
* @post The current field was consumed and there is no current field now.
*/
- inline pbf_reader get_message() {
- return pbf_reader(get_data());
+ pbf_reader get_message() {
+ return pbf_reader(get_view());
}
///@}
-private:
-
- template <typename T>
- class const_varint_iterator : public std::iterator<std::forward_iterator_tag, T> {
-
- protected:
-
- const char* m_data;
- const char* m_end;
-
- public:
-
- const_varint_iterator() noexcept :
- m_data(nullptr),
- m_end(nullptr) {
- }
-
- const_varint_iterator(const char *data, const char* end) noexcept :
- m_data(data),
- m_end(end) {
- }
-
- const_varint_iterator(const const_varint_iterator&) noexcept = default;
- const_varint_iterator(const_varint_iterator&&) noexcept = default;
-
- const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
- const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
-
- ~const_varint_iterator() noexcept = default;
-
- T operator*() {
- const char* d = m_data; // will be thrown away
- return static_cast<T>(decode_varint(&d, m_end));
- }
-
- const_varint_iterator& operator++() {
- // Ignore the result, we call decode_varint() just for the
- // side-effect of updating m_data.
- decode_varint(&m_data, m_end);
- return *this;
- }
-
- const_varint_iterator operator++(int) {
- const const_varint_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
-
- bool operator==(const const_varint_iterator& rhs) const noexcept {
- return m_data == rhs.m_data && m_end == rhs.m_end;
- }
-
- bool operator!=(const const_varint_iterator& rhs) const noexcept {
- return !(*this == rhs);
- }
-
- }; // class const_varint_iterator
-
- template <typename T>
- class const_svarint_iterator : public const_varint_iterator<T> {
-
- public:
-
- const_svarint_iterator() noexcept :
- const_varint_iterator<T>() {
- }
-
- const_svarint_iterator(const char *data, const char* end) noexcept :
- const_varint_iterator<T>(data, end) {
- }
-
- const_svarint_iterator(const const_svarint_iterator&) = default;
- const_svarint_iterator(const_svarint_iterator&&) = default;
-
- const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
- const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
-
- ~const_svarint_iterator() = default;
-
- T operator*() {
- const char* d = this->m_data; // will be thrown away
- return static_cast<T>(decode_zigzag64(decode_varint(&d, this->m_end)));
- }
-
- const_svarint_iterator& operator++() {
- // Ignore the result, we call decode_varint() just for the
- // side-effect of updating m_data.
- decode_varint(&this->m_data, this->m_end);
- return *this;
- }
-
- const_svarint_iterator operator++(int) {
- const const_svarint_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
-
- }; // class const_svarint_iterator
-
-public:
-
/// Forward iterator for iterating over bool (int32 varint) values.
- typedef const_varint_iterator< int32_t> const_bool_iterator;
+ using const_bool_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over enum (int32 varint) values.
- typedef const_varint_iterator< int32_t> const_enum_iterator;
+ using const_enum_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over int32 (varint) values.
- typedef const_varint_iterator< int32_t> const_int32_iterator;
+ using const_int32_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over sint32 (varint) values.
- typedef const_svarint_iterator<int32_t> const_sint32_iterator;
+ using const_sint32_iterator = const_svarint_iterator<int32_t>;
/// Forward iterator for iterating over uint32 (varint) values.
- typedef const_varint_iterator<uint32_t> const_uint32_iterator;
+ using const_uint32_iterator = const_varint_iterator<uint32_t>;
/// Forward iterator for iterating over int64 (varint) values.
- typedef const_varint_iterator< int64_t> const_int64_iterator;
+ using const_int64_iterator = const_varint_iterator< int64_t>;
/// Forward iterator for iterating over sint64 (varint) values.
- typedef const_svarint_iterator<int64_t> const_sint64_iterator;
+ using const_sint64_iterator = const_svarint_iterator<int64_t>;
/// Forward iterator for iterating over uint64 (varint) values.
- typedef const_varint_iterator<uint64_t> const_uint64_iterator;
+ using const_uint64_iterator = const_varint_iterator<uint64_t>;
///@{
/**
@@ -677,7 +684,9 @@ public:
* @pre The current field must be of type "repeated packed bool".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> get_packed_bool();
+ iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
+ return get_packed<pbf_reader::const_bool_iterator>();
+ }
/**
* Consume current "repeated packed enum" field.
@@ -688,7 +697,9 @@ public:
* @pre The current field must be of type "repeated packed enum".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> get_packed_enum();
+ iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
+ return get_packed<pbf_reader::const_enum_iterator>();
+ }
/**
* Consume current "repeated packed int32" field.
@@ -699,7 +710,9 @@ public:
* @pre The current field must be of type "repeated packed int32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> get_packed_int32();
+ iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
+ return get_packed<pbf_reader::const_int32_iterator>();
+ }
/**
* Consume current "repeated packed sint32" field.
@@ -710,7 +723,9 @@ public:
* @pre The current field must be of type "repeated packed sint32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> get_packed_sint32();
+ iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
+ return get_packed<pbf_reader::const_sint32_iterator>();
+ }
/**
* Consume current "repeated packed uint32" field.
@@ -721,7 +736,9 @@ public:
* @pre The current field must be of type "repeated packed uint32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> get_packed_uint32();
+ iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
+ return get_packed<pbf_reader::const_uint32_iterator>();
+ }
/**
* Consume current "repeated packed int64" field.
@@ -732,7 +749,9 @@ public:
* @pre The current field must be of type "repeated packed int64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> get_packed_int64();
+ iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
+ return get_packed<pbf_reader::const_int64_iterator>();
+ }
/**
* Consume current "repeated packed sint64" field.
@@ -743,7 +762,9 @@ public:
* @pre The current field must be of type "repeated packed sint64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> get_packed_sint64();
+ iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
+ return get_packed<pbf_reader::const_sint64_iterator>();
+ }
/**
* Consume current "repeated packed uint64" field.
@@ -754,7 +775,9 @@ public:
* @pre The current field must be of type "repeated packed uint64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> get_packed_uint64();
+ iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
+ return get_packed<pbf_reader::const_uint64_iterator>();
+ }
/**
* Consume current "repeated packed fixed32" field.
@@ -765,7 +788,7 @@ public:
* @pre The current field must be of type "repeated packed fixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
+ auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
return packed_fixed<uint32_t>();
}
@@ -778,7 +801,7 @@ public:
* @pre The current field must be of type "repeated packed sfixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
+ auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
return packed_fixed<int32_t>();
}
@@ -791,7 +814,7 @@ public:
* @pre The current field must be of type "repeated packed fixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
+ auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
return packed_fixed<uint64_t>();
}
@@ -804,7 +827,7 @@ public:
* @pre The current field must be of type "repeated packed sfixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
+ auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
return packed_fixed<int64_t>();
}
@@ -817,7 +840,7 @@ public:
* @pre The current field must be of type "repeated packed float".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_float() -> decltype(packed_fixed<float>()) {
+ auto get_packed_float() -> decltype(packed_fixed<float>()) {
return packed_fixed<float>();
}
@@ -830,7 +853,7 @@ public:
* @pre The current field must be of type "repeated packed double".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_double() -> decltype(packed_fixed<double>()) {
+ auto get_packed_double() -> decltype(packed_fixed<double>()) {
return packed_fixed<double>();
}
@@ -838,238 +861,14 @@ public:
}; // class pbf_reader
-pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept
- : m_data(data),
- m_end(data + length),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::pbf_reader(std::pair<const char *, std::size_t> data) noexcept
- : m_data(data.first),
- m_end(data.first + data.second),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::pbf_reader(const std::string& data) noexcept
- : m_data(data.data()),
- m_end(data.data() + data.size()),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::operator bool() const noexcept {
- return m_data < m_end;
-}
-
-bool pbf_reader::next() {
- if (m_data == m_end) {
- return false;
- }
-
- auto value = get_varint<uint32_t>();
- m_tag = value >> 3;
-
- // tags 0 and 19000 to 19999 are not allowed as per
- // https://developers.google.com/protocol-buffers/docs/proto
- protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
-
- m_wire_type = pbf_wire_type(value & 0x07);
- switch (m_wire_type) {
- case pbf_wire_type::varint:
- case pbf_wire_type::fixed64:
- case pbf_wire_type::length_delimited:
- case pbf_wire_type::fixed32:
- break;
- default:
- throw unknown_pbf_wire_type_exception();
- }
-
- return true;
-}
-
-bool pbf_reader::next(pbf_tag_type requested_tag) {
- while (next()) {
- if (m_tag == requested_tag) {
- return true;
- } else {
- skip();
- }
- }
- return false;
-}
-
-pbf_tag_type pbf_reader::tag() const noexcept {
- return m_tag;
-}
-
-pbf_wire_type pbf_reader::wire_type() const noexcept {
- return m_wire_type;
-}
-
-bool pbf_reader::has_wire_type(pbf_wire_type type) const noexcept {
- return wire_type() == type;
-}
-
-void pbf_reader::skip_bytes(pbf_length_type len) {
- if (m_data + len > m_end) {
- throw end_of_buffer_exception();
- }
- m_data += len;
-
-// In debug builds reset the tag to zero so that we can detect (some)
-// wrong code.
-#ifndef NDEBUG
- m_tag = 0;
-#endif
-}
-
-void pbf_reader::skip() {
- protozero_assert(tag() != 0 && "call next() before calling skip()");
- switch (wire_type()) {
- case pbf_wire_type::varint:
- (void)get_uint32(); // called for the side-effect of skipping value
- break;
- case pbf_wire_type::fixed64:
- skip_bytes(8);
- break;
- case pbf_wire_type::length_delimited:
- skip_bytes(get_length());
- break;
- case pbf_wire_type::fixed32:
- skip_bytes(4);
- break;
- default:
- protozero_assert(false && "can not be here because next() should have thrown already");
- }
-}
-
-pbf_length_type pbf_reader::get_len_and_skip() {
- auto len = get_length();
- skip_bytes(len);
- return len;
-}
-
-template <typename T>
-T pbf_reader::get_varint() {
- return static_cast<T>(decode_varint(&m_data, m_end));
-}
-
-template <typename T>
-T pbf_reader::get_svarint() {
- protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
- return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
-}
-
-uint32_t pbf_reader::get_fixed32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<uint32_t>();
-}
-
-int32_t pbf_reader::get_sfixed32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<int32_t>();
-}
-
-uint64_t pbf_reader::get_fixed64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<uint64_t>();
-}
-
-int64_t pbf_reader::get_sfixed64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<int64_t>();
-}
-
-float pbf_reader::get_float() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<float>();
-}
-
-double pbf_reader::get_double() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<double>();
-}
-
-bool pbf_reader::get_bool() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
- protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
- skip_bytes(1);
- return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
-}
-
-std::pair<const char*, pbf_length_type> pbf_reader::get_data() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
- auto len = get_len_and_skip();
- return std::make_pair(m_data-len, len);
-}
-
-std::string pbf_reader::get_bytes() {
- auto d = get_data();
- return std::string(d.first, d.second);
-}
-
-std::string pbf_reader::get_string() {
- return get_bytes();
-}
-
-std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> pbf_reader::get_packed_bool() {
- return get_packed_int32();
-}
-
-std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> pbf_reader::get_packed_enum() {
- return get_packed_int32();
-}
-
-std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> pbf_reader::get_packed_int32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_int32_iterator(m_data-len, m_data),
- pbf_reader::const_int32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> pbf_reader::get_packed_uint32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_uint32_iterator(m_data-len, m_data),
- pbf_reader::const_uint32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> pbf_reader::get_packed_sint32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_sint32_iterator(m_data-len, m_data),
- pbf_reader::const_sint32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> pbf_reader::get_packed_int64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_int64_iterator(m_data-len, m_data),
- pbf_reader::const_int64_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> pbf_reader::get_packed_uint64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_uint64_iterator(m_data-len, m_data),
- pbf_reader::const_uint64_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> pbf_reader::get_packed_sint64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_sint64_iterator(m_data-len, m_data),
- pbf_reader::const_sint64_iterator(m_data, m_data));
+/**
+ * Swap two pbf_reader objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
+ lhs.swap(rhs);
}
} // end namespace protozero
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
index 422e147..3ce0f14 100644
--- a/include/protozero/pbf_writer.hpp
+++ b/include/protozero/pbf_writer.hpp
@@ -22,6 +22,7 @@ documentation.
#include <iterator>
#include <limits>
#include <string>
+#include <utility>
#include <protozero/config.hpp>
#include <protozero/types.hpp>
@@ -68,38 +69,38 @@ class pbf_writer {
// parent to the position where the data of the submessage is written to.
std::size_t m_pos = 0;
- inline void add_varint(uint64_t value) {
+ void add_varint(uint64_t value) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
write_varint(std::back_inserter(*m_data), value);
}
- inline void add_field(pbf_tag_type tag, pbf_wire_type type) {
+ void add_field(pbf_tag_type tag, pbf_wire_type type) {
protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
- uint32_t b = (tag << 3) | uint32_t(type);
+ const uint32_t b = (tag << 3) | uint32_t(type);
add_varint(b);
}
- inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
+ void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::varint);
add_varint(value);
}
template <typename T>
- inline void add_fixed(T value) {
+ void add_fixed(T value) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
#else
- auto size = m_data->size();
+ const auto size = m_data->size();
m_data->resize(size + sizeof(T));
byteswap<sizeof(T)>(reinterpret_cast<const char*>(&value), const_cast<char*>(m_data->data() + size));
#endif
}
template <typename T, typename It>
- inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
if (first == last) {
return;
}
@@ -112,12 +113,12 @@ class pbf_writer {
}
template <typename T, typename It>
- inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
if (first == last) {
return;
}
- auto length = std::distance(first, last);
+ const auto length = std::distance(first, last);
add_length_varint(tag, sizeof(T) * pbf_length_type(length));
reserve(sizeof(T) * std::size_t(length));
@@ -127,7 +128,7 @@ class pbf_writer {
}
template <typename It>
- inline void add_packed_varint(pbf_tag_type tag, It first, It last) {
+ void add_packed_varint(pbf_tag_type tag, It first, It last) {
if (first == last) {
return;
}
@@ -140,7 +141,7 @@ class pbf_writer {
}
template <typename It>
- inline void add_packed_svarint(pbf_tag_type tag, It first, It last) {
+ void add_packed_svarint(pbf_tag_type tag, It first, It last) {
if (first == last) {
return;
}
@@ -155,14 +156,14 @@ class pbf_writer {
// The number of bytes to reserve for the varint holding the length of
// a length-delimited field. The length has to fit into pbf_length_type,
// and a varint needs 8 bit for every 7 bit.
- static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
+ static constexpr const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
// If m_rollpack_pos is set to this special value, it means that when
// the submessage is closed, nothing needs to be done, because the length
// of the submessage has already been written correctly.
- static const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
+ static constexpr const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
- inline void open_submessage(pbf_tag_type tag, std::size_t size) {
+ void open_submessage(pbf_tag_type tag, std::size_t size) {
protozero_assert(m_pos == 0);
protozero_assert(m_data);
if (size == 0) {
@@ -177,7 +178,7 @@ class pbf_writer {
m_pos = m_data->size();
}
- inline void rollback_submessage() {
+ void rollback_submessage() {
protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data);
@@ -185,20 +186,20 @@ class pbf_writer {
m_pos = 0;
}
- inline void commit_submessage() {
+ void commit_submessage() {
protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data);
- auto length = pbf_length_type(m_data->size() - m_pos);
+ const auto length = pbf_length_type(m_data->size() - m_pos);
protozero_assert(m_data->size() >= m_pos - reserve_bytes);
- auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
+ const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
m_pos = 0;
}
- inline void close_submessage() {
+ void close_submessage() {
protozero_assert(m_data);
if (m_pos == 0 || m_rollback_pos == size_is_known) {
return;
@@ -210,7 +211,7 @@ class pbf_writer {
}
}
- inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
+ void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
add_field(tag, pbf_wire_type::length_delimited);
add_varint(length);
}
@@ -222,7 +223,7 @@ public:
* stores a reference to that string and adds all data to it. The string
* doesn't have to be empty. The pbf_writer will just append data.
*/
- inline explicit pbf_writer(std::string& data) noexcept :
+ explicit pbf_writer(std::string& data) noexcept :
m_data(&data),
m_parent_writer(nullptr),
m_pos(0) {
@@ -232,7 +233,7 @@ public:
* Create a writer without a data store. In this form the writer can not
* be used!
*/
- inline pbf_writer() noexcept :
+ pbf_writer() noexcept :
m_data(nullptr),
m_parent_writer(nullptr),
m_pos(0) {
@@ -248,7 +249,7 @@ public:
* Setting this allows some optimizations but is only possible in
* a few very specific cases.
*/
- inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
+ pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
m_data(parent_writer.m_data),
m_parent_writer(&parent_writer),
m_pos(0) {
@@ -262,18 +263,31 @@ public:
pbf_writer& operator=(const pbf_writer&) noexcept = default;
/// A pbf_writer object can be moved
- inline pbf_writer(pbf_writer&&) noexcept = default;
+ pbf_writer(pbf_writer&&) noexcept = default;
/// A pbf_writer object can be moved
- inline pbf_writer& operator=(pbf_writer&&) noexcept = default;
+ pbf_writer& operator=(pbf_writer&&) noexcept = default;
- inline ~pbf_writer() {
+ ~pbf_writer() {
if (m_parent_writer) {
m_parent_writer->close_submessage();
}
}
/**
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
+ */
+ void swap(pbf_writer& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_parent_writer, other.m_parent_writer);
+ swap(m_rollback_pos, other.m_rollback_pos);
+ swap(m_pos, other.m_pos);
+ }
+
+ /**
* Reserve size bytes in the underlying message store in addition to
* whatever the message store already holds. So unlike
* the `std::string::reserve()` method this is not an absolute size,
@@ -286,7 +300,14 @@ public:
m_data->reserve(m_data->size() + size);
}
- inline void rollback() {
+ /**
+ * Cancel writing of this submessage. The complete submessage will be
+ * removed as if it was never created and no fields were added.
+ *
+ * @pre Must be a pbf_writer of a submessage, ie one opened with the
+ * pbf_writer constructor taking a parent message.
+ */
+ void rollback() {
protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
m_parent_writer->rollback_submessage();
@@ -304,7 +325,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_bool(pbf_tag_type tag, bool value) {
+ void add_bool(pbf_tag_type tag, bool value) {
add_field(tag, pbf_wire_type::varint);
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
@@ -317,7 +338,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_enum(pbf_tag_type tag, int32_t value) {
+ void add_enum(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -327,7 +348,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_int32(pbf_tag_type tag, int32_t value) {
+ void add_int32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -337,7 +358,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sint32(pbf_tag_type tag, int32_t value) {
+ void add_sint32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, encode_zigzag32(value));
}
@@ -347,7 +368,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_uint32(pbf_tag_type tag, uint32_t value) {
+ void add_uint32(pbf_tag_type tag, uint32_t value) {
add_tagged_varint(tag, value);
}
@@ -357,7 +378,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_int64(pbf_tag_type tag, int64_t value) {
+ void add_int64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -367,7 +388,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sint64(pbf_tag_type tag, int64_t value) {
+ void add_sint64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, encode_zigzag64(value));
}
@@ -377,7 +398,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_uint64(pbf_tag_type tag, uint64_t value) {
+ void add_uint64(pbf_tag_type tag, uint64_t value) {
add_tagged_varint(tag, value);
}
@@ -387,7 +408,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_fixed32(pbf_tag_type tag, uint32_t value) {
+ void add_fixed32(pbf_tag_type tag, uint32_t value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<uint32_t>(value);
}
@@ -398,7 +419,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sfixed32(pbf_tag_type tag, int32_t value) {
+ void add_sfixed32(pbf_tag_type tag, int32_t value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<int32_t>(value);
}
@@ -409,7 +430,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_fixed64(pbf_tag_type tag, uint64_t value) {
+ void add_fixed64(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<uint64_t>(value);
}
@@ -420,7 +441,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sfixed64(pbf_tag_type tag, int64_t value) {
+ void add_sfixed64(pbf_tag_type tag, int64_t value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<int64_t>(value);
}
@@ -431,7 +452,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_float(pbf_tag_type tag, float value) {
+ void add_float(pbf_tag_type tag, float value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<float>(value);
}
@@ -442,7 +463,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_double(pbf_tag_type tag, double value) {
+ void add_double(pbf_tag_type tag, double value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<double>(value);
}
@@ -454,7 +475,7 @@ public:
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
- inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
@@ -468,7 +489,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_bytes(pbf_tag_type tag, const std::string& value) {
+ void add_bytes(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_bytes(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -479,7 +510,7 @@ public:
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
- inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size);
}
@@ -489,7 +520,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_string(pbf_tag_type tag, const std::string& value) {
+ void add_string(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_string(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -500,7 +541,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Pointer to value to be written
*/
- inline void add_string(pbf_tag_type tag, const char* value) {
+ void add_string(pbf_tag_type tag, const char* value) {
add_bytes(tag, value, std::strlen(value));
}
@@ -511,7 +552,7 @@ public:
* @param value Pointer to message to be written
* @param size Length of the message
*/
- inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size);
}
@@ -521,7 +562,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written. The value must be a complete message.
*/
- inline void add_message(pbf_tag_type tag, const std::string& value) {
+ void add_message(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -535,126 +586,126 @@ public:
/**
* Add "repeated packed bool" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to bool.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed enum" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed int32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed sint32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last);
}
/**
* Add "repeated packed uint32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed int64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed sint64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last);
}
/**
* Add "repeated packed uint64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed fixed32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -662,14 +713,14 @@ public:
/**
* Add "repeated packed sfixed32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -677,14 +728,14 @@ public:
/**
* Add "repeated packed fixed64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -692,14 +743,14 @@ public:
/**
* Add "repeated packed sfixed64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -707,14 +758,14 @@ public:
/**
* Add "repeated packed float" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to float.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<float, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -722,14 +773,14 @@ public:
/**
* Add "repeated packed double" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to double.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<double, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -742,6 +793,16 @@ public:
}; // class pbf_writer
+/**
+ * Swap two pbf_writer objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
namespace detail {
class packed_field {
@@ -817,19 +878,46 @@ namespace detail {
} // end namespace detail
+/// Class for generating packed repeated bool fields.
using packed_field_bool = detail::packed_field_varint<bool>;
+
+/// Class for generating packed repeated enum fields.
using packed_field_enum = detail::packed_field_varint<int32_t>;
+
+/// Class for generating packed repeated int32 fields.
using packed_field_int32 = detail::packed_field_varint<int32_t>;
+
+/// Class for generating packed repeated sint32 fields.
using packed_field_sint32 = detail::packed_field_svarint<int32_t>;
+
+/// Class for generating packed repeated uint32 fields.
using packed_field_uint32 = detail::packed_field_varint<uint32_t>;
+
+/// Class for generating packed repeated int64 fields.
using packed_field_int64 = detail::packed_field_varint<int64_t>;
+
+/// Class for generating packed repeated sint64 fields.
using packed_field_sint64 = detail::packed_field_svarint<int64_t>;
+
+/// Class for generating packed repeated uint64 fields.
using packed_field_uint64 = detail::packed_field_varint<uint64_t>;
+
+/// Class for generating packed repeated fixed32 fields.
using packed_field_fixed32 = detail::packed_field_fixed<uint32_t>;
+
+/// Class for generating packed repeated sfixed32 fields.
using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
+
+/// Class for generating packed repeated fixed64 fields.
using packed_field_fixed64 = detail::packed_field_fixed<uint64_t>;
+
+/// Class for generating packed repeated sfixed64 fields.
using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
+
+/// Class for generating packed repeated float fields.
using packed_field_float = detail::packed_field_fixed<float>;
+
+/// Class for generating packed repeated double fields.
using packed_field_double = detail::packed_field_fixed<double>;
} // end namespace protozero
diff --git a/include/protozero/types.hpp b/include/protozero/types.hpp
index 6856b3d..8b04638 100644
--- a/include/protozero/types.hpp
+++ b/include/protozero/types.hpp
@@ -16,33 +16,173 @@ documentation.
* @brief Contains the declaration of low-level types used in the pbf format.
*/
+#include <cstddef>
#include <cstdint>
+#include <cstring>
+#include <string>
+#include <utility>
+
+#include <protozero/config.hpp>
namespace protozero {
+/**
+ * The type used for field tags (field numbers).
+ */
+using pbf_tag_type = uint32_t;
+
+/**
+ * The type used to encode type information.
+ * See the table on
+ * https://developers.google.com/protocol-buffers/docs/encoding
+ */
+enum class pbf_wire_type : uint32_t {
+ varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
+ fixed64 = 1, // fixed64, sfixed64, double
+ length_delimited = 2, // string, bytes, embedded messages,
+ // packed repeated fields
+ fixed32 = 5, // fixed32, sfixed32, float
+ unknown = 99 // used for default setting in this library
+};
+
+/**
+ * The type used for length values, such as the length of a field.
+ */
+using pbf_length_type = uint32_t;
+
+#ifdef PROTOZERO_USE_VIEW
+using data_view = PROTOZERO_USE_VIEW;
+#else
+
+/**
+ * Holds a pointer to some data and a length.
+ *
+ * This class is supposed to be compatible with the std::string_view
+ * that will be available in C++17.
+ */
+class data_view {
+
+ const char* m_data;
+ std::size_t m_size;
+
+public:
+
+ /**
+ * Default constructor. Construct an empty data_view.
+ */
+ constexpr data_view() noexcept
+ : m_data(nullptr),
+ m_size(0) {
+ }
+
+ /**
+ * Create data_view from pointer and size.
+ *
+ * @param data Pointer to the data.
+ * @param size Length of the data.
+ */
+ constexpr data_view(const char* data, std::size_t size) noexcept
+ : m_data(data),
+ m_size(size) {
+ }
+
/**
- * The type used for field tags (field numbers).
+ * Create data_view from string.
+ *
+ * @param str String with the data.
*/
- typedef uint32_t pbf_tag_type;
+ data_view(const std::string& str) noexcept
+ : m_data(str.data()),
+ m_size(str.size()) {
+ }
/**
- * The type used to encode type information.
- * See the table on
- * https://developers.google.com/protocol-buffers/docs/encoding
+ * Create data_view from zero-terminated string.
+ *
+ * @param data Pointer to the data.
*/
- enum class pbf_wire_type : uint32_t {
- varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
- fixed64 = 1, // fixed64, sfixed64, double
- length_delimited = 2, // string, bytes, embedded messages,
- // packed repeated fields
- fixed32 = 5, // fixed32, sfixed32, float
- unknown = 99 // used for default setting in this library
- };
+ data_view(const char* data) noexcept
+ : m_data(data),
+ m_size(std::strlen(data)) {
+ }
/**
- * The type used for length values, such as the length of a field.
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
*/
- typedef uint32_t pbf_length_type;
+ void swap(data_view& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_size, other.m_size);
+ }
+
+ /// Return pointer to data.
+ constexpr const char* data() const noexcept {
+ return m_data;
+ }
+
+ /// Return length of data in bytes.
+ constexpr std::size_t size() const noexcept {
+ return m_size;
+ }
+
+ /**
+ * Convert data view to string.
+ *
+ * @pre Must not be default constructed data_view.
+ */
+ std::string to_string() const {
+ protozero_assert(m_data);
+ return std::string{m_data, m_size};
+ }
+
+ /**
+ * Convert data view to string.
+ *
+ * @pre Must not be default constructed data_view.
+ */
+ explicit operator std::string() const {
+ protozero_assert(m_data);
+ return std::string{m_data, m_size};
+ }
+
+}; // class data_view
+
+/**
+ * Swap two data_view objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(data_view& lhs, data_view& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+/**
+ * Two data_view instances are equal if they have the same size and the
+ * same content.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline bool operator==(data_view& lhs, data_view& rhs) noexcept {
+ return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data());
+}
+
+/**
+ * Two data_view instances are not equal if they have different sizes or the
+ * content differs.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline bool operator!=(data_view& lhs, data_view& rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+#endif
+
} // end namespace protozero
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
index 4242df9..f6142d1 100644
--- a/include/protozero/varint.hpp
+++ b/include/protozero/varint.hpp
@@ -23,13 +23,54 @@ documentation.
namespace protozero {
/**
- * The maximum length of a 64bit varint.
+ * The maximum length of a 64 bit varint.
*/
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
-// from https://github.com/facebook/folly/blob/master/folly/Varint.h
+namespace detail {
+
+ // from https://github.com/facebook/folly/blob/master/folly/Varint.h
+ inline uint64_t decode_varint_impl(const char** data, const char* end) {
+ const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
+ const int8_t* iend = reinterpret_cast<const int8_t*>(end);
+ const int8_t* p = begin;
+ uint64_t val = 0;
+
+ if (iend - begin >= max_varint_length) { // fast path
+ do {
+ int64_t b;
+ b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
+ throw varint_too_long_exception();
+ } while (false);
+ } else {
+ int shift = 0;
+ while (p != iend && *p < 0) {
+ val |= uint64_t(*p++ & 0x7f) << shift;
+ shift += 7;
+ }
+ if (p == iend) {
+ throw end_of_buffer_exception();
+ }
+ val |= uint64_t(*p++) << shift;
+ }
+
+ *data = reinterpret_cast<const char*>(p);
+ return val;
+ }
+
+} // end namespace detail
+
/**
- * Decode a 64bit varint.
+ * Decode a 64 bit varint.
*
* Strong exception guarantee: if there is an exception the data pointer will
* not be changed.
@@ -39,54 +80,68 @@ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
* @param[in] end Pointer one past the end of the input data.
* @returns The decoded integer
* @throws varint_too_long_exception if the varint is longer then the maximum
- * length that would fit in a 64bit int. Usually this means your data
+ * length that would fit in a 64 bit int. Usually this means your data
* is corrupted or you are trying to read something as a varint that
* isn't.
* @throws end_of_buffer_exception if the *end* of the buffer was reached
* before the end of the varint.
*/
inline uint64_t decode_varint(const char** data, const char* end) {
+ // If this is a one-byte varint, decode it here.
+ if (end != *data && ((**data & 0x80) == 0)) {
+ uint64_t val = uint64_t(**data);
+ ++(*data);
+ return val;
+ }
+ // If this varint is more than one byte, defer to complete implementation.
+ return detail::decode_varint_impl(data, end);
+}
+
+/**
+ * Skip over a varint.
+ *
+ * Strong exception guarantee: if there is an exception the data pointer will
+ * not be changed.
+ *
+ * @param[in,out] data Pointer to pointer to the input data. After the function
+ * returns this will point to the next data to be read.
+ * @param[in] end Pointer one past the end of the input data.
+ * @throws end_of_buffer_exception if the *end* of the buffer was reached
+ * before the end of the varint.
+ */
+inline void skip_varint(const char** data, const char* end) {
const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
const int8_t* iend = reinterpret_cast<const int8_t*>(end);
const int8_t* p = begin;
- uint64_t val = 0;
-
- if (iend - begin >= max_varint_length) { // fast path
- do {
- int64_t b;
- b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
- throw varint_too_long_exception();
- } while (false);
- } else {
- int shift = 0;
- while (p != iend && *p < 0) {
- val |= uint64_t(*p++ & 0x7f) << shift;
- shift += 7;
- }
- if (p == iend) {
- throw end_of_buffer_exception();
- }
- val |= uint64_t(*p++) << shift;
+
+ while (p != iend && *p < 0) {
+ ++p;
+ }
+
+ if (p >= begin + max_varint_length) {
+ throw varint_too_long_exception();
}
+ if (p == iend) {
+ throw end_of_buffer_exception();
+ }
+
+ ++p;
+
*data = reinterpret_cast<const char*>(p);
- return val;
}
/**
- * Varint-encode a 64bit integer.
+ * Varint encode a 64 bit integer.
+ *
+ * @tparam T An output iterator type.
+ * @param data Output iterator the varint encoded value will be written to
+ * byte by byte.
+ * @param value The integer that will be encoded.
+ * @throws Any exception thrown by increment or dereference operator on data.
*/
-template <typename OutputIterator>
-inline int write_varint(OutputIterator data, uint64_t value) {
+template <typename T>
+inline int write_varint(T data, uint64_t value) {
int n=1;
while (value >= 0x80) {
diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp
index 7b60e2e..d427941 100644
--- a/include/protozero/version.hpp
+++ b/include/protozero/version.hpp
@@ -10,13 +10,26 @@ documentation.
*****************************************************************************/
+/**
+ * @file version.hpp
+ *
+ * @brief Contains macros defining the protozero version.
+ */
+
+/// The major version number
#define PROTOZERO_VERSION_MAJOR 1
-#define PROTOZERO_VERSION_MINOR 3
+
+/// The minor version number
+#define PROTOZERO_VERSION_MINOR 4
+
+/// The patch number
#define PROTOZERO_VERSION_PATCH 0
+/// The complete version number
#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
-#define PROTOZERO_VERSION_STRING "1.3.0"
+/// Version number as string
+#define PROTOZERO_VERSION_STRING "1.4.0"
#endif // PROTOZERO_VERSION_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 241c192..f7b35e8 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -85,6 +85,40 @@ function(add_unit_test _tgroup _tname)
endif()
endfunction()
+
+#-----------------------------------------------------------------------------
+#
+# Prepare some variables so querying it for tests work properly.
+#
+#-----------------------------------------------------------------------------
+
+if(NOT GEOS_FOUND)
+ set(GEOS_FOUND FALSE)
+endif()
+
+if(NOT PROJ_FOUND)
+ set(PROJ_FOUND FALSE)
+endif()
+
+if(NOT SPARSEHASH_FOUND)
+ set(SPARSEHASH_FOUND FALSE)
+endif()
+
+if(NOT BZIP2_FOUND)
+ set(BZIP2_FOUND FALSE)
+endif()
+
+if(NOT Threads_FOUND)
+ set(Threads_FOUND FALSE)
+endif()
+
+if(GEOS_FOUND AND PROJ_FOUND)
+ set(GEOS_AND_PROJ_FOUND TRUE)
+else()
+ set(GEOS_AND_PROJ_FOUND FALSE)
+endif()
+
+
#-----------------------------------------------------------------------------
#
# Add all tests.
@@ -112,11 +146,6 @@ add_unit_test(buffer test_buffer_purge)
add_unit_test(builder test_attr)
-if(GEOS_FOUND AND PROJ_FOUND)
- set(GEOS_AND_PROJ_FOUND TRUE)
-else()
- set(GEOS_AND_PROJ_FOUND FALSE)
-endif()
add_unit_test(geom test_factory_with_projection
ENABLE_IF ${GEOS_AND_PROJ_FOUND}
LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY})
@@ -129,7 +158,7 @@ add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY})
add_unit_test(geom test_mercator)
add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY})
add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
-add_unit_test(geom test_tile)
+add_unit_test(geom test_tile ENABLE_IF ${GEOS_FOUND})
add_unit_test(geom test_wkb)
add_unit_test(geom test_wkt)
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index 2a7146a..879fc5b 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
/*
- * Catch v1.4.0
- * Generated: 2016-03-15 07:23:12.623111
+ * Catch v1.5.6
+ * Generated: 2016-06-09 19:20:41.460328
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -106,8 +106,16 @@
// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
-#if defined(__cplusplus) && __cplusplus >= 201103L
-# define CATCH_CPP11_OR_GREATER
+#ifdef __cplusplus
+
+# if __cplusplus >= 201103L
+# define CATCH_CPP11_OR_GREATER
+# endif
+
+# if __cplusplus >= 201402L
+# define CATCH_CPP14_OR_GREATER
+# endif
+
#endif
#ifdef __clang__
@@ -2065,7 +2073,7 @@ namespace Catch {
__catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
} \
INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::isTrue( false && static_cast<bool>(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
@@ -3450,7 +3458,7 @@ namespace Catch {
};
class DebugOutStream : public IStream {
- std::auto_ptr<StreamBufBase> m_streamBuf;
+ CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
mutable std::ostream m_os;
public:
DebugOutStream();
@@ -3598,7 +3606,7 @@ namespace Catch {
}
ConfigData m_data;
- std::auto_ptr<IStream const> m_stream;
+ CATCH_AUTO_PTR( IStream const ) m_stream;
TestSpec m_testSpec;
};
@@ -3618,7 +3626,7 @@ namespace Catch {
#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
// #included from: ../external/clara.h
-// Version 0.0.1.1
+// Version 0.0.2.4
// Only use header guard if we are not using an outer namespace
#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
@@ -3934,6 +3942,10 @@ namespace Tbc {
#include <stdexcept>
#include <memory>
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
// Use optional outer namespace
#ifdef STITCH_CLARA_OPEN_NAMESPACE
STITCH_CLARA_OPEN_NAMESPACE
@@ -3957,9 +3969,6 @@ namespace Clara {
const unsigned int consoleWidth = 80;
#endif
- // Use this to try and stop compiler from warning about unreachable code
- inline bool isTrue( bool value ) { return value; }
-
using namespace Tbc;
inline bool startsWith( std::string const& str, std::string const& prefix ) {
@@ -3995,14 +4004,6 @@ namespace Clara {
else
throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
}
- inline void convertInto( bool _source, bool& _dest ) {
- _dest = _source;
- }
- template<typename T>
- inline void convertInto( bool, T& ) {
- if( isTrue( true ) )
- throw std::runtime_error( "Invalid conversion" );
- }
template<typename ConfigT>
struct IArgFunction {
@@ -4012,7 +4013,6 @@ namespace Clara {
IArgFunction( IArgFunction const& ) = default;
#endif
virtual void set( ConfigT& config, std::string const& value ) const = 0;
- virtual void setFlag( ConfigT& config ) const = 0;
virtual bool takesArg() const = 0;
virtual IArgFunction* clone() const = 0;
};
@@ -4034,9 +4034,6 @@ namespace Clara {
void set( ConfigT& config, std::string const& value ) const {
functionObj->set( config, value );
}
- void setFlag( ConfigT& config ) const {
- functionObj->setFlag( config );
- }
bool takesArg() const { return functionObj->takesArg(); }
bool isSet() const {
@@ -4049,7 +4046,6 @@ namespace Clara {
template<typename C>
struct NullBinder : IArgFunction<C>{
virtual void set( C&, std::string const& ) const {}
- virtual void setFlag( C& ) const {}
virtual bool takesArg() const { return true; }
virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
};
@@ -4060,9 +4056,6 @@ namespace Clara {
virtual void set( C& p, std::string const& stringValue ) const {
convertInto( stringValue, p.*member );
}
- virtual void setFlag( C& p ) const {
- convertInto( true, p.*member );
- }
virtual bool takesArg() const { return !IsBool<M>::value; }
virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
M C::* member;
@@ -4075,11 +4068,6 @@ namespace Clara {
convertInto( stringValue, value );
(p.*member)( value );
}
- virtual void setFlag( C& p ) const {
- typename RemoveConstRef<M>::type value;
- convertInto( true, value );
- (p.*member)( value );
- }
virtual bool takesArg() const { return !IsBool<M>::value; }
virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
void (C::*member)( M );
@@ -4093,9 +4081,6 @@ namespace Clara {
if( value )
(p.*member)();
}
- virtual void setFlag( C& p ) const {
- (p.*member)();
- }
virtual bool takesArg() const { return false; }
virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
void (C::*member)();
@@ -4110,9 +4095,6 @@ namespace Clara {
if( value )
function( obj );
}
- virtual void setFlag( C& p ) const {
- function( p );
- }
virtual bool takesArg() const { return false; }
virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
void (*function)( C& );
@@ -4126,11 +4108,6 @@ namespace Clara {
convertInto( stringValue, value );
function( obj, value );
}
- virtual void setFlag( C& obj ) const {
- typename RemoveConstRef<T>::type value;
- convertInto( true, value );
- function( obj, value );
- }
virtual bool takesArg() const { return !IsBool<T>::value; }
virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
void (*function)( C&, T );
@@ -4138,8 +4115,20 @@ namespace Clara {
} // namespace Detail
- struct Parser {
- Parser() : separators( " \t=:" ) {}
+ inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+ std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+ for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+ args[i] = argv[i];
+
+ return args;
+ }
+
+ class Parser {
+ enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+ Mode mode;
+ std::size_t from;
+ bool inQuotes;
+ public:
struct Token {
enum Type { Positional, ShortOpt, LongOpt };
@@ -4148,38 +4137,75 @@ namespace Clara {
std::string data;
};
- void parseIntoTokens( int argc, char const* const argv[], std::vector<Parser::Token>& tokens ) const {
+ Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+ void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
const std::string doubleDash = "--";
- for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
- parseIntoTokens( argv[i] , tokens);
- }
- void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
- while( !arg.empty() ) {
- Parser::Token token( Parser::Token::Positional, arg );
- arg = "";
- if( token.data[0] == '-' ) {
- if( token.data.size() > 1 && token.data[1] == '-' ) {
- token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
- }
- else {
- token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
- if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
- arg = "-" + token.data.substr( 1 );
- token.data = token.data.substr( 0, 1 );
- }
- }
- }
- if( token.type != Parser::Token::Positional ) {
- std::size_t pos = token.data.find_first_of( separators );
- if( pos != std::string::npos ) {
- arg = token.data.substr( pos+1 );
- token.data = token.data.substr( 0, pos );
- }
- }
- tokens.push_back( token );
+ for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+ parseIntoTokens( args[i], tokens);
+ }
+
+ void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+ for( std::size_t i = 0; i <= arg.size(); ++i ) {
+ char c = arg[i];
+ if( c == '"' )
+ inQuotes = !inQuotes;
+ mode = handleMode( i, c, arg, tokens );
+ }
+ }
+ Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ switch( mode ) {
+ case None: return handleNone( i, c );
+ case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+ case ShortOpt:
+ case LongOpt:
+ case SlashOpt: return handleOpt( i, c, arg, tokens );
+ case Positional: return handlePositional( i, c, arg, tokens );
+ default: throw std::logic_error( "Unknown mode" );
+ }
+ }
+
+ Mode handleNone( std::size_t i, char c ) {
+ if( inQuotes ) {
+ from = i;
+ return Positional;
+ }
+ switch( c ) {
+ case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+ case '/': from = i+1; return SlashOpt;
+#endif
+ default: from = i; return Positional;
+ }
+ }
+ Mode handleMaybeShortOpt( std::size_t i, char c ) {
+ switch( c ) {
+ case '-': from = i+1; return LongOpt;
+ default: from = i; return ShortOpt;
}
}
- std::string separators;
+ Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string optName = arg.substr( from, i-from );
+ if( mode == ShortOpt )
+ for( std::size_t j = 0; j < optName.size(); ++j )
+ tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+ else if( mode == SlashOpt && optName.size() == 1 )
+ tokens.push_back( Token( Token::ShortOpt, optName ) );
+ else
+ tokens.push_back( Token( Token::LongOpt, optName ) );
+ return None;
+ }
+ Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string data = arg.substr( from, i-from );
+ tokens.push_back( Token( Token::Positional, data ) );
+ return None;
+ }
};
template<typename ConfigT>
@@ -4482,21 +4508,21 @@ namespace Clara {
return oss.str();
}
- ConfigT parse( int argc, char const* const argv[] ) const {
+ ConfigT parse( std::vector<std::string> const& args ) const {
ConfigT config;
- parseInto( argc, argv, config );
+ parseInto( args, config );
return config;
}
- std::vector<Parser::Token> parseInto( int argc, char const* argv[], ConfigT& config ) const {
- std::string processName = argv[0];
+ std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+ std::string processName = args[0];
std::size_t lastSlash = processName.find_last_of( "/\\" );
if( lastSlash != std::string::npos )
processName = processName.substr( lastSlash+1 );
m_boundProcessName.set( config, processName );
std::vector<Parser::Token> tokens;
Parser parser;
- parser.parseIntoTokens( argc, argv, tokens );
+ parser.parseIntoTokens( args, tokens );
return populate( tokens, config );
}
@@ -4527,7 +4553,7 @@ namespace Clara {
arg.boundField.set( config, tokens[++i].data );
}
else {
- arg.boundField.setFlag( config );
+ arg.boundField.set( config, "true" );
}
break;
}
@@ -5235,6 +5261,8 @@ namespace Catch
bool aborting;
};
+ class MultipleReporters;
+
struct IStreamingReporter : IShared {
virtual ~IStreamingReporter();
@@ -5262,6 +5290,8 @@ namespace Catch
virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+ virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
};
struct IReporterFactory : IShared {
@@ -5479,6 +5509,10 @@ namespace TestCaseTracking {
virtual void addChild( Ptr<ITracker> const& child ) = 0;
virtual ITracker* findChild( std::string const& name ) = 0;
virtual void openChild() = 0;
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+ virtual bool isIndexTracker() const = 0;
};
class TrackerContext {
@@ -5603,6 +5637,10 @@ namespace TestCaseTracking {
m_parent->openChild();
}
}
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
void open() {
m_runState = Executing;
moveToThis();
@@ -5666,13 +5704,16 @@ namespace TestCaseTracking {
{}
virtual ~SectionTracker();
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
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 );
+ assert( childTracker );
+ assert( childTracker->isSectionTracker() );
+ section = static_cast<SectionTracker*>( childTracker );
}
else {
section = new SectionTracker( name, ctx, ¤tTracker );
@@ -5697,13 +5738,16 @@ namespace TestCaseTracking {
{}
virtual ~IndexTracker();
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
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 );
+ assert( childTracker );
+ assert( childTracker->isIndexTracker() );
+ tracker = static_cast<IndexTracker*>( childTracker );
}
else {
tracker = new IndexTracker( name, ctx, ¤tTracker, size );
@@ -6306,10 +6350,10 @@ namespace Catch {
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* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
try {
m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
- m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+ m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
if( m_configData.showHelp )
showHelp( m_configData.processName );
m_config.reset();
@@ -6333,16 +6377,13 @@ namespace Catch {
m_config.reset();
}
- int run( int argc, char const* argv[] ) {
+ int run( int argc, char const* const* const argv ) {
int returnCode = applyCommandLine( argc, argv );
if( returnCode == 0 )
returnCode = run();
return returnCode;
}
- int run( int argc, char* argv[] ) {
- return run( argc, const_cast<char const**>( argv ) );
- }
int run() {
if( m_configData.showHelp )
@@ -6406,13 +6447,31 @@ namespace Catch {
#include <iostream>
#include <algorithm>
+#ifdef CATCH_CPP14_OR_GREATER
+#include <random>
+#endif
+
namespace Catch {
- struct LexSort {
- bool operator() (TestCase i,TestCase j) const { return (i<j);}
- };
struct RandomNumberGenerator {
- int operator()( int n ) const { return std::rand() % n; }
+ typedef int result_type;
+
+ result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CPP14_OR_GREATER
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return 1000000; }
+ result_type operator()() const { return std::rand() % max(); }
+#endif
+ template<typename V>
+ static void shuffle( V& vector ) {
+ RandomNumberGenerator rng;
+#ifdef CATCH_CPP14_OR_GREATER
+ std::shuffle( vector.begin(), vector.end(), rng );
+#else
+ std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+ }
};
inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
@@ -6421,14 +6480,12 @@ namespace Catch {
switch( config.runOrder() ) {
case RunTests::InLexicographicalOrder:
- std::sort( sorted.begin(), sorted.end(), LexSort() );
+ std::sort( sorted.begin(), sorted.end() );
break;
case RunTests::InRandomOrder:
{
seedRng( config );
-
- RandomNumberGenerator rng;
- std::random_shuffle( sorted.begin(), sorted.end(), rng );
+ RandomNumberGenerator::shuffle( sorted );
}
break;
case RunTests::InDeclarationOrder:
@@ -6447,13 +6504,15 @@ namespace Catch {
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);
+ if( !prev.second ) {
+ std::ostringstream ss;
+
+ ss << 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;
+
+ throw std::runtime_error(ss.str());
}
}
}
@@ -7512,7 +7571,7 @@ namespace Catch {
return os;
}
- Version libraryVersion( 1, 4, 0, "", 0 );
+ Version libraryVersion( 1, 5, 6, "", 0 );
}
@@ -8491,13 +8550,18 @@ public: // IStreamingReporter
++it )
(*it)->skipTest( testInfo );
}
+
+ virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+ return this;
+ }
+
};
Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
Ptr<IStreamingReporter> resultingReporter;
if( existingReporter ) {
- MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
+ MultipleReporters* multi = existingReporter->tryAsMulti();
if( !multi ) {
multi = new MultipleReporters;
resultingReporter = Ptr<IStreamingReporter>( multi );
@@ -8677,7 +8741,7 @@ namespace Catch {
virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
- virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
assert( !m_sectionStack.empty() );
SectionNode& sectionNode = *m_sectionStack.back();
sectionNode.assertions.push_back( assertionStats );
diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp
index 2fe2bc7..e6c1b53 100644
--- a/test/t/basic/test_location.cpp
+++ b/test/t/basic/test_location.cpp
@@ -222,7 +222,7 @@ TEST_CASE("Parsing coordinates from strings") {
C("1.1234567", 11234567);
C("1.12345670", 11234567);
C("1.12345674", 11234567);
- C("1.12345675", 11234568);
+ C("1.123456751", 11234568);
C("1.12345679", 11234568);
C("1.12345680", 11234568);
C("1.12345681", 11234568);
@@ -233,6 +233,37 @@ TEST_CASE("Parsing coordinates from strings") {
C("179.99999999", 1800000000);
C("200.123", 2001230000);
+ C("1e2", 1000000000);
+ C("1e1", 100000000);
+ C("1e0", 10000000);
+ C("1e-1", 1000000);
+ C("1e-2", 100000);
+ C("1e-3", 10000);
+ C("1e-4", 1000);
+ C("1e-5", 100);
+ C("1e-6", 10);
+ C("1e-7", 1);
+
+ C("1.0e2", 1000000000);
+ C("1.1e1", 110000000);
+ C("0.1e1", 10000000);
+ C("1.2e0", 12000000);
+ C("1.9e-1", 1900000);
+ C("2.0e-2", 200000);
+ C("2.1e-3", 21000);
+ C("9.0e-4", 9000);
+ C("9.1e-5", 910);
+ C("1.0e-6", 10);
+ C("1.0e-7", 1);
+ C("1.4e-7", 1);
+ C("1.5e-7", 2);
+ C("1.9e-7", 2);
+
+ F("e");
+ F(" e");
+ F(" 1.1e2");
+ F("1.1e2 ");
+ F("1.1e2x");
}
#undef C
diff --git a/test/t/geom/test_geos.cpp b/test/t/geom/test_geos.cpp
index d85a603..f74027c 100644
--- a/test/t/geom/test_geos.cpp
+++ b/test/t/geom/test_geos.cpp
@@ -1,6 +1,7 @@
#include "catch.hpp"
#include <osmium/geom/geos.hpp>
+#include <osmium/geom/mercator_projection.hpp>
#include "area_helper.hpp"
#include "wnl_helper.hpp"
@@ -11,16 +12,16 @@ TEST_CASE("GEOS geometry factory - create point") {
std::unique_ptr<geos::geom::Point> point {factory.create_point(osmium::Location(3.2, 4.2))};
REQUIRE(3.2 == point->getX());
REQUIRE(4.2 == point->getY());
- REQUIRE(-1 == point->getSRID());
+ REQUIRE(4326 == point->getSRID());
}
-TEST_CASE("GEOS geometry factory - create point with non-default srid") {
- osmium::geom::GEOSFactory<> factory(4326);
+TEST_CASE("GEOS geometry factory - create point in web mercator") {
+ osmium::geom::GEOSFactory<osmium::geom::MercatorProjection> factory;
std::unique_ptr<geos::geom::Point> point {factory.create_point(osmium::Location(3.2, 4.2))};
- REQUIRE(3.2 == point->getX());
- REQUIRE(4.2 == point->getY());
- REQUIRE(4326 == point->getSRID());
+ REQUIRE(Approx(356222.3705384755l) == point->getX());
+ REQUIRE(Approx(467961.143605213l) == point->getY());
+ REQUIRE(3857 == point->getSRID());
}
TEST_CASE("GEOS geometry factory - create point with externally created GEOS factory") {
diff --git a/test/t/geom/test_wkb.cpp b/test/t/geom/test_wkb.cpp
index 49710cb..12cd5b6 100644
--- a/test/t/geom/test_wkb.cpp
+++ b/test/t/geom/test_wkb.cpp
@@ -1,5 +1,6 @@
#include "catch.hpp"
+#include <osmium/geom/mercator_projection.hpp>
#include <osmium/geom/wkb.hpp>
#include "wnl_helper.hpp"
@@ -14,13 +15,27 @@ SECTION("point") {
REQUIRE(std::string{"01010000009A99999999990940CDCCCCCCCCCC1040"} == wkb);
}
-SECTION("point_ewkb") {
+SECTION("point in web mercator") {
+ osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+
+ std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"010100000028706E7BF9BD1541B03E0D93E48F1C41"} == wkb);
+}
+
+SECTION("point in ewkb") {
osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))};
REQUIRE(std::string{"0101000020E61000009A99999999990940CDCCCCCCCCCC1040"} == wkb);
}
+SECTION("point in ewkb in web mercator") {
+ osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex);
+
+ std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41"} == wkb);
+}
+
SECTION("linestring") {
osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
diff --git a/test/t/geom/test_wkt.cpp b/test/t/geom/test_wkt.cpp
index 55ccb4a..3767be3 100644
--- a/test/t/geom/test_wkt.cpp
+++ b/test/t/geom/test_wkt.cpp
@@ -1,5 +1,6 @@
#include "catch.hpp"
+#include <osmium/geom/mercator_projection.hpp>
#include <osmium/geom/wkt.hpp>
#include "area_helper.hpp"
@@ -14,6 +15,20 @@ SECTION("point") {
REQUIRE(std::string{"POINT(3.2 4.2)"} == wkt);
}
+SECTION("point in ewkt") {
+ osmium::geom::WKTFactory<> factory(7, osmium::geom::wkt_type::ewkt);
+
+ std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"SRID=4326;POINT(3.2 4.2)"} == wkt);
+}
+
+SECTION("point in ewkt in web mercator") {
+ osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory(2, osmium::geom::wkt_type::ewkt);
+
+ std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+ REQUIRE(std::string{"SRID=3857;POINT(356222.37 467961.14)"} == wkt);
+}
+
SECTION("empty_point") {
osmium::geom::WKTFactory<> factory;
diff --git a/test/t/io/test_string_table.cpp b/test/t/io/test_string_table.cpp
index ab977e8..1e76245 100644
--- a/test/t/io/test_string_table.cpp
+++ b/test/t/io/test_string_table.cpp
@@ -7,6 +7,8 @@ TEST_CASE("String store") {
SECTION("empty") {
REQUIRE(ss.begin() == ss.end());
+ REQUIRE(ss.get_chunk_size() == 100);
+ REQUIRE(ss.get_chunk_count() == 1);
}
SECTION("add zero-length string") {
@@ -17,6 +19,8 @@ TEST_CASE("String store") {
REQUIRE(s1 == *it);
REQUIRE(std::string(*it) == "");
REQUIRE(++it == ss.end());
+
+ REQUIRE(ss.get_chunk_count() == 1);
}
SECTION("add strings") {
@@ -30,6 +34,9 @@ TEST_CASE("String store") {
REQUIRE(s1 == *it++);
REQUIRE(s2 == *it++);
REQUIRE(it == ss.end());
+
+ ss.clear();
+ REQUIRE(ss.begin() == ss.end());
}
SECTION("add zero-length string and longer strings") {
@@ -45,9 +52,9 @@ TEST_CASE("String store") {
}
SECTION("add many strings") {
- for (const char* teststring : {"a", "abc", "abcd", "abcde"}) {
+ for (const char* teststring : {"", "a", "abc", "abcd", "abcde"}) {
int i = 0;
- for (; i < 100; ++i) {
+ for (; i < 200; ++i) {
ss.add(teststring);
}
@@ -57,7 +64,9 @@ TEST_CASE("String store") {
}
REQUIRE(i == 0);
+ REQUIRE(ss.get_chunk_count() > 1);
ss.clear();
+ REQUIRE(ss.get_chunk_count() == 1);
}
}
@@ -90,5 +99,32 @@ TEST_CASE("String table") {
REQUIRE(st.size() == 1);
}
+ SECTION("add empty string") {
+ REQUIRE(st.add("") == 1);
+ REQUIRE(st.size() == 2);
+ REQUIRE(st.add("") == 1);
+ REQUIRE(st.size() == 2);
+ }
+
+}
+
+TEST_CASE("lots of strings in string table so chunk overflows") {
+ osmium::io::detail::StringTable st{100};
+ REQUIRE(st.size() == 1);
+
+ const int n = 1000;
+ for (int i = 0; i < n; ++i) {
+ auto s = std::to_string(i);
+ st.add(s.c_str());
+ }
+
+ REQUIRE(st.size() == n + 1);
+
+ auto it = st.begin();
+ REQUIRE(std::string{} == *it++);
+ for (int i = 0; i < n; ++i) {
+ REQUIRE(atoi(*it++) == i);
+ }
+ REQUIRE(it == st.end());
}
diff --git a/test/t/util/test_delta.cpp b/test/t/util/test_delta.cpp
index 667c9b4..27bd8be 100644
--- a/test/t/util/test_delta.cpp
+++ b/test/t/util/test_delta.cpp
@@ -70,25 +70,3 @@ TEST_CASE("delta encode and decode") {
}
-TEST_CASE("delta encode iterator") {
- std::vector<int> data = { 4, 5, 13, 22, 12 };
-
- auto l = [](std::vector<int>::const_iterator it) -> int {
- return *it;
- };
-
- typedef osmium::util::DeltaEncodeIterator<std::vector<int>::const_iterator, decltype(l), int> it_type;
- it_type it(data.begin(), data.end(), l);
- it_type end(data.end(), data.end(), l);
-
- REQUIRE(*it == 4);
- ++it;
- REQUIRE(*it++ == 1);
- REQUIRE(*it == 8);
- ++it;
- REQUIRE(*it++ == 9);
- REQUIRE(*it == -10);
- ++it;
- REQUIRE(it == end);
-}
-
--
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