From: Otto Moerbeek Date: Tue, 13 Oct 2020 13:50:19 +0000 (+0200) Subject: Version 1.70 of protozero X-Git-Tag: dnsdist-1.6.0-alpha0~11^2~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d509757915fa1bf6a26327a466fef6e45fb495d2;p=thirdparty%2Fpdns.git Version 1.70 of protozero --- diff --git a/builder-support/debian/recursor/debian-buster/copyright b/builder-support/debian/recursor/debian-buster/copyright index cb8c1422b0..253fcfcac6 100644 --- a/builder-support/debian/recursor/debian-buster/copyright +++ b/builder-support/debian/recursor/debian-buster/copyright @@ -46,6 +46,10 @@ Files: ext/luawrapper/* Copyright: 2013, Pierre KRIEGER License: BSD-3 +Files: ext/protozero/* +Copyright: protozero copyright (c) Mapbox. +License: Protozero + License: BSD-3 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -105,3 +109,27 @@ License: GPL-2 On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. + +License: Protozero + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/protozero/CHANGELOG.md b/ext/protozero/CHANGELOG.md new file mode 100644 index 0000000000..cfdb6a6478 --- /dev/null +++ b/ext/protozero/CHANGELOG.md @@ -0,0 +1,411 @@ + +# Changelog + +All notable changes to this project will be documented in this file. +The format is based on [Keep a Changelog](http://keepachangelog.com/) +This project adheres to [Semantic Versioning](http://semver.org/). + +## [unreleased] - + +### Added + +### Changed + +### 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 + +- Various code cleanups due to clang-tidy warnings. + +### Fixed + +- Made `data_view::compare` noexcept. + + +## [1.6.7] - 2018-02-21 + +### Fixed + +- Signed-unsigned comparison on 32 bit systems. + + +## [1.6.6] - 2018-02-20 + +### Fixed + +- Fixed several place with possible undefined behaviour. + + +## [1.6.5] - 2018-02-05 + +### Fixed + +- Avoid UB: Do not calculate pointer outside array bounds. +- Specify proto2 syntax in .proto files to appease protoc. + + +## [1.6.4] - 2018-11-08 + +### Added + +- Add function `data()` to get the not yet read data from a `pbf_reader`. +- New `add_packed_fixed()` template function for `pbf_writer`. +- New `length_of_varint()` helper function calculates how long a varint + would be for a specified value. + +### Changed + +- More consistent implementation of operators as free friend functions. + +### Fixed + +- Fixed some zigzag encoding tests on MSVC. +- Add extra cast so we do an xor with unsigned ints. +- No more bitwise operations on signed integers in varint decoder. +- No more bitwise operations on signed integers in zigzag encoder/decoder. + + +## [1.6.3] - 2018-07-17 + +### Changed + +- Moved `byteswap_inplace` functions from detail into protozero namespace. + They can be useful outsize protozero. +- More asserts and unit tests and small cleanups. + + +## [1.6.2] - 2018-03-09 + +### Changed + +- Update included catch.hpp to v1.12.0. +- Move basic unit tests into their own directory (`test/unit`). +- Improved clang-tidy config and fixed some code producing warnings. + +### Fixed + +- Buffer overflow in pbf-decoder tool. + + +## [1.6.1] - 2017-11-16 + +### Added + +- Document internal handling of varints. +- Add aliases for fixed iterators, too. + +### Changed + +- The `const_fixed_iterator` is now a random access iterator making code + using it potentially more performant (for instance when using + `std::distance`) +- Overloads `std::distance` for the varint and svarint iterators. This is + better than the workaround with the `rage_size` function used before. + +### Fixed + +- Rename `.proto` files in some tests to be unique. This solves a problem + when building with newer versions of the Google Protobuf library. +- Floating point comparisons in tests are now always correctly done using + `Approx()`. + + +## [1.6.0] - 2017-10-24 + +### Added + +- Comparison functions (<, <=, >, >=) for `data_view`. Allows use in `std::map` + for instance. +- Tool `pbf-decoder` for decoding raw messages. This has limited use for + normal users, but it can be used for fuzzing. + +### Changed + +- Protozero now uses CMake to build the tests etc. This does not affect + simple users of the library, but if you are using CMake yourself you might + want to use the `cmake/FindProtozero.cmake` module provided. The README + contains more information about build options. +- Moved `data_view` class from `types.hpp` into its own header file + `data_view.hpp`. +- Implementation of the `const_fixed_iterator` to use only a single pointer + instead of two. +- Made `operator==` and `operator!=` on `data_view` constexpr. +- The `pbf_reader` constructor taking a `std::pair` is deprecated. Use one + of the other constructors instead. + +### Fixed + +- Varints where the last byte was larger than what would fit in 64bit were + triggering undefined behaviour. This can only happen when the message + being decoded was corrupt in some way. +- Do not assert when reading too long varints for bools any more. A valid + encoder should never generate varints with more than one byte for bools, + but if they are longer that's not really a problem, so just handle it. +- Throw exception if the length of a packed repeated field of a fixed-length + type is invalid. The length must always be a multiple of the size of the + underlying type. This can only happen if the data is corrupted in some way, + a valid encoder would never generate data like this. +- Throw an exception when reading invalid tags. This can only happen if the + data is corrupted in some way, a valid encoder would never generate invalid + tags. + + +## [1.5.3] - 2017-09-22 + +### Added + +- More documentation. +- New `size()` method on iterator range used for packed repeated fields to + find out how many elements there are in the range. This is much faster + compared to the `std::difference()` call you had to do before, because the + varints don't have to be fully decoded. See [Advanced + Topics](doc/advanced.md) for details. + +### Changed + +- Updated clang-tidy settings in Makefiles and fixed a lot of minor issues + reported by clang-tidy. +- Update included catch.hpp to version 1.10.0. +- Miscellaneous code cleanups. +- Support for invalid state in `pbf_writer` and `packed_repeated_fields`. + This fixes move construction and move assignement in `pbf_writer` and + disables the copy construction and copy assignement which don't have + clear semantics. It introduces an invalid or empty state in the + `pbf_writer`, `pbf_builder`, and `packed_repeated_fields` classes used for + default-constructed, moved from, or committed objects. There is a new + `commit()` function for `pbf_writer` and the `packed_repeated_fields` which + basically does the same as the destructor but can be called explicitly. + +### Fixed + +- The `empty()` method of the iterator range now returns a `bool` instead of + a `size_t`. + + +## [1.5.2] - 2017-06-30 + +### Added + +- Add missing two-parameter version of `pbf_message::next()` function. +- Add `data_view::empty()` function. +- Add missing versions of `add_bytes()`, `add_string()`, and `add_message()` + to `pbf_builder`. + +### Changed + +- Clarify include file usage in tutorial. +- Updated included Catch unit test framework to version 1.9.6 and updated + tests to work with the current version. +- Make some constructors explicit (best practice to avoid silent conversions). + +### Fixed + +- Important bugfix in `data_view` equality operator. The equality operator is + actually never used in the protozero code itself, but users of protozero + might use it. This is a serious bug that could lead to buffer overrun type + problems. + + +## [1.5.1] - 2017-01-14 + +### Added + +- Better documentation for `tag_and_type()` in doc/advanced.md. + +### Fixed + +- Fixed broken "make doc" build. + + +## [1.5.0] - 2017-01-12 + +### Added + +- Add `add_bytes_vectored()` methods to `pbf_writer` and `pbf_builder`. This + allows single-copy scatter-gather type adding of data that has been prepared + in pieces to a protobuf message. +- New functions to check the tag and wire type at the same time: Two parameter + version of `pbf_reader::next()` and `pbf_reader::tag_and_type()` can be used + together with the free function `tag_and_type()` to easily and quickly check + that not only the tag but also the wire type is correct for a field. + +### Changed + +- `packed_field_*` classes now work with `pbf_builder`. +- Reorganized documentation. Advanced docs are now under doc/advanced.md. + +### Fixed + +- `packed_field` class is now non-copyable because data can get corrupted if + you copy it around. +- Comparison operators of `data_view` now have const& parameters. +- Make zigzag encoding/decoding functions constexpr. + + +## [1.4.5] - 2016-11-18 + +### Fixed + +- Undefined behaviour in packed fixed iterator. As a result, the macro + `PROTOZERO_DO_NOT_USE_BARE_POINTER` is not used any more. + + +## [1.4.4] - 2016-11-15 + +### Fixed + +- Byteswap implementation. + + +## [1.4.3] - 2016-11-15 + +### Fixed + +- Undefined behaviour in byte swapping code. +- Rename some parameters to avoid "shadow" warning from some compilers. + + +## [1.4.2] - 2016-08-27 + +### Fixed + +- Compile fix: Variable shadowing. + + +## [1.4.1] - 2016-08-21 + +### Fixed + +- GCC 4.8 compile fixed + +### Added + +- New ability to dynamically require the module as a node module to ease + building against from other node C++ modules. + +## [1.4.0] - 2016-07-22 + +### Changed + +- Use more efficient new `skip_varint()` function when iterating over + packed varints. +- Split `decode_varint()` function into two functions speeding up the + common case where a varint is only one byte long. +- Introduce new class `iterator_range` used instead of `std::pair` of + iterators. This way the objects can be used in range-based for loops. + Read UPGRADING.md for details. +- Introduce new class `data_view` and functions using and returning it. + Read UPGRADING.md for details. + + +## [1.3.0] - 2016-02-18 + +### Added + +- Added `config.hpp` header which now includes all the macro magic to + configure the library for different architectures etc. +- New way to create repeated packed fields without using an iterator. +- Add `rollback()` function to `pbf_writer` for "manual" rollback. + +### Changed + +- Various test and documentation cleanups. +- Rename `pbf_types.hpp` to `types.hpp`. + + +## [1.2.3] - 2015-11-30 + +### Added + +- Added `config.hpp` header which now includes all the macro magic to + configure the library for different architectures etc. + +### Fixed + +- Unaligned access to floats/doubles on some ARM architectures. + + +## [1.2.2] - 2015-10-13 + +### Fixed + +- Fix the recently broken writing of bools on big-endian architectures. + + +## [1.2.1] - 2015-10-12 + +### Fixed + +- Removed unneeded code (1-byte "swap") which lead to test failures. + + +## [1.2.0] - 2015-10-08 + +### Added + +- `pbf_message` and `pbf_builder` template classes wrapping `pbf_reader` + and `pbf_writer`, respectively. The new classes are the preferred + interface now. + +### Changed + +- Improved byte swapping operation. +- Detect some types of data corruption earlier and throw. + + +## [1.1.0] - 2015-08-22 + +### Changed + +- Make pbf reader and writer code endianess-aware. + + +[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 +[1.6.5]: https://github.com/osmcode/libosmium/compare/v1.6.4...v1.6.5 +[1.6.4]: https://github.com/osmcode/libosmium/compare/v1.6.3...v1.6.4 +[1.6.3]: https://github.com/osmcode/libosmium/compare/v1.6.2...v1.6.3 +[1.6.2]: https://github.com/osmcode/libosmium/compare/v1.6.1...v1.6.2 +[1.6.1]: https://github.com/osmcode/libosmium/compare/v1.6.0...v1.6.1 +[1.6.0]: https://github.com/osmcode/libosmium/compare/v1.5.3...v1.6.0 +[1.5.3]: https://github.com/osmcode/libosmium/compare/v1.5.2...v1.5.3 +[1.5.2]: https://github.com/osmcode/libosmium/compare/v1.5.1...v1.5.2 +[1.5.1]: https://github.com/osmcode/libosmium/compare/v1.5.0...v1.5.1 +[1.5.0]: https://github.com/osmcode/libosmium/compare/v1.4.5...v1.5.0 +[1.4.5]: https://github.com/osmcode/libosmium/compare/v1.4.4...v1.4.5 +[1.4.4]: https://github.com/osmcode/libosmium/compare/v1.4.3...v1.4.4 +[1.4.3]: https://github.com/osmcode/libosmium/compare/v1.4.2...v1.4.3 +[1.4.2]: https://github.com/osmcode/libosmium/compare/v1.4.1...v1.4.2 +[1.4.1]: https://github.com/osmcode/libosmium/compare/v1.4.0...v1.4.1 +[1.4.0]: https://github.com/osmcode/libosmium/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/osmcode/libosmium/compare/v1.2.3...v1.3.0 +[1.2.3]: https://github.com/osmcode/libosmium/compare/v1.2.2...v1.2.3 +[1.2.2]: https://github.com/osmcode/libosmium/compare/v1.2.1...v1.2.2 +[1.2.1]: https://github.com/osmcode/libosmium/compare/v1.2.0...v1.2.1 +[1.2.0]: https://github.com/osmcode/libosmium/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/osmcode/libosmium/compare/v1.0.0...v1.1.0 + diff --git a/ext/protozero/LICENSE.from_folly b/ext/protozero/LICENSE.from_folly new file mode 100644 index 0000000000..f433b1a53f --- /dev/null +++ b/ext/protozero/LICENSE.from_folly @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/ext/protozero/LICENSE.md b/ext/protozero/LICENSE.md new file mode 100644 index 0000000000..d0b30117c1 --- /dev/null +++ b/ext/protozero/LICENSE.md @@ -0,0 +1,24 @@ +protozero copyright (c) Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/protozero/include/protozero/basic_pbf_builder.hpp b/ext/protozero/include/protozero/basic_pbf_builder.hpp new file mode 100644 index 0000000000..0ede726faf --- /dev/null +++ b/ext/protozero/include/protozero/basic_pbf_builder.hpp @@ -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 --git a/ext/protozero/include/protozero/basic_pbf_writer.hpp b/ext/protozero/include/protozero/basic_pbf_writer.hpp new file mode 100644 index 0000000000..f167c4d1d5 --- /dev/null +++ b/ext/protozero/include/protozero/basic_pbf_writer.hpp @@ -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 --git a/ext/protozero/include/protozero/buffer_fixed.hpp b/ext/protozero/include/protozero/buffer_fixed.hpp new file mode 100644 index 0000000000..b2e6d1d270 --- /dev/null +++ b/ext/protozero/include/protozero/buffer_fixed.hpp @@ -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 --git a/ext/protozero/include/protozero/buffer_string.hpp b/ext/protozero/include/protozero/buffer_string.hpp new file mode 100644 index 0000000000..1f0f902a80 --- /dev/null +++ b/ext/protozero/include/protozero/buffer_string.hpp @@ -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 --git a/ext/protozero/include/protozero/buffer_tmpl.hpp b/ext/protozero/include/protozero/buffer_tmpl.hpp new file mode 100644 index 0000000000..ac223996d3 --- /dev/null +++ b/ext/protozero/include/protozero/buffer_tmpl.hpp @@ -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 --git a/ext/protozero/include/protozero/buffer_vector.hpp b/ext/protozero/include/protozero/buffer_vector.hpp new file mode 100644 index 0000000000..6a34b072e5 --- /dev/null +++ b/ext/protozero/include/protozero/buffer_vector.hpp @@ -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 --git a/ext/protozero/include/protozero/byteswap.hpp b/ext/protozero/include/protozero/byteswap.hpp new file mode 100644 index 0000000000..799d1795d5 --- /dev/null +++ b/ext/protozero/include/protozero/byteswap.hpp @@ -0,0 +1,99 @@ +#ifndef PROTOZERO_BYTESWAP_HPP +#define PROTOZERO_BYTESWAP_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 byteswap.hpp + * + * @brief Contains functions to swap bytes in values (for different endianness). + */ + +#include "config.hpp" + +#include + +namespace protozero { +namespace detail { + +inline uint32_t byteswap_impl(uint32_t value) noexcept { +#ifdef PROTOZERO_USE_BUILTIN_BSWAP + return __builtin_bswap32(value); +#else + return ((value & 0xff000000U) >> 24U) | + ((value & 0x00ff0000U) >> 8U) | + ((value & 0x0000ff00U) << 8U) | + ((value & 0x000000ffU) << 24U); +#endif +} + +inline uint64_t byteswap_impl(uint64_t value) noexcept { +#ifdef PROTOZERO_USE_BUILTIN_BSWAP + return __builtin_bswap64(value); +#else + return ((value & 0xff00000000000000ULL) >> 56U) | + ((value & 0x00ff000000000000ULL) >> 40U) | + ((value & 0x0000ff0000000000ULL) >> 24U) | + ((value & 0x000000ff00000000ULL) >> 8U) | + ((value & 0x00000000ff000000ULL) << 8U) | + ((value & 0x0000000000ff0000ULL) << 24U) | + ((value & 0x000000000000ff00ULL) << 40U) | + ((value & 0x00000000000000ffULL) << 56U); +#endif +} + +} // end namespace detail + +/// byteswap the data pointed to by ptr in-place. +inline void byteswap_inplace(uint32_t* ptr) noexcept { + *ptr = detail::byteswap_impl(*ptr); +} + +/// byteswap the data pointed to by ptr in-place. +inline void byteswap_inplace(uint64_t* ptr) noexcept { + *ptr = detail::byteswap_impl(*ptr); +} + +/// byteswap the data pointed to by ptr in-place. +inline void byteswap_inplace(int32_t* ptr) noexcept { + 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); + *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); + *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); + *bptr = detail::byteswap_impl(*bptr); +} + +namespace detail { + + // Added for backwards compatibility with any code that might use this + // function (even if it shouldn't have). Will be removed in a later + // version of protozero. + using ::protozero::byteswap_inplace; + +} // end namespace detail + +} // end namespace protozero + +#endif // PROTOZERO_BYTESWAP_HPP diff --git a/ext/protozero/include/protozero/config.hpp b/ext/protozero/include/protozero/config.hpp new file mode 100644 index 0000000000..6fc7749049 --- /dev/null +++ b/ext/protozero/include/protozero/config.hpp @@ -0,0 +1,48 @@ +#ifndef PROTOZERO_CONFIG_HPP +#define PROTOZERO_CONFIG_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. + +*****************************************************************************/ + +#include + +/** + * @file config.hpp + * + * @brief Contains macro checks for different configurations. + */ + +#define PROTOZERO_LITTLE_ENDIAN 1234 +#define PROTOZERO_BIG_ENDIAN 4321 + +// Find out which byte order the machine has. +#if defined(__BYTE_ORDER) +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN +# endif +# if (__BYTE_ORDER == __BIG_ENDIAN) +# define PROTOZERO_BYTE_ORDER PROTOZERO_BIG_ENDIAN +# endif +#else +// This probably isn't a very good default, but might do until we figure +// out something better. +# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN +#endif + +// Check whether __builtin_bswap is available +#if defined(__GNUC__) || defined(__clang__) +# define PROTOZERO_USE_BUILTIN_BSWAP +#endif + +// Wrapper for assert() used for testing +#ifndef protozero_assert +# define protozero_assert(x) assert(x) +#endif + +#endif // PROTOZERO_CONFIG_HPP diff --git a/ext/protozero/include/protozero/data_view.hpp b/ext/protozero/include/protozero/data_view.hpp new file mode 100644 index 0000000000..3ec87af34e --- /dev/null +++ b/ext/protozero/include/protozero/data_view.hpp @@ -0,0 +1,236 @@ +#ifndef PROTOZERO_DATA_VIEW_HPP +#define PROTOZERO_DATA_VIEW_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 data_view.hpp + * + * @brief Contains the implementation of the data_view class. + */ + +#include "config.hpp" + +#include +#include +#include +#include +#include + +namespace protozero { + +#ifdef PROTOZERO_USE_VIEW +using data_view = PROTOZERO_USE_VIEW; +#else + +/** + * Holds a pointer to some data and a length. + * + * This class is supposed to be compatible with the std::string_view + * that will be available in C++17. + */ +class data_view { + + const char* m_data = nullptr; + std::size_t m_size = 0; + +public: + + /** + * Default constructor. Construct an empty data_view. + */ + constexpr data_view() noexcept = default; + + /** + * Create data_view from pointer and size. + * + * @param ptr Pointer to the data. + * @param length Length of the data. + */ + constexpr data_view(const char* ptr, std::size_t length) noexcept + : m_data{ptr}, + m_size{length} { + } + + /** + * Create data_view from string. + * + * @param str String with the data. + */ + data_view(const std::string& str) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + : m_data{str.data()}, + m_size{str.size()} { + } + + /** + * Create data_view from zero-terminated string. + * + * @param ptr Pointer to the data. + */ + data_view(const char* ptr) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + : m_data{ptr}, + m_size{std::strlen(ptr)} { + } + + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(data_view& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_size, other.m_size); + } + + /// Return pointer to data. + constexpr const char* data() const noexcept { + return m_data; + } + + /// Return length of data in bytes. + constexpr std::size_t size() const noexcept { + return m_size; + } + + /// Returns true if size is 0. + constexpr bool empty() const noexcept { + return m_size == 0; + } + +#ifndef PROTOZERO_STRICT_API + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + * + * @deprecated to_string() is not available in C++17 string_view so it + * should not be used to make conversion to that class easier + * in the future. + */ + std::string to_string() const { + protozero_assert(m_data); + return {m_data, m_size}; + } +#endif + + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + */ + explicit operator std::string() const { + protozero_assert(m_data); + return {m_data, m_size}; + } + + /** + * Compares the contents of this object with the given other object. + * + * @returns 0 if they are the same, <0 if this object is smaller than + * the other or >0 if it is larger. If both objects have the + * same size returns <0 if this object is lexicographically + * before the other, >0 otherwise. + * + * @pre Must not be default constructed data_view. + */ + int compare(data_view other) const noexcept { + assert(m_data && other.m_data); + const int cmp = std::memcmp(data(), other.data(), + std::min(size(), other.size())); + if (cmp == 0) { + if (size() == other.size()) { + return 0; + } + return size() < other.size() ? -1 : 1; + } + return cmp; + } + +}; // class data_view + +/** + * Swap two data_view objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(data_view& lhs, data_view& rhs) noexcept { + lhs.swap(rhs); +} + +/** + * Two data_view instances are equal if they have the same size and the + * same content. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline constexpr bool operator==(const data_view lhs, const data_view rhs) noexcept { + return lhs.size() == rhs.size() && + std::equal(lhs.data(), lhs.data() + lhs.size(), rhs.data()); +} + +/** + * Two data_view instances are not equal if they have different sizes or the + * content differs. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline constexpr bool operator!=(const data_view lhs, const data_view rhs) noexcept { + return !(lhs == rhs); +} + +/** + * Returns true if lhs.compare(rhs) < 0. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator<(const data_view lhs, const data_view rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +/** + * Returns true if lhs.compare(rhs) <= 0. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator<=(const data_view lhs, const data_view rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +/** + * Returns true if lhs.compare(rhs) > 0. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator>(const data_view lhs, const data_view rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +/** + * Returns true if lhs.compare(rhs) >= 0. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator>=(const data_view lhs, const data_view rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +#endif + +} // end namespace protozero + +#endif // PROTOZERO_DATA_VIEW_HPP diff --git a/ext/protozero/include/protozero/exception.hpp b/ext/protozero/include/protozero/exception.hpp new file mode 100644 index 0000000000..a3cd0f15db --- /dev/null +++ b/ext/protozero/include/protozero/exception.hpp @@ -0,0 +1,101 @@ +#ifndef PROTOZERO_EXCEPTION_HPP +#define PROTOZERO_EXCEPTION_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 exception.hpp + * + * @brief Contains the exceptions used in the protozero library. + */ + +#include + +/** + * @brief All parts of the protozero header-only library are in this namespace. + */ +namespace protozero { + +/** + * All exceptions explicitly thrown by the functions of the protozero library + * derive from this exception. + */ +struct exception : std::exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "pbf exception"; + } +}; + +/** + * This exception is thrown when parsing a varint thats larger than allowed. + * This should never happen unless the data is corrupted. + */ +struct varint_too_long_exception : exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "varint too long exception"; + } +}; + +/** + * This exception is thrown when the wire type of a pdf field is unknown. + * This should never happen unless the data is corrupted. + */ +struct unknown_pbf_wire_type_exception : exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "unknown pbf field type exception"; + } +}; + +/** + * This exception is thrown when we are trying to read a field and there + * are not enough bytes left in the buffer to read it. Almost all functions + * of the pbf_reader class can throw this exception. + * + * This should never happen unless the data is corrupted or you have + * initialized the pbf_reader object with incomplete data. + */ +struct end_of_buffer_exception : exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "end of buffer exception"; + } +}; + +/** + * This exception is thrown when a tag has an invalid value. Tags must be + * unsigned integers between 1 and 2^29-1. Tags between 19000 and 19999 are + * not allowed. See + * https://developers.google.com/protocol-buffers/docs/proto#assigning-tags + */ +struct invalid_tag_exception : exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "invalid tag exception"; + } +}; + +/** + * This exception is thrown when a length field of a packed repeated field is + * invalid. For fixed size types the length must be a multiple of the size of + * the type. + */ +struct invalid_length_exception : exception { + /// Returns the explanatory string. + const char* what() const noexcept override { + return "invalid length exception"; + } +}; + +} // end namespace protozero + +#endif // PROTOZERO_EXCEPTION_HPP diff --git a/ext/protozero/include/protozero/iterators.hpp b/ext/protozero/include/protozero/iterators.hpp new file mode 100644 index 0000000000..ee8ef8ecfd --- /dev/null +++ b/ext/protozero/include/protozero/iterators.hpp @@ -0,0 +1,481 @@ +#ifndef PROTOZERO_ITERATORS_HPP +#define PROTOZERO_ITERATORS_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file iterators.hpp + * + * @brief Contains the iterators for access to packed repeated fields. + */ + +#include "config.hpp" +#include "varint.hpp" + +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN +# include +#endif + +#include +#include +#include +#include + +namespace protozero { + +/** + * A range of iterators based on std::pair. Created from beginning and + * end iterators. Used as a return type from some pbf_reader methods + * that is easy to use with range-based for loops. + */ +template > +class iterator_range : +#ifdef PROTOZERO_STRICT_API + protected +#else + public +#endif + P { + +public: + + /// The type of the iterators in this range. + using iterator = T; + + /// The value type of the underlying iterator. + using value_type = typename std::iterator_traits::value_type; + + /** + * Default constructor. Create empty iterator_range. + */ + constexpr iterator_range() : + P{iterator{}, iterator{}} { + } + + /** + * Create iterator range from two iterators. + * + * @param first_iterator Iterator to beginning of range. + * @param last_iterator Iterator to end of range. + */ + constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) : + P{std::forward(first_iterator), + std::forward(last_iterator)} { + } + + /// Return iterator to beginning of range. + constexpr iterator begin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator end() const noexcept { + return this->second; + } + + /// Return iterator to beginning of range. + constexpr iterator cbegin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator cend() const noexcept { + return this->second; + } + + /** + * Return true if this range is empty. + * + * Complexity: Constant. + */ + constexpr bool empty() const noexcept { + return begin() == end(); + } + + /** + * Get the size of the range, ie the number of elements it contains. + * + * Complexity: Constant or linear depending on the underlaying iterator. + */ + std::size_t size() const noexcept { + return static_cast(std::distance(begin(), end())); + } + + /** + * Get element at the beginning of the range. + * + * @pre Range must not be empty. + */ + value_type front() const { + protozero_assert(!empty()); + return *(this->first); + } + + /** + * Advance beginning of range by one. + * + * @pre Range must not be empty. + */ + void drop_front() { + protozero_assert(!empty()); + ++this->first; + } + + /** + * Swap the contents of this range with the other. + * + * @param other Other range to swap data with. + */ + void swap(iterator_range& other) noexcept { + using std::swap; + swap(this->first, other.first); + swap(this->second, other.second); + } + +}; // struct iterator_range + +/** + * Swap two iterator_ranges. + * + * @param lhs First range. + * @param rhs Second range. + */ +template +inline void swap(iterator_range& lhs, iterator_range& rhs) noexcept { + lhs.swap(rhs); +} + +/** + * A forward iterator used for accessing packed repeated fields of fixed + * length (fixed32, sfixed32, float, double). + */ +template +class const_fixed_iterator { + + /// Pointer to current iterator position + const char* m_data = nullptr; + +public: + + /// @cond usual iterator functions not documented + + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_fixed_iterator() noexcept = default; + + explicit const_fixed_iterator(const char* data) noexcept : + m_data{data} { + } + + const_fixed_iterator(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator(const_fixed_iterator&&) noexcept = default; + + const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; + + ~const_fixed_iterator() noexcept = default; + + value_type operator*() const noexcept { + value_type result; + std::memcpy(&result, m_data, sizeof(value_type)); +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN + byteswap_inplace(&result); +#endif + return result; + } + + const_fixed_iterator& operator++() noexcept { + m_data += sizeof(value_type); + return *this; + } + + const_fixed_iterator operator++(int) noexcept { + const const_fixed_iterator tmp{*this}; + ++(*this); + return tmp; + } + + const_fixed_iterator& operator--() noexcept { + m_data -= sizeof(value_type); + return *this; + } + + const_fixed_iterator operator--(int) noexcept { + const const_fixed_iterator tmp{*this}; + --(*this); + return tmp; + } + + friend bool operator==(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return lhs.m_data == rhs.m_data; + } + + friend bool operator!=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return !(lhs == rhs); + } + + friend bool operator<(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return lhs.m_data < rhs.m_data; + } + + friend bool operator>(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return rhs < lhs; + } + + friend bool operator<=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return !(lhs > rhs); + } + + friend bool operator>=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return !(lhs < rhs); + } + + const_fixed_iterator& operator+=(difference_type val) noexcept { + m_data += (sizeof(value_type) * val); + return *this; + } + + friend const_fixed_iterator operator+(const_fixed_iterator lhs, difference_type rhs) noexcept { + const_fixed_iterator tmp{lhs}; + tmp.m_data += (sizeof(value_type) * rhs); + return tmp; + } + + friend const_fixed_iterator operator+(difference_type lhs, const_fixed_iterator rhs) noexcept { + const_fixed_iterator tmp{rhs}; + tmp.m_data += (sizeof(value_type) * lhs); + return tmp; + } + + const_fixed_iterator& operator-=(difference_type val) noexcept { + m_data -= (sizeof(value_type) * val); + return *this; + } + + friend const_fixed_iterator operator-(const_fixed_iterator lhs, difference_type rhs) noexcept { + const_fixed_iterator tmp{lhs}; + tmp.m_data -= (sizeof(value_type) * rhs); + return tmp; + } + + friend difference_type operator-(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return static_cast(lhs.m_data - rhs.m_data) / static_cast(sizeof(T)); + } + + value_type operator[](difference_type n) const noexcept { + return *(*this + n); + } + + /// @endcond + +}; // class const_fixed_iterator + +/** + * A forward iterator used for accessing packed repeated varint fields + * (int32, uint32, int64, uint64, bool, enum). + */ +template +class const_varint_iterator { + +protected: + + /// Pointer to current iterator position + const char* m_data = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors) + + /// Pointer to end iterator position + const char* m_end = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors) + +public: + + /// @cond usual iterator functions not documented + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept { + // The "distance" between default initialized const_varint_iterator's + // is always 0. + if (!begin.m_data) { + return 0; + } + // We know that each varint contains exactly one byte with the most + // significant bit not set. We can use this to quickly figure out + // how many varints there are without actually decoding the varints. + return std::count_if(begin.m_data, end.m_data, [](char c) noexcept { + return (static_cast(c) & 0x80U) == 0; + }); + } + + const_varint_iterator() noexcept = default; + + const_varint_iterator(const char* data, const char* end) noexcept : + m_data{data}, + m_end{end} { + } + + const_varint_iterator(const const_varint_iterator&) noexcept = default; + const_varint_iterator(const_varint_iterator&&) noexcept = default; + + const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; + const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; + + ~const_varint_iterator() noexcept = default; + + value_type operator*() const { + protozero_assert(m_data); + const char* d = m_data; // will be thrown away + return static_cast(decode_varint(&d, m_end)); + } + + const_varint_iterator& operator++() { + protozero_assert(m_data); + skip_varint(&m_data, m_end); + return *this; + } + + const_varint_iterator operator++(int) { + protozero_assert(m_data); + const const_varint_iterator tmp{*this}; + ++(*this); + return tmp; + } + + bool operator==(const const_varint_iterator& rhs) const noexcept { + return m_data == rhs.m_data && m_end == rhs.m_end; + } + + bool operator!=(const const_varint_iterator& rhs) const noexcept { + return !(*this == rhs); + } + + /// @endcond + +}; // class const_varint_iterator + +/** + * A forward iterator used for accessing packed repeated svarint fields + * (sint32, sint64). + */ +template +class const_svarint_iterator : public const_varint_iterator { + +public: + + /// @cond usual iterator functions not documented + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_svarint_iterator() noexcept : + const_varint_iterator{} { + } + + const_svarint_iterator(const char* data, const char* end) noexcept : + const_varint_iterator{data, end} { + } + + const_svarint_iterator(const const_svarint_iterator&) = default; + const_svarint_iterator(const_svarint_iterator&&) noexcept = default; + + const_svarint_iterator& operator=(const const_svarint_iterator&) = default; + const_svarint_iterator& operator=(const_svarint_iterator&&) noexcept = default; + + ~const_svarint_iterator() = default; + + value_type operator*() const { + protozero_assert(this->m_data); + const char* d = this->m_data; // will be thrown away + return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); + } + + const_svarint_iterator& operator++() { + protozero_assert(this->m_data); + skip_varint(&this->m_data, this->m_end); + return *this; + } + + const_svarint_iterator operator++(int) { + protozero_assert(this->m_data); + const const_svarint_iterator tmp{*this}; + ++(*this); + return tmp; + } + + /// @endcond + +}; // class const_svarint_iterator + +} // end namespace protozero + +namespace std { + + // Specialize std::distance for all the protozero iterators. Because + // functions can't be partially specialized, we have to do this for + // every value_type we are using. + + /// @cond individual overloads do not need to be documented + + template <> + inline typename protozero::const_varint_iterator::difference_type + distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_varint_iterator last) { + return protozero::const_varint_iterator::distance(first, last); + } + + template <> + inline typename protozero::const_varint_iterator::difference_type + distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_varint_iterator last) { + return protozero::const_varint_iterator::distance(first, last); + } + + template <> + inline typename protozero::const_varint_iterator::difference_type + distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_varint_iterator last) { + return protozero::const_varint_iterator::distance(first, last); + } + + template <> + inline typename protozero::const_varint_iterator::difference_type + distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_varint_iterator last) { + return protozero::const_varint_iterator::distance(first, last); + } + + template <> + inline typename protozero::const_svarint_iterator::difference_type + distance>(protozero::const_svarint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_svarint_iterator last) { + return protozero::const_svarint_iterator::distance(first, last); + } + + template <> + inline typename protozero::const_svarint_iterator::difference_type + distance>(protozero::const_svarint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) + protozero::const_svarint_iterator last) { + return protozero::const_svarint_iterator::distance(first, last); + } + + /// @endcond + +} // end namespace std + +#endif // PROTOZERO_ITERATORS_HPP diff --git a/ext/protozero/include/protozero/pbf_builder.hpp b/ext/protozero/include/protozero/pbf_builder.hpp new file mode 100644 index 0000000000..71a2dec2bd --- /dev/null +++ b/ext/protozero/include/protozero/pbf_builder.hpp @@ -0,0 +1,32 @@ +#ifndef PROTOZERO_PBF_BUILDER_HPP +#define PROTOZERO_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 pbf_builder.hpp + * + * @brief Contains the pbf_builder template class. + */ + +#include "basic_pbf_builder.hpp" +#include "pbf_writer.hpp" + +#include + +namespace protozero { + +/// Specialization of basic_pbf_builder using std::string as buffer type. +template +using pbf_builder = basic_pbf_builder; + +} // end namespace protozero + +#endif // PROTOZERO_PBF_BUILDER_HPP diff --git a/ext/protozero/include/protozero/pbf_message.hpp b/ext/protozero/include/protozero/pbf_message.hpp new file mode 100644 index 0000000000..d7fd8b5d0d --- /dev/null +++ b/ext/protozero/include/protozero/pbf_message.hpp @@ -0,0 +1,184 @@ +#ifndef PROTOZERO_PBF_MESSAGE_HPP +#define PROTOZERO_PBF_MESSAGE_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 pbf_message.hpp + * + * @brief Contains the pbf_message template class. + */ + +#include "pbf_reader.hpp" +#include "types.hpp" + +#include + +namespace protozero { + +/** + * This class represents a protobuf message. Either a top-level message or + * a nested sub-message. Top-level messages can be created from any buffer + * with a pointer and length: + * + * @code + * enum class Message : protozero::pbf_tag_type { + * ... + * }; + * + * std::string buffer; + * // fill buffer... + * pbf_message message{buffer.data(), buffer.size()}; + * @endcode + * + * Sub-messages are created using get_message(): + * + * @code + * enum class SubMessage : protozero::pbf_tag_type { + * ... + * }; + * + * pbf_message message{...}; + * message.next(); + * pbf_message submessage = message.get_message(); + * @endcode + * + * All methods of the pbf_message class except get_bytes() and get_string() + * provide the strong exception guarantee, ie they either succeed or do not + * change the pbf_message object they are called on. Use the get_data() method + * instead of get_bytes() or get_string(), if you need this guarantee. + * + * This template class is based on the pbf_reader class and has all the same + * methods. The difference is that whereever the pbf_reader class takes an + * integer tag, this template class takes a tag of the template type T. + * + * Read the tutorial to understand how this class is used. + */ +template +class pbf_message : public pbf_reader { + + 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 read. + using enum_type = T; + + /** + * Construct a pbf_message. All arguments are forwarded to the pbf_reader + * parent class. + */ + template + pbf_message(Args&&... args) noexcept : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + pbf_reader{std::forward(args)...} { + } + + /** + * Set next field in the message as the current field. This is usually + * called in a while loop: + * + * @code + * pbf_message<...> message(...); + * while (message.next()) { + * // handle field + * } + * @endcode + * + * @returns `true` if there is a next field, `false` if not. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now. + */ + bool next() { + return pbf_reader::next(); + } + + /** + * Set next field with given tag in the message as the current field. + * Fields with other tags are skipped. This is usually called in a while + * loop for repeated fields: + * + * @code + * pbf_message message{...}; + * while (message.next(Example1::repeated_fixed64_r)) { + * // handle field + * } + * @endcode + * + * or you can call it just once to get the one field with this tag: + * + * @code + * pbf_message message{...}; + * if (message.next(Example1::required_uint32_x)) { + * // handle field + * } + * @endcode + * + * Note that this will not check the wire type. The two-argument version + * of this function will also check the wire type. + * + * @returns `true` if there is a next field with this tag. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now with the given tag. + */ + bool next(T next_tag) { + return pbf_reader::next(pbf_tag_type(next_tag)); + } + + /** + * Set next field with given tag and wire type in the message as the + * current field. Fields with other tags are skipped. This is usually + * called in a while loop for repeated fields: + * + * @code + * pbf_message message{...}; + * while (message.next(Example1::repeated_fixed64_r, pbf_wire_type::varint)) { + * // handle field + * } + * @endcode + * + * or you can call it just once to get the one field with this tag: + * + * @code + * pbf_message message{...}; + * if (message.next(Example1::required_uint32_x, pbf_wire_type::varint)) { + * // handle field + * } + * @endcode + * + * Note that this will also check the wire type. The one-argument version + * of this function will not check the wire type. + * + * @returns `true` if there is a next field with this tag. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now with the given tag. + */ + bool next(T next_tag, pbf_wire_type type) { + return pbf_reader::next(pbf_tag_type(next_tag), type); + } + + /** + * The tag of the current field. The tag is the enum value for the field + * number from the description in the .proto file. + * + * Call next() before calling this function to set the current field. + * + * @returns tag of the current field. + * @pre There must be a current field (ie. next() must have returned `true`). + */ + T tag() const noexcept { + return T(pbf_reader::tag()); + } + +}; // class pbf_message + +} // end namespace protozero + +#endif // PROTOZERO_PBF_MESSAGE_HPP diff --git a/ext/protozero/include/protozero/pbf_reader.hpp b/ext/protozero/include/protozero/pbf_reader.hpp new file mode 100644 index 0000000000..92bfdee5ea --- /dev/null +++ b/ext/protozero/include/protozero/pbf_reader.hpp @@ -0,0 +1,977 @@ +#ifndef PROTOZERO_PBF_READER_HPP +#define PROTOZERO_PBF_READER_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 pbf_reader.hpp + * + * @brief Contains the pbf_reader class. + */ + +#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 +#endif + +#include +#include +#include +#include +#include + +namespace protozero { + +/** + * This class represents a protobuf message. Either a top-level message or + * a nested sub-message. Top-level messages can be created from any buffer + * with a pointer and length: + * + * @code + * std::string buffer; + * // fill buffer... + * pbf_reader message{buffer.data(), buffer.size()}; + * @endcode + * + * Sub-messages are created using get_message(): + * + * @code + * pbf_reader message{...}; + * message.next(); + * pbf_reader submessage = message.get_message(); + * @endcode + * + * All methods of the pbf_reader class except get_bytes() and get_string() + * provide the strong exception guarantee, ie they either succeed or do not + * change the pbf_reader object they are called on. Use the get_view() method + * instead of get_bytes() or get_string(), if you need this guarantee. + */ +class pbf_reader { + + // A pointer to the next unread data. + const char* m_data = nullptr; + + // A pointer to one past the end of data. + const char* m_end = nullptr; + + // The wire type of the current field. + pbf_wire_type m_wire_type = pbf_wire_type::unknown; + + // The tag of the current field. + pbf_tag_type m_tag = 0; + + template + T get_fixed() { + T result; + const char* data = m_data; + skip_bytes(sizeof(T)); + std::memcpy(&result, data, sizeof(T)); +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN + byteswap_inplace(&result); +#endif + return result; + } + + template + iterator_range> packed_fixed() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + const auto len = get_len_and_skip(); + if (len % sizeof(T) != 0) { + throw invalid_length_exception{}; + } + return {const_fixed_iterator(m_data - len), + const_fixed_iterator(m_data)}; + } + + template + T get_varint() { + const auto val = static_cast(decode_varint(&m_data, m_end)); + return val; + } + + template + T get_svarint() { + protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint"); + return static_cast(decode_zigzag64(decode_varint(&m_data, m_end))); + } + + pbf_length_type get_length() { + return get_varint(); + } + + void skip_bytes(pbf_length_type len) { + if (m_end - m_data < static_cast(len)) { + throw end_of_buffer_exception{}; + } + m_data += len; + +#ifndef NDEBUG + // In debug builds reset the tag to zero so that we can detect (some) + // wrong code. + m_tag = 0; +#endif + } + + pbf_length_type get_len_and_skip() { + const auto len = get_length(); + skip_bytes(len); + return len; + } + + template + iterator_range get_packed() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + const auto len = get_len_and_skip(); + return {T{m_data - len, m_data}, + T{m_data, m_data}}; + } + +public: + + /** + * Construct a pbf_reader message from a data_view. The pointer from the + * data_view will be stored inside the pbf_reader object, no data is + * copied. So you must make sure the view stays valid as long as the + * pbf_reader object is used. + * + * The buffer must contain a complete protobuf message. + * + * @post There is no current field. + */ + explicit pbf_reader(const data_view& view) noexcept + : m_data{view.data()}, + m_end{view.data() + view.size()} { + } + + /** + * Construct a pbf_reader message from a data pointer and a length. The + * pointer will be stored inside the pbf_reader object, no data is copied. + * So you must make sure the buffer stays valid as long as the pbf_reader + * object is used. + * + * The buffer must contain a complete protobuf message. + * + * @post There is no current field. + */ + pbf_reader(const char* data, std::size_t size) noexcept + : m_data{data}, + m_end{data + size} { + } + +#ifndef PROTOZERO_STRICT_API + /** + * Construct a pbf_reader message from a data pointer and a length. The + * pointer will be stored inside the pbf_reader object, no data is copied. + * So you must make sure the buffer stays valid as long as the pbf_reader + * object is used. + * + * The buffer must contain a complete protobuf message. + * + * @post There is no current field. + * @deprecated Use one of the other constructors. + */ + explicit pbf_reader(const std::pair& data) noexcept + : m_data{data.first}, + m_end{data.first + data.second} { + } +#endif + + /** + * Construct a pbf_reader message from a std::string. A pointer to the + * string internals will be stored inside the pbf_reader object, no data + * is copied. So you must make sure the string is unchanged as long as the + * pbf_reader object is used. + * + * The string must contain a complete protobuf message. + * + * @post There is no current field. + */ + explicit pbf_reader(const std::string& data) noexcept + : m_data{data.data()}, + m_end{data.data() + data.size()} { + } + + /** + * pbf_reader can be default constructed and behaves like it has an empty + * buffer. + */ + pbf_reader() noexcept = default; + + /// pbf_reader messages can be copied trivially. + pbf_reader(const pbf_reader&) noexcept = default; + + /// pbf_reader messages can be moved trivially. + pbf_reader(pbf_reader&&) noexcept = default; + + /// pbf_reader messages can be copied trivially. + pbf_reader& operator=(const pbf_reader& other) noexcept = default; + + /// pbf_reader messages can be moved trivially. + pbf_reader& operator=(pbf_reader&& other) noexcept = default; + + ~pbf_reader() = default; + + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(pbf_reader& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_end, other.m_end); + swap(m_wire_type, other.m_wire_type); + swap(m_tag, other.m_tag); + } + + /** + * In a boolean context the pbf_reader class evaluates to `true` if there + * are still fields available and to `false` if the last field has been + * read. + */ + operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + return m_data != m_end; + } + + /** + * Get a view of the not yet read data. + */ + data_view data() const noexcept { + return {m_data, static_cast(m_end - m_data)}; + } + + /** + * Return the length in bytes of the current message. If you have + * already called next() and/or any of the get_*() functions, this will + * return the remaining length. + * + * This can, for instance, be used to estimate the space needed for a + * buffer. Of course you have to know reasonably well what data to expect + * and how it is encoded for this number to have any meaning. + */ + std::size_t length() const noexcept { + return std::size_t(m_end - m_data); + } + + /** + * Set next field in the message as the current field. This is usually + * called in a while loop: + * + * @code + * pbf_reader message(...); + * while (message.next()) { + * // handle field + * } + * @endcode + * + * @returns `true` if there is a next field, `false` if not. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now. + */ + bool next() { + if (m_data == m_end) { + return false; + } + + const auto value = get_varint(); + m_tag = pbf_tag_type(value >> 3U); + + // tags 0 and 19000 to 19999 are not allowed as per + // https://developers.google.com/protocol-buffers/docs/proto#assigning-tags + if (m_tag == 0 || (m_tag >= 19000 && m_tag <= 19999)) { + throw invalid_tag_exception{}; + } + + m_wire_type = pbf_wire_type(value & 0x07U); + switch (m_wire_type) { + case pbf_wire_type::varint: + case pbf_wire_type::fixed64: + case pbf_wire_type::length_delimited: + case pbf_wire_type::fixed32: + break; + default: + throw unknown_pbf_wire_type_exception{}; + } + + return true; + } + + /** + * Set next field with given tag in the message as the current field. + * Fields with other tags are skipped. This is usually called in a while + * loop for repeated fields: + * + * @code + * pbf_reader message{...}; + * while (message.next(17)) { + * // handle field + * } + * @endcode + * + * or you can call it just once to get the one field with this tag: + * + * @code + * pbf_reader message{...}; + * if (message.next(17)) { + * // handle field + * } + * @endcode + * + * Note that this will not check the wire type. The two-argument version + * of this function will also check the wire type. + * + * @returns `true` if there is a next field with this tag. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now with the given tag. + */ + bool next(pbf_tag_type next_tag) { + while (next()) { + if (m_tag == next_tag) { + return true; + } + skip(); + } + return false; + } + + /** + * Set next field with given tag and wire type in the message as the + * current field. Fields with other tags are skipped. This is usually + * called in a while loop for repeated fields: + * + * @code + * pbf_reader message{...}; + * while (message.next(17, pbf_wire_type::varint)) { + * // handle field + * } + * @endcode + * + * or you can call it just once to get the one field with this tag: + * + * @code + * pbf_reader message{...}; + * if (message.next(17, pbf_wire_type::varint)) { + * // handle field + * } + * @endcode + * + * Note that this will also check the wire type. The one-argument version + * of this function will not check the wire type. + * + * @returns `true` if there is a next field with this tag. + * @pre There must be no current field. + * @post If it returns `true` there is a current field now with the given tag. + */ + bool next(pbf_tag_type next_tag, pbf_wire_type type) { + while (next()) { + if (m_tag == next_tag && m_wire_type == type) { + return true; + } + skip(); + } + return false; + } + + /** + * The tag of the current field. The tag is the field number from the + * description in the .proto file. + * + * Call next() before calling this function to set the current field. + * + * @returns tag of the current field. + * @pre There must be a current field (ie. next() must have returned `true`). + */ + pbf_tag_type tag() const noexcept { + return m_tag; + } + + /** + * Get the wire type of the current field. The wire types are: + * + * * 0 - varint + * * 1 - 64 bit + * * 2 - length-delimited + * * 5 - 32 bit + * + * All other types are illegal. + * + * Call next() before calling this function to set the current field. + * + * @returns wire type of the current field. + * @pre There must be a current field (ie. next() must have returned `true`). + */ + pbf_wire_type wire_type() const noexcept { + return m_wire_type; + } + + /** + * Get the tag and wire type of the current field in one integer suitable + * for comparison with a switch statement. + * + * Use it like this: + * + * @code + * pbf_reader message{...}; + * while (message.next()) { + * switch (message.tag_and_type()) { + * case tag_and_type(17, pbf_wire_type::length_delimited): + * .... + * break; + * case tag_and_type(21, pbf_wire_type::varint): + * .... + * break; + * default: + * message.skip(); + * } + * } + * @endcode + */ + uint32_t tag_and_type() const noexcept { + return protozero::tag_and_type(tag(), wire_type()); + } + + /** + * Check the wire type of the current field. + * + * @returns `true` if the current field has the given wire type. + * @pre There must be a current field (ie. next() must have returned `true`). + */ + bool has_wire_type(pbf_wire_type type) const noexcept { + return wire_type() == type; + } + + /** + * Consume the current field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @post The current field was consumed and there is no current field now. + */ + void skip() { + protozero_assert(tag() != 0 && "call next() before calling skip()"); + switch (wire_type()) { + case pbf_wire_type::varint: + skip_varint(&m_data, m_end); + break; + case pbf_wire_type::fixed64: + skip_bytes(8); + break; + case pbf_wire_type::length_delimited: + skip_bytes(get_length()); + break; + case pbf_wire_type::fixed32: + skip_bytes(4); + break; + default: + break; + } + } + + ///@{ + /** + * @name Scalar field accessor functions + */ + + /** + * Consume and return value of current "bool" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bool". + * @post The current field was consumed and there is no current field now. + */ + 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 bool result = m_data[0] != 0; + skip_varint(&m_data, m_end); + return result; + } + + /** + * Consume and return value of current "enum" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "enum". + * @post The current field was consumed and there is no current field now. + */ + int32_t get_enum() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_varint(); + } + + /** + * Consume and return value of current "int32" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "int32". + * @post The current field was consumed and there is no current field now. + */ + int32_t get_int32() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_varint(); + } + + /** + * Consume and return value of current "sint32" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "sint32". + * @post The current field was consumed and there is no current field now. + */ + int32_t get_sint32() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_svarint(); + } + + /** + * Consume and return value of current "uint32" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "uint32". + * @post The current field was consumed and there is no current field now. + */ + uint32_t get_uint32() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_varint(); + } + + /** + * Consume and return value of current "int64" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "int64". + * @post The current field was consumed and there is no current field now. + */ + int64_t get_int64() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_varint(); + } + + /** + * Consume and return value of current "sint64" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "sint64". + * @post The current field was consumed and there is no current field now. + */ + int64_t get_sint64() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_svarint(); + } + + /** + * Consume and return value of current "uint64" varint field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "uint64". + * @post The current field was consumed and there is no current field now. + */ + uint64_t get_uint64() { + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + return get_varint(); + } + + /** + * Consume and return value of current "fixed32" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "fixed32". + * @post The current field was consumed and there is no current field now. + */ + uint32_t get_fixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "sfixed32" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "sfixed32". + * @post The current field was consumed and there is no current field now. + */ + int32_t get_sfixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "fixed64" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "fixed64". + * @post The current field was consumed and there is no current field now. + */ + uint64_t get_fixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "sfixed64" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "sfixed64". + * @post The current field was consumed and there is no current field now. + */ + int64_t get_sfixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "float" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "float". + * @post The current field was consumed and there is no current field now. + */ + float get_float() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "double" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "double". + * @post The current field was consumed and there is no current field now. + */ + double get_double() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "bytes", "string", or "message" + * field. + * + * @returns A data_view object. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bytes", "string", or "message". + * @post The current field was consumed and there is no current field now. + */ + data_view get_view() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return {m_data - len, len}; + } + +#ifndef PROTOZERO_STRICT_API + /** + * Consume and return value of current "bytes" or "string" field. + * + * @returns A pair with a pointer to the data and the length of the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bytes" or "string". + * @post The current field was consumed and there is no current field now. + */ + std::pair get_data() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return {m_data - len, len}; + } +#endif + + /** + * Consume and return value of current "bytes" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bytes". + * @post The current field was consumed and there is no current field now. + */ + std::string get_bytes() { + return std::string(get_view()); + } + + /** + * Consume and return value of current "string" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "string". + * @post The current field was consumed and there is no current field now. + */ + std::string get_string() { + return std::string(get_view()); + } + + /** + * Consume and return value of current "message" field. + * + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "message". + * @post The current field was consumed and there is no current field now. + */ + pbf_reader get_message() { + return pbf_reader{get_view()}; + } + + ///@} + + /// Forward iterator for iterating over bool (int32 varint) values. + using const_bool_iterator = const_varint_iterator< int32_t>; + + /// Forward iterator for iterating over enum (int32 varint) values. + using const_enum_iterator = const_varint_iterator< int32_t>; + + /// Forward iterator for iterating over int32 (varint) values. + using const_int32_iterator = const_varint_iterator< int32_t>; + + /// Forward iterator for iterating over sint32 (varint) values. + using const_sint32_iterator = const_svarint_iterator; + + /// Forward iterator for iterating over uint32 (varint) values. + using const_uint32_iterator = const_varint_iterator; + + /// Forward iterator for iterating over int64 (varint) values. + using const_int64_iterator = const_varint_iterator< int64_t>; + + /// Forward iterator for iterating over sint64 (varint) values. + using const_sint64_iterator = const_svarint_iterator; + + /// Forward iterator for iterating over uint64 (varint) values. + using const_uint64_iterator = const_varint_iterator; + + /// Forward iterator for iterating over fixed32 values. + using const_fixed32_iterator = const_fixed_iterator; + + /// Forward iterator for iterating over sfixed32 values. + using const_sfixed32_iterator = const_fixed_iterator; + + /// Forward iterator for iterating over fixed64 values. + using const_fixed64_iterator = const_fixed_iterator; + + /// Forward iterator for iterating over sfixed64 values. + using const_sfixed64_iterator = const_fixed_iterator; + + /// Forward iterator for iterating over float values. + using const_float_iterator = const_fixed_iterator; + + /// Forward iterator for iterating over double values. + using const_double_iterator = const_fixed_iterator; + + ///@{ + /** + * @name Repeated packed field accessor functions + */ + + /** + * Consume current "repeated packed bool" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed bool". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_bool() { + return get_packed(); + } + + /** + * Consume current "repeated packed enum" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed enum". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_enum() { + return get_packed(); + } + + /** + * Consume current "repeated packed int32" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed int32". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_int32() { + return get_packed(); + } + + /** + * Consume current "repeated packed sint32" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed sint32". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_sint32() { + return get_packed(); + } + + /** + * Consume current "repeated packed uint32" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed uint32". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_uint32() { + return get_packed(); + } + + /** + * Consume current "repeated packed int64" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed int64". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_int64() { + return get_packed(); + } + + /** + * Consume current "repeated packed sint64" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed sint64". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_sint64() { + return get_packed(); + } + + /** + * Consume current "repeated packed uint64" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed uint64". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_uint64() { + return get_packed(); + } + + /** + * Consume current "repeated packed fixed32" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed fixed32". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_fixed32() { + return packed_fixed(); + } + + /** + * Consume current "repeated packed sfixed32" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed sfixed32". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_sfixed32() { + return packed_fixed(); + } + + /** + * Consume current "repeated packed fixed64" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed fixed64". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_fixed64() { + return packed_fixed(); + } + + /** + * Consume current "repeated packed sfixed64" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed sfixed64". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_sfixed64() { + return packed_fixed(); + } + + /** + * Consume current "repeated packed float" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed float". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_float() { + return packed_fixed(); + } + + /** + * Consume current "repeated packed double" field. + * + * @returns a pair of iterators to the beginning and one past the end of + * the data. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "repeated packed double". + * @post The current field was consumed and there is no current field now. + */ + iterator_range get_packed_double() { + return packed_fixed(); + } + + ///@} + +}; // class pbf_reader + +/** + * Swap two pbf_reader objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept { + lhs.swap(rhs); +} + +} // end namespace protozero + +#endif // PROTOZERO_PBF_READER_HPP diff --git a/ext/protozero/include/protozero/pbf_writer.hpp b/ext/protozero/include/protozero/pbf_writer.hpp new file mode 100644 index 0000000000..9a07bd5b9c --- /dev/null +++ b/ext/protozero/include/protozero/pbf_writer.hpp @@ -0,0 +1,76 @@ +#ifndef PROTOZERO_PBF_WRITER_HPP +#define PROTOZERO_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 pbf_writer.hpp + * + * @brief Contains the pbf_writer class. + */ + +#include "basic_pbf_writer.hpp" +#include "buffer_string.hpp" + +#include +#include + +namespace protozero { + +/** + * Specialization of basic_pbf_writer using std::string as buffer type. + */ +using pbf_writer = basic_pbf_writer; + +/// Class for generating packed repeated bool fields. +using packed_field_bool = detail::packed_field_varint; + +/// Class for generating packed repeated enum fields. +using packed_field_enum = detail::packed_field_varint; + +/// Class for generating packed repeated int32 fields. +using packed_field_int32 = detail::packed_field_varint; + +/// Class for generating packed repeated sint32 fields. +using packed_field_sint32 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint32 fields. +using packed_field_uint32 = detail::packed_field_varint; + +/// Class for generating packed repeated int64 fields. +using packed_field_int64 = detail::packed_field_varint; + +/// Class for generating packed repeated sint64 fields. +using packed_field_sint64 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint64 fields. +using packed_field_uint64 = detail::packed_field_varint; + +/// Class for generating packed repeated fixed32 fields. +using packed_field_fixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed32 fields. +using packed_field_sfixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated fixed64 fields. +using packed_field_fixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed64 fields. +using packed_field_sfixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated float fields. +using packed_field_float = detail::packed_field_fixed; + +/// Class for generating packed repeated double fields. +using packed_field_double = detail::packed_field_fixed; + +} // end namespace protozero + +#endif // PROTOZERO_PBF_WRITER_HPP diff --git a/ext/protozero/include/protozero/types.hpp b/ext/protozero/include/protozero/types.hpp new file mode 100644 index 0000000000..3aefddfb51 --- /dev/null +++ b/ext/protozero/include/protozero/types.hpp @@ -0,0 +1,66 @@ +#ifndef PROTOZERO_TYPES_HPP +#define PROTOZERO_TYPES_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 types.hpp + * + * @brief Contains the declaration of low-level types used in the pbf format. + */ + +#include "config.hpp" + +#include +#include +#include +#include +#include +#include + +namespace protozero { + +/** + * The type used for field tags (field numbers). + */ +using pbf_tag_type = uint32_t; + +/** + * The type used to encode type information. + * See the table on + * https://developers.google.com/protocol-buffers/docs/encoding + */ +enum class pbf_wire_type : uint32_t { + varint = 0, // int32/64, uint32/64, sint32/64, bool, enum + fixed64 = 1, // fixed64, sfixed64, double + length_delimited = 2, // string, bytes, nested messages, packed repeated fields + fixed32 = 5, // fixed32, sfixed32, float + unknown = 99 // used for default setting in this library +}; + +/** + * Get the tag and wire type of the current field in one integer suitable + * for comparison with a switch statement. + * + * See pbf_reader.tag_and_type() for an example how to use this. + */ +template +constexpr inline uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept { + return (static_cast(static_cast(tag)) << 3U) | static_cast(wire_type); +} + +/** + * The type used for length values, such as the length of a field. + */ +using pbf_length_type = uint32_t; + +} // end namespace protozero + +#endif // PROTOZERO_TYPES_HPP diff --git a/ext/protozero/include/protozero/varint.hpp b/ext/protozero/include/protozero/varint.hpp new file mode 100644 index 0000000000..b4648a4421 --- /dev/null +++ b/ext/protozero/include/protozero/varint.hpp @@ -0,0 +1,245 @@ +#ifndef PROTOZERO_VARINT_HPP +#define PROTOZERO_VARINT_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 varint.hpp + * + * @brief Contains low-level varint and zigzag encoding and decoding functions. + */ + +#include "buffer_tmpl.hpp" +#include "exception.hpp" + +#include + +namespace protozero { + +/** + * The maximum length of a 64 bit varint. + */ +constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; + +namespace detail { + + // from https://github.com/facebook/folly/blob/master/folly/Varint.h + inline uint64_t decode_varint_impl(const char** data, const char* end) { + const 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 = *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; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 28U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 35U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 42U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 49U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 56U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x01U) << 63U); if (b >= 0) { break; } + throw varint_too_long_exception{}; + } while (false); + } else { + unsigned int shift = 0; + while (p != iend && *p < 0) { + val |= (uint64_t(*p++) & 0x7fU) << shift; + shift += 7; + } + if (p == iend) { + throw end_of_buffer_exception{}; + } + val |= uint64_t(*p++) << shift; + } + + *data = reinterpret_cast(p); + return val; + } + +} // end namespace detail + +/** + * Decode a 64 bit varint. + * + * Strong exception guarantee: if there is an exception the data pointer will + * not be changed. + * + * @param[in,out] data Pointer to pointer to the input data. After the function + * returns this will point to the next data to be read. + * @param[in] end Pointer one past the end of the input data. + * @returns The decoded integer + * @throws varint_too_long_exception if the varint is longer then the maximum + * length that would fit in a 64 bit int. Usually this means your data + * is corrupted or you are trying to read something as a varint that + * isn't. + * @throws end_of_buffer_exception if the *end* of the buffer was reached + * before the end of the varint. + */ +inline uint64_t decode_varint(const char** data, const char* end) { + // If this is a one-byte varint, decode it here. + if (end != *data && ((static_cast(**data) & 0x80U) == 0)) { + const auto val = static_cast(**data); + ++(*data); + return val; + } + // If this varint is more than one byte, defer to complete implementation. + return detail::decode_varint_impl(data, end); +} + +/** + * Skip over a varint. + * + * Strong exception guarantee: if there is an exception the data pointer will + * not be changed. + * + * @param[in,out] data Pointer to pointer to the input data. After the function + * returns this will point to the next data to be read. + * @param[in] end Pointer one past the end of the input data. + * @throws end_of_buffer_exception if the *end* of the buffer was reached + * before the end of the varint. + */ +inline void skip_varint(const char** data, const char* end) { + const auto* begin = reinterpret_cast(*data); + const auto* iend = reinterpret_cast(end); + const int8_t* p = begin; + + while (p != iend && *p < 0) { + ++p; + } + + if (p - begin >= max_varint_length) { + throw varint_too_long_exception{}; + } + + if (p == iend) { + throw end_of_buffer_exception{}; + } + + ++p; + + *data = reinterpret_cast(p); +} + +/** + * Varint encode a 64 bit integer. + * + * @tparam T An output iterator type. + * @param data Output iterator the varint encoded value will be written to + * byte by byte. + * @param value The integer that will be encoded. + * @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) { + int n = 1; + + while (value >= 0x80U) { + *data++ = char((value & 0x7fU) | 0x80U); + value >>= 7U; + ++n; + } + *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; +} + +/** + * Get the length of the varint the specified value would produce. + * + * @param value The integer to be encoded. + * @returns the number of bytes the varint would have if we created it. + */ +inline int length_of_varint(uint64_t value) noexcept { + int n = 1; + + while (value >= 0x80U) { + value >>= 7U; + ++n; + } + + return n; +} + +/** + * ZigZag encodes a 32 bit integer. + */ +inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept { + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 31U)); +} + +/** + * ZigZag encodes a 64 bit integer. + */ +inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept { + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 63U)); +} + +/** + * Decodes a 32 bit ZigZag-encoded integer. + */ +inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept { + return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U))); +} + +/** + * Decodes a 64 bit ZigZag-encoded integer. + */ +inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept { + return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U))); +} + +} // end namespace protozero + +#endif // PROTOZERO_VARINT_HPP diff --git a/ext/protozero/include/protozero/version.hpp b/ext/protozero/include/protozero/version.hpp new file mode 100644 index 0000000000..9a0e4cc9f5 --- /dev/null +++ b/ext/protozero/include/protozero/version.hpp @@ -0,0 +1,34 @@ +#ifndef PROTOZERO_VERSION_HPP +#define PROTOZERO_VERSION_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 version.hpp + * + * @brief Contains macros defining the protozero version. + */ + +/// The major version number +#define PROTOZERO_VERSION_MAJOR 1 + +/// The minor version number +#define PROTOZERO_VERSION_MINOR 7 + +/// The patch number +#define PROTOZERO_VERSION_PATCH 0 + +/// The complete version number +#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) + +/// Version number as string +#define PROTOZERO_VERSION_STRING "1.7.0" + +#endif // PROTOZERO_VERSION_HPP diff --git a/pdns/recursordist/README.md b/pdns/recursordist/README.md index 49fc8bcf50..f5df7ed3d9 100644 --- a/pdns/recursordist/README.md +++ b/pdns/recursordist/README.md @@ -78,19 +78,51 @@ desired version. e.g.: (On older versions of Debian/Ubuntu, you'll need to pass `--with-lua=lua5.1` instead.) Documentation -------------- +============= After compiling, run `pdns\_recursor --config` to view the configuration options and a short description. The full documentation is online at https://doc.powerdns.com/recursor/ Reporting bugs --------------- +============== Bugs can be reported on GitHub: https://github.com/PowerDNS/pdns/issues, please check first if your issue is not fixed in the latest version or has already been reported. License -------- +======= PowerDNS is copyright © 2001-2019 by PowerDNS.COM BV and lots of contributors, using the GNU GPLv2 license (see NOTICE for the exact license and exception used). + +Third party software +==================== +We use code from the following projects: + +Protozero +--------- +protozero copyright (c) Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/pdns/recursordist/docs/index.rst b/pdns/recursordist/docs/index.rst index 483a39b7c3..50d7880d50 100644 --- a/pdns/recursordist/docs/index.rst +++ b/pdns/recursordist/docs/index.rst @@ -75,3 +75,34 @@ I have a good idea for a feature! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We like to work on new things! You can file a feature request on `GitHub feature request `_. + +Third party software +-------------------- +We use code from the project listed below. + +Protozero +^^^^^^^^^ +protozero copyright (c) Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.