diff -Nru protozero-1.6.8/CHANGELOG.md protozero-1.7.0/CHANGELOG.md --- protozero-1.6.8/CHANGELOG.md 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/CHANGELOG.md 2020-06-08 07:25:41.000000000 +0000 @@ -5,7 +5,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) This project adheres to [Semantic Versioning](http://semver.org/). - ## [unreleased] - ### Added @@ -15,6 +14,24 @@ ### Fixed +## [1.7.0] - 2020-06-08 + +### Added + +- Support for buffer types other that `std::string`. `pbf_writer` is now + just a typedef for `basic_pbf_writer`. Other buffer types + can be used with `basic_pbf_writer`. See `doc/advanced.md` for details. + +### Changed + +- Switched to *catch2* for testing. +- Some minor tweaks. + +### Fixed + +- Removed some undefined behaviour. + + ## [1.6.8] - 2019-08-15 ### Changed @@ -364,7 +381,8 @@ - Make pbf reader and writer code endianess-aware. -[unreleased]: https://github.com/osmcode/libosmium/compare/v1.6.8...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v1.7.0...HEAD +[1.7.0]: https://github.com/osmcode/libosmium/compare/v1.6.8...v1.7.0 [1.6.8]: https://github.com/osmcode/libosmium/compare/v1.6.7...v1.6.8 [1.6.7]: https://github.com/osmcode/libosmium/compare/v1.6.6...v1.6.7 [1.6.6]: https://github.com/osmcode/libosmium/compare/v1.6.5...v1.6.6 diff -Nru protozero-1.6.8/.clang-tidy protozero-1.7.0/.clang-tidy --- protozero-1.6.8/.clang-tidy 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/.clang-tidy 2020-06-08 07:25:41.000000000 +0000 @@ -1,11 +1,17 @@ --- -Checks: '*,-cert-dcl21-cpp,-cert-err60-cpp,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-reinterpret-cast,-fuchsia-*,-google-runtime-references,-hicpp-avoid-c-arrays,-hicpp-no-array-decay,-modernize-avoid-c-arrays,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-magic-numbers' +Checks: '*,-bugprone-signed-char-misuse,-cert-dcl21-cpp,-cert-err58-cpp,-cert-err60-cpp,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-reinterpret-cast,-fuchsia-*,-google-runtime-references,-hicpp-avoid-c-arrays,-hicpp-no-array-decay,-hicpp-vararg,-modernize-avoid-c-arrays,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-magic-numbers' # # Disabled checks: # +# bugprone-signed-char-misuse +# Lots of warnings in varint.hpp otherwise. +# # cert-dcl21-cpp # It is unclear whether this is still a good recommendation in modern C++. # +# cert-err58-cpp +# Due to the Catch2 test framework. +# # cert-err60-cpp # Reports std::runtime_error as broken which we can't do anything about. # diff -Nru protozero-1.6.8/CMakeLists.txt protozero-1.7.0/CMakeLists.txt --- protozero-1.6.8/CMakeLists.txt 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/CMakeLists.txt 2020-06-08 07:25:41.000000000 +0000 @@ -13,8 +13,8 @@ project(protozero) set(PROTOZERO_VERSION_MAJOR 1) -set(PROTOZERO_VERSION_MINOR 6) -set(PROTOZERO_VERSION_PATCH 8) +set(PROTOZERO_VERSION_MINOR 7) +set(PROTOZERO_VERSION_PATCH 0) set(PROTOZERO_VERSION "${PROTOZERO_VERSION_MAJOR}.${PROTOZERO_VERSION_MINOR}.${PROTOZERO_VERSION_PATCH}") @@ -66,7 +66,8 @@ ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/test/*.cpp - ${CMAKE_SOURCE_DIR}/test/t/*/*.cpp + ${CMAKE_SOURCE_DIR}/test/t/*/reader_test_cases.cpp + ${CMAKE_SOURCE_DIR}/test/t/*/writer_test_cases.cpp ${CMAKE_SOURCE_DIR}/test/unit/*.cpp ${CMAKE_SOURCE_DIR}/tools/*.cpp ) diff -Nru protozero-1.6.8/debian/changelog protozero-1.7.0/debian/changelog --- protozero-1.6.8/debian/changelog 2020-06-04 08:39:20.000000000 +0000 +++ protozero-1.7.0/debian/changelog 2020-06-08 13:38:54.000000000 +0000 @@ -1,9 +1,14 @@ -protozero (1.6.8-1ubuntu2) groovy; urgency=medium +protozero (1.7.0-1) unstable; urgency=medium - * Force building with -O2, see - https://github.com/mapbox/protozero/issues/101 + * New upstream release. + * Bump Standards-Version to 4.5.0, no changes. + * Drop Name field from upstream metadata. + * Bump debhelper compat to 10, changes: + - Drop --parallel option, enabled by default + * Update copyright years for Two Blue Cubes Ltd. + * Add patch to fix privacy-breach lintian issues. - -- Matthias Klose Thu, 04 Jun 2020 10:39:20 +0200 + -- Bas Couwenberg Mon, 08 Jun 2020 15:38:54 +0200 protozero (1.6.8-1) unstable; urgency=medium diff -Nru protozero-1.6.8/debian/compat protozero-1.7.0/debian/compat --- protozero-1.6.8/debian/compat 2017-01-12 14:17:23.000000000 +0000 +++ protozero-1.7.0/debian/compat 2020-03-19 19:18:01.000000000 +0000 @@ -1 +1 @@ -9 +10 diff -Nru protozero-1.6.8/debian/control protozero-1.7.0/debian/control --- protozero-1.6.8/debian/control 2019-07-10 16:54:27.000000000 +0000 +++ protozero-1.7.0/debian/control 2020-03-19 19:18:07.000000000 +0000 @@ -3,14 +3,14 @@ Uploaders: Bas Couwenberg Section: science Priority: optional -Build-Depends: debhelper (>= 9), +Build-Depends: debhelper (>= 10~), cmake (>= 2.8), doxygen, graphviz, libprotobuf-dev, protobuf-compiler, pkg-config -Standards-Version: 4.4.0 +Standards-Version: 4.5.0 Vcs-Browser: https://salsa.debian.org/debian-gis-team/protozero/ Vcs-Git: https://salsa.debian.org/debian-gis-team/protozero.git Homepage: https://github.com/mapbox/protozero diff -Nru protozero-1.6.8/debian/copyright protozero-1.7.0/debian/copyright --- protozero-1.6.8/debian/copyright 2018-03-15 05:55:39.000000000 +0000 +++ protozero-1.7.0/debian/copyright 2020-06-08 13:38:16.000000000 +0000 @@ -8,7 +8,7 @@ License: BSD-2-Clause and Apache-2.0 Files: test/catch/catch.hpp -Copyright: 2012, Two Blue Cubes Ltd. +Copyright: 2020, Two Blue Cubes Ltd. License: BSL-1.0 Files: debian/* diff -Nru protozero-1.6.8/debian/patches/privacy-breach.patch protozero-1.7.0/debian/patches/privacy-breach.patch --- protozero-1.6.8/debian/patches/privacy-breach.patch 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/debian/patches/privacy-breach.patch 2020-06-08 13:38:54.000000000 +0000 @@ -0,0 +1,19 @@ +Description: Don't embedded external images. + Fixes privacy breach lintian issues. +Author: Bas Couwenberg +Forwarded: not-needed + +--- a/README.md ++++ b/README.md +@@ -11,11 +11,6 @@ changing frequently or lazy decoding is + this approach offers no value: just use the C++ API that can be generated with + the Google Protobufs `protoc` program. + +-[![Travis Build Status](https://travis-ci.org/mapbox/protozero.svg?branch=master)](https://travis-ci.org/mapbox/protozero) +-[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/mapbox/protozero?svg=true)](https://ci.appveyor.com/project/Mapbox/protozero) +-[![Coverage Status](https://codecov.io/gh/mapbox/protozero/branch/master/graph/badge.svg)](https://codecov.io/gh/mapbox/protozero) +-[![Packaging status](https://repology.org/badge/tiny-repos/protozero.svg)](https://repology.org/metapackage/protozero) +- + ## Depends + + * C++11 compiler diff -Nru protozero-1.6.8/debian/patches/series protozero-1.7.0/debian/patches/series --- protozero-1.6.8/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/debian/patches/series 2020-06-08 13:38:54.000000000 +0000 @@ -0,0 +1 @@ +privacy-breach.patch diff -Nru protozero-1.6.8/debian/rules protozero-1.7.0/debian/rules --- protozero-1.6.8/debian/rules 2020-06-04 08:39:20.000000000 +0000 +++ protozero-1.7.0/debian/rules 2020-03-19 19:18:12.000000000 +0000 @@ -5,14 +5,10 @@ # Verbose make output export VERBOSE=1 -# see https://github.com/mapbox/protozero/issues/101 -export DEB_CXXFLAGS_MAINT_STRIP = -O3 -export DEB_CXXFLAGS_MAINT_APPEND = -O2 - DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) %: - dh $@ --parallel + dh $@ override_dh_auto_build: dh_auto_build diff -Nru protozero-1.6.8/debian/upstream/metadata protozero-1.7.0/debian/upstream/metadata --- protozero-1.6.8/debian/upstream/metadata 2017-01-12 14:17:23.000000000 +0000 +++ protozero-1.7.0/debian/upstream/metadata 2019-12-09 08:32:51.000000000 +0000 @@ -1,6 +1,5 @@ --- Bug-Database: https://github.com/mapbox/protozero/issues Bug-Submit: https://github.com/mapbox/protozero/issues/new -Name: protozero Repository: https://github.com/mapbox/protozero.git Repository-Browse: https://github.com/mapbox/protozero diff -Nru protozero-1.6.8/doc/advanced.md protozero-1.7.0/doc/advanced.md --- protozero-1.6.8/doc/advanced.md 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/doc/advanced.md 2020-06-08 07:25:41.000000000 +0000 @@ -269,3 +269,58 @@ your use case to see whether the `reserve()` (or whatever you are using the `size()` for) is worth it. + +## Using a different buffer class than std::string + +Normally you are using the `pbf_writer` or `pbf_builder` classes which use a +`std::string` that you supply as their buffer for building the actual protocol +buffers message into. But you can use a different buffer implementation +instead. This might be useful if you want to use a fixed-size buffer for +instance. + +The `pbf_writer` and `pbf_builder` classes are actually only aliases for the +`basic_pbf_writer` and `basic_pbf_builder` template classes: + +```cpp +using pbf_writer = basic_pbf_writer; + +template +using pbf_builder = basic_pbf_builder; +``` + +If you want to use a different buffer type, use the `basic_*` form of the +class and use the buffer class as template parameter. When instantiating the +`basic_pbf_writer` or `basic_pbf_builder`, the only parameter to the +constructor must always be a reference to an object of the buffer class. + +```cpp +some_buffer_class buffer; +basic_pbf_writer writer{buffer}; +``` + +For this to work you must supply template specializations for some static +functions in the `protozero::buffer_customization` struct, see +`buffer_tmpl.hpp` for details. + +Protozero already supports two buffer types: +* `std::string` (to use include `protozero/buffer_string.hpp`) +* `std::vector` (to use include `protozero/buffer_vector.hpp`) + +There is a class `protozero::fixed_size_buffer_adaptor` you can use as adaptor +for any fixed-sized buffer you might have. Include `protozero/buffer_fixed.hpp` +to use it: + +```cpp +#include + +your_buffer_class some_buffer; +protozero::fixed_size_buffer_adaptor buffer_adaptor{some_buffer.data(), some_buffer.size()}; +basic_pbf_writer writer{buffer_adaptor}; +``` + +The buffer adaptor can be initialized with any container if it supports the +`data()` and `size()` member functions: + +```cpp +protozero::fixed_size_buffer_adaptor buffer_adaptor{some_buffer}; +``` diff -Nru protozero-1.6.8/doc/Doxyfile.in protozero-1.7.0/doc/Doxyfile.in --- protozero-1.6.8/doc/Doxyfile.in 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/doc/Doxyfile.in 2020-06-08 07:25:41.000000000 +0000 @@ -2046,7 +2046,7 @@ # interpreter (i.e. the result of 'which perl'). # The default file (with absolute path) is: /usr/bin/perl. -PERL_PATH = /usr/bin/perl +#PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool @@ -2068,7 +2068,7 @@ # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +#MSCGEN_PATH = # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The diff -Nru protozero-1.6.8/include/protozero/basic_pbf_builder.hpp protozero-1.7.0/include/protozero/basic_pbf_builder.hpp --- protozero-1.6.8/include/protozero/basic_pbf_builder.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/basic_pbf_builder.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,266 @@ +#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP +#define PROTOZERO_BASIC_PBF_BUILDER_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 basic_pbf_builder.hpp + * + * @brief Contains the basic_pbf_builder template class. + */ + +#include "basic_pbf_writer.hpp" +#include "types.hpp" + +#include + +namespace protozero { + +/** + * The basic_pbf_builder is used to write PBF formatted messages into a buffer. + * It is based on the basic_pbf_writer class and has all the same methods. The + * difference is that while the pbf_writer class takes an integer tag, + * this template class takes a tag of the template type T. The idea is that + * T will be an enumeration value and this helps reduce the possibility of + * programming errors. + * + * Almost all methods in this class can throw an std::bad_alloc exception if + * the underlying buffer class wants to resize. + * + * Read the tutorial to understand how this class is used. In most cases you + * want to use the pbf_builder class which uses a std::string as buffer type. + */ +template +class basic_pbf_builder : public basic_pbf_writer { + + static_assert(std::is_same::type>::value, + "T must be enum with underlying type protozero::pbf_tag_type"); + +public: + + /// The type of messages this class will build. + using enum_type = T; + + basic_pbf_builder() = default; + + /** + * Create a builder using the given string as a data store. The object + * stores a reference to that string and adds all data to it. The string + * doesn't have to be empty. The pbf_message object will just append data. + */ + explicit basic_pbf_builder(TBuffer& data) noexcept : + basic_pbf_writer{data} { + } + + /** + * Construct a pbf_builder for a submessage from the pbf_message or + * pbf_writer of the parent message. + * + * @param parent_writer The parent pbf_message or pbf_writer + * @param tag Tag of the field that will be written + */ + template + basic_pbf_builder(basic_pbf_writer& parent_writer, P tag) noexcept : + basic_pbf_writer{parent_writer, pbf_tag_type(tag)} { + } + +/// @cond INTERNAL +#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ + void add_##name(T tag, type value) { \ + basic_pbf_writer::add_##name(pbf_tag_type(tag), value); \ + } + + PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) + +#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR +/// @endcond + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_bytes(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value, size); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_bytes(T tag, const data_view& value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_bytes(T tag, const std::string& value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag of the field + * @param value Pointer to zero-delimited value to be written + */ + void add_bytes(T tag, const char* value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data using vectored input. All the data in the + * 2nd and further arguments is "concatenated" with only a single copy + * into the final buffer. + * + * This will work with objects of any type supporting the data() and + * size() methods like std::string or protozero::data_view. + * + * Example: + * @code + * std::string data1 = "abc"; + * std::string data2 = "xyz"; + * builder.add_bytes_vectored(1, data1, data2); + * @endcode + * + * @tparam Ts List of types supporting data() and size() methods. + * @param tag Tag of the field + * @param values List of objects of types Ts with data to be appended. + */ + template + void add_bytes_vectored(T tag, Ts&&... values) { + basic_pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward(values)...); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_string(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value, size); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_string(T tag, const data_view& value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_string(T tag, const std::string& value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "string" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + */ + void add_string(T tag, const char* value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Pointer to message to be written + * @param size Length of the message + */ + void add_message(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value, size); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(T tag, const data_view& value) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(T tag, const std::string& value) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value); + } + +/// @cond INTERNAL +#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ + template \ + void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ + basic_pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ + } + + PROTOZERO_WRITER_WRAP_ADD_PACKED(bool) + PROTOZERO_WRITER_WRAP_ADD_PACKED(enum) + PROTOZERO_WRITER_WRAP_ADD_PACKED(int32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(int64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(float) + PROTOZERO_WRITER_WRAP_ADD_PACKED(double) + +#undef PROTOZERO_WRITER_WRAP_ADD_PACKED +/// @endcond + +}; // class basic_pbf_builder + +} // end namespace protozero + +#endif // PROTOZERO_BASIC_PBF_BUILDER_HPP diff -Nru protozero-1.6.8/include/protozero/basic_pbf_writer.hpp protozero-1.7.0/include/protozero/basic_pbf_writer.hpp --- protozero-1.6.8/include/protozero/basic_pbf_writer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/basic_pbf_writer.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,1054 @@ +#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP +#define PROTOZERO_BASIC_PBF_WRITER_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 basic_pbf_writer.hpp + * + * @brief Contains the basic_pbf_writer template class. + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" +#include "data_view.hpp" +#include "types.hpp" +#include "varint.hpp" + +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace protozero { + +namespace detail { + + template class packed_field_varint; + template class packed_field_svarint; + template class packed_field_fixed; + +} // end namespace detail + +/** + * The basic_pbf_writer is used to write PBF formatted messages into a buffer. + * + * This uses TBuffer as the type for the underlaying buffer. In typical uses + * this is std::string, but you can use a different type that must support + * the right interface. Please see the documentation for details. + * + * Almost all methods in this class can throw an std::bad_alloc exception if + * the underlying buffer class wants to resize. + */ +template +class basic_pbf_writer { + + // A pointer to a buffer holding the data already written to the PBF + // message. For default constructed writers or writers that have been + // rolled back, this is a nullptr. + TBuffer* m_data = nullptr; + + // A pointer to a parent writer object if this is a submessage. If this + // is a top-level writer, it is a nullptr. + basic_pbf_writer* m_parent_writer = nullptr; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the rollback position, ie. the last position before the + // submessage was started. This is the position where the header of the + // submessage starts. + std::size_t m_rollback_pos = 0; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the position where the data of the submessage is written to. + std::size_t m_pos = 0; + + void add_varint(uint64_t value) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + add_varint_to_buffer(m_data, value); + } + + void add_field(pbf_tag_type tag, pbf_wire_type type) { + protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) && "tag out of range"); + const uint32_t b = (tag << 3U) | uint32_t(type); + add_varint(b); + } + + void add_tagged_varint(pbf_tag_type tag, uint64_t value) { + add_field(tag, pbf_wire_type::varint); + add_varint(value); + } + + template + void add_fixed(T value) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN + byteswap_inplace(&value); +#endif + buffer_customization::append(m_data, reinterpret_cast(&value), sizeof(T)); + } + + template + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_fixed(*first++); + } + } + + template + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) { + if (first == last) { + return; + } + + const auto length = std::distance(first, last); + add_length_varint(tag, sizeof(T) * pbf_length_type(length)); + reserve(sizeof(T) * std::size_t(length)); + + while (first != last) { + add_fixed(*first++); + } + } + + template + void add_packed_varint(pbf_tag_type tag, It first, It last) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_varint(uint64_t(*first++)); + } + } + + template + void add_packed_svarint(pbf_tag_type tag, It first, It last) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_varint(encode_zigzag64(*first++)); + } + } + + // 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. + enum : 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. + enum : std::size_t { + size_is_known = std::numeric_limits::max() + }; + + void open_submessage(pbf_tag_type tag, std::size_t size) { + protozero_assert(m_pos == 0); + protozero_assert(m_data); + if (size == 0) { + m_rollback_pos = buffer_customization::size(m_data); + add_field(tag, pbf_wire_type::length_delimited); + buffer_customization::append_zeros(m_data, std::size_t(reserve_bytes)); + } else { + m_rollback_pos = size_is_known; + add_length_varint(tag, pbf_length_type(size)); + reserve(size); + } + m_pos = buffer_customization::size(m_data); + } + + void rollback_submessage() { + protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); + protozero_assert(m_data); + buffer_customization::resize(m_data, m_rollback_pos); + m_pos = 0; + } + + void commit_submessage() { + protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); + protozero_assert(m_data); + const auto length = pbf_length_type(buffer_customization::size(m_data) - m_pos); + + protozero_assert(buffer_customization::size(m_data) >= m_pos - reserve_bytes); + const auto n = add_varint_to_buffer(buffer_customization::at_pos(m_data, m_pos - reserve_bytes), length); + + buffer_customization::erase_range(m_data, m_pos - reserve_bytes + n, m_pos); + m_pos = 0; + } + + void close_submessage() { + protozero_assert(m_data); + if (m_pos == 0 || m_rollback_pos == size_is_known) { + return; + } + if (buffer_customization::size(m_data) - m_pos == 0) { + rollback_submessage(); + } else { + commit_submessage(); + } + } + + void add_length_varint(pbf_tag_type tag, pbf_length_type length) { + add_field(tag, pbf_wire_type::length_delimited); + add_varint(length); + } + +public: + + /** + * Create a writer using the specified buffer as a data store. The + * basic_pbf_writer stores a pointer to that buffer and adds all data to + * it. The buffer doesn't have to be empty. The basic_pbf_writer will just + * append data. + */ + explicit basic_pbf_writer(TBuffer& buffer) noexcept : + m_data{&buffer} { + } + + /** + * Create a writer without a data store. In this form the writer can not + * be used! + */ + basic_pbf_writer() noexcept = default; + + /** + * Construct a basic_pbf_writer for a submessage from the basic_pbf_writer + * of the parent message. + * + * @param parent_writer The basic_pbf_writer + * @param tag Tag (field number) of the field that will be written + * @param size Optional size of the submessage in bytes (use 0 for unknown). + * Setting this allows some optimizations but is only possible in + * a few very specific cases. + */ + basic_pbf_writer(basic_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_parent_writer->open_submessage(tag, size); + } + + /// A basic_pbf_writer object can not be copied + basic_pbf_writer(const basic_pbf_writer&) = delete; + + /// A basic_pbf_writer object can not be copied + basic_pbf_writer& operator=(const basic_pbf_writer&) = delete; + + /** + * A basic_pbf_writer object can be moved. After this the other + * basic_pbf_writer will be invalid. + */ + basic_pbf_writer(basic_pbf_writer&& other) noexcept : + m_data{other.m_data}, + m_parent_writer{other.m_parent_writer}, + m_rollback_pos{other.m_rollback_pos}, + m_pos{other.m_pos} { + other.m_data = nullptr; + other.m_parent_writer = nullptr; + other.m_rollback_pos = 0; + other.m_pos = 0; + } + + /** + * A basic_pbf_writer object can be moved. After this the other + * basic_pbf_writer will be invalid. + */ + basic_pbf_writer& operator=(basic_pbf_writer&& other) noexcept { + m_data = other.m_data; + m_parent_writer = other.m_parent_writer; + m_rollback_pos = other.m_rollback_pos; + m_pos = other.m_pos; + other.m_data = nullptr; + other.m_parent_writer = nullptr; + other.m_rollback_pos = 0; + other.m_pos = 0; + return *this; + } + + ~basic_pbf_writer() noexcept { + try { + if (m_parent_writer != nullptr) { + m_parent_writer->close_submessage(); + } + } catch (...) { + // This try/catch is used to make the destructor formally noexcept. + // close_submessage() is not noexcept, but will not throw the way + // it is called here, so we are good. But to be paranoid, call... + std::terminate(); + } + } + + /** + * Check if this writer is valid. A writer is invalid if it was default + * constructed, moved from, or if commit() has been called on it. + * Otherwise it is valid. + */ + bool valid() const noexcept { + return m_data != nullptr; + } + + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(basic_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, + * but additional memory that should be reserved. + * + * @param size Number of bytes to reserve in underlying message store. + */ + void reserve(std::size_t size) { + protozero_assert(m_data); + buffer_customization::reserve_additional(m_data, size); + } + + /** + * Commit this submessage. This does the same as when the basic_pbf_writer + * goes out of scope and is destructed. + * + * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the + * basic_pbf_writer constructor taking a parent message. + * @post The basic_pbf_writer is invalid and can't be used any more. + */ + void commit() { + protozero_assert(m_parent_writer && "you can't call commit() on a basic_pbf_writer without a parent"); + protozero_assert(m_pos == 0 && "you can't call commit() on a basic_pbf_writer that has an open nested submessage"); + m_parent_writer->close_submessage(); + m_parent_writer = nullptr; + m_data = nullptr; + } + + /** + * 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 basic_pbf_writer of a submessage, ie one opened with the + * basic_pbf_writer constructor taking a parent message. + * @post The basic_pbf_writer is invalid and can't be used any more. + */ + void rollback() { + protozero_assert(m_parent_writer && "you can't call rollback() on a basic_pbf_writer without a parent"); + protozero_assert(m_pos == 0 && "you can't call rollback() on a basic_pbf_writer that has an open nested submessage"); + m_parent_writer->rollback_submessage(); + m_parent_writer = nullptr; + m_data = nullptr; + } + + ///@{ + /** + * @name Scalar field writer functions + */ + + /** + * Add "bool" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + 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 basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + m_data->push_back(char(value)); + } + + /** + * Add "enum" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_enum(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "int32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_int32(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "sint32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sint32(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, encode_zigzag32(value)); + } + + /** + * Add "uint32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_uint32(pbf_tag_type tag, uint32_t value) { + add_tagged_varint(tag, value); + } + + /** + * Add "int64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_int64(pbf_tag_type tag, int64_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "sint64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sint64(pbf_tag_type tag, int64_t value) { + add_tagged_varint(tag, encode_zigzag64(value)); + } + + /** + * Add "uint64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_uint64(pbf_tag_type tag, uint64_t value) { + add_tagged_varint(tag, value); + } + + /** + * Add "fixed32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_fixed32(pbf_tag_type tag, uint32_t value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "sfixed32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sfixed32(pbf_tag_type tag, int32_t value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "fixed64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_fixed64(pbf_tag_type tag, uint64_t value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "sfixed64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sfixed64(pbf_tag_type tag, int64_t value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "float" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_float(pbf_tag_type tag, float value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "double" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_double(pbf_tag_type tag, double value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + 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 basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + protozero_assert(size <= std::numeric_limits::max()); + add_length_varint(tag, pbf_length_type(size)); + buffer_customization::append(m_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 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()); + } + + /** + * Add "bytes" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag (field number) of the field + * @param value Pointer to zero-delimited value to be written + */ + void add_bytes(pbf_tag_type tag, const char* value) { + add_bytes(tag, value, std::strlen(value)); + } + + /** + * Add "bytes" field to data using vectored input. All the data in the + * 2nd and further arguments is "concatenated" with only a single copy + * into the final buffer. + * + * This will work with objects of any type supporting the data() and + * size() methods like std::string or protozero::data_view. + * + * Example: + * @code + * std::string data1 = "abc"; + * std::string data2 = "xyz"; + * writer.add_bytes_vectored(1, data1, data2); + * @endcode + * + * @tparam Ts List of types supporting data() and size() methods. + * @param tag Tag (field number) of the field + * @param values List of objects of types Ts with data to be appended. + */ + template + void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + size_t sum_size = 0; + (void)std::initializer_list{sum_size += values.size()...}; + protozero_assert(sum_size <= std::numeric_limits::max()); + add_length_varint(tag, pbf_length_type(sum_size)); + buffer_customization::reserve_additional(m_data, sum_size); + (void)std::initializer_list{(buffer_customization::append(m_data, values.data(), values.size()), 0)...}; + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_string(pbf_tag_type tag, const char* value, std::size_t size) { + add_bytes(tag, 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 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()); + } + + /** + * Add "string" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + */ + void add_string(pbf_tag_type tag, const char* value) { + add_bytes(tag, value, std::strlen(value)); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to message to be written + * @param size Length of the message + */ + void add_message(pbf_tag_type tag, const char* value, std::size_t size) { + add_bytes(tag, 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 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()); + } + + ///@} + + ///@{ + /** + * @name Repeated packed field writer functions + */ + + /** + * Add "repeated packed bool" field to data. + * + * @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 + 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 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 + 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 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 + 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 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 + 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 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 + 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 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 + 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 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 + 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 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 + void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add a "repeated packed" fixed-size field to data. The following + * fixed-size fields are available: + * + * uint32_t -> repeated packed fixed32 + * int32_t -> repeated packed sfixed32 + * uint64_t -> repeated packed fixed64 + * int64_t -> repeated packed sfixed64 + * double -> repeated packed double + * float -> repeated packed float + * + * @tparam ValueType One of the following types: (u)int32/64_t, double, float. + * @tparam InputIterator A type satisfying the InputIterator concept. + * @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 + void add_packed_fixed(pbf_tag_type tag, InputIterator first, InputIterator last) { + static_assert(std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, "Only some types are allowed"); + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed fixed32" field to data. + * + * @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 + void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed sfixed32" field to data. + * + * @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 + void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed fixed64" field to data. + * + * @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 + void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed sfixed64" field to data. + * + * @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 + void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed float" field to data. + * + * @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 + void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed double" field to data. + * + * @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 + void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + ///@} + + template friend class detail::packed_field_varint; + template friend class detail::packed_field_svarint; + template friend class detail::packed_field_fixed; + +}; // class basic_pbf_writer + +/** + * Swap two basic_pbf_writer objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +template +inline void swap(basic_pbf_writer& lhs, basic_pbf_writer& rhs) noexcept { + lhs.swap(rhs); +} + +namespace detail { + + template + class packed_field { + + basic_pbf_writer m_writer{}; + + public: + + packed_field(const packed_field&) = delete; + packed_field& operator=(const packed_field&) = delete; + + packed_field(packed_field&&) noexcept = default; + packed_field& operator=(packed_field&&) noexcept = default; + + packed_field() = default; + + packed_field(basic_pbf_writer& parent_writer, pbf_tag_type tag) : + m_writer{parent_writer, tag} { + } + + packed_field(basic_pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : + m_writer{parent_writer, tag, size} { + } + + ~packed_field() noexcept = default; + + bool valid() const noexcept { + return m_writer.valid(); + } + + void commit() { + m_writer.commit(); + } + + void rollback() { + m_writer.rollback(); + } + + basic_pbf_writer& writer() noexcept { + return m_writer; + } + + }; // class packed_field + + template + class packed_field_fixed : public packed_field { + + public: + + packed_field_fixed() : + packed_field{} { + } + + template + packed_field_fixed(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + template + packed_field_fixed(basic_pbf_writer& parent_writer, P tag, std::size_t size) : + packed_field{parent_writer, static_cast(tag), size * sizeof(T)} { + } + + void add_element(T value) { + this->writer().template add_fixed(value); + } + + }; // class packed_field_fixed + + template + class packed_field_varint : public packed_field { + + public: + + packed_field_varint() : + packed_field{} { + } + + template + packed_field_varint(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + void add_element(T value) { + this->writer().add_varint(uint64_t(value)); + } + + }; // class packed_field_varint + + template + class packed_field_svarint : public packed_field { + + public: + + packed_field_svarint() : + packed_field{} { + } + + template + packed_field_svarint(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + void add_element(T value) { + this->writer().add_varint(encode_zigzag64(value)); + } + + }; // class packed_field_svarint + +} // end namespace detail + +} // end namespace protozero + +#endif // PROTOZERO_BASIC_PBF_WRITER_HPP diff -Nru protozero-1.6.8/include/protozero/buffer_fixed.hpp protozero-1.7.0/include/protozero/buffer_fixed.hpp --- protozero-1.6.8/include/protozero/buffer_fixed.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/buffer_fixed.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,222 @@ +#ifndef PROTOZERO_BUFFER_FIXED_HPP +#define PROTOZERO_BUFFER_FIXED_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 buffer_fixed.hpp + * + * @brief Contains the fixed_size_buffer_adaptor class. + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" + +#include +#include +#include +#include + +namespace protozero { + +/** + * This class can be used instead of std::string if you want to create a + * vector tile in a fixed-size buffer. Any operation that needs more space + * than is available will fail with a std::length_error exception. + */ +class fixed_size_buffer_adaptor { + + char* m_data; + std::size_t m_capacity; + std::size_t m_size = 0; + +public: + + /// @cond usual container typedefs not documented + + using size_type = std::size_t; + + using value_type = char; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using iterator = pointer; + using const_iterator = const_pointer; + + /// @endcond + + /** + * Constructor. + * + * @param data Pointer to some memory allocated for the buffer. + * @param capacity Number of bytes available. + */ + fixed_size_buffer_adaptor(char* data, std::size_t capacity) noexcept : + m_data(data), + m_capacity(capacity) { + } + + /** + * Constructor. + * + * @param container Some container class supporting the member functions + * data() and size(). + */ + template + explicit fixed_size_buffer_adaptor(T& container) : + m_data(container.data()), + m_capacity(container.size()) { + } + + /// Returns a pointer to the data in the buffer. + const char* data() const noexcept { + return m_data; + } + + /// Returns a pointer to the data in the buffer. + char* data() noexcept { + return m_data; + } + + /// The capacity this buffer was created with. + std::size_t capacity() const noexcept { + return m_capacity; + } + + /// The number of bytes used in the buffer. Always <= capacity(). + std::size_t size() const noexcept { + return m_size; + } + + /// Return iterator to beginning of data. + char* begin() noexcept { + return m_data; + } + + /// Return iterator to beginning of data. + const char* begin() const noexcept { + return m_data; + } + + /// Return iterator to beginning of data. + const char* cbegin() const noexcept { + return m_data; + } + + /// Return iterator to end of data. + char* end() noexcept { + return m_data + m_size; + } + + /// Return iterator to end of data. + const char* end() const noexcept { + return m_data + m_size; + } + + /// Return iterator to end of data. + const char* cend() const noexcept { + return m_data + m_size; + } + +/// @cond INTERNAL + + // Do not rely on anything beyond this point + + void append(const char* data, std::size_t count) { + if (m_size + count > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + std::copy_n(data, count, m_data + m_size); + m_size += count; + } + + void append_zeros(std::size_t count) { + if (m_size + count > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + std::fill_n(m_data + m_size, count, '\0'); + m_size += count; + } + + void resize(std::size_t size) { + protozero_assert(size < m_size); + if (size > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + m_size = size; + } + + void erase_range(std::size_t from, std::size_t to) { + protozero_assert(from <= m_size); + protozero_assert(to <= m_size); + protozero_assert(from < to); + std::copy(m_data + to, m_data + m_size, m_data + from); + m_size -= (to - from); + } + + char* at_pos(std::size_t pos) { + protozero_assert(pos <= m_size); + return m_data + pos; + } + + void push_back(char ch) { + if (m_size >= m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + m_data[m_size++] = ch; + } +/// @endcond + +}; // class fixed_size_buffer_adaptor + +/// @cond INTERNAL +template <> +struct buffer_customization { + + static std::size_t size(const fixed_size_buffer_adaptor* buffer) noexcept { + return buffer->size(); + } + + static void append(fixed_size_buffer_adaptor* buffer, const char* data, std::size_t count) { + buffer->append(data, count); + } + + static void append_zeros(fixed_size_buffer_adaptor* buffer, std::size_t count) { + buffer->append_zeros(count); + } + + static void resize(fixed_size_buffer_adaptor* buffer, std::size_t size) { + buffer->resize(size); + } + + static void reserve_additional(fixed_size_buffer_adaptor* /*buffer*/, std::size_t /*size*/) { + /* nothing to be done for fixed-size buffers */ + } + + static void erase_range(fixed_size_buffer_adaptor* buffer, std::size_t from, std::size_t to) { + buffer->erase_range(from, to); + } + + static char* at_pos(fixed_size_buffer_adaptor* buffer, std::size_t pos) { + return buffer->at_pos(pos); + } + + static void push_back(fixed_size_buffer_adaptor* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_FIXED_HPP diff -Nru protozero-1.6.8/include/protozero/buffer_string.hpp protozero-1.7.0/include/protozero/buffer_string.hpp --- protozero-1.6.8/include/protozero/buffer_string.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/buffer_string.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,76 @@ +#ifndef PROTOZERO_BUFFER_STRING_HPP +#define PROTOZERO_BUFFER_STRING_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 buffer_string.hpp + * + * @brief Contains the customization points for buffer implementation based + * on std::string + */ + +#include "buffer_tmpl.hpp" + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::string + +/// @cond INTERNAL +template <> +struct buffer_customization { + + static std::size_t size(const std::string* buffer) noexcept { + return buffer->size(); + } + + static void append(std::string* buffer, const char* data, std::size_t count) { + buffer->append(data, count); + } + + static void append_zeros(std::string* buffer, std::size_t count) { + buffer->append(count, '\0'); + } + + static void resize(std::string* buffer, std::size_t size) { + protozero_assert(size < buffer->size()); + buffer->resize(size); + } + + static void reserve_additional(std::string* buffer, std::size_t size) { + buffer->reserve(buffer->size() + size); + } + + static void erase_range(std::string* buffer, std::size_t from, std::size_t to) { + protozero_assert(from <= buffer->size()); + protozero_assert(to <= buffer->size()); + protozero_assert(from <= to); + buffer->erase(std::next(buffer->begin(), from), std::next(buffer->begin(), to)); + } + + static char* at_pos(std::string* buffer, std::size_t pos) { + protozero_assert(pos <= buffer->size()); + return (&*buffer->begin()) + pos; + } + + static void push_back(std::string* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_STRING_HPP diff -Nru protozero-1.6.8/include/protozero/buffer_tmpl.hpp protozero-1.7.0/include/protozero/buffer_tmpl.hpp --- protozero-1.6.8/include/protozero/buffer_tmpl.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/buffer_tmpl.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,113 @@ +#ifndef PROTOZERO_BUFFER_TMPL_HPP +#define PROTOZERO_BUFFER_TMPL_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 buffer_tmpl.hpp + * + * @brief Contains the customization points for buffer implementations. + */ + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::string + +/// @cond INTERNAL +template +struct buffer_customization { + + /** + * Get the number of bytes currently used in the buffer. + * + * @param buffer Pointer to the buffer. + * @returns number of bytes used in the buffer. + */ + static std::size_t size(const std::string* buffer); + + /** + * Append count bytes from data to the buffer. + * + * @param buffer Pointer to the buffer. + * @param data Pointer to the data. + * @param count Number of bytes to be added to the buffer. + */ + static void append(std::string* buffer, const char* data, std::size_t count); + + /** + * Append count zero bytes to the buffer. + * + * @param buffer Pointer to the buffer. + * @param count Number of bytes to be added to the buffer. + */ + static void append_zeros(std::string* buffer, std::size_t count); + + /** + * Shrink the buffer to the specified size. The new size will always be + * smaller than the current size. + * + * @param buffer Pointer to the buffer. + * @param size New size of the buffer. + * + * @pre size < current size of buffer + */ + static void resize(std::string* buffer, std::size_t size); + + /** + * Reserve an additional size bytes for use in the buffer. This is used for + * variable-sized buffers to tell the buffer implementation that soon more + * memory will be used. The implementation can ignore this. + * + * @param buffer Pointer to the buffer. + * @param size Number of bytes to reserve. + */ + static void reserve_additional(std::string* buffer, std::size_t size); + + /** + * Delete data from the buffer. This must move back the data after the + * part being deleted and resize the buffer accordingly. + * + * @param buffer Pointer to the buffer. + * @param from Offset into the buffer where we want to erase from. + * @param to Offset into the buffer one past the last byte we want to erase. + * + * @pre from, to <= size of the buffer, from < to + */ + static void erase_range(std::string* buffer, std::size_t from, std::size_t to); + + /** + * Return a pointer to the memory at the specified position in the buffer. + * + * @param buffer Pointer to the buffer. + * @param pos The position in the buffer. + * @returns pointer to the memory in the buffer at the specified position. + * + * @pre pos <= size of the buffer + */ + static char* at_pos(std::string* buffer, std::size_t pos); + + /** + * Add a char to the buffer incrementing the number of chars in the buffer. + * + * @param buffer Pointer to the buffer. + * @param ch The character to add. + */ + static void push_back(std::string* buffer, char ch); + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_TMPL_HPP diff -Nru protozero-1.6.8/include/protozero/buffer_vector.hpp protozero-1.7.0/include/protozero/buffer_vector.hpp --- protozero-1.6.8/include/protozero/buffer_vector.hpp 1970-01-01 00:00:00.000000000 +0000 +++ protozero-1.7.0/include/protozero/buffer_vector.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -0,0 +1,76 @@ +#ifndef PROTOZERO_BUFFER_VECTOR_HPP +#define PROTOZERO_BUFFER_VECTOR_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 buffer_vector.hpp + * + * @brief Contains the customization points for buffer implementation based + * on std::vector + */ + +#include "buffer_tmpl.hpp" + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::vector + +/// @cond INTERNAL +template <> +struct buffer_customization> { + + static std::size_t size(const std::vector* buffer) noexcept { + return buffer->size(); + } + + static void append(std::vector* buffer, const char* data, std::size_t count) { + buffer->insert(buffer->end(), data, data + count); + } + + static void append_zeros(std::vector* buffer, std::size_t count) { + buffer->insert(buffer->end(), count, '\0'); + } + + static void resize(std::vector* buffer, std::size_t size) { + protozero_assert(size < buffer->size()); + buffer->resize(size); + } + + static void reserve_additional(std::vector* buffer, std::size_t size) { + buffer->reserve(buffer->size() + size); + } + + static void erase_range(std::vector* buffer, std::size_t from, std::size_t to) { + protozero_assert(from <= buffer->size()); + protozero_assert(to <= buffer->size()); + protozero_assert(from <= to); + buffer->erase(std::next(buffer->begin(), from), std::next(buffer->begin(), to)); + } + + static char* at_pos(std::vector* buffer, std::size_t pos) { + protozero_assert(pos <= buffer->size()); + return (&*buffer->begin()) + pos; + } + + static void push_back(std::vector* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_VECTOR_HPP diff -Nru protozero-1.6.8/include/protozero/byteswap.hpp protozero-1.7.0/include/protozero/byteswap.hpp --- protozero-1.6.8/include/protozero/byteswap.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/byteswap.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,7 +16,7 @@ * @brief Contains functions to swap bytes in values (for different endianness). */ -#include +#include "config.hpp" #include @@ -63,25 +63,25 @@ /// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(int32_t* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); + auto* bptr = reinterpret_cast(ptr); *bptr = detail::byteswap_impl(*bptr); } /// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(int64_t* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); + auto* bptr = reinterpret_cast(ptr); *bptr = detail::byteswap_impl(*bptr); } /// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(float* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); + auto* bptr = reinterpret_cast(ptr); *bptr = detail::byteswap_impl(*bptr); } /// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(double* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); + auto* bptr = reinterpret_cast(ptr); *bptr = detail::byteswap_impl(*bptr); } diff -Nru protozero-1.6.8/include/protozero/data_view.hpp protozero-1.7.0/include/protozero/data_view.hpp --- protozero-1.6.8/include/protozero/data_view.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/data_view.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,7 +16,7 @@ * @brief Contains the implementation of the data_view class. */ -#include +#include "config.hpp" #include #include diff -Nru protozero-1.6.8/include/protozero/iterators.hpp protozero-1.7.0/include/protozero/iterators.hpp --- protozero-1.6.8/include/protozero/iterators.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/iterators.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,8 +16,8 @@ * @brief Contains the iterators for access to packed repeated fields. */ -#include -#include +#include "config.hpp" +#include "varint.hpp" #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include diff -Nru protozero-1.6.8/include/protozero/pbf_builder.hpp protozero-1.7.0/include/protozero/pbf_builder.hpp --- protozero-1.6.8/include/protozero/pbf_builder.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/pbf_builder.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,249 +16,16 @@ * @brief Contains the pbf_builder template class. */ -#include -#include +#include "basic_pbf_builder.hpp" +#include "pbf_writer.hpp" -#include +#include namespace protozero { -/** - * The pbf_builder is used to write PBF formatted messages into a buffer. It - * is based on the pbf_writer class and has all the same methods. The - * difference is that while the pbf_writer class takes an integer tag, - * this template class takes a tag of the template type T. The idea is that - * T will be an enumeration value and this helps reduce the possibility of - * programming errors. - * - * Almost all methods in this class can throw an std::bad_alloc exception if - * the std::string used as a buffer wants to resize. - * - * Read the tutorial to understand how this class is used. - */ +/// Specialization of basic_pbf_builder using std::string as buffer type. template -class pbf_builder : public pbf_writer { - - static_assert(std::is_same::type>::value, - "T must be enum with underlying type protozero::pbf_tag_type"); - -public: - - /// The type of messages this class will build. - using enum_type = T; - - pbf_builder() = default; - - /** - * Create a builder using the given string as a data store. The object - * stores a reference to that string and adds all data to it. The string - * doesn't have to be empty. The pbf_message object will just append data. - */ - explicit pbf_builder(std::string& data) noexcept : - pbf_writer{data} { - } - - /** - * Construct a pbf_builder for a submessage from the pbf_message or - * pbf_writer of the parent message. - * - * @param parent_writer The parent pbf_message or pbf_writer - * @param tag Tag of the field that will be written - */ - template - pbf_builder(pbf_writer& parent_writer, P tag) noexcept : - pbf_writer{parent_writer, pbf_tag_type(tag)} { - } - -/// @cond INTERNAL -#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ - void add_##name(T tag, type value) { \ - pbf_writer::add_##name(pbf_tag_type(tag), value); \ - } - - PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) - -#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR -/// @endcond - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_bytes(T tag, const char* value, std::size_t size) { - pbf_writer::add_bytes(pbf_tag_type(tag), value, size); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_bytes(T tag, const data_view& value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_bytes(T tag, const std::string& value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag of the field - * @param value Pointer to zero-delimited value to be written - */ - void add_bytes(T tag, const char* value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data using vectored input. All the data in the - * 2nd and further arguments is "concatenated" with only a single copy - * into the final buffer. - * - * This will work with objects of any type supporting the data() and - * size() methods like std::string or protozero::data_view. - * - * Example: - * @code - * std::string data1 = "abc"; - * std::string data2 = "xyz"; - * builder.add_bytes_vectored(1, data1, data2); - * @endcode - * - * @tparam Ts List of types supporting data() and size() methods. - * @param tag Tag of the field - * @param values List of objects of types Ts with data to be appended. - */ - template - void add_bytes_vectored(T tag, Ts&&... values) { - pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward(values)...); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_string(T tag, const char* value, std::size_t size) { - pbf_writer::add_string(pbf_tag_type(tag), value, size); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_string(T tag, const data_view& value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_string(T tag, const std::string& value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "string" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - */ - void add_string(T tag, const char* value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Pointer to message to be written - * @param size Length of the message - */ - void add_message(T tag, const char* value, std::size_t size) { - pbf_writer::add_message(pbf_tag_type(tag), value, size); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Value to be written. The value must be a complete message. - */ - void add_message(T tag, const data_view& value) { - pbf_writer::add_message(pbf_tag_type(tag), value); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Value to be written. The value must be a complete message. - */ - 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 \ - void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ - pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ - } - - PROTOZERO_WRITER_WRAP_ADD_PACKED(bool) - PROTOZERO_WRITER_WRAP_ADD_PACKED(enum) - PROTOZERO_WRITER_WRAP_ADD_PACKED(int32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(int64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(float) - PROTOZERO_WRITER_WRAP_ADD_PACKED(double) - -#undef PROTOZERO_WRITER_WRAP_ADD_PACKED -/// @endcond - -}; // class pbf_builder +using pbf_builder = basic_pbf_builder; } // end namespace protozero diff -Nru protozero-1.6.8/include/protozero/pbf_message.hpp protozero-1.7.0/include/protozero/pbf_message.hpp --- protozero-1.6.8/include/protozero/pbf_message.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/pbf_message.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,8 +16,8 @@ * @brief Contains the pbf_message template class. */ -#include -#include +#include "pbf_reader.hpp" +#include "types.hpp" #include diff -Nru protozero-1.6.8/include/protozero/pbf_reader.hpp protozero-1.7.0/include/protozero/pbf_reader.hpp --- protozero-1.6.8/include/protozero/pbf_reader.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/pbf_reader.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,12 +16,12 @@ * @brief Contains the pbf_reader class. */ -#include -#include -#include -#include -#include -#include +#include "config.hpp" +#include "data_view.hpp" +#include "exception.hpp" +#include "iterators.hpp" +#include "types.hpp" +#include "varint.hpp" #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include @@ -494,9 +494,9 @@ 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"); - const auto data = m_data; + const bool result = m_data[0] != 0; skip_varint(&m_data, m_end); - return data[0] != 0; + return result; } /** diff -Nru protozero-1.6.8/include/protozero/pbf_writer.hpp protozero-1.7.0/include/protozero/pbf_writer.hpp --- protozero-1.6.8/include/protozero/pbf_writer.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/pbf_writer.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,1069 +16,60 @@ * @brief Contains the pbf_writer class. */ -#include -#include -#include -#include +#include "basic_pbf_writer.hpp" +#include "buffer_string.hpp" -#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN -# include -#endif - -#include #include -#include -#include -#include -#include #include -#include namespace protozero { -namespace detail { - - template class packed_field_varint; - template class packed_field_svarint; - template class packed_field_fixed; - -} // end namespace detail - -/** - * The pbf_writer is used to write PBF formatted messages into a buffer. - * - * Almost all methods in this class can throw an std::bad_alloc exception if - * the std::string used as a buffer wants to resize. - */ -class pbf_writer { - - // A pointer to a string buffer holding the data already written to the - // PBF message. For default constructed writers or writers that have been - // rolled back, this is a nullptr. - std::string* m_data = nullptr; - - // A pointer to a parent writer object if this is a submessage. If this - // is a top-level writer, it is a nullptr. - pbf_writer* m_parent_writer = nullptr; - - // This is usually 0. If there is an open submessage, this is set in the - // parent to the rollback position, ie. the last position before the - // submessage was started. This is the position where the header of the - // submessage starts. - std::size_t m_rollback_pos = 0; - - // This is usually 0. If there is an open submessage, this is set in the - // parent to the position where the data of the submessage is written to. - std::size_t m_pos = 0; - - 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); - } - - void add_field(pbf_tag_type tag, pbf_wire_type type) { - protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) && "tag out of range"); - const uint32_t b = (tag << 3U) | uint32_t(type); - add_varint(b); - } - - void add_tagged_varint(pbf_tag_type tag, uint64_t value) { - add_field(tag, pbf_wire_type::varint); - add_varint(value); - } - - template - 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 - byteswap_inplace(&value); -#endif - m_data->append(reinterpret_cast(&value), sizeof(T)); - } - - template - void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_fixed(*first++); - } - } - - template - void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) { - if (first == last) { - return; - } - - const auto length = std::distance(first, last); - add_length_varint(tag, sizeof(T) * pbf_length_type(length)); - reserve(sizeof(T) * std::size_t(length)); - - while (first != last) { - add_fixed(*first++); - } - } - - template - void add_packed_varint(pbf_tag_type tag, It first, It last) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_varint(uint64_t(*first++)); - } - } - - template - void add_packed_svarint(pbf_tag_type tag, It first, It last) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_varint(encode_zigzag64(*first++)); - } - } - - // 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. - enum constant_reserve_bytes : 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. - enum constant_size_is_known : std::size_t { - size_is_known = std::numeric_limits::max() - }; - - void open_submessage(pbf_tag_type tag, std::size_t size) { - protozero_assert(m_pos == 0); - protozero_assert(m_data); - if (size == 0) { - m_rollback_pos = m_data->size(); - add_field(tag, pbf_wire_type::length_delimited); - m_data->append(std::size_t(reserve_bytes), '\0'); - } else { - m_rollback_pos = size_is_known; - add_length_varint(tag, pbf_length_type(size)); - reserve(size); - } - m_pos = m_data->size(); - } - - void rollback_submessage() { - protozero_assert(m_pos != 0); - protozero_assert(m_rollback_pos != size_is_known); - protozero_assert(m_data); - m_data->resize(m_rollback_pos); - m_pos = 0; - } - - void commit_submessage() { - protozero_assert(m_pos != 0); - protozero_assert(m_rollback_pos != size_is_known); - protozero_assert(m_data); - const auto length = pbf_length_type(m_data->size() - m_pos); - - protozero_assert(m_data->size() >= m_pos - reserve_bytes); - const auto n = write_varint(m_data->begin() + int64_t(m_pos) - reserve_bytes, length); - - m_data->erase(m_data->begin() + int64_t(m_pos) - reserve_bytes + n, m_data->begin() + int64_t(m_pos)); - m_pos = 0; - } - - void close_submessage() { - protozero_assert(m_data); - if (m_pos == 0 || m_rollback_pos == size_is_known) { - return; - } - if (m_data->size() - m_pos == 0) { - rollback_submessage(); - } else { - commit_submessage(); - } - } - - void add_length_varint(pbf_tag_type tag, pbf_length_type length) { - add_field(tag, pbf_wire_type::length_delimited); - add_varint(length); - } - -public: - - /** - * Create a writer using the given string as a data store. The pbf_writer - * 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. - */ - explicit pbf_writer(std::string& data) noexcept : - m_data{&data} { - } - - /** - * Create a writer without a data store. In this form the writer can not - * be used! - */ - pbf_writer() noexcept = default; - - /** - * Construct a pbf_writer for a submessage from the pbf_writer of the - * parent message. - * - * @param parent_writer The pbf_writer - * @param tag Tag (field number) of the field that will be written - * @param size Optional size of the submessage in bytes (use 0 for unknown). - * Setting this allows some optimizations but is only possible in - * a few very specific cases. - */ - 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_parent_writer->open_submessage(tag, size); - } - - /// A pbf_writer object can not be copied - pbf_writer(const pbf_writer&) = delete; - - /// A pbf_writer object can not be copied - pbf_writer& operator=(const pbf_writer&) = delete; - - /** - * A pbf_writer object can be moved. After this the other pbf_writer will - * be invalid. - */ - pbf_writer(pbf_writer&& other) noexcept : - m_data{other.m_data}, - m_parent_writer{other.m_parent_writer}, - m_rollback_pos{other.m_rollback_pos}, - m_pos{other.m_pos} { - other.m_data = nullptr; - other.m_parent_writer = nullptr; - other.m_rollback_pos = 0; - other.m_pos = 0; - } - - /** - * A pbf_writer object can be moved. After this the other pbf_writer will - * be invalid. - */ - pbf_writer& operator=(pbf_writer&& other) noexcept { - m_data = other.m_data; - m_parent_writer = other.m_parent_writer; - m_rollback_pos = other.m_rollback_pos; - m_pos = other.m_pos; - other.m_data = nullptr; - other.m_parent_writer = nullptr; - other.m_rollback_pos = 0; - other.m_pos = 0; - return *this; - } - - ~pbf_writer() noexcept { - try { - if (m_parent_writer != nullptr) { - m_parent_writer->close_submessage(); - } - } catch (...) { - // This try/catch is used to make the destructor formally noexcept. - // close_submessage() is not noexcept, but will not throw the way - // it is called here, so we are good. But to be paranoid, call... - std::terminate(); - } - } - - /** - * Check if this writer is valid. A writer is invalid if it was default - * constructed, moved from, or if commit() has been called on it. - * Otherwise it is valid. - */ - bool valid() const noexcept { - return m_data != nullptr; - } - - /** - * 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, - * but additional memory that should be reserved. - * - * @param size Number of bytes to reserve in underlying message store. - */ - void reserve(std::size_t size) { - protozero_assert(m_data); - m_data->reserve(m_data->size() + size); - } - - /** - * Commit this submessage. This does the same as when the pbf_writer - * goes out of scope and is destructed. - * - * @pre Must be a pbf_writer of a submessage, ie one opened with the - * pbf_writer constructor taking a parent message. - * @post The pbf_writer is invalid and can't be used any more. - */ - void commit() { - protozero_assert(m_parent_writer && "you can't call commit() on a pbf_writer without a parent"); - protozero_assert(m_pos == 0 && "you can't call commit() on a pbf_writer that has an open nested submessage"); - m_parent_writer->close_submessage(); - m_parent_writer = nullptr; - m_data = nullptr; - } - - /** - * 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. - * @post The pbf_writer is invalid and can't be used any more. - */ - 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(); - m_parent_writer = nullptr; - m_data = nullptr; - } - - ///@{ - /** - * @name Scalar field writer functions - */ - - /** - * Add "bool" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - 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); - m_data->append(1, char(value)); - } - - /** - * Add "enum" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_enum(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "int32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_int32(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "sint32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sint32(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, encode_zigzag32(value)); - } - - /** - * Add "uint32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_uint32(pbf_tag_type tag, uint32_t value) { - add_tagged_varint(tag, value); - } - - /** - * Add "int64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_int64(pbf_tag_type tag, int64_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "sint64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sint64(pbf_tag_type tag, int64_t value) { - add_tagged_varint(tag, encode_zigzag64(value)); - } - - /** - * Add "uint64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_uint64(pbf_tag_type tag, uint64_t value) { - add_tagged_varint(tag, value); - } - - /** - * Add "fixed32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_fixed32(pbf_tag_type tag, uint32_t value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "sfixed32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sfixed32(pbf_tag_type tag, int32_t value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "fixed64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_fixed64(pbf_tag_type tag, uint64_t value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "sfixed64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sfixed64(pbf_tag_type tag, int64_t value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "float" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_float(pbf_tag_type tag, float value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "double" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_double(pbf_tag_type tag, double value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - 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::max()); - add_length_varint(tag, pbf_length_type(size)); - m_data->append(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 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()); - } - - /** - * Add "bytes" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag (field number) of the field - * @param value Pointer to zero-delimited value to be written - */ - void add_bytes(pbf_tag_type tag, const char* value) { - add_bytes(tag, value, std::strlen(value)); - } - - /** - * Add "bytes" field to data using vectored input. All the data in the - * 2nd and further arguments is "concatenated" with only a single copy - * into the final buffer. - * - * This will work with objects of any type supporting the data() and - * size() methods like std::string or protozero::data_view. - * - * Example: - * @code - * std::string data1 = "abc"; - * std::string data2 = "xyz"; - * writer.add_bytes_vectored(1, data1, data2); - * @endcode - * - * @tparam Ts List of types supporting data() and size() methods. - * @param tag Tag (field number) of the field - * @param values List of objects of types Ts with data to be appended. - */ - template - void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) { - 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); - size_t sum_size = 0; - (void)std::initializer_list{sum_size += values.size()...}; - protozero_assert(sum_size <= std::numeric_limits::max()); - add_length_varint(tag, pbf_length_type(sum_size)); - m_data->reserve(m_data->size() + sum_size); - (void)std::initializer_list{(m_data->append(values.data(), values.size()), 0)...}; - } - - /** - * Add "string" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_string(pbf_tag_type tag, const char* value, std::size_t size) { - add_bytes(tag, 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 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()); - } - - /** - * Add "string" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - */ - void add_string(pbf_tag_type tag, const char* value) { - add_bytes(tag, value, std::strlen(value)); - } - - /** - * Add "message" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to message to be written - * @param size Length of the message - */ - void add_message(pbf_tag_type tag, const char* value, std::size_t size) { - add_bytes(tag, 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 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()); - } - - ///@} - - ///@{ - /** - * @name Repeated packed field writer functions - */ - - /** - * Add "repeated packed bool" field to data. - * - * @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 - 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 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 - 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 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 - 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 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 - 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 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 - 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 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 - 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 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 - 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 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 - void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add a "repeated packed" fixed-size field to data. The following - * fixed-size fields are available: - * - * uint32_t -> repeated packed fixed32 - * int32_t -> repeated packed sfixed32 - * uint64_t -> repeated packed fixed64 - * int64_t -> repeated packed sfixed64 - * double -> repeated packed double - * float -> repeated packed float - * - * @tparam ValueType One of the following types: (u)int32/64_t, double, float. - * @tparam InputIterator A type satisfying the InputIterator concept. - * @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 - void add_packed_fixed(pbf_tag_type tag, InputIterator first, InputIterator last) { - static_assert(std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value, "Only some types are allowed"); - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed fixed32" field to data. - * - * @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 - void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed sfixed32" field to data. - * - * @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 - void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed fixed64" field to data. - * - * @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 - void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed sfixed64" field to data. - * - * @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 - void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed float" field to data. - * - * @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 - void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - /** - * Add "repeated packed double" field to data. - * - * @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 - void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category{}); - } - - ///@} - - template friend class detail::packed_field_varint; - template friend class detail::packed_field_svarint; - template friend class detail::packed_field_fixed; - -}; // class pbf_writer - /** - * Swap two pbf_writer objects. - * - * @param lhs First object. - * @param rhs Second object. + * Specialization of basic_pbf_writer using std::string as buffer type. */ -inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept { - lhs.swap(rhs); -} - -namespace detail { - - class packed_field { - - protected: - - pbf_writer m_writer{}; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors) - - public: - - packed_field(const packed_field&) = delete; - packed_field& operator=(const packed_field&) = delete; - - packed_field(packed_field&&) noexcept = default; - packed_field& operator=(packed_field&&) noexcept = default; - - packed_field() = default; - - packed_field(pbf_writer& parent_writer, pbf_tag_type tag) : - m_writer{parent_writer, tag} { - } - - packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : - m_writer{parent_writer, tag, size} { - } - - ~packed_field() noexcept = default; - - bool valid() const noexcept { - return m_writer.valid(); - } - - void commit() { - m_writer.commit(); - } - - void rollback() { - m_writer.rollback(); - } - - }; // class packed_field - - template - class packed_field_fixed : public packed_field { - - public: - - packed_field_fixed() : - packed_field{} { - } - - template - packed_field_fixed(pbf_writer& parent_writer, P tag) : - packed_field{parent_writer, static_cast(tag)} { - } - - template - packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) : - packed_field{parent_writer, static_cast(tag), size * sizeof(T)} { - } - - void add_element(T value) { - m_writer.add_fixed(value); - } - - }; // class packed_field_fixed - - template - class packed_field_varint : public packed_field { - - public: - - packed_field_varint() : - packed_field{} { - } - - template - packed_field_varint(pbf_writer& parent_writer, P tag) : - packed_field{parent_writer, static_cast(tag)} { - } - - void add_element(T value) { - m_writer.add_varint(uint64_t(value)); - } - - }; // class packed_field_varint - - template - class packed_field_svarint : public packed_field { - - public: - - packed_field_svarint() : - packed_field{} { - } - - template - packed_field_svarint(pbf_writer& parent_writer, P tag) : - packed_field{parent_writer, static_cast(tag)} { - } - - void add_element(T value) { - m_writer.add_varint(encode_zigzag64(value)); - } - - }; // class packed_field_svarint - -} // end namespace detail +using pbf_writer = basic_pbf_writer; /// Class for generating packed repeated bool fields. -using packed_field_bool = detail::packed_field_varint; +using packed_field_bool = detail::packed_field_varint; /// Class for generating packed repeated enum fields. -using packed_field_enum = detail::packed_field_varint; +using packed_field_enum = detail::packed_field_varint; /// Class for generating packed repeated int32 fields. -using packed_field_int32 = detail::packed_field_varint; +using packed_field_int32 = detail::packed_field_varint; /// Class for generating packed repeated sint32 fields. -using packed_field_sint32 = detail::packed_field_svarint; +using packed_field_sint32 = detail::packed_field_svarint; /// Class for generating packed repeated uint32 fields. -using packed_field_uint32 = detail::packed_field_varint; +using packed_field_uint32 = detail::packed_field_varint; /// Class for generating packed repeated int64 fields. -using packed_field_int64 = detail::packed_field_varint; +using packed_field_int64 = detail::packed_field_varint; /// Class for generating packed repeated sint64 fields. -using packed_field_sint64 = detail::packed_field_svarint; +using packed_field_sint64 = detail::packed_field_svarint; /// Class for generating packed repeated uint64 fields. -using packed_field_uint64 = detail::packed_field_varint; +using packed_field_uint64 = detail::packed_field_varint; /// Class for generating packed repeated fixed32 fields. -using packed_field_fixed32 = detail::packed_field_fixed; +using packed_field_fixed32 = detail::packed_field_fixed; /// Class for generating packed repeated sfixed32 fields. -using packed_field_sfixed32 = detail::packed_field_fixed; +using packed_field_sfixed32 = detail::packed_field_fixed; /// Class for generating packed repeated fixed64 fields. -using packed_field_fixed64 = detail::packed_field_fixed; +using packed_field_fixed64 = detail::packed_field_fixed; /// Class for generating packed repeated sfixed64 fields. -using packed_field_sfixed64 = detail::packed_field_fixed; +using packed_field_sfixed64 = detail::packed_field_fixed; /// Class for generating packed repeated float fields. -using packed_field_float = detail::packed_field_fixed; +using packed_field_float = detail::packed_field_fixed; /// Class for generating packed repeated double fields. -using packed_field_double = detail::packed_field_fixed; +using packed_field_double = detail::packed_field_fixed; } // end namespace protozero diff -Nru protozero-1.6.8/include/protozero/types.hpp protozero-1.7.0/include/protozero/types.hpp --- protozero-1.6.8/include/protozero/types.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/types.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,7 +16,7 @@ * @brief Contains the declaration of low-level types used in the pbf format. */ -#include +#include "config.hpp" #include #include diff -Nru protozero-1.6.8/include/protozero/varint.hpp protozero-1.7.0/include/protozero/varint.hpp --- protozero-1.6.8/include/protozero/varint.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/varint.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -16,7 +16,8 @@ * @brief Contains low-level varint and zigzag encoding and decoding functions. */ -#include +#include "buffer_tmpl.hpp" +#include "exception.hpp" #include @@ -31,15 +32,15 @@ // from https://github.com/facebook/folly/blob/master/folly/Varint.h inline uint64_t decode_varint_impl(const char** data, const char* end) { - const auto begin = reinterpret_cast(*data); - const auto iend = reinterpret_cast(end); + const auto* begin = reinterpret_cast(*data); + const auto* iend = reinterpret_cast(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) & 0x7fU) ); if (b >= 0) { break; } + int64_t b = *p++; + val = ((uint64_t(b) & 0x7fU) ); if (b >= 0) { break; } b = *p++; val |= ((uint64_t(b) & 0x7fU) << 7U); if (b >= 0) { break; } b = *p++; val |= ((uint64_t(b) & 0x7fU) << 14U); if (b >= 0) { break; } b = *p++; val |= ((uint64_t(b) & 0x7fU) << 21U); if (b >= 0) { break; } @@ -110,8 +111,8 @@ * before the end of the varint. */ inline void skip_varint(const char** data, const char* end) { - const auto begin = reinterpret_cast(*data); - const auto iend = reinterpret_cast(end); + const auto* begin = reinterpret_cast(*data); + const auto* iend = reinterpret_cast(end); const int8_t* p = begin; while (p != iend && *p < 0) { @@ -140,6 +141,7 @@ * @param value The integer that will be encoded. * @returns the number of bytes written * @throws Any exception thrown by increment or dereference operator on data. + * @deprecated Use add_varint_to_buffer() instead. */ template inline int write_varint(T data, uint64_t value) { @@ -150,7 +152,45 @@ value >>= 7U; ++n; } - *data++ = char(value); + *data = char(value); + + return n; +} + +/** + * Varint encode a 64 bit integer. + * + * @tparam TBuffer A buffer type. + * @param buffer Output buffer the varint will be written to. + * @param value The integer that will be encoded. + * @returns the number of bytes written + * @throws Any exception thrown by calling the buffer_push_back() function. + */ +template +inline void add_varint_to_buffer(TBuffer* buffer, uint64_t value) { + while (value >= 0x80U) { + buffer_customization::push_back(buffer, char((value & 0x7fU) | 0x80U)); + value >>= 7U; + } + buffer_customization::push_back(buffer, char(value)); +} + +/** + * Varint encode a 64 bit integer. + * + * @param data Where to add the varint. There must be enough space available! + * @param value The integer that will be encoded. + * @returns the number of bytes written + */ +inline int add_varint_to_buffer(char* data, uint64_t value) noexcept { + int n = 1; + + while (value >= 0x80U) { + *data++ = char((value & 0x7fU) | 0x80U); + value >>= 7U; + ++n; + } + *data = char(value); return n; } diff -Nru protozero-1.6.8/include/protozero/version.hpp protozero-1.7.0/include/protozero/version.hpp --- protozero-1.6.8/include/protozero/version.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/include/protozero/version.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -20,15 +20,15 @@ #define PROTOZERO_VERSION_MAJOR 1 /// The minor version number -#define PROTOZERO_VERSION_MINOR 6 +#define PROTOZERO_VERSION_MINOR 7 /// The patch number -#define PROTOZERO_VERSION_PATCH 8 +#define PROTOZERO_VERSION_PATCH 0 /// The complete version number #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) /// Version number as string -#define PROTOZERO_VERSION_STRING "1.6.8" +#define PROTOZERO_VERSION_STRING "1.7.0" #endif // PROTOZERO_VERSION_HPP diff -Nru protozero-1.6.8/README.md protozero-1.7.0/README.md --- protozero-1.6.8/README.md 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/README.md 2020-06-08 07:25:41.000000000 +0000 @@ -56,9 +56,9 @@ * Read the [upgrading instructions](UPGRADING.md) if you are upgrading from an older version of Protozero. -The build process will also build the Doxygen-based reference documentation -if you have [Doxygen](http://www.stack.nl/~dimitri/doxygen/) installed. Then -open `doc/html/index.html` in your browser to read it. +The build process will also build the Doxygen-based reference documentation if +you have Doxygen installed. Then open `doc/html/index.html` in your browser to +read it. ## Endianness diff -Nru protozero-1.6.8/test/catch/catch.hpp protozero-1.7.0/test/catch/catch.hpp --- protozero-1.6.8/test/catch/catch.hpp 2019-08-15 07:15:10.000000000 +0000 +++ protozero-1.7.0/test/catch/catch.hpp 2020-06-08 07:25:41.000000000 +0000 @@ -1,17 +1,21 @@ /* - * Catch v1.12.0 - * Generated: 2018-01-11 21:56:34.893972 + * Catch v2.12.1 + * Generated: 2020-04-21 19:29:20.964532 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp -#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 12 +#define CATCH_VERSION_PATCH 1 #ifdef __clang__ # pragma clang system_header @@ -19,36 +23,66 @@ # pragma GCC system_header #endif -// #included from: internal/catch_suppress_warnings.h +// start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wparentheses" + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif +// end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS #endif +// end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -56,93 +90,110 @@ # endif #endif -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED +// start catch_user_interfaces.h -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED +namespace Catch { + unsigned int rngSeed(); +} -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? +// start catch_common.h -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? +// start catch_compiler_capabilities.h -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? +// Detect a number of compiler features - by compiler +// The following features are defined: +// // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER # endif -# if __cplusplus >= 201402L -# define CATCH_CPP14_OR_GREATER +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER # endif #endif -#ifdef __clang__ +#if defined(__cpp_lib_uncaught_exceptions) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ # endif -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) -# endif +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# endif +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ @@ -151,225 +202,273 @@ #endif //////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -#endif // __BORLANDC__ +# endif +#endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ +// Visual C++ +#if defined(_MSC_VER) -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -#endif // __DMC__ +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below +#endif // _MSC_VER -#endif // __GNUC__ +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#endif - -#endif // _MSC_VER - //////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) +//////////////////////////////////////////////////////////////////////////////// -// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, -// because __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif -#endif - //////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif -#endif // __cplusplus >= 201103L +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC #endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif -# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) #else -# define CATCH_OVERRIDE +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) #endif -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif +// end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER @@ -378,95 +477,48 @@ # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) +#include +#include +#include -#include -#include +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - struct SourceLineInfo { - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; @@ -474,24 +526,17 @@ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { @@ -499,363 +544,812 @@ } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +// end catch_common.h namespace Catch { - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED +// start catch_interfaces_testcase.h -#include +#include namespace Catch { - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); }; - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); + class TestCase; + struct IConfig; - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; - IGeneratorsForTest* createGeneratorsForTest(); + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); -} // end namespace Catch +} -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED +// end catch_interfaces_testcase.h +// start catch_stringref.h -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif +#include +#include +#include +#include namespace Catch { - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } + using size_type = std::size_t; + using const_iterator = const char*; private: - T* m_p; - }; + static constexpr char const* const s_empty = ""; - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; - template - struct SharedImpl : T { + StringRef( char const* rawChars ) noexcept; - SharedImpl() : m_rc( 0 ){} + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} - virtual void addRef() const { - ++m_rc; + explicit operator std::string() const { + return std::string(m_start, m_size); } - virtual void release() const { - if( --m_rc == 0 ) - delete this; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); } - mutable unsigned int m_rc; - }; + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } -} // end namespace Catch + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; -namespace Catch { + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; - struct IContext - { - virtual ~IContext(); + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } }; - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); } -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED +// end catch_stringref.h +// start catch_preprocessor.hpp -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED -#include +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif -namespace Catch { +#define CATCH_REC_END(...) +#define CATCH_REC_OUT - class TestSpec; +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() - struct ITestCase : IShared { - virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); - }; +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif - class TestCase; - struct IConfig; +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template