[protozero] 01/08: Imported Upstream version 1.4.0
Bas Couwenberg
sebastic at debian.org
Fri Jul 22 14:39:23 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository protozero.
commit 523cc155bfdd75f449759fa0cc61ce46aab7345f
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Fri Jul 22 13:06:25 2016 +0200
Imported Upstream version 1.4.0
---
.travis.yml | 2 +-
CHANGELOG.md | 18 +-
CONTRIBUTING.md | 1 +
Makefile | 22 +-
README.md | 41 +-
UPGRADING.md | 66 +++
doc/Doxyfile | 4 +-
doc/cheatsheet.md | 67 +++
doc/macros.md | 48 ++
tutorial.md => doc/tutorial.md | 8 +-
include/protozero/byteswap.hpp | 6 +-
include/protozero/exception.hpp | 8 +-
include/protozero/iterators.hpp | 373 ++++++++++++
include/protozero/pbf_builder.hpp | 20 +-
include/protozero/pbf_message.hpp | 10 +-
include/protozero/pbf_reader.hpp | 763 +++++++++----------------
include/protozero/pbf_writer.hpp | 240 +++++---
include/protozero/types.hpp | 170 +++++-
include/protozero/varint.hpp | 125 ++--
include/protozero/version.hpp | 17 +-
package.json | 2 +-
test/include/packed_access.hpp | 234 ++++++++
test/include/scalar_access.hpp | 20 +
test/include/test.hpp | 4 +
test/t/basic/test_cases.cpp | 64 +--
test/t/complex/test_cases.cpp | 56 +-
test/t/data_view/test_cases.cpp | 83 +++
test/t/exceptions/test_cases.cpp | 34 +-
test/t/fixed32/data-pos200.pbf | Bin 0 -> 5 bytes
test/t/fixed32/testcase.cpp | 3 +
test/t/fixed64/data-pos200.pbf | Bin 0 -> 9 bytes
test/t/fixed64/testcase.cpp | 3 +
test/t/int32/data-neg200.pbf | 1 +
test/t/int32/data-pos200.pbf | 1 +
test/t/int32/testcase.cpp | 6 +
test/t/int64/data-neg200.pbf | 1 +
test/t/int64/data-pos200.pbf | 1 +
test/t/int64/testcase.cpp | 6 +
test/t/message/test_cases.cpp | 7 +
test/t/repeated_packed_bool/test_cases.cpp | 16 +-
test/t/repeated_packed_double/test_cases.cpp | 12 +-
test/t/repeated_packed_enum/test_cases.cpp | 16 +-
test/t/repeated_packed_fixed32/data-many.pbf | Bin 18 -> 22 bytes
test/t/repeated_packed_fixed32/test_cases.cpp | 202 +------
test/t/repeated_packed_fixed32/testcase.cpp | 1 +
test/t/repeated_packed_fixed64/data-many.pbf | Bin 34 -> 42 bytes
test/t/repeated_packed_fixed64/test_cases.cpp | 93 +--
test/t/repeated_packed_fixed64/testcase.cpp | 1 +
test/t/repeated_packed_float/test_cases.cpp | 12 +-
test/t/repeated_packed_int32/data-many.pbf | Bin 30 -> 42 bytes
test/t/repeated_packed_int32/test_cases.cpp | 86 +--
test/t/repeated_packed_int32/testcase.cpp | 4 +-
test/t/repeated_packed_int64/data-many.pbf | Bin 34 -> 46 bytes
test/t/repeated_packed_int64/test_cases.cpp | 122 +---
test/t/repeated_packed_int64/testcase.cpp | 4 +-
test/t/repeated_packed_sfixed32/data-many.pbf | Bin 26 -> 34 bytes
test/t/repeated_packed_sfixed32/test_cases.cpp | 85 +--
test/t/repeated_packed_sfixed32/testcase.cpp | 4 +-
test/t/repeated_packed_sfixed64/data-many.pbf | Bin 50 -> 66 bytes
test/t/repeated_packed_sfixed64/test_cases.cpp | 84 +--
test/t/repeated_packed_sfixed64/testcase.cpp | 4 +-
test/t/repeated_packed_sint32/data-many.pbf | Bin 16 -> 20 bytes
test/t/repeated_packed_sint32/test_cases.cpp | 84 +--
test/t/repeated_packed_sint32/testcase.cpp | 4 +-
test/t/repeated_packed_sint64/data-many.pbf | Bin 26 -> 30 bytes
test/t/repeated_packed_sint64/test_cases.cpp | 122 +---
test/t/repeated_packed_sint64/testcase.cpp | 4 +-
test/t/repeated_packed_uint32/data-many.pbf | Bin 10 -> 12 bytes
test/t/repeated_packed_uint32/test_cases.cpp | 82 +--
test/t/repeated_packed_uint32/testcase.cpp | 1 +
test/t/repeated_packed_uint64/data-many.pbf | Bin 15 -> 17 bytes
test/t/repeated_packed_uint64/test_cases.cpp | 82 +--
test/t/repeated_packed_uint64/testcase.cpp | 1 +
test/t/rollback/test_cases.cpp | 12 +-
test/t/sfixed32/data-neg200.pbf | 1 +
test/t/sfixed32/data-pos200.pbf | Bin 0 -> 5 bytes
test/t/sfixed32/testcase.cpp | 6 +
test/t/sfixed64/data-neg200.pbf | 1 +
test/t/sfixed64/data-pos200.pbf | Bin 0 -> 9 bytes
test/t/sfixed64/testcase.cpp | 6 +
test/t/sint32/data-neg200.pbf | 1 +
test/t/sint32/data-pos200.pbf | 1 +
test/t/sint32/testcase.cpp | 8 +-
test/t/sint64/data-neg200.pbf | 1 +
test/t/sint64/data-pos200.pbf | 1 +
test/t/sint64/testcase.cpp | 6 +
test/t/skip/test_cases.cpp | 228 ++++----
test/t/string/test_cases.cpp | 49 +-
test/t/uint32/data-pos200.pbf | 1 +
test/t/uint32/testcase.cpp | 3 +
test/t/uint64/data-pos200.pbf | 1 +
test/t/uint64/testcase.cpp | 3 +
test/t/varint/test_cases.cpp | 114 +++-
test/t/vector_tile/test_cases.cpp | 29 +-
test/t/wrong_type_access/test_cases.cpp | 16 +-
test/t/zigzag/test_cases.cpp | 62 +-
96 files changed, 2251 insertions(+), 1960 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 6ab690d..8a83b9c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,7 @@ addons_shortcuts:
packages: [ 'clang-3.5', 'libprotobuf-dev','protobuf-compiler' ]
addons_clang38: &clang38
apt:
- sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise' ]
+ sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8' ]
packages: [ 'clang-3.8', 'libprotobuf-dev','protobuf-compiler' ]
addons_gcc49: &gcc47
apt:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db457d1..43dbe13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,21 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
+## [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
@@ -75,7 +90,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Make pbf reader and writer code endianess-aware.
-[unreleased]: https://github.com/osmcode/libosmium/compare/v1.3.0...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v1.4.0...HEAD
+[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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9da5be3..b662b25 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,6 +9,7 @@ To release a new protozero version:
- include/protozero/version.hpp
- package.json
- Update CHANGELOG.md
+ - Update UPGRADING.md
- Create a new tag and push to github `git push --tags`
- Publish to npm:
- First run `make testpack` to see what files will be published
diff --git a/Makefile b/Makefile
index 83aeb39..5c72235 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@ CXX := $(CXX)
CXXFLAGS := $(CXXFLAGS)
LDFLAGS := $(LDFLAGS)
+# Installation directory
+DESTDIR ?= /usr
+
WARNING_FLAGS := -Wall -Wextra -pedantic -Wsign-compare -Wsign-conversion -Wunused-parameter -Wno-float-equal -Wno-covered-switch-default
ifneq ($(findstring clang,$(CXX)),)
@@ -38,18 +41,25 @@ PROTO_FILES_H := $(subst .proto,.pb.h,$(PROTO_FILES))
PROTO_FILES_O := $(subst .proto,.pb.o,$(PROTO_FILES))
HPP_FILES := include/protozero/byteswap.hpp \
+ include/protozero/config.hpp \
include/protozero/exception.hpp \
+ include/protozero/iterators.hpp \
+ include/protozero/pbf_builder.hpp \
+ include/protozero/pbf_message.hpp \
+ include/protozero/pbf_reader.hpp \
+ include/protozero/pbf_writer.hpp \
include/protozero/types.hpp \
include/protozero/varint.hpp \
- include/protozero/pbf_reader.hpp \
- include/protozero/pbf_writer.hpp
+ include/protozero/version.hpp
+
+DOC_FILES = doc/cheatsheet.md doc/macros.md doc/tutorial.md
CFLAGS_PROTOBUF := $(subst -I,-isystem ,$(shell pkg-config protobuf --cflags))
LDFLAGS_PROTOBUF := $(shell pkg-config protobuf --libs-only-L)
all: ./test/tests test/writer_tests
-./test/t/%/test_cases.o: test/t/%/test_cases.cpp test/include/test.hpp test/include/scalar_access.hpp $(HPP_FILES)
+./test/t/%/test_cases.o: test/t/%/test_cases.cpp test/include/test.hpp test/include/scalar_access.hpp test/include/packed_access.hpp $(HPP_FILES)
$(CXX) -c -Iinclude -Itest/include $(CXXFLAGS) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
./test/tests.o: test/tests.cpp $(HPP_FILES)
@@ -89,7 +99,7 @@ iwyu: $(HPP_FILES) test/tests.cpp test/writer_tests.cpp
check: $(HPP_FILES) test/tests.cpp test/include/test.hpp test/include/testcase.hpp test/t/*/testcase.cpp $(TEST_CASES)
cppcheck -Uassert --std=c++11 --enable=all --suppress=incorrectStringBooleanError $^
-doc: doc/Doxyfile README.md tutorial.md $(HPP_FILES)
+doc: doc/Doxyfile README.md UPGRADING.md $(DOC_FILES) $(HPP_FILES)
doxygen doc/Doxyfile
clean:
@@ -118,5 +128,9 @@ testpack:
tar -ztvf *tgz
rm -f ./*tgz
+install:
+ install -m 0755 -o root -g root -d $(DESTDIR)/include/protozero
+ install -m 0644 -o root -g root include/protozero/* $(DESTDIR)/include/protozero
+
.PHONY: all test iwyu check doc
diff --git a/README.md b/README.md
index e575022..fef52e2 100644
--- a/README.md
+++ b/README.md
@@ -15,12 +15,9 @@ C++ API that can be generated via the Google Protobufs `protoc` program.
[](https://ci.appveyor.com/project/Mapbox/protozero)
[](https://coveralls.io/github/mapbox/protozero?branch=master)
-
## Depends
- C++11 compiler
- - A working knowledge of how
- [protocol buffer encoding works](https://developers.google.com/protocol-buffers/docs/encoding).
## How it works
@@ -41,13 +38,31 @@ information from the `.proto` description. This results in a few restrictions:
The library will make sure not to overrun the buffer it was given, but
basically all other checks have to be made in user code!
-See the [tutorial](tutorial.md) for more information on how to use it.
-Call `make doc` to build the Doxygen documentation. (You'll need
-[Doxygen](http://www.stack.nl/~dimitri/doxygen/) installed.) Then open
+## Documentation
+
+You have to have a working knowledge of how
+[protocol buffer encoding works](https://developers.google.com/protocol-buffers/docs/encoding).
+
+* Read the [tutorial](doc/tutorial.md) for an introduction on how to use
+ Protozero.
+* There is a table of all types and functions in the
+ [cheat sheet](doc/cheatsheet.md).
+* [Macros defined or used by Protozero](doc/macros.md).
+* Read the [upgrading instructions](UPGRADING.md) if you are upgrading from
+ an older version of Protozero.
+
+Call `make doc` to build the Doxygen-based reference documentation. (You'll
+need [Doxygen](http://www.stack.nl/~dimitri/doxygen/) installed.) Then open
`doc/html/index.html` in your browser to read it.
+## Installation
+
+Call `make install` to install include files in `/usr/include/protozero`. Call
+`make install DESTDIR=/usr/local` or similar to change install directory.
+
+
## Limitations
* A protobuf message has to fit into memory completely, otherwise it can not
@@ -133,3 +148,17 @@ For extra checks with [Cppcheck](http://cppcheck.sourceforge.net/) you can call
make check
+
+## Who is using Protozero?
+
+* [Carmen](https://github.com/mapbox/carmen-cache)
+* [Libosmium](https://github.com/osmcode/libosmium)
+* [Mapnik](https://github.com/mapbox/mapnik-vector-tile)
+* [Mapbox GL Native](https://github.com/mapbox/mapbox-gl-native)
+* [OSRM](https://github.com/Project-OSRM/osrm-backend)
+* [Tippecanoe](https://github.com/mapbox/tippecanoe)
+
+Are you using Protozero? Tell us! Send a pull request with changes to this
+README.
+
+
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..22d198c
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,66 @@
+
+# Upgrading
+
+This file contains instructions for users of Protozero who are upgrading from
+one version to another.
+
+You do not need to change anything if only the minor version changes, but it
+is better to keep up with changes if you can. The switch to the next major
+version will be easier then. And you might get some more convenient usage.
+
+To help you with upgrading to new versions, you can define the C++ preprocessor
+macro `PROTOZERO_STRICT_API` in which case Protozero will compile without the
+code used for backwards compatibilty. You will then get compile errors for
+older API usages.
+
+## Upgrading from *v1.3.0* to *v1.4.0*
+
+* Functions in `pbf_reader` (and the derived `pbf_message`) called
+ `get_packed_*()` now return an `iterator_range` instead of a `std::pair`.
+ The new class is derived from `std::pair`, so changes are usually not
+ strictly necessary. For future compatibility, you should change all
+ attribute accesses on the returned objects from `first` and `second` to
+ `begin()` and `end()`, respectively. So change something like this:
+
+ auto x = message.get_packed_int32();
+ for (auto it = x.first; it != x.second; ++it) {
+ ....
+ }
+
+ to:
+
+ auto x = message.get_packed_int32();
+ for (auto it = x.begin(); it != x.end(); ++it) {
+ ....
+ }
+
+ or even better use the range-based for loop:
+
+ auto x = message.get_packed_int32();
+ for (auto val : x) {
+ ....
+ }
+
+ Ranges can also be used in this way. This will change the range in-place:
+
+ auto range = message.get_packed_int32();
+ while (!range.empty()) {
+ auto value = range.front();
+ range.drop_front();
+ ....
+ }
+
+* The class `pbf_reader` has a new method `get_view()` returning an object
+ of the new `protozero::data_view` class. The `data_view` only has minimal
+ functionality, but what it has is compatible to the `std::string_view` class
+ which will be coming in C++17. The view autoconverts to a `std::string` if
+ needed. Use `get_view()` instead of `get_data()` giving you a more intuitive
+ interface (call `data()` and `size()` on the view instead of using `first`
+ and `second` on the `std::pair` returned by `get_data()`).
+
+ You can set the macro `PROTOZERO_USE_VIEW` (before including `types.hpp`) to
+ the name of any class that behaves like `protozero::data_view` and
+ `data_view` will be an alias to that class instead of the implementation
+ from protozero. This way you can use the C++17 `string_view` or a similar
+ class if it is already available on your system.
+
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 14e0311..b4e20d4 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -753,7 +753,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
-INPUT = README.md tutorial.md include/protozero
+INPUT = README.md UPGRADING.md doc include/protozero
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -815,7 +815,7 @@ EXCLUDE_PATTERNS =
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS = protozero::detail protozero_assert PROTOZERO_*_ENDIAN PROTOZERO_BYTE_ORDER PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md
new file mode 100644
index 0000000..ef17f24
--- /dev/null
+++ b/doc/cheatsheet.md
@@ -0,0 +1,67 @@
+
+# Protozero Cheat Sheet
+
+See also this
+[handy table](https://developers.google.com/protocol-buffers/docs/proto#scalar)
+from the Google Protocol Buffers documentation.
+
+## Scalar Types
+
+| PBF Type | Underlying Storage | C++ Type | Getter | Notes |
+| -------- | ------------------ | ------------- | ---------------- | ----- |
+| int32 | varint | `int32_t` | `get_int32()` | |
+| sint32 | varint (zigzag) | `int32_t` | `get_sint32()` | |
+| uint32 | varint | `uint32_t` | `get_uint32()` | |
+| int64 | varint | `int64_t` | `get_int64()` | |
+| sint64 | varint (zigzag) | `int64_t` | `get_sint64()` | |
+| uint64 | varint | `uint64_t` | `get_uint64()` | |
+| bool | varint | `bool` | `get_bool()` | |
+| enum | varint | `int32_t` | `get_enum()` | |
+| fixed32 | 32bit fixed | `uint32_t` | `get_fixed32()` | |
+| sfixed32 | 32bit fixed | `int32_t` | `get_sfixed32()` | |
+| fixed64 | 64bit fixed | `uint64_t` | `get_fixed64()` | |
+| sfixed64 | 64bit fixed | `int64_t` | `get_sfixed64()` | |
+| float | 32bit fixed | `float` | `get_float()` | |
+| double | 64bit fixed | `double` | `get_double()` | |
+| string | length-delimited | `data_view` | `get_view()` | (1) |
+| string | length-delimited | pair | `get_data()` | (2) |
+| string | length-delimited | `std::string` | `get_string()` | |
+| bytes | length-delimited | `data_view` | `get_view()` | (1) |
+| bytes | length-delimited | pair | `get_data()` | (2) |
+| bytes | length-delimited | `std::string` | `get_bytes()` | |
+| message | length-delimited | `data_view` | `get_view()` | (1) |
+| message | length-delimited | pair | `get_data()` | (2) |
+| message | length-delimited | `pbf_reader` | `get_message()` | |
+
+### Notes:
+
+* (1) preferred form, returns `protozero::data_view` which is convertible to
+ `std::string` if needed.
+* (2) deprecated form, returns `std::pair<const char*, pbf_length_type>`,
+ use `get_view()` instead. This form is only available if
+ `PROTOZERO_STRICT_API` is not defined.
+* The setter function of `pbf_writer` is always `add_` + the PBF type. Several
+ overloads are available.
+
+
+## Packed Repeated Fields
+
+| PBF Type | Getter |
+| -------- | ----------------------- |
+| int32 | `get_packed_int32()` |
+| sint32 | `get_packed_sint32()` |
+| uint32 | `get_packed_uint32()` |
+| int64 | `get_packed_int64()` |
+| sint64 | `get_packed_sint64()` |
+| uint64 | `get_packed_uint64()` |
+| bool | `get_packed_bool()` |
+| enum | `get_packed_enum()` |
+| fixed32 | `get_packed_fixed32()` |
+| sfixed32 | `get_packed_sfixed32()` |
+| fixed64 | `get_packed_fixed64()` |
+| sfixed64 | `get_packed_sfixed64()` |
+| float | `get_packed_float()` |
+| double | `get_packed_double()` |
+
+Packed repeated fields for string, bytes, and message types are not possible.
+
diff --git a/doc/macros.md b/doc/macros.md
new file mode 100644
index 0000000..4b9fe65
--- /dev/null
+++ b/doc/macros.md
@@ -0,0 +1,48 @@
+
+# Macros
+
+## Version number
+
+If `protozero/version.hpp` is included, the following macros are set:
+
+| Macro | Example | Description |
+| -------------------------- | ------- | ---------------------------------------------- |
+| `PROTOZERO_VERSION_MAJOR` | 1 | Major version number |
+| `PROTOZERO_VERSION_MINOR` | 3 | Minor version number |
+| `PROTOZERO_VERSION_PATCH` | 2 | Patch number |
+| `PROTOZERO_VERSION_CODE` | 10302 | Version (major * 10,000 + minor * 100 + patch) |
+| `PROTOZERO_VERSION_STRING` | "1.3.2" | Version string |
+
+## Changing Protozero behaviour
+
+The behaviour of Protozero can be changed by defining the following macros.
+They have to be set before including any of the Protozero headers.
+
+## `PROTOZERO_STRICT_API`
+
+If this is set, you will get some extra warnings or errors during compilation
+if you are using an old (deprecated) interface to Protozero. Enable this if
+you want to make sure your code will work with future versions of Protozero.
+
+## `PROTOZERO_USE_VIEW`
+
+If this is unset `protozero::data_view` is Protozero's own implementation of
+a *string view* class.
+
+Set this if you want to use a different implementation such as the C++17
+`std::string_view` class. In this case `protozero::data_view` will simply be
+an alias to the class you specify.
+
+ #define PROTOZERO_USE_VIEW std::string_view
+
+This affects the result type of the `pbf_reader::get_view()` method and a few
+others taking a `protozero::data_view` as parameters.
+
+## `PROTOZERO_DO_NOT_USE_BARE_POINTER`
+
+Can be set to force Protozero to not use bare pointers for some iterators
+returned from `get_packed_*` calls. It is usually not necessary to use this
+and might affect performance if you do. If you are getting errors about
+unaligned memory access or a SIGBUS, you can try to set this. (Please also
+report on error if this is the case.)
+
diff --git a/tutorial.md b/doc/tutorial.md
similarity index 98%
rename from tutorial.md
rename to doc/tutorial.md
index c139739..58b2d43 100644
--- a/tutorial.md
+++ b/doc/tutorial.md
@@ -16,7 +16,7 @@ Protocol Buffer documentation:
Make sure you understand the basic types of values supported by Protocol
Buffers. Refer to this
[handy table](https://developers.google.com/protocol-buffers/docs/proto#scalar)
-if you are getting lost.
+and [the cheat sheet](cheatsheet.md) if you are getting lost.
## Prerequisites
@@ -132,9 +132,9 @@ function called `get_` + _field type_.
For `string` and `bytes` types the internal handling is exactly the same, but
both `get_string()` and `get_bytes()` are provided to make the code
self-documenting. Both theses calls allocate and return a `std::string` which
-can add some overhead. You can call the `get_data()` function instead which
-returns a `std::pair<const char*, uint32_t>`, ie a pointer into the data and
-the length of the data.
+can add some overhead. You can call the `get_view()` function instead which
+returns a `data_view` containing a pointer into the data (access with `data()`)
+and the length of the data (access with `size()`).
### Handling Repeated Packed Fields
diff --git a/include/protozero/byteswap.hpp b/include/protozero/byteswap.hpp
index a018c1c..06ba6de 100644
--- a/include/protozero/byteswap.hpp
+++ b/include/protozero/byteswap.hpp
@@ -28,7 +28,7 @@ namespace protozero {
* be specialized to actually work.
*/
template <int N>
-inline void byteswap(const char* /*data*/, char* /*result*/) {
+inline void byteswap(const char* /*data*/, char* /*result*/) noexcept {
static_assert(N == 1, "Can only swap 4 or 8 byte values");
}
@@ -36,7 +36,7 @@ inline void byteswap(const char* /*data*/, char* /*result*/) {
* Swap 4 byte value (int32_t, uint32_t, float) between endianness formats.
*/
template <>
-inline void byteswap<4>(const char* data, char* result) {
+inline void byteswap<4>(const char* data, char* result) noexcept {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint32_t*>(result) = __builtin_bswap32(*reinterpret_cast<const uint32_t*>(data));
#else
@@ -51,7 +51,7 @@ inline void byteswap<4>(const char* data, char* result) {
* Swap 8 byte value (int64_t, uint64_t, double) between endianness formats.
*/
template <>
-inline void byteswap<8>(const char* data, char* result) {
+inline void byteswap<8>(const char* data, char* result) noexcept {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint64_t*>(result) = __builtin_bswap64(*reinterpret_cast<const uint64_t*>(data));
#else
diff --git a/include/protozero/exception.hpp b/include/protozero/exception.hpp
index 5c7ab54..ca4340e 100644
--- a/include/protozero/exception.hpp
+++ b/include/protozero/exception.hpp
@@ -29,7 +29,7 @@ namespace protozero {
*/
struct exception : std::exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "pbf exception"; }
+ const char* what() const noexcept override { return "pbf exception"; }
};
/**
@@ -38,7 +38,7 @@ struct exception : std::exception {
*/
struct varint_too_long_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "varint too long exception"; }
+ const char* what() const noexcept override { return "varint too long exception"; }
};
/**
@@ -47,7 +47,7 @@ struct varint_too_long_exception : exception {
*/
struct unknown_pbf_wire_type_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "unknown pbf field type exception"; }
+ const char* what() const noexcept override { return "unknown pbf field type exception"; }
};
/**
@@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception {
*/
struct end_of_buffer_exception : exception {
/// Returns the explanatory string.
- const char *what() const noexcept override { return "end of buffer exception"; }
+ const char* what() const noexcept override { return "end of buffer exception"; }
};
} // end namespace protozero
diff --git a/include/protozero/iterators.hpp b/include/protozero/iterators.hpp
new file mode 100644
index 0000000..813d96b
--- /dev/null
+++ b/include/protozero/iterators.hpp
@@ -0,0 +1,373 @@
+#ifndef PROTOZERO_ITERATORS_HPP
+#define PROTOZERO_ITERATORS_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file iterators.hpp
+ *
+ * @brief Contains the iterators for access to packed repeated fields.
+ */
+
+#include <cstring>
+#include <iterator>
+#include <utility>
+
+#include <protozero/config.hpp>
+#include <protozero/varint.hpp>
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <protozero/byteswap.hpp>
+#endif
+
+namespace protozero {
+
+namespace detail {
+
+ // Copy N bytes from src to dest on little endian machines, on big
+ // endian swap the bytes in the process.
+ template <int N>
+ inline void copy_or_byteswap(const char* src, void* dest) noexcept {
+#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
+ std::memcpy(dest, src, N);
+#else
+ byteswap<N>(src, reinterpret_cast<char*>(dest));
+#endif
+ }
+
+} // end namespace detail
+
+/**
+ * A range of iterators based on std::pair. Created from beginning and
+ * end iterators. Used as a return type from some pbf_reader methods
+ * that is easy to use with range-based for loops.
+ */
+template <typename T, typename P = std::pair<T, T>>
+class iterator_range :
+#ifdef PROTOZERO_STRICT_API
+ protected
+#else
+ public
+#endif
+ P {
+
+public:
+
+ /// The type of the iterators in this range.
+ using iterator = T;
+
+ /// The value type of the underlying iterator.
+ using value_type = typename std::iterator_traits<T>::value_type;
+
+ /**
+ * Default constructor. Create empty iterator_range.
+ */
+ constexpr iterator_range() :
+ P(iterator{}, iterator{}) {
+ }
+
+ /**
+ * Create iterator range from two iterators.
+ *
+ * @param first Iterator to beginning or range.
+ * @param last Iterator to end or range.
+ */
+ constexpr iterator_range(iterator&& first, iterator&& last) :
+ P(std::forward<iterator>(first),
+ std::forward<iterator>(last)) {
+ }
+
+ /// Return iterator to beginning of range.
+ constexpr iterator begin() const noexcept {
+ return this->first;
+ }
+
+ /// Return iterator to end of range.
+ constexpr iterator end() const noexcept {
+ return this->second;
+ }
+
+ /// Return iterator to beginning of range.
+ constexpr iterator cbegin() const noexcept {
+ return this->first;
+ }
+
+ /// Return iterator to end of range.
+ constexpr iterator cend() const noexcept {
+ return this->second;
+ }
+
+ /// Return true if this range is empty.
+ constexpr std::size_t empty() const noexcept {
+ return begin() == end();
+ }
+
+ /**
+ * Get element at the beginning of the range.
+ *
+ * @pre Range must not be empty.
+ */
+ value_type front() const {
+ protozero_assert(!empty());
+ return *(this->first);
+ }
+
+ /**
+ * Advance beginning of range by one.
+ *
+ * @pre Range must not be empty.
+ */
+ void drop_front() {
+ protozero_assert(!empty());
+ ++this->first;
+ }
+
+ /**
+ * Swap the contents of this range with the other.
+ *
+ * @param other Other range to swap data with.
+ */
+ void swap(iterator_range& other) noexcept {
+ using std::swap;
+ swap(this->first, other.first);
+ swap(this->second, other.second);
+ }
+
+}; // struct iterator_range
+
+/**
+ * Swap two iterator_ranges.
+ *
+ * @param lhs First range.
+ * @param rhs Second range.
+ */
+template <typename T>
+inline void swap(iterator_range<T>& lhs, iterator_range<T>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+
+template <typename T>
+using const_fixed_iterator = const T*;
+
+/**
+ * Create iterator_range from char pointers to beginning and end of range.
+ *
+ * @param first Beginning of range.
+ * @param last End of range.
+ */
+template <typename T>
+inline iterator_range<const_fixed_iterator<T>> create_fixed_iterator_range(const char* first, const char* last) {
+ return iterator_range<const_fixed_iterator<T>>{reinterpret_cast<const T*>(first),
+ reinterpret_cast<const T*>(last)};
+}
+
+#else
+
+/**
+ * A forward iterator used for accessing packed repeated fields of fixed
+ * length (fixed32, sfixed32, float, double).
+ */
+template <typename T>
+class const_fixed_iterator {
+
+ /// Pointer to current iterator position
+ const char* m_data;
+
+ /// Pointer to end iterator position
+ const char* m_end;
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_fixed_iterator() noexcept :
+ m_data(nullptr),
+ m_end(nullptr) {
+ }
+
+ const_fixed_iterator(const char* data, const char* end) noexcept :
+ m_data(data),
+ m_end(end) {
+ }
+
+ const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
+ const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
+
+ const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
+ const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
+
+ ~const_fixed_iterator() noexcept = default;
+
+ value_type operator*() const {
+ value_type result;
+ detail::copy_or_byteswap<sizeof(value_type)>(m_data , &result);
+ return result;
+ }
+
+ const_fixed_iterator& operator++() {
+ m_data += sizeof(value_type);
+ return *this;
+ }
+
+ const_fixed_iterator operator++(int) {
+ const const_fixed_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+ bool operator==(const const_fixed_iterator& rhs) const noexcept {
+ return m_data == rhs.m_data && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const const_fixed_iterator& rhs) const noexcept {
+ return !(*this == rhs);
+ }
+
+}; // class const_fixed_iterator
+
+/**
+ * Create iterator_range from char pointers to beginning and end of range.
+ *
+ * @param first Beginning of range.
+ * @param last End of range.
+ */
+template <typename T>
+inline iterator_range<const_fixed_iterator<T>> create_fixed_iterator_range(const char* first, const char* last) {
+ return iterator_range<const_fixed_iterator<T>>{const_fixed_iterator<T>(first, last),
+ const_fixed_iterator<T>(last, last)};
+}
+
+#endif
+
+/**
+ * A forward iterator used for accessing packed repeated varint fields
+ * (int32, uint32, int64, uint64, bool, enum).
+ */
+template <typename T>
+class const_varint_iterator {
+
+protected:
+
+ /// Pointer to current iterator position
+ const char* m_data;
+
+ /// Pointer to end iterator position
+ const char* m_end;
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_varint_iterator() noexcept :
+ m_data(nullptr),
+ m_end(nullptr) {
+ }
+
+ const_varint_iterator(const char* data, const char* end) noexcept :
+ m_data(data),
+ m_end(end) {
+ }
+
+ const_varint_iterator(const const_varint_iterator&) noexcept = default;
+ const_varint_iterator(const_varint_iterator&&) noexcept = default;
+
+ const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
+ const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
+
+ ~const_varint_iterator() noexcept = default;
+
+ value_type operator*() const {
+ const char* d = m_data; // will be thrown away
+ return static_cast<value_type>(decode_varint(&d, m_end));
+ }
+
+ const_varint_iterator& operator++() {
+ skip_varint(&m_data, m_end);
+ return *this;
+ }
+
+ const_varint_iterator operator++(int) {
+ const const_varint_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+ bool operator==(const const_varint_iterator& rhs) const noexcept {
+ return m_data == rhs.m_data && m_end == rhs.m_end;
+ }
+
+ bool operator!=(const const_varint_iterator& rhs) const noexcept {
+ return !(*this == rhs);
+ }
+
+}; // class const_varint_iterator
+
+/**
+ * A forward iterator used for accessing packed repeated svarint fields
+ * (sint32, sint64).
+ */
+template <typename T>
+class const_svarint_iterator : public const_varint_iterator<T> {
+
+public:
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ const_svarint_iterator() noexcept :
+ const_varint_iterator<T>() {
+ }
+
+ const_svarint_iterator(const char* data, const char* end) noexcept :
+ const_varint_iterator<T>(data, end) {
+ }
+
+ const_svarint_iterator(const const_svarint_iterator&) = default;
+ const_svarint_iterator(const_svarint_iterator&&) = default;
+
+ const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
+ const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
+
+ ~const_svarint_iterator() = default;
+
+ value_type operator*() const {
+ const char* d = this->m_data; // will be thrown away
+ return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
+ }
+
+ const_svarint_iterator& operator++() {
+ skip_varint(&this->m_data, this->m_end);
+ return *this;
+ }
+
+ const_svarint_iterator operator++(int) {
+ const const_svarint_iterator tmp(*this);
+ ++(*this);
+ return tmp;
+ }
+
+}; // class const_svarint_iterator
+
+} // end namespace protozero
+
+#endif // PROTOZERO_ITERATORS_HPP
diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp
index 548f4ce..39af53f 100644
--- a/include/protozero/pbf_builder.hpp
+++ b/include/protozero/pbf_builder.hpp
@@ -57,7 +57,7 @@ public:
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
- inline void add_##name(T tag, type value) { \
+ void add_##name(T tag, type value) { \
pbf_writer::add_##name(pbf_tag_type(tag), value); \
}
@@ -79,38 +79,38 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
/// @endcond
- inline void add_bytes(T tag, const char* value, std::size_t size) {
+ void add_bytes(T tag, const char* value, std::size_t size) {
pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
}
- inline void add_bytes(T tag, const std::string& value) {
+ void add_bytes(T tag, const std::string& value) {
pbf_writer::add_bytes(pbf_tag_type(tag), value);
}
- inline void add_string(T tag, const char* value, std::size_t size) {
+ void add_string(T tag, const char* value, std::size_t size) {
pbf_writer::add_string(pbf_tag_type(tag), value, size);
}
- inline void add_string(T tag, const std::string& value) {
+ void add_string(T tag, const std::string& value) {
pbf_writer::add_string(pbf_tag_type(tag), value);
}
- inline void add_string(T tag, const char* value) {
+ void add_string(T tag, const char* value) {
pbf_writer::add_string(pbf_tag_type(tag), value);
}
- inline void add_message(T tag, const char* value, std::size_t size) {
+ void add_message(T tag, const char* value, std::size_t size) {
pbf_writer::add_message(pbf_tag_type(tag), value, size);
}
- inline void add_message(T tag, const std::string& value) {
+ void add_message(T tag, const std::string& value) {
pbf_writer::add_message(pbf_tag_type(tag), value);
}
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \
template <typename InputIterator> \
- inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
+ void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \
}
@@ -132,7 +132,7 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_PACKED
/// @endcond
-};
+}; // class pbf_builder
} // end namespace protozero
diff --git a/include/protozero/pbf_message.hpp b/include/protozero/pbf_message.hpp
index 45f01c1..0557734 100644
--- a/include/protozero/pbf_message.hpp
+++ b/include/protozero/pbf_message.hpp
@@ -13,7 +13,7 @@ documentation.
/**
* @file pbf_message.hpp
*
- * @brief Contains the pbf_message class.
+ * @brief Contains the pbf_message template class.
*/
#include <type_traits>
@@ -75,19 +75,19 @@ public:
pbf_reader(std::forward<Args>(args)...) {
}
- inline bool next() {
+ bool next() {
return pbf_reader::next();
}
- inline bool next(T tag) {
+ bool next(T tag) {
return pbf_reader::next(pbf_tag_type(tag));
}
- inline T tag() const noexcept {
+ T tag() const noexcept {
return T(pbf_reader::tag());
}
-};
+}; // class pbf_message
} // end namespace protozero
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
index 58b3884..2f4054d 100644
--- a/include/protozero/pbf_reader.hpp
+++ b/include/protozero/pbf_reader.hpp
@@ -18,13 +18,12 @@ documentation.
#include <cstddef>
#include <cstdint>
-#include <cstring>
-#include <iterator>
#include <string>
#include <utility>
#include <protozero/config.hpp>
#include <protozero/exception.hpp>
+#include <protozero/iterators.hpp>
#include <protozero/types.hpp>
#include <protozero/varint.hpp>
@@ -55,16 +54,16 @@ namespace protozero {
*
* All methods of the pbf_reader class except get_bytes() and get_string()
* provide the strong exception guarantee, ie they either succeed or do not
- * change the pbf_reader object they are called on. Use the get_data() method
+ * change the pbf_reader object they are called on. Use the get_view() method
* instead of get_bytes() or get_string(), if you need this guarantee.
*/
class pbf_reader {
// A pointer to the next unread data.
- const char *m_data = nullptr;
+ const char* m_data = nullptr;
// A pointer to one past the end of data.
- const char *m_end = nullptr;
+ const char* m_end = nullptr;
// The wire type of the current field.
pbf_wire_type m_wire_type = pbf_wire_type::unknown;
@@ -72,119 +71,84 @@ class pbf_reader {
// The tag of the current field.
pbf_tag_type m_tag = 0;
- // Copy N bytes from src to dest on little endian machines, on big endian
- // swap the bytes in the process.
- template <int N>
- static void copy_or_byteswap(const char* src, void* dest) noexcept {
-#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
- memcpy(dest, src, N);
-#else
- byteswap<N>(src, reinterpret_cast<char*>(dest));
-#endif
- }
-
template <typename T>
- inline T get_fixed() {
+ T get_fixed() {
T result;
skip_bytes(sizeof(T));
- copy_or_byteswap<sizeof(T)>(m_data - sizeof(T), &result);
+ detail::copy_or_byteswap<sizeof(T)>(m_data - sizeof(T), &result);
return result;
}
-#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
-
template <typename T>
- using const_fixed_iterator = const T*;
+ iterator_range<const_fixed_iterator<T>> packed_fixed() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ const auto len = get_len_and_skip();
+ protozero_assert(len % sizeof(T) == 0);
+ return create_fixed_iterator_range<T>(m_data - len, m_data);
+ }
template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
- return std::make_pair(reinterpret_cast<const T*>(first),
- reinterpret_cast<const T*>(last));
+ T get_varint() {
+ return static_cast<T>(decode_varint(&m_data, m_end));
}
-#else
-
template <typename T>
- class const_fixed_iterator : public std::iterator<std::forward_iterator_tag, T> {
-
- const char* m_data;
- const char* m_end;
-
- public:
-
- const_fixed_iterator() noexcept :
- m_data(nullptr),
- m_end(nullptr) {
- }
-
- const_fixed_iterator(const char *data, const char* end) noexcept :
- m_data(data),
- m_end(end) {
- }
-
- const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
- const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
-
- const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
- const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
-
- ~const_fixed_iterator() noexcept = default;
-
- T operator*() {
- T result;
- copy_or_byteswap<sizeof(T)>(m_data , &result);
- return result;
- }
-
- const_fixed_iterator& operator++() {
- m_data += sizeof(T);
- return *this;
- }
-
- const_fixed_iterator operator++(int) {
- const const_fixed_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
+ T get_svarint() {
+ protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
+ return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
+ }
- bool operator==(const const_fixed_iterator& rhs) const noexcept {
- return m_data == rhs.m_data && m_end == rhs.m_end;
- }
+ pbf_length_type get_length() {
+ return get_varint<pbf_length_type>();
+ }
- bool operator!=(const const_fixed_iterator& rhs) const noexcept {
- return !(*this == rhs);
+ void skip_bytes(pbf_length_type len) {
+ if (m_data + len > m_end) {
+ throw end_of_buffer_exception();
}
+ m_data += len;
- }; // class const_fixed_iterator
-
- template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
- return std::make_pair(const_fixed_iterator<T>(first, last),
- const_fixed_iterator<T>(last, last));
+ // In debug builds reset the tag to zero so that we can detect (some)
+ // wrong code.
+#ifndef NDEBUG
+ m_tag = 0;
+#endif
}
-#endif
+ pbf_length_type get_len_and_skip() {
+ const auto len = get_length();
+ skip_bytes(len);
+ return len;
+ }
template <typename T>
- inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> packed_fixed() {
+ iterator_range<T> get_packed() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- protozero_assert(len % sizeof(T) == 0);
- return create_fixed_iterator_pair<T>(m_data-len, m_data);
+ const auto len = get_len_and_skip();
+ return iterator_range<T>{T{m_data - len, m_data},
+ T{m_data, m_data}};
}
- template <typename T> inline T get_varint();
- template <typename T> inline T get_svarint();
-
- inline pbf_length_type get_length() { return get_varint<pbf_length_type>(); }
-
- inline void skip_bytes(pbf_length_type len);
-
- inline pbf_length_type get_len_and_skip();
-
public:
/**
+ * Construct a pbf_reader message from a data_view. The pointer from the
+ * data_view will be stored inside the pbf_reader object, no data is
+ * copied. So you must* make sure the view stays valid as long as the
+ * pbf_reader object is used.
+ *
+ * The buffer must contain a complete protobuf message.
+ *
+ * @post There is no current field.
+ */
+ explicit pbf_reader(const data_view& view) noexcept
+ : m_data(view.data()),
+ m_end(view.data() + view.size()),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
+
+ /**
* Construct a pbf_reader message from a data pointer and a length. The pointer
* will be stored inside the pbf_reader object, no data is copied. So you must
* make sure the buffer stays valid as long as the pbf_reader object is used.
@@ -193,7 +157,12 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(const char *data, std::size_t length) noexcept;
+ pbf_reader(const char* data, std::size_t length) noexcept
+ : m_data(data),
+ m_end(data + length),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* Construct a pbf_reader message from a data pointer and a length. The pointer
@@ -204,7 +173,12 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(std::pair<const char *, std::size_t> data) noexcept;
+ pbf_reader(std::pair<const char*, std::size_t> data) noexcept
+ : m_data(data.first),
+ m_end(data.first + data.second),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* Construct a pbf_reader message from a std::string. A pointer to the string
@@ -216,33 +190,53 @@ public:
*
* @post There is no current field.
*/
- inline pbf_reader(const std::string& data) noexcept;
+ pbf_reader(const std::string& data) noexcept
+ : m_data(data.data()),
+ m_end(data.data() + data.size()),
+ m_wire_type(pbf_wire_type::unknown),
+ m_tag(0) {
+ }
/**
* pbf_reader can be default constructed and behaves like it has an empty
* buffer.
*/
- inline pbf_reader() noexcept = default;
+ pbf_reader() noexcept = default;
/// pbf_reader messages can be copied trivially.
- inline pbf_reader(const pbf_reader&) noexcept = default;
+ pbf_reader(const pbf_reader&) noexcept = default;
/// pbf_reader messages can be moved trivially.
- inline pbf_reader(pbf_reader&&) noexcept = default;
+ pbf_reader(pbf_reader&&) noexcept = default;
/// pbf_reader messages can be copied trivially.
- inline pbf_reader& operator=(const pbf_reader& other) noexcept = default;
+ pbf_reader& operator=(const pbf_reader& other) noexcept = default;
/// pbf_reader messages can be moved trivially.
- inline pbf_reader& operator=(pbf_reader&& other) noexcept = default;
+ pbf_reader& operator=(pbf_reader&& other) noexcept = default;
+
+ ~pbf_reader() = default;
- inline ~pbf_reader() = default;
+ /**
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
+ */
+ void swap(pbf_reader& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_end, other.m_end);
+ swap(m_wire_type, other.m_wire_type);
+ swap(m_tag, other.m_tag);
+ }
/**
* In a boolean context the pbf_reader class evaluates to `true` if there are
* still fields available and to `false` if the last field has been read.
*/
- inline operator bool() const noexcept;
+ operator bool() const noexcept {
+ return m_data < m_end;
+ }
/**
* Return the length in bytes of the current message. If you have
@@ -272,7 +266,31 @@ public:
* @pre There must be no current field.
* @post If it returns `true` there is a current field now.
*/
- inline bool next();
+ bool next() {
+ if (m_data == m_end) {
+ return false;
+ }
+
+ const auto value = get_varint<uint32_t>();
+ m_tag = pbf_tag_type(value >> 3);
+
+ // tags 0 and 19000 to 19999 are not allowed as per
+ // https://developers.google.com/protocol-buffers/docs/proto
+ protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
+
+ m_wire_type = pbf_wire_type(value & 0x07);
+ switch (m_wire_type) {
+ case pbf_wire_type::varint:
+ case pbf_wire_type::fixed64:
+ case pbf_wire_type::length_delimited:
+ case pbf_wire_type::fixed32:
+ break;
+ default:
+ throw unknown_pbf_wire_type_exception();
+ }
+
+ return true;
+ }
/**
* Set next field with given tag in the message as the current field.
@@ -299,7 +317,16 @@ public:
* @pre There must be no current field.
* @post If it returns `true` there is a current field now with the given tag.
*/
- inline bool next(pbf_tag_type tag);
+ bool next(pbf_tag_type tag) {
+ while (next()) {
+ if (m_tag == tag) {
+ return true;
+ } else {
+ skip();
+ }
+ }
+ return false;
+ }
/**
* The tag of the current field. The tag is the field number from the
@@ -310,7 +337,9 @@ public:
* @returns tag of the current field.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline pbf_tag_type tag() const noexcept;
+ pbf_tag_type tag() const noexcept {
+ return m_tag;
+ }
/**
* Get the wire type of the current field. The wire types are:
@@ -327,7 +356,9 @@ public:
* @returns wire type of the current field.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline pbf_wire_type wire_type() const noexcept;
+ pbf_wire_type wire_type() const noexcept {
+ return m_wire_type;
+ }
/**
* Check the wire type of the current field.
@@ -335,7 +366,9 @@ public:
* @returns `true` if the current field has the given wire type.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
- inline bool has_wire_type(pbf_wire_type type) const noexcept;
+ bool has_wire_type(pbf_wire_type type) const noexcept {
+ return wire_type() == type;
+ }
/**
* Consume the current field.
@@ -343,7 +376,25 @@ public:
* @pre There must be a current field (ie. next() must have returned `true`).
* @post The current field was consumed and there is no current field now.
*/
- inline void skip();
+ void skip() {
+ protozero_assert(tag() != 0 && "call next() before calling skip()");
+ switch (wire_type()) {
+ case pbf_wire_type::varint:
+ skip_varint(&m_data, m_end);
+ break;
+ case pbf_wire_type::fixed64:
+ skip_bytes(8);
+ break;
+ case pbf_wire_type::length_delimited:
+ skip_bytes(get_length());
+ break;
+ case pbf_wire_type::fixed32:
+ skip_bytes(4);
+ break;
+ default:
+ protozero_assert(false && "can not be here because next() should have thrown already");
+ }
+ }
///@{
/**
@@ -357,7 +408,13 @@ public:
* @pre The current field must be of type "bool".
* @post The current field was consumed and there is no current field now.
*/
- inline bool get_bool();
+ bool get_bool() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+ protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
+ skip_bytes(1);
+ return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
+ }
/**
* Consume and return value of current "enum" field.
@@ -366,7 +423,7 @@ public:
* @pre The current field must be of type "enum".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_enum() {
+ int32_t get_enum() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
@@ -378,7 +435,7 @@ public:
* @pre The current field must be of type "int32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_int32() {
+ int32_t get_int32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
@@ -390,7 +447,7 @@ public:
* @pre The current field must be of type "sint32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_sint32() {
+ int32_t get_sint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int32_t>();
}
@@ -402,7 +459,7 @@ public:
* @pre The current field must be of type "uint32".
* @post The current field was consumed and there is no current field now.
*/
- inline uint32_t get_uint32() {
+ uint32_t get_uint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint32_t>();
}
@@ -414,7 +471,7 @@ public:
* @pre The current field must be of type "int64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_int64() {
+ int64_t get_int64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int64_t>();
}
@@ -426,7 +483,7 @@ public:
* @pre The current field must be of type "sint64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_sint64() {
+ int64_t get_sint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int64_t>();
}
@@ -438,7 +495,7 @@ public:
* @pre The current field must be of type "uint64".
* @post The current field was consumed and there is no current field now.
*/
- inline uint64_t get_uint64() {
+ uint64_t get_uint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint64_t>();
}
@@ -450,7 +507,11 @@ public:
* @pre The current field must be of type "fixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline uint32_t get_fixed32();
+ uint32_t get_fixed32() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<uint32_t>();
+ }
/**
* Consume and return value of current "sfixed32" field.
@@ -459,7 +520,11 @@ public:
* @pre The current field must be of type "sfixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline int32_t get_sfixed32();
+ int32_t get_sfixed32() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<int32_t>();
+ }
/**
* Consume and return value of current "fixed64" field.
@@ -468,7 +533,11 @@ public:
* @pre The current field must be of type "fixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline uint64_t get_fixed64();
+ uint64_t get_fixed64() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<uint64_t>();
+ }
/**
* Consume and return value of current "sfixed64" field.
@@ -477,7 +546,11 @@ public:
* @pre The current field must be of type "sfixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline int64_t get_sfixed64();
+ int64_t get_sfixed64() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<int64_t>();
+ }
/**
* Consume and return value of current "float" field.
@@ -486,7 +559,11 @@ public:
* @pre The current field must be of type "float".
* @post The current field was consumed and there is no current field now.
*/
- inline float get_float();
+ float get_float() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+ return get_fixed<float>();
+ }
/**
* Consume and return value of current "double" field.
@@ -495,8 +572,29 @@ public:
* @pre The current field must be of type "double".
* @post The current field was consumed and there is no current field now.
*/
- inline double get_double();
+ double get_double() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+ return get_fixed<double>();
+ }
+
+ /**
+ * Consume and return value of current "bytes", "string", or "message"
+ * field.
+ *
+ * @returns A data_view object.
+ * @pre There must be a current field (ie. next() must have returned `true`).
+ * @pre The current field must be of type "bytes", "string", or "message".
+ * @post The current field was consumed and there is no current field now.
+ */
+ data_view get_view() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
+ const auto len = get_len_and_skip();
+ return data_view{m_data-len, len};
+ }
+#ifndef PROTOZERO_STRICT_API
/**
* Consume and return value of current "bytes" or "string" field.
*
@@ -505,7 +603,13 @@ public:
* @pre The current field must be of type "bytes" or "string".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<const char*, pbf_length_type> get_data();
+ std::pair<const char*, pbf_length_type> get_data() {
+ protozero_assert(tag() != 0 && "call next() before accessing field value");
+ protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
+ const auto len = get_len_and_skip();
+ return std::make_pair(m_data-len, len);
+ }
+#endif
/**
* Consume and return value of current "bytes" field.
@@ -514,7 +618,9 @@ public:
* @pre The current field must be of type "bytes".
* @post The current field was consumed and there is no current field now.
*/
- inline std::string get_bytes();
+ std::string get_bytes() {
+ return std::string(get_view());
+ }
/**
* Consume and return value of current "string" field.
@@ -523,7 +629,9 @@ public:
* @pre The current field must be of type "string".
* @post The current field was consumed and there is no current field now.
*/
- inline std::string get_string();
+ std::string get_string() {
+ return std::string(get_view());
+ }
/**
* Consume and return value of current "message" field.
@@ -532,136 +640,35 @@ public:
* @pre The current field must be of type "message".
* @post The current field was consumed and there is no current field now.
*/
- inline pbf_reader get_message() {
- return pbf_reader(get_data());
+ pbf_reader get_message() {
+ return pbf_reader(get_view());
}
///@}
-private:
-
- template <typename T>
- class const_varint_iterator : public std::iterator<std::forward_iterator_tag, T> {
-
- protected:
-
- const char* m_data;
- const char* m_end;
-
- public:
-
- const_varint_iterator() noexcept :
- m_data(nullptr),
- m_end(nullptr) {
- }
-
- const_varint_iterator(const char *data, const char* end) noexcept :
- m_data(data),
- m_end(end) {
- }
-
- const_varint_iterator(const const_varint_iterator&) noexcept = default;
- const_varint_iterator(const_varint_iterator&&) noexcept = default;
-
- const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
- const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
-
- ~const_varint_iterator() noexcept = default;
-
- T operator*() {
- const char* d = m_data; // will be thrown away
- return static_cast<T>(decode_varint(&d, m_end));
- }
-
- const_varint_iterator& operator++() {
- // Ignore the result, we call decode_varint() just for the
- // side-effect of updating m_data.
- decode_varint(&m_data, m_end);
- return *this;
- }
-
- const_varint_iterator operator++(int) {
- const const_varint_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
-
- bool operator==(const const_varint_iterator& rhs) const noexcept {
- return m_data == rhs.m_data && m_end == rhs.m_end;
- }
-
- bool operator!=(const const_varint_iterator& rhs) const noexcept {
- return !(*this == rhs);
- }
-
- }; // class const_varint_iterator
-
- template <typename T>
- class const_svarint_iterator : public const_varint_iterator<T> {
-
- public:
-
- const_svarint_iterator() noexcept :
- const_varint_iterator<T>() {
- }
-
- const_svarint_iterator(const char *data, const char* end) noexcept :
- const_varint_iterator<T>(data, end) {
- }
-
- const_svarint_iterator(const const_svarint_iterator&) = default;
- const_svarint_iterator(const_svarint_iterator&&) = default;
-
- const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
- const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
-
- ~const_svarint_iterator() = default;
-
- T operator*() {
- const char* d = this->m_data; // will be thrown away
- return static_cast<T>(decode_zigzag64(decode_varint(&d, this->m_end)));
- }
-
- const_svarint_iterator& operator++() {
- // Ignore the result, we call decode_varint() just for the
- // side-effect of updating m_data.
- decode_varint(&this->m_data, this->m_end);
- return *this;
- }
-
- const_svarint_iterator operator++(int) {
- const const_svarint_iterator tmp(*this);
- ++(*this);
- return tmp;
- }
-
- }; // class const_svarint_iterator
-
-public:
-
/// Forward iterator for iterating over bool (int32 varint) values.
- typedef const_varint_iterator< int32_t> const_bool_iterator;
+ using const_bool_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over enum (int32 varint) values.
- typedef const_varint_iterator< int32_t> const_enum_iterator;
+ using const_enum_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over int32 (varint) values.
- typedef const_varint_iterator< int32_t> const_int32_iterator;
+ using const_int32_iterator = const_varint_iterator< int32_t>;
/// Forward iterator for iterating over sint32 (varint) values.
- typedef const_svarint_iterator<int32_t> const_sint32_iterator;
+ using const_sint32_iterator = const_svarint_iterator<int32_t>;
/// Forward iterator for iterating over uint32 (varint) values.
- typedef const_varint_iterator<uint32_t> const_uint32_iterator;
+ using const_uint32_iterator = const_varint_iterator<uint32_t>;
/// Forward iterator for iterating over int64 (varint) values.
- typedef const_varint_iterator< int64_t> const_int64_iterator;
+ using const_int64_iterator = const_varint_iterator< int64_t>;
/// Forward iterator for iterating over sint64 (varint) values.
- typedef const_svarint_iterator<int64_t> const_sint64_iterator;
+ using const_sint64_iterator = const_svarint_iterator<int64_t>;
/// Forward iterator for iterating over uint64 (varint) values.
- typedef const_varint_iterator<uint64_t> const_uint64_iterator;
+ using const_uint64_iterator = const_varint_iterator<uint64_t>;
///@{
/**
@@ -677,7 +684,9 @@ public:
* @pre The current field must be of type "repeated packed bool".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> get_packed_bool();
+ iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
+ return get_packed<pbf_reader::const_bool_iterator>();
+ }
/**
* Consume current "repeated packed enum" field.
@@ -688,7 +697,9 @@ public:
* @pre The current field must be of type "repeated packed enum".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> get_packed_enum();
+ iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
+ return get_packed<pbf_reader::const_enum_iterator>();
+ }
/**
* Consume current "repeated packed int32" field.
@@ -699,7 +710,9 @@ public:
* @pre The current field must be of type "repeated packed int32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> get_packed_int32();
+ iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
+ return get_packed<pbf_reader::const_int32_iterator>();
+ }
/**
* Consume current "repeated packed sint32" field.
@@ -710,7 +723,9 @@ public:
* @pre The current field must be of type "repeated packed sint32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> get_packed_sint32();
+ iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
+ return get_packed<pbf_reader::const_sint32_iterator>();
+ }
/**
* Consume current "repeated packed uint32" field.
@@ -721,7 +736,9 @@ public:
* @pre The current field must be of type "repeated packed uint32".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> get_packed_uint32();
+ iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
+ return get_packed<pbf_reader::const_uint32_iterator>();
+ }
/**
* Consume current "repeated packed int64" field.
@@ -732,7 +749,9 @@ public:
* @pre The current field must be of type "repeated packed int64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> get_packed_int64();
+ iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
+ return get_packed<pbf_reader::const_int64_iterator>();
+ }
/**
* Consume current "repeated packed sint64" field.
@@ -743,7 +762,9 @@ public:
* @pre The current field must be of type "repeated packed sint64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> get_packed_sint64();
+ iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
+ return get_packed<pbf_reader::const_sint64_iterator>();
+ }
/**
* Consume current "repeated packed uint64" field.
@@ -754,7 +775,9 @@ public:
* @pre The current field must be of type "repeated packed uint64".
* @post The current field was consumed and there is no current field now.
*/
- inline std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> get_packed_uint64();
+ iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
+ return get_packed<pbf_reader::const_uint64_iterator>();
+ }
/**
* Consume current "repeated packed fixed32" field.
@@ -765,7 +788,7 @@ public:
* @pre The current field must be of type "repeated packed fixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
+ auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
return packed_fixed<uint32_t>();
}
@@ -778,7 +801,7 @@ public:
* @pre The current field must be of type "repeated packed sfixed32".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
+ auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
return packed_fixed<int32_t>();
}
@@ -791,7 +814,7 @@ public:
* @pre The current field must be of type "repeated packed fixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
+ auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
return packed_fixed<uint64_t>();
}
@@ -804,7 +827,7 @@ public:
* @pre The current field must be of type "repeated packed sfixed64".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
+ auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
return packed_fixed<int64_t>();
}
@@ -817,7 +840,7 @@ public:
* @pre The current field must be of type "repeated packed float".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_float() -> decltype(packed_fixed<float>()) {
+ auto get_packed_float() -> decltype(packed_fixed<float>()) {
return packed_fixed<float>();
}
@@ -830,7 +853,7 @@ public:
* @pre The current field must be of type "repeated packed double".
* @post The current field was consumed and there is no current field now.
*/
- inline auto get_packed_double() -> decltype(packed_fixed<double>()) {
+ auto get_packed_double() -> decltype(packed_fixed<double>()) {
return packed_fixed<double>();
}
@@ -838,238 +861,14 @@ public:
}; // class pbf_reader
-pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept
- : m_data(data),
- m_end(data + length),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::pbf_reader(std::pair<const char *, std::size_t> data) noexcept
- : m_data(data.first),
- m_end(data.first + data.second),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::pbf_reader(const std::string& data) noexcept
- : m_data(data.data()),
- m_end(data.data() + data.size()),
- m_wire_type(pbf_wire_type::unknown),
- m_tag(0) {
-}
-
-pbf_reader::operator bool() const noexcept {
- return m_data < m_end;
-}
-
-bool pbf_reader::next() {
- if (m_data == m_end) {
- return false;
- }
-
- auto value = get_varint<uint32_t>();
- m_tag = value >> 3;
-
- // tags 0 and 19000 to 19999 are not allowed as per
- // https://developers.google.com/protocol-buffers/docs/proto
- protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
-
- m_wire_type = pbf_wire_type(value & 0x07);
- switch (m_wire_type) {
- case pbf_wire_type::varint:
- case pbf_wire_type::fixed64:
- case pbf_wire_type::length_delimited:
- case pbf_wire_type::fixed32:
- break;
- default:
- throw unknown_pbf_wire_type_exception();
- }
-
- return true;
-}
-
-bool pbf_reader::next(pbf_tag_type requested_tag) {
- while (next()) {
- if (m_tag == requested_tag) {
- return true;
- } else {
- skip();
- }
- }
- return false;
-}
-
-pbf_tag_type pbf_reader::tag() const noexcept {
- return m_tag;
-}
-
-pbf_wire_type pbf_reader::wire_type() const noexcept {
- return m_wire_type;
-}
-
-bool pbf_reader::has_wire_type(pbf_wire_type type) const noexcept {
- return wire_type() == type;
-}
-
-void pbf_reader::skip_bytes(pbf_length_type len) {
- if (m_data + len > m_end) {
- throw end_of_buffer_exception();
- }
- m_data += len;
-
-// In debug builds reset the tag to zero so that we can detect (some)
-// wrong code.
-#ifndef NDEBUG
- m_tag = 0;
-#endif
-}
-
-void pbf_reader::skip() {
- protozero_assert(tag() != 0 && "call next() before calling skip()");
- switch (wire_type()) {
- case pbf_wire_type::varint:
- (void)get_uint32(); // called for the side-effect of skipping value
- break;
- case pbf_wire_type::fixed64:
- skip_bytes(8);
- break;
- case pbf_wire_type::length_delimited:
- skip_bytes(get_length());
- break;
- case pbf_wire_type::fixed32:
- skip_bytes(4);
- break;
- default:
- protozero_assert(false && "can not be here because next() should have thrown already");
- }
-}
-
-pbf_length_type pbf_reader::get_len_and_skip() {
- auto len = get_length();
- skip_bytes(len);
- return len;
-}
-
-template <typename T>
-T pbf_reader::get_varint() {
- return static_cast<T>(decode_varint(&m_data, m_end));
-}
-
-template <typename T>
-T pbf_reader::get_svarint() {
- protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
- return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
-}
-
-uint32_t pbf_reader::get_fixed32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<uint32_t>();
-}
-
-int32_t pbf_reader::get_sfixed32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<int32_t>();
-}
-
-uint64_t pbf_reader::get_fixed64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<uint64_t>();
-}
-
-int64_t pbf_reader::get_sfixed64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<int64_t>();
-}
-
-float pbf_reader::get_float() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
- return get_fixed<float>();
-}
-
-double pbf_reader::get_double() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
- return get_fixed<double>();
-}
-
-bool pbf_reader::get_bool() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
- protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
- skip_bytes(1);
- return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
-}
-
-std::pair<const char*, pbf_length_type> pbf_reader::get_data() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
- auto len = get_len_and_skip();
- return std::make_pair(m_data-len, len);
-}
-
-std::string pbf_reader::get_bytes() {
- auto d = get_data();
- return std::string(d.first, d.second);
-}
-
-std::string pbf_reader::get_string() {
- return get_bytes();
-}
-
-std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> pbf_reader::get_packed_bool() {
- return get_packed_int32();
-}
-
-std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> pbf_reader::get_packed_enum() {
- return get_packed_int32();
-}
-
-std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> pbf_reader::get_packed_int32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_int32_iterator(m_data-len, m_data),
- pbf_reader::const_int32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> pbf_reader::get_packed_uint32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_uint32_iterator(m_data-len, m_data),
- pbf_reader::const_uint32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> pbf_reader::get_packed_sint32() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_sint32_iterator(m_data-len, m_data),
- pbf_reader::const_sint32_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> pbf_reader::get_packed_int64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_int64_iterator(m_data-len, m_data),
- pbf_reader::const_int64_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> pbf_reader::get_packed_uint64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_uint64_iterator(m_data-len, m_data),
- pbf_reader::const_uint64_iterator(m_data, m_data));
-}
-
-std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> pbf_reader::get_packed_sint64() {
- protozero_assert(tag() != 0 && "call next() before accessing field value");
- auto len = get_len_and_skip();
- return std::make_pair(pbf_reader::const_sint64_iterator(m_data-len, m_data),
- pbf_reader::const_sint64_iterator(m_data, m_data));
+/**
+ * Swap two pbf_reader objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
+ lhs.swap(rhs);
}
} // end namespace protozero
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
index 422e147..3ce0f14 100644
--- a/include/protozero/pbf_writer.hpp
+++ b/include/protozero/pbf_writer.hpp
@@ -22,6 +22,7 @@ documentation.
#include <iterator>
#include <limits>
#include <string>
+#include <utility>
#include <protozero/config.hpp>
#include <protozero/types.hpp>
@@ -68,38 +69,38 @@ class pbf_writer {
// parent to the position where the data of the submessage is written to.
std::size_t m_pos = 0;
- inline void add_varint(uint64_t value) {
+ void add_varint(uint64_t value) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
write_varint(std::back_inserter(*m_data), value);
}
- inline void add_field(pbf_tag_type tag, pbf_wire_type type) {
+ void add_field(pbf_tag_type tag, pbf_wire_type type) {
protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
- uint32_t b = (tag << 3) | uint32_t(type);
+ const uint32_t b = (tag << 3) | uint32_t(type);
add_varint(b);
}
- inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
+ void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::varint);
add_varint(value);
}
template <typename T>
- inline void add_fixed(T value) {
+ void add_fixed(T value) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
#else
- auto size = m_data->size();
+ const auto size = m_data->size();
m_data->resize(size + sizeof(T));
byteswap<sizeof(T)>(reinterpret_cast<const char*>(&value), const_cast<char*>(m_data->data() + size));
#endif
}
template <typename T, typename It>
- inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
if (first == last) {
return;
}
@@ -112,12 +113,12 @@ class pbf_writer {
}
template <typename T, typename It>
- inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
if (first == last) {
return;
}
- auto length = std::distance(first, last);
+ const auto length = std::distance(first, last);
add_length_varint(tag, sizeof(T) * pbf_length_type(length));
reserve(sizeof(T) * std::size_t(length));
@@ -127,7 +128,7 @@ class pbf_writer {
}
template <typename It>
- inline void add_packed_varint(pbf_tag_type tag, It first, It last) {
+ void add_packed_varint(pbf_tag_type tag, It first, It last) {
if (first == last) {
return;
}
@@ -140,7 +141,7 @@ class pbf_writer {
}
template <typename It>
- inline void add_packed_svarint(pbf_tag_type tag, It first, It last) {
+ void add_packed_svarint(pbf_tag_type tag, It first, It last) {
if (first == last) {
return;
}
@@ -155,14 +156,14 @@ class pbf_writer {
// The number of bytes to reserve for the varint holding the length of
// a length-delimited field. The length has to fit into pbf_length_type,
// and a varint needs 8 bit for every 7 bit.
- static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
+ static constexpr const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
// If m_rollpack_pos is set to this special value, it means that when
// the submessage is closed, nothing needs to be done, because the length
// of the submessage has already been written correctly.
- static const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
+ static constexpr const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
- inline void open_submessage(pbf_tag_type tag, std::size_t size) {
+ void open_submessage(pbf_tag_type tag, std::size_t size) {
protozero_assert(m_pos == 0);
protozero_assert(m_data);
if (size == 0) {
@@ -177,7 +178,7 @@ class pbf_writer {
m_pos = m_data->size();
}
- inline void rollback_submessage() {
+ void rollback_submessage() {
protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data);
@@ -185,20 +186,20 @@ class pbf_writer {
m_pos = 0;
}
- inline void commit_submessage() {
+ void commit_submessage() {
protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data);
- auto length = pbf_length_type(m_data->size() - m_pos);
+ const auto length = pbf_length_type(m_data->size() - m_pos);
protozero_assert(m_data->size() >= m_pos - reserve_bytes);
- auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
+ const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
m_pos = 0;
}
- inline void close_submessage() {
+ void close_submessage() {
protozero_assert(m_data);
if (m_pos == 0 || m_rollback_pos == size_is_known) {
return;
@@ -210,7 +211,7 @@ class pbf_writer {
}
}
- inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
+ void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
add_field(tag, pbf_wire_type::length_delimited);
add_varint(length);
}
@@ -222,7 +223,7 @@ public:
* stores a reference to that string and adds all data to it. The string
* doesn't have to be empty. The pbf_writer will just append data.
*/
- inline explicit pbf_writer(std::string& data) noexcept :
+ explicit pbf_writer(std::string& data) noexcept :
m_data(&data),
m_parent_writer(nullptr),
m_pos(0) {
@@ -232,7 +233,7 @@ public:
* Create a writer without a data store. In this form the writer can not
* be used!
*/
- inline pbf_writer() noexcept :
+ pbf_writer() noexcept :
m_data(nullptr),
m_parent_writer(nullptr),
m_pos(0) {
@@ -248,7 +249,7 @@ public:
* Setting this allows some optimizations but is only possible in
* a few very specific cases.
*/
- inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
+ pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
m_data(parent_writer.m_data),
m_parent_writer(&parent_writer),
m_pos(0) {
@@ -262,18 +263,31 @@ public:
pbf_writer& operator=(const pbf_writer&) noexcept = default;
/// A pbf_writer object can be moved
- inline pbf_writer(pbf_writer&&) noexcept = default;
+ pbf_writer(pbf_writer&&) noexcept = default;
/// A pbf_writer object can be moved
- inline pbf_writer& operator=(pbf_writer&&) noexcept = default;
+ pbf_writer& operator=(pbf_writer&&) noexcept = default;
- inline ~pbf_writer() {
+ ~pbf_writer() {
if (m_parent_writer) {
m_parent_writer->close_submessage();
}
}
/**
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
+ */
+ void swap(pbf_writer& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_parent_writer, other.m_parent_writer);
+ swap(m_rollback_pos, other.m_rollback_pos);
+ swap(m_pos, other.m_pos);
+ }
+
+ /**
* Reserve size bytes in the underlying message store in addition to
* whatever the message store already holds. So unlike
* the `std::string::reserve()` method this is not an absolute size,
@@ -286,7 +300,14 @@ public:
m_data->reserve(m_data->size() + size);
}
- inline void rollback() {
+ /**
+ * Cancel writing of this submessage. The complete submessage will be
+ * removed as if it was never created and no fields were added.
+ *
+ * @pre Must be a pbf_writer of a submessage, ie one opened with the
+ * pbf_writer constructor taking a parent message.
+ */
+ void rollback() {
protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
m_parent_writer->rollback_submessage();
@@ -304,7 +325,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_bool(pbf_tag_type tag, bool value) {
+ void add_bool(pbf_tag_type tag, bool value) {
add_field(tag, pbf_wire_type::varint);
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
@@ -317,7 +338,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_enum(pbf_tag_type tag, int32_t value) {
+ void add_enum(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -327,7 +348,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_int32(pbf_tag_type tag, int32_t value) {
+ void add_int32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -337,7 +358,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sint32(pbf_tag_type tag, int32_t value) {
+ void add_sint32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, encode_zigzag32(value));
}
@@ -347,7 +368,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_uint32(pbf_tag_type tag, uint32_t value) {
+ void add_uint32(pbf_tag_type tag, uint32_t value) {
add_tagged_varint(tag, value);
}
@@ -357,7 +378,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_int64(pbf_tag_type tag, int64_t value) {
+ void add_int64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, uint64_t(value));
}
@@ -367,7 +388,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sint64(pbf_tag_type tag, int64_t value) {
+ void add_sint64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, encode_zigzag64(value));
}
@@ -377,7 +398,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_uint64(pbf_tag_type tag, uint64_t value) {
+ void add_uint64(pbf_tag_type tag, uint64_t value) {
add_tagged_varint(tag, value);
}
@@ -387,7 +408,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_fixed32(pbf_tag_type tag, uint32_t value) {
+ void add_fixed32(pbf_tag_type tag, uint32_t value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<uint32_t>(value);
}
@@ -398,7 +419,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sfixed32(pbf_tag_type tag, int32_t value) {
+ void add_sfixed32(pbf_tag_type tag, int32_t value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<int32_t>(value);
}
@@ -409,7 +430,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_fixed64(pbf_tag_type tag, uint64_t value) {
+ void add_fixed64(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<uint64_t>(value);
}
@@ -420,7 +441,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_sfixed64(pbf_tag_type tag, int64_t value) {
+ void add_sfixed64(pbf_tag_type tag, int64_t value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<int64_t>(value);
}
@@ -431,7 +452,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_float(pbf_tag_type tag, float value) {
+ void add_float(pbf_tag_type tag, float value) {
add_field(tag, pbf_wire_type::fixed32);
add_fixed<float>(value);
}
@@ -442,7 +463,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_double(pbf_tag_type tag, double value) {
+ void add_double(pbf_tag_type tag, double value) {
add_field(tag, pbf_wire_type::fixed64);
add_fixed<double>(value);
}
@@ -454,7 +475,7 @@ public:
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
- inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
protozero_assert(m_data);
protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
@@ -468,7 +489,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_bytes(pbf_tag_type tag, const std::string& value) {
+ void add_bytes(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_bytes(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -479,7 +510,7 @@ public:
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
- inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size);
}
@@ -489,7 +520,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written
*/
- inline void add_string(pbf_tag_type tag, const std::string& value) {
+ void add_string(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_string(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -500,7 +541,7 @@ public:
* @param tag Tag (field number) of the field
* @param value Pointer to value to be written
*/
- inline void add_string(pbf_tag_type tag, const char* value) {
+ void add_string(pbf_tag_type tag, const char* value) {
add_bytes(tag, value, std::strlen(value));
}
@@ -511,7 +552,7 @@ public:
* @param value Pointer to message to be written
* @param size Length of the message
*/
- inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
+ void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size);
}
@@ -521,7 +562,17 @@ public:
* @param tag Tag (field number) of the field
* @param value Value to be written. The value must be a complete message.
*/
- inline void add_message(pbf_tag_type tag, const std::string& value) {
+ void add_message(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(pbf_tag_type tag, const std::string& value) {
add_bytes(tag, value.data(), value.size());
}
@@ -535,126 +586,126 @@ public:
/**
* Add "repeated packed bool" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to bool.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed enum" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed int32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed sint32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last);
}
/**
* Add "repeated packed uint32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed int64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed sint64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last);
}
/**
* Add "repeated packed uint64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last);
}
/**
* Add "repeated packed fixed32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -662,14 +713,14 @@ public:
/**
* Add "repeated packed sfixed32" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -677,14 +728,14 @@ public:
/**
* Add "repeated packed fixed64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -692,14 +743,14 @@ public:
/**
* Add "repeated packed sfixed64" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -707,14 +758,14 @@ public:
/**
* Add "repeated packed float" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to float.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<float, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -722,14 +773,14 @@ public:
/**
* Add "repeated packed double" field to data.
*
- * @tparam InputIterator An type satisfying the InputIterator concept.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to double.
* @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data
*/
template <typename InputIterator>
- inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<double, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category());
}
@@ -742,6 +793,16 @@ public:
}; // class pbf_writer
+/**
+ * Swap two pbf_writer objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
namespace detail {
class packed_field {
@@ -817,19 +878,46 @@ namespace detail {
} // end namespace detail
+/// Class for generating packed repeated bool fields.
using packed_field_bool = detail::packed_field_varint<bool>;
+
+/// Class for generating packed repeated enum fields.
using packed_field_enum = detail::packed_field_varint<int32_t>;
+
+/// Class for generating packed repeated int32 fields.
using packed_field_int32 = detail::packed_field_varint<int32_t>;
+
+/// Class for generating packed repeated sint32 fields.
using packed_field_sint32 = detail::packed_field_svarint<int32_t>;
+
+/// Class for generating packed repeated uint32 fields.
using packed_field_uint32 = detail::packed_field_varint<uint32_t>;
+
+/// Class for generating packed repeated int64 fields.
using packed_field_int64 = detail::packed_field_varint<int64_t>;
+
+/// Class for generating packed repeated sint64 fields.
using packed_field_sint64 = detail::packed_field_svarint<int64_t>;
+
+/// Class for generating packed repeated uint64 fields.
using packed_field_uint64 = detail::packed_field_varint<uint64_t>;
+
+/// Class for generating packed repeated fixed32 fields.
using packed_field_fixed32 = detail::packed_field_fixed<uint32_t>;
+
+/// Class for generating packed repeated sfixed32 fields.
using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
+
+/// Class for generating packed repeated fixed64 fields.
using packed_field_fixed64 = detail::packed_field_fixed<uint64_t>;
+
+/// Class for generating packed repeated sfixed64 fields.
using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
+
+/// Class for generating packed repeated float fields.
using packed_field_float = detail::packed_field_fixed<float>;
+
+/// Class for generating packed repeated double fields.
using packed_field_double = detail::packed_field_fixed<double>;
} // end namespace protozero
diff --git a/include/protozero/types.hpp b/include/protozero/types.hpp
index 6856b3d..8b04638 100644
--- a/include/protozero/types.hpp
+++ b/include/protozero/types.hpp
@@ -16,33 +16,173 @@ documentation.
* @brief Contains the declaration of low-level types used in the pbf format.
*/
+#include <cstddef>
#include <cstdint>
+#include <cstring>
+#include <string>
+#include <utility>
+
+#include <protozero/config.hpp>
namespace protozero {
+/**
+ * The type used for field tags (field numbers).
+ */
+using pbf_tag_type = uint32_t;
+
+/**
+ * The type used to encode type information.
+ * See the table on
+ * https://developers.google.com/protocol-buffers/docs/encoding
+ */
+enum class pbf_wire_type : uint32_t {
+ varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
+ fixed64 = 1, // fixed64, sfixed64, double
+ length_delimited = 2, // string, bytes, embedded messages,
+ // packed repeated fields
+ fixed32 = 5, // fixed32, sfixed32, float
+ unknown = 99 // used for default setting in this library
+};
+
+/**
+ * The type used for length values, such as the length of a field.
+ */
+using pbf_length_type = uint32_t;
+
+#ifdef PROTOZERO_USE_VIEW
+using data_view = PROTOZERO_USE_VIEW;
+#else
+
+/**
+ * Holds a pointer to some data and a length.
+ *
+ * This class is supposed to be compatible with the std::string_view
+ * that will be available in C++17.
+ */
+class data_view {
+
+ const char* m_data;
+ std::size_t m_size;
+
+public:
+
+ /**
+ * Default constructor. Construct an empty data_view.
+ */
+ constexpr data_view() noexcept
+ : m_data(nullptr),
+ m_size(0) {
+ }
+
+ /**
+ * Create data_view from pointer and size.
+ *
+ * @param data Pointer to the data.
+ * @param size Length of the data.
+ */
+ constexpr data_view(const char* data, std::size_t size) noexcept
+ : m_data(data),
+ m_size(size) {
+ }
+
/**
- * The type used for field tags (field numbers).
+ * Create data_view from string.
+ *
+ * @param str String with the data.
*/
- typedef uint32_t pbf_tag_type;
+ data_view(const std::string& str) noexcept
+ : m_data(str.data()),
+ m_size(str.size()) {
+ }
/**
- * The type used to encode type information.
- * See the table on
- * https://developers.google.com/protocol-buffers/docs/encoding
+ * Create data_view from zero-terminated string.
+ *
+ * @param data Pointer to the data.
*/
- enum class pbf_wire_type : uint32_t {
- varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
- fixed64 = 1, // fixed64, sfixed64, double
- length_delimited = 2, // string, bytes, embedded messages,
- // packed repeated fields
- fixed32 = 5, // fixed32, sfixed32, float
- unknown = 99 // used for default setting in this library
- };
+ data_view(const char* data) noexcept
+ : m_data(data),
+ m_size(std::strlen(data)) {
+ }
/**
- * The type used for length values, such as the length of a field.
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
*/
- typedef uint32_t pbf_length_type;
+ void swap(data_view& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_size, other.m_size);
+ }
+
+ /// Return pointer to data.
+ constexpr const char* data() const noexcept {
+ return m_data;
+ }
+
+ /// Return length of data in bytes.
+ constexpr std::size_t size() const noexcept {
+ return m_size;
+ }
+
+ /**
+ * Convert data view to string.
+ *
+ * @pre Must not be default constructed data_view.
+ */
+ std::string to_string() const {
+ protozero_assert(m_data);
+ return std::string{m_data, m_size};
+ }
+
+ /**
+ * Convert data view to string.
+ *
+ * @pre Must not be default constructed data_view.
+ */
+ explicit operator std::string() const {
+ protozero_assert(m_data);
+ return std::string{m_data, m_size};
+ }
+
+}; // class data_view
+
+/**
+ * Swap two data_view objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline void swap(data_view& lhs, data_view& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+/**
+ * Two data_view instances are equal if they have the same size and the
+ * same content.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline bool operator==(data_view& lhs, data_view& rhs) noexcept {
+ return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data());
+}
+
+/**
+ * Two data_view instances are not equal if they have different sizes or the
+ * content differs.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+inline bool operator!=(data_view& lhs, data_view& rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+#endif
+
} // end namespace protozero
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
index 4242df9..f6142d1 100644
--- a/include/protozero/varint.hpp
+++ b/include/protozero/varint.hpp
@@ -23,13 +23,54 @@ documentation.
namespace protozero {
/**
- * The maximum length of a 64bit varint.
+ * The maximum length of a 64 bit varint.
*/
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
-// from https://github.com/facebook/folly/blob/master/folly/Varint.h
+namespace detail {
+
+ // from https://github.com/facebook/folly/blob/master/folly/Varint.h
+ inline uint64_t decode_varint_impl(const char** data, const char* end) {
+ const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
+ const int8_t* iend = reinterpret_cast<const int8_t*>(end);
+ const int8_t* p = begin;
+ uint64_t val = 0;
+
+ if (iend - begin >= max_varint_length) { // fast path
+ do {
+ int64_t b;
+ b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
+ b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
+ throw varint_too_long_exception();
+ } while (false);
+ } else {
+ int shift = 0;
+ while (p != iend && *p < 0) {
+ val |= uint64_t(*p++ & 0x7f) << shift;
+ shift += 7;
+ }
+ if (p == iend) {
+ throw end_of_buffer_exception();
+ }
+ val |= uint64_t(*p++) << shift;
+ }
+
+ *data = reinterpret_cast<const char*>(p);
+ return val;
+ }
+
+} // end namespace detail
+
/**
- * Decode a 64bit varint.
+ * Decode a 64 bit varint.
*
* Strong exception guarantee: if there is an exception the data pointer will
* not be changed.
@@ -39,54 +80,68 @@ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
* @param[in] end Pointer one past the end of the input data.
* @returns The decoded integer
* @throws varint_too_long_exception if the varint is longer then the maximum
- * length that would fit in a 64bit int. Usually this means your data
+ * length that would fit in a 64 bit int. Usually this means your data
* is corrupted or you are trying to read something as a varint that
* isn't.
* @throws end_of_buffer_exception if the *end* of the buffer was reached
* before the end of the varint.
*/
inline uint64_t decode_varint(const char** data, const char* end) {
+ // If this is a one-byte varint, decode it here.
+ if (end != *data && ((**data & 0x80) == 0)) {
+ uint64_t val = uint64_t(**data);
+ ++(*data);
+ return val;
+ }
+ // If this varint is more than one byte, defer to complete implementation.
+ return detail::decode_varint_impl(data, end);
+}
+
+/**
+ * Skip over a varint.
+ *
+ * Strong exception guarantee: if there is an exception the data pointer will
+ * not be changed.
+ *
+ * @param[in,out] data Pointer to pointer to the input data. After the function
+ * returns this will point to the next data to be read.
+ * @param[in] end Pointer one past the end of the input data.
+ * @throws end_of_buffer_exception if the *end* of the buffer was reached
+ * before the end of the varint.
+ */
+inline void skip_varint(const char** data, const char* end) {
const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
const int8_t* iend = reinterpret_cast<const int8_t*>(end);
const int8_t* p = begin;
- uint64_t val = 0;
-
- if (iend - begin >= max_varint_length) { // fast path
- do {
- int64_t b;
- b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
- b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
- throw varint_too_long_exception();
- } while (false);
- } else {
- int shift = 0;
- while (p != iend && *p < 0) {
- val |= uint64_t(*p++ & 0x7f) << shift;
- shift += 7;
- }
- if (p == iend) {
- throw end_of_buffer_exception();
- }
- val |= uint64_t(*p++) << shift;
+
+ while (p != iend && *p < 0) {
+ ++p;
+ }
+
+ if (p >= begin + max_varint_length) {
+ throw varint_too_long_exception();
}
+ if (p == iend) {
+ throw end_of_buffer_exception();
+ }
+
+ ++p;
+
*data = reinterpret_cast<const char*>(p);
- return val;
}
/**
- * Varint-encode a 64bit integer.
+ * Varint encode a 64 bit integer.
+ *
+ * @tparam T An output iterator type.
+ * @param data Output iterator the varint encoded value will be written to
+ * byte by byte.
+ * @param value The integer that will be encoded.
+ * @throws Any exception thrown by increment or dereference operator on data.
*/
-template <typename OutputIterator>
-inline int write_varint(OutputIterator data, uint64_t value) {
+template <typename T>
+inline int write_varint(T data, uint64_t value) {
int n=1;
while (value >= 0x80) {
diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp
index 7b60e2e..d427941 100644
--- a/include/protozero/version.hpp
+++ b/include/protozero/version.hpp
@@ -10,13 +10,26 @@ documentation.
*****************************************************************************/
+/**
+ * @file version.hpp
+ *
+ * @brief Contains macros defining the protozero version.
+ */
+
+/// The major version number
#define PROTOZERO_VERSION_MAJOR 1
-#define PROTOZERO_VERSION_MINOR 3
+
+/// The minor version number
+#define PROTOZERO_VERSION_MINOR 4
+
+/// The patch number
#define PROTOZERO_VERSION_PATCH 0
+/// The complete version number
#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
-#define PROTOZERO_VERSION_STRING "1.3.0"
+/// Version number as string
+#define PROTOZERO_VERSION_STRING "1.4.0"
#endif // PROTOZERO_VERSION_HPP
diff --git a/package.json b/package.json
index 41806a9..679d59c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "protozero",
- "version": "1.3.0",
+ "version": "1.4.0",
"description": "Minimalist protocol buffer decoder and encoder in C++",
"main": "./package.json",
"repository" : {
diff --git a/test/include/packed_access.hpp b/test/include/packed_access.hpp
new file mode 100644
index 0000000..3d8b98d
--- /dev/null
+++ b/test/include/packed_access.hpp
@@ -0,0 +1,234 @@
+
+#define PBF_TYPE_NAME PROTOZERO_TEST_STRING(PBF_TYPE)
+#define GET_TYPE PROTOZERO_TEST_CONCAT(get_packed_, PBF_TYPE)
+#define ADD_TYPE PROTOZERO_TEST_CONCAT(add_packed_, PBF_TYPE)
+
+using packed_field_type = PROTOZERO_TEST_CONCAT(protozero::packed_field_, PBF_TYPE);
+
+TEST_CASE("read repeated packed field: " PBF_TYPE_NAME) {
+
+ // Run these tests twice, the second time we basically move the data
+ // one byte down in the buffer. It doesn't matter how the data or buffer
+ // is aligned before that, in at least one of these cases the ints will
+ // not be aligned properly. So we test that even in that case the ints
+ // will be extracted properly.
+
+ for (std::string::size_type n = 0; n < 2; ++n) {
+
+ std::string abuffer;
+ abuffer.reserve(1000);
+ abuffer.append(n, '\0');
+
+ SECTION("empty") {
+ abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
+
+ protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+ REQUIRE(!item.next());
+ }
+
+ SECTION("one") {
+ abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
+
+ protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+ REQUIRE(item.next());
+ const auto it_range = item.GET_TYPE();
+ REQUIRE(!item.next());
+
+ REQUIRE(it_range.begin() != it_range.end());
+ REQUIRE(*it_range.begin() == 17);
+ REQUIRE(std::next(it_range.begin()) == it_range.end());
+ }
+
+ SECTION("many") {
+ abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
+
+ protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+ REQUIRE(item.next());
+ const auto it_range = item.GET_TYPE();
+ REQUIRE(!item.next());
+
+ auto it = it_range.begin();
+ REQUIRE(it != it_range.end());
+ REQUIRE(*it++ == 17);
+ REQUIRE(*it++ == 200);
+ REQUIRE(*it++ == 0);
+ REQUIRE(*it++ == 1);
+ REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
+#if PBF_TYPE_IS_SIGNED
+ REQUIRE(*it++ == -200);
+ REQUIRE(*it++ == -1);
+ REQUIRE(*it++ == std::numeric_limits<cpp_type>::min());
+#endif
+ REQUIRE(it == it_range.end());
+ }
+
+ SECTION("swap iterator range") {
+ abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
+
+ protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+ REQUIRE(item.next());
+ auto it_range1 = item.GET_TYPE();
+ REQUIRE(!item.next());
+
+ decltype(it_range1) it_range;
+ using std::swap;
+ swap(it_range, it_range1);
+
+ auto it = it_range.begin();
+ REQUIRE(it != it_range.end());
+ REQUIRE(*it++ == 17);
+ REQUIRE(*it++ == 200);
+ REQUIRE(*it++ == 0);
+ REQUIRE(*it++ == 1);
+ REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
+ }
+
+ SECTION("end_of_buffer") {
+ abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
+
+ for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
+ protozero::pbf_reader item(abuffer.data() + n, i);
+ REQUIRE(item.next());
+ REQUIRE_THROWS_AS(item.GET_TYPE(), protozero::end_of_buffer_exception);
+ }
+ }
+
+ }
+
+}
+
+TEST_CASE("write repeated packed field: " PBF_TYPE_NAME) {
+
+ std::string buffer;
+ protozero::pbf_writer pw(buffer);
+
+ SECTION("empty") {
+ cpp_type data[] = { 17 };
+ pw.ADD_TYPE(1, std::begin(data), std::begin(data) /* !!!! */);
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
+ }
+
+ SECTION("one") {
+ cpp_type data[] = { 17 };
+ pw.ADD_TYPE(1, std::begin(data), std::end(data));
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
+ }
+
+ SECTION("many") {
+ cpp_type data[] = {
+ 17
+ , 200
+ , 0
+ , 1
+ ,std::numeric_limits<cpp_type>::max()
+#if PBF_TYPE_IS_SIGNED
+ ,-200
+ , -1
+ ,std::numeric_limits<cpp_type>::min()
+#endif
+ };
+ pw.ADD_TYPE(1, std::begin(data), std::end(data));
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
+ }
+
+}
+
+TEST_CASE("write repeated packed field using packed field: " PBF_TYPE_NAME) {
+
+ std::string buffer;
+ protozero::pbf_writer pw(buffer);
+
+ SECTION("empty - should do rollback") {
+ {
+ packed_field_type field{pw, 1};
+ }
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
+ }
+
+ SECTION("one") {
+ {
+ packed_field_type field{pw, 1};
+ field.add_element(cpp_type(17));
+ }
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
+ }
+
+ SECTION("many") {
+ {
+ packed_field_type field{pw, 1};
+ field.add_element(cpp_type( 17));
+ field.add_element(cpp_type( 200));
+ field.add_element(cpp_type( 0));
+ field.add_element(cpp_type( 1));
+ field.add_element(std::numeric_limits<cpp_type>::max());
+#if PBF_TYPE_IS_SIGNED
+ field.add_element(cpp_type(-200));
+ field.add_element(cpp_type( -1));
+ field.add_element(std::numeric_limits<cpp_type>::min());
+#endif
+ }
+
+ REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
+ }
+
+}
+
+TEST_CASE("write from different types of iterators: " PBF_TYPE_NAME) {
+
+ std::string buffer;
+ protozero::pbf_writer pw(buffer);
+
+ SECTION("from uint16_t") {
+#if PBF_TYPE_IS_SIGNED
+ const int16_t data[] = { 1, 4, 9, 16, 25 };
+#else
+ const uint16_t data[] = { 1, 4, 9, 16, 25 };
+#endif
+
+ pw.ADD_TYPE(1, std::begin(data), std::end(data));
+ }
+
+ SECTION("from string") {
+ std::string data = "1 4 9 16 25";
+ std::stringstream sdata(data);
+
+#if PBF_TYPE_IS_SIGNED
+ using test_type = int32_t;
+#else
+ using test_type = uint32_t;
+#endif
+
+ std::istream_iterator<test_type> eod;
+ std::istream_iterator<test_type> it(sdata);
+
+ pw.ADD_TYPE(1, it, eod);
+ }
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ auto it_range = item.GET_TYPE();
+ REQUIRE(!item.next());
+ REQUIRE(std::distance(it_range.begin(), it_range.end()) == 5);
+
+ REQUIRE(it_range.front() == 1); it_range.drop_front();
+ REQUIRE(it_range.front() == 4); it_range.drop_front();
+ REQUIRE(it_range.front() == 9); it_range.drop_front();
+ REQUIRE(it_range.front() == 16); it_range.drop_front();
+ REQUIRE(it_range.front() == 25); it_range.drop_front();
+ REQUIRE(it_range.empty());
+
+ REQUIRE_THROWS_AS(it_range.front(), assert_error);
+ REQUIRE_THROWS_AS(it_range.drop_front(), assert_error);
+
+}
+
diff --git a/test/include/scalar_access.hpp b/test/include/scalar_access.hpp
index f435a87..6137119 100644
--- a/test/include/scalar_access.hpp
+++ b/test/include/scalar_access.hpp
@@ -25,6 +25,16 @@ TEST_CASE("read field: " PBF_TYPE_NAME) {
REQUIRE(!item.next());
}
+ SECTION("pos200") {
+ const std::string buffer = load_data(PBF_TYPE_NAME "/data-pos200");
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ REQUIRE(item.GET_TYPE() == 200);
+ REQUIRE(!item.next());
+ }
+
SECTION("max") {
const std::string buffer = load_data(PBF_TYPE_NAME "/data-max");
@@ -48,6 +58,16 @@ TEST_CASE("read field: " PBF_TYPE_NAME) {
}
}
+ SECTION("neg200") {
+ const std::string buffer = load_data(PBF_TYPE_NAME "/data-neg200");
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ REQUIRE(item.GET_TYPE() == -200);
+ REQUIRE(!item.next());
+ }
+
SECTION("min") {
if (std::is_signed<cpp_type>::value) {
const std::string buffer = load_data(PBF_TYPE_NAME "/data-min");
diff --git a/test/include/test.hpp b/test/include/test.hpp
index e88903a..5e3e704 100644
--- a/test/include/test.hpp
+++ b/test/include/test.hpp
@@ -1,4 +1,8 @@
+#ifdef _MSC_VER
+# define _SCL_SECURE_NO_WARNINGS
+#endif
+
#include <catch.hpp>
#include <stdexcept>
diff --git a/test/t/basic/test_cases.cpp b/test/t/basic/test_cases.cpp
index a728810..96b5d55 100644
--- a/test/t/basic/test_cases.cpp
+++ b/test/t/basic/test_cases.cpp
@@ -1,46 +1,42 @@
#include <test.hpp>
-TEST_CASE("basic") {
+TEST_CASE("default constructed pbf_reader is okay") {
+ protozero::pbf_reader item;
- SECTION("default constructed pbf message is okay") {
- protozero::pbf_reader item;
-
- REQUIRE(item.length() == 0);
- REQUIRE(!item); // test operator bool()
- REQUIRE(!item.next());
- }
+ REQUIRE(item.length() == 0);
+ REQUIRE(!item); // test operator bool()
+ REQUIRE(!item.next());
+}
- SECTION("empty buffer is okay") {
- const std::string buffer;
- protozero::pbf_reader item(buffer);
+TEST_CASE("empty buffer in pbf_reader is okay") {
+ const std::string buffer;
+ protozero::pbf_reader item{buffer};
- REQUIRE(item.length() == 0);
- REQUIRE(!item); // test operator bool()
- REQUIRE(!item.next());
- }
+ REQUIRE(item.length() == 0);
+ REQUIRE(!item); // test operator bool()
+ REQUIRE(!item.next());
+}
- SECTION("check every possible value for single byte in buffer") {
- char buffer[1];
- for (int i = 0; i <= 255; ++i) {
- *buffer = static_cast<char>(i);
- protozero::pbf_reader item(buffer, 1);
-
- REQUIRE(item.length() == 1);
- REQUIRE(!!item); // test operator bool()
- REQUIRE_THROWS({
- item.next();
- item.skip();
- });
- }
+TEST_CASE("check every possible value for single byte in buffer") {
+ char buffer;
+ for (int i = 0; i <= 255; ++i) {
+ buffer = static_cast<char>(i);
+ protozero::pbf_reader item(&buffer, 1);
+
+ REQUIRE(item.length() == 1);
+ REQUIRE(!!item); // test operator bool()
+ REQUIRE_THROWS({
+ item.next();
+ item.skip();
+ });
}
+}
- SECTION("illegal wire type") {
- char buffer[1] = { 1 << 3 | 7 };
-
- protozero::pbf_reader item(buffer, 1);
- REQUIRE_THROWS_AS(item.next(), protozero::unknown_pbf_wire_type_exception);
- }
+TEST_CASE("next() should throw when illegal wire type is encountered") {
+ char buffer = 1 << 3 | 7;
+ protozero::pbf_reader item{&buffer, 1};
+ REQUIRE_THROWS_AS(item.next(), protozero::unknown_pbf_wire_type_exception);
}
diff --git a/test/t/complex/test_cases.cpp b/test/t/complex/test_cases.cpp
index d00a605..9772da0 100644
--- a/test/t/complex/test_cases.cpp
+++ b/test/t/complex/test_cases.cpp
@@ -50,7 +50,10 @@ TEST_CASE("read complex data using pbf_reader") {
SECTION("some") {
const std::string buffer = load_data("complex/data-some");
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item2(buffer);
+ protozero::pbf_reader item;
+ using std::swap;
+ swap(item, item2);
uint32_t sum_of_u = 0;
while (item.next()) {
@@ -118,10 +121,10 @@ TEST_CASE("read complex data using pbf_reader") {
break;
}
case 7: {
- auto pi = item.get_packed_sint32();
+ const auto pi = item.get_packed_sint32();
int32_t sum = 0;
- for (auto it = pi.first; it != pi.second; ++it) {
- sum += *it;
+ for (auto val : pi) {
+ sum += val;
}
REQUIRE(sum == 5);
break;
@@ -196,7 +199,10 @@ TEST_CASE("read complex data using pbf_message") {
SECTION("some") {
const std::string buffer = load_data("complex/data-some");
- protozero::pbf_message<TestComplex::Test> item(buffer);
+ protozero::pbf_message<TestComplex::Test> item2(buffer);
+ protozero::pbf_message<TestComplex::Test> item;
+ using std::swap;
+ swap(item, item2);
uint32_t sum_of_u = 0;
while (item.next()) {
@@ -264,10 +270,10 @@ TEST_CASE("read complex data using pbf_message") {
break;
}
case TestComplex::Test::packed_sint32_d: {
- auto pi = item.get_packed_sint32();
+ const auto pi = item.get_packed_sint32();
int32_t sum = 0;
- for (auto it = pi.first; it != pi.second; ++it) {
- sum += *it;
+ for (auto val : pi) {
+ sum += val;
}
REQUIRE(sum == 5);
break;
@@ -349,8 +355,12 @@ TEST_CASE("write complex data using pbf_writer") {
SECTION("some") {
std::string buffer;
- protozero::pbf_writer pw(buffer);
- pw.add_fixed32(1, 12345678);
+ protozero::pbf_writer pw2(buffer);
+ pw2.add_fixed32(1, 12345678);
+
+ protozero::pbf_writer pw;
+ using std::swap;
+ swap(pw, pw2);
std::string submessage;
protozero::pbf_writer pws(submessage);
@@ -380,9 +390,10 @@ TEST_CASE("write complex data using pbf_writer") {
break;
}
case 5: {
- protozero::pbf_reader subitem = item.get_message();
+ auto view = item.get_view();
+ protozero::pbf_reader subitem{view};
REQUIRE(subitem.next());
- REQUIRE(subitem.get_string() == "foobar");
+ REQUIRE(std::string(subitem.get_view()) == "foobar");
REQUIRE(!subitem.next());
break;
}
@@ -448,10 +459,10 @@ TEST_CASE("write complex data using pbf_writer") {
break;
}
case 7: {
- auto pi = item.get_packed_sint32();
+ const auto pi = item.get_packed_sint32();
int32_t sum = 0;
- for (auto it = pi.first; it != pi.second; ++it) {
- sum += *it;
+ for (auto val : pi) {
+ sum += val;
}
REQUIRE(sum == 5);
break;
@@ -508,8 +519,13 @@ TEST_CASE("write complex data using pbf_builder") {
SECTION("some") {
std::string buffer;
- protozero::pbf_builder<TestComplex::Test> pw(buffer);
- pw.add_fixed32(TestComplex::Test::required_fixed32_f, 12345678);
+ protozero::pbf_builder<TestComplex::Test> pw2(buffer);
+ pw2.add_fixed32(TestComplex::Test::required_fixed32_f, 12345678);
+
+ std::string dummy_buffer;
+ protozero::pbf_builder<TestComplex::Test> pw(dummy_buffer);
+ using std::swap;
+ swap(pw, pw2);
std::string submessage;
protozero::pbf_builder<TestComplex::Sub> pws(submessage);
@@ -607,10 +623,10 @@ TEST_CASE("write complex data using pbf_builder") {
break;
}
case 7: {
- auto pi = item.get_packed_sint32();
+ const auto pi = item.get_packed_sint32();
int32_t sum = 0;
- for (auto it = pi.first; it != pi.second; ++it) {
- sum += *it;
+ for (auto val : pi) {
+ sum += val;
}
REQUIRE(sum == 5);
break;
diff --git a/test/t/data_view/test_cases.cpp b/test/t/data_view/test_cases.cpp
new file mode 100644
index 0000000..f71b9b1
--- /dev/null
+++ b/test/t/data_view/test_cases.cpp
@@ -0,0 +1,83 @@
+
+#include <test.hpp>
+
+#include <protozero/types.hpp>
+
+TEST_CASE("default constructed data_view") {
+ protozero::data_view view;
+ REQUIRE(view.data() == nullptr);
+ REQUIRE(view.size() == 0);
+}
+
+TEST_CASE("data_view from C string") {
+ protozero::data_view view{"foobar"};
+ REQUIRE(view.data());
+ REQUIRE(view.size() == 6);
+}
+
+TEST_CASE("data_view from std::string") {
+ std::string str{"foobar"};
+ protozero::data_view view{str};
+ REQUIRE(view.data());
+ REQUIRE(view.size() == 6);
+}
+
+TEST_CASE("data_view from ptr, size") {
+ std::string str{"foobar"};
+ protozero::data_view view{str.data(), str.size()};
+ REQUIRE(view.data());
+ REQUIRE(view.size() == 6);
+}
+
+TEST_CASE("convert data_view to std::string") {
+ protozero::data_view view{"foobar"};
+
+ std::string s = std::string(view);
+ REQUIRE(s == "foobar");
+ REQUIRE(std::string(view) == "foobar");
+ REQUIRE(view.to_string() == "foobar");
+}
+
+TEST_CASE("converting default constructed data_view to string fails") {
+ protozero::data_view view;
+ REQUIRE_THROWS_AS({
+ view.to_string();
+ }, assert_error);
+}
+
+TEST_CASE("swapping data_view") {
+ protozero::data_view view1{"foo"};
+ protozero::data_view view2{"bar"};
+
+ REQUIRE(view1.to_string() == "foo");
+ REQUIRE(view2.to_string() == "bar");
+
+ using std::swap;
+ swap(view1, view2);
+
+ REQUIRE(view2.to_string() == "foo");
+ REQUIRE(view1.to_string() == "bar");
+}
+
+TEST_CASE("comparing data_views") {
+ protozero::data_view v1{"foo"};
+ protozero::data_view v2{"bar"};
+ protozero::data_view v3{"foox"};
+ protozero::data_view v4{"foo"};
+
+ REQUIRE_FALSE(v1 == v2);
+ REQUIRE_FALSE(v1 == v3);
+ REQUIRE(v1 == v4);
+ REQUIRE_FALSE(v2 == v3);
+ REQUIRE_FALSE(v2 == v4);
+ REQUIRE_FALSE(v3 == v4);
+
+ REQUIRE(v1 != v2);
+ REQUIRE(v1 != v3);
+ REQUIRE_FALSE(v1 != v4);
+ REQUIRE(v2 != v3);
+ REQUIRE(v2 != v4);
+ REQUIRE(v3 != v4);
+}
+
+
diff --git a/test/t/exceptions/test_cases.cpp b/test/t/exceptions/test_cases.cpp
index 5e302c8..b1063f1 100644
--- a/test/t/exceptions/test_cases.cpp
+++ b/test/t/exceptions/test_cases.cpp
@@ -1,27 +1,23 @@
#include <test.hpp>
-TEST_CASE("exceptions messages") {
-
- SECTION("pbf") {
- protozero::exception e;
- REQUIRE(std::string(e.what()) == std::string("pbf exception"));
- }
-
- SECTION("varint too long") {
- protozero::varint_too_long_exception e;
- REQUIRE(std::string(e.what()) == std::string("varint too long exception"));
- }
+TEST_CASE("exceptions messages for pbf exception") {
+ protozero::exception e;
+ REQUIRE(std::string{e.what()} == std::string{"pbf exception"});
+}
- SECTION("unknown pbf field type") {
- protozero::unknown_pbf_wire_type_exception e;
- REQUIRE(std::string(e.what()) == std::string("unknown pbf field type exception"));
- }
+TEST_CASE("exceptions messages for varint too long") {
+ protozero::varint_too_long_exception e;
+ REQUIRE(std::string{e.what()} == std::string{"varint too long exception"});
+}
- SECTION("end of buffer") {
- protozero::end_of_buffer_exception e;
- REQUIRE(std::string(e.what()) == std::string("end of buffer exception"));
- }
+TEST_CASE("exceptions messages for unknown pbf field type") {
+ protozero::unknown_pbf_wire_type_exception e;
+ REQUIRE(std::string{e.what()} == std::string{"unknown pbf field type exception"});
+}
+TEST_CASE("exceptions messages for end of buffer") {
+ protozero::end_of_buffer_exception e;
+ REQUIRE(std::string{e.what()} == std::string{"end of buffer exception"});
}
diff --git a/test/t/fixed32/data-pos200.pbf b/test/t/fixed32/data-pos200.pbf
new file mode 100644
index 0000000..7dc726e
Binary files /dev/null and b/test/t/fixed32/data-pos200.pbf differ
diff --git a/test/t/fixed32/testcase.cpp b/test/t/fixed32/testcase.cpp
index 8394e75..450b78a 100644
--- a/test/t/fixed32/testcase.cpp
+++ b/test/t/fixed32/testcase.cpp
@@ -11,6 +11,9 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(std::numeric_limits<uint32_t>::max());
write_to_file(msg, "data-max.pbf");
}
diff --git a/test/t/fixed64/data-pos200.pbf b/test/t/fixed64/data-pos200.pbf
new file mode 100644
index 0000000..be400ca
Binary files /dev/null and b/test/t/fixed64/data-pos200.pbf differ
diff --git a/test/t/fixed64/testcase.cpp b/test/t/fixed64/testcase.cpp
index eb1eea1..8c6daff 100644
--- a/test/t/fixed64/testcase.cpp
+++ b/test/t/fixed64/testcase.cpp
@@ -11,6 +11,9 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(std::numeric_limits<uint64_t>::max());
write_to_file(msg, "data-max.pbf");
}
diff --git a/test/t/int32/data-neg200.pbf b/test/t/int32/data-neg200.pbf
new file mode 100644
index 0000000..29df43f
--- /dev/null
+++ b/test/t/int32/data-neg200.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int32/data-pos200.pbf b/test/t/int32/data-pos200.pbf
new file mode 100644
index 0000000..d9eeb99
--- /dev/null
+++ b/test/t/int32/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/int32/testcase.cpp b/test/t/int32/testcase.cpp
index 9347b90..f70711f 100644
--- a/test/t/int32/testcase.cpp
+++ b/test/t/int32/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(-1);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int32_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/int64/data-neg200.pbf b/test/t/int64/data-neg200.pbf
new file mode 100644
index 0000000..29df43f
--- /dev/null
+++ b/test/t/int64/data-neg200.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int64/data-pos200.pbf b/test/t/int64/data-pos200.pbf
new file mode 100644
index 0000000..d9eeb99
--- /dev/null
+++ b/test/t/int64/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/int64/testcase.cpp b/test/t/int64/testcase.cpp
index fd57a23..1c4d65d 100644
--- a/test/t/int64/testcase.cpp
+++ b/test/t/int64/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(-1);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int64_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/message/test_cases.cpp b/test/t/message/test_cases.cpp
index 0ebf28f..c1253f3 100644
--- a/test/t/message/test_cases.cpp
+++ b/test/t/message/test_cases.cpp
@@ -65,6 +65,13 @@ TEST_CASE("write message field") {
pbf_submessage.add_string(1, "foobar");
}
+ SECTION("string with subwriter with reserved size") {
+ std::string str{"foobar"};
+ auto size = 1 /* tag */ + 1 /* length field */ + str.size();
+ protozero::pbf_writer pbf_submessage(pbf_test, 1, size);
+ pbf_submessage.add_string(1, "foobar");
+ }
+
REQUIRE(buffer_test == load_data("message/data-message"));
}
diff --git a/test/t/repeated_packed_bool/test_cases.cpp b/test/t/repeated_packed_bool/test_cases.cpp
index 98bf88b..9ce2f79 100644
--- a/test/t/repeated_packed_bool/test_cases.cpp
+++ b/test/t/repeated_packed_bool/test_cases.cpp
@@ -17,12 +17,12 @@ TEST_CASE("read repeated packed bool field") {
protozero::pbf_reader item(buffer);
REQUIRE(item.next());
- auto it_pair = item.get_packed_bool();
+ auto it_range = item.get_packed_bool();
REQUIRE(!item.next());
- REQUIRE(it_pair.first != it_pair.second);
- REQUIRE(*it_pair.first);
- REQUIRE(++it_pair.first == it_pair.second);
+ REQUIRE(it_range.begin() != it_range.end());
+ REQUIRE(*it_range.begin());
+ REQUIRE(std::next(it_range.begin()) == it_range.end());
}
SECTION("many") {
@@ -31,16 +31,16 @@ TEST_CASE("read repeated packed bool field") {
protozero::pbf_reader item(buffer);
REQUIRE(item.next());
- auto it_pair = item.get_packed_bool();
+ auto it_range = item.get_packed_bool();
REQUIRE(!item.next());
- auto it = it_pair.first;
- REQUIRE(it != it_pair.second);
+ auto it = it_range.begin();
+ REQUIRE(it != it_range.end());
REQUIRE(*it++);
REQUIRE(*it++);
REQUIRE(! *it++);
REQUIRE(*it++);
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
}
SECTION("end_of_buffer") {
diff --git a/test/t/repeated_packed_double/test_cases.cpp b/test/t/repeated_packed_double/test_cases.cpp
index 059d4b8..f11e797 100644
--- a/test/t/repeated_packed_double/test_cases.cpp
+++ b/test/t/repeated_packed_double/test_cases.cpp
@@ -27,11 +27,11 @@ TEST_CASE("read repeated packed double field") {
protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
REQUIRE(item.next());
- auto it_pair = item.get_packed_double();
+ auto it_range = item.get_packed_double();
REQUIRE(!item.next());
- REQUIRE(*it_pair.first == 17.34);
- REQUIRE(++it_pair.first == it_pair.second);
+ REQUIRE(*it_range.begin() == 17.34);
+ REQUIRE(std::next(it_range.begin()) == it_range.end());
}
SECTION("many") {
@@ -39,16 +39,16 @@ TEST_CASE("read repeated packed double field") {
protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
REQUIRE(item.next());
- auto it_pair = item.get_packed_double();
+ auto it_range = item.get_packed_double();
REQUIRE(!item.next());
- auto it = it_pair.first;
+ auto it = it_range.begin();
REQUIRE(*it++ == 17.34);
REQUIRE(*it++ == 0.0);
REQUIRE(*it++ == 1.0);
REQUIRE(*it++ == std::numeric_limits<double>::min());
REQUIRE(*it++ == std::numeric_limits<double>::max());
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
}
SECTION("end_of_buffer") {
diff --git a/test/t/repeated_packed_enum/test_cases.cpp b/test/t/repeated_packed_enum/test_cases.cpp
index be1473a..1d1196a 100644
--- a/test/t/repeated_packed_enum/test_cases.cpp
+++ b/test/t/repeated_packed_enum/test_cases.cpp
@@ -17,12 +17,12 @@ TEST_CASE("read repeated packed enum field") {
protozero::pbf_reader item(buffer);
REQUIRE(item.next());
- auto it_pair = item.get_packed_enum();
+ auto it_range = item.get_packed_enum();
REQUIRE(!item.next());
- REQUIRE(it_pair.first != it_pair.second);
- REQUIRE(*it_pair.first == 0 /* BLACK */);
- REQUIRE(++it_pair.first == it_pair.second);
+ REQUIRE(it_range.begin() != it_range.end());
+ REQUIRE(*it_range.begin() == 0 /* BLACK */);
+ REQUIRE(std::next(it_range.begin()) == it_range.end());
}
SECTION("many") {
@@ -31,15 +31,15 @@ TEST_CASE("read repeated packed enum field") {
protozero::pbf_reader item(buffer);
REQUIRE(item.next());
- auto it_pair = item.get_packed_enum();
+ auto it_range = item.get_packed_enum();
REQUIRE(!item.next());
- auto it = it_pair.first;
- REQUIRE(it != it_pair.second);
+ auto it = it_range.begin();
+ REQUIRE(it != it_range.end());
REQUIRE(*it++ == 0 /* BLACK */);
REQUIRE(*it++ == 3 /* BLUE */);
REQUIRE(*it++ == 2 /* GREEN */);
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
}
SECTION("end_of_buffer") {
diff --git a/test/t/repeated_packed_fixed32/data-many.pbf b/test/t/repeated_packed_fixed32/data-many.pbf
index dff236c..eff5226 100644
Binary files a/test/t/repeated_packed_fixed32/data-many.pbf and b/test/t/repeated_packed_fixed32/data-many.pbf differ
diff --git a/test/t/repeated_packed_fixed32/test_cases.cpp b/test/t/repeated_packed_fixed32/test_cases.cpp
index e8fd3a9..fc2a179 100644
--- a/test/t/repeated_packed_fixed32/test_cases.cpp
+++ b/test/t/repeated_packed_fixed32/test_cases.cpp
@@ -1,203 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed fixed32 field") {
+#define PBF_TYPE fixed32
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint32_t;
- // Run these tests twice, the second time we basically move the data
- // one byte down in the buffer. It doesn't matter how the data or buffer
- // is aligned before that, in at least one of these cases the int32s will
- // not be aligned properly. So we test that even in that case the int32s
- // will be extracted properly.
-
- for (std::string::size_type n = 0; n < 2; ++n) {
-
- std::string abuffer;
- abuffer.reserve(1000);
- abuffer.append(n, '\0');
-
- SECTION("empty") {
- abuffer.append(load_data("repeated_packed_fixed32/data-empty"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- abuffer.append(load_data("repeated_packed_fixed32/data-one"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_fixed32();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17UL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- abuffer.append(load_data("repeated_packed_fixed32/data-many"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_fixed32();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17UL);
- REQUIRE(*it++ == 0UL);
- REQUIRE(*it++ == 1UL);
- REQUIRE(*it++ == std::numeric_limits<uint32_t>::max());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- abuffer.append(load_data("repeated_packed_fixed32/data-many"));
-
- for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
- protozero::pbf_reader item(abuffer.data() + n, i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_fixed32(), protozero::end_of_buffer_exception);
- }
- }
-
- }
-
-}
-
-TEST_CASE("write repeated packed fixed32 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- uint32_t data[] = { 17UL };
- pw.add_packed_fixed32(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-empty"));
- }
-
- SECTION("one") {
- uint32_t data[] = { 17UL };
- pw.add_packed_fixed32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
- }
-
- SECTION("many") {
- uint32_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint32_t>::max() };
- pw.add_packed_fixed32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
- }
-
-}
-
-TEST_CASE("write repeated packed fixed32 field using packed_field_fixed32") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty - should do rollback") {
- {
- protozero::packed_field_fixed32 field{pw, 1};
- }
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-empty"));
- }
-
- SECTION("one") {
- {
- protozero::packed_field_fixed32 field{pw, 1};
- field.add_element(17UL);
- }
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
- }
-
- SECTION("one with predefined size") {
- {
- protozero::packed_field_fixed32 field{pw, 1, 1};
- field.add_element(17UL);
- }
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
- }
-
- SECTION("many") {
- {
- protozero::packed_field_fixed32 field{pw, 1};
- field.add_element(17UL);
- field.add_element(0UL);
- field.add_element(1UL);
- field.add_element(std::numeric_limits<uint32_t>::max());
- }
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
- }
-
- SECTION("many with predefined size") {
- {
- protozero::packed_field_fixed32 field{pw, 1, 4};
- field.add_element(17UL);
- field.add_element(0UL);
- field.add_element(1UL);
- field.add_element(std::numeric_limits<uint32_t>::max());
- }
-
- REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
- }
-
- SECTION("failure when not closing properly after zero elements") {
- protozero::packed_field_fixed32 field{pw, 1};
- REQUIRE_THROWS_AS({
- pw.add_fixed32(2, 1234); // dummy values
- }, assert_error);
- }
-
- SECTION("failure when not closing properly after one element") {
- protozero::packed_field_fixed32 field{pw, 1};
- field.add_element(17UL);
- REQUIRE_THROWS_AS({
- pw.add_fixed32(2, 1234); // dummy values
- }, assert_error);
- }
-
-}
-
-TEST_CASE("write from different types of iterators") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("from uint16_t") {
- uint16_t data[] = { 1, 4, 9, 16, 25 };
-
- pw.add_packed_fixed32(1, std::begin(data), std::end(data));
- }
-
- SECTION("from string") {
- std::string data = "1 4 9 16 25";
- std::stringstream sdata(data);
-
- std::istream_iterator<uint32_t> eod;
- std::istream_iterator<uint32_t> it(sdata);
-
- pw.add_packed_fixed32(1, it, eod);
- }
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_fixed32();
- REQUIRE(!item.next());
- REQUIRE(std::distance(it_pair.first, it_pair.second) == 5);
-
- auto i = it_pair.first;
- REQUIRE(*i++ == 1);
- REQUIRE(*i++ == 4);
- REQUIRE(*i++ == 9);
- REQUIRE(*i++ == 16);
- REQUIRE(*i++ == 25);
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_fixed32/testcase.cpp b/test/t/repeated_packed_fixed32/testcase.cpp
index 34237a7..901e6ad 100644
--- a/test/t/repeated_packed_fixed32/testcase.cpp
+++ b/test/t/repeated_packed_fixed32/testcase.cpp
@@ -10,6 +10,7 @@ int main(int c, char *argv[]) {
msg.add_i(17UL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200UL);
msg.add_i(0UL);
msg.add_i(1UL);
msg.add_i(std::numeric_limits<uint32_t>::max());
diff --git a/test/t/repeated_packed_fixed64/data-many.pbf b/test/t/repeated_packed_fixed64/data-many.pbf
index dc5cd98..c007cb9 100644
Binary files a/test/t/repeated_packed_fixed64/data-many.pbf and b/test/t/repeated_packed_fixed64/data-many.pbf differ
diff --git a/test/t/repeated_packed_fixed64/test_cases.cpp b/test/t/repeated_packed_fixed64/test_cases.cpp
index 97c14ad..4959fe6 100644
--- a/test/t/repeated_packed_fixed64/test_cases.cpp
+++ b/test/t/repeated_packed_fixed64/test_cases.cpp
@@ -1,94 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed fixed64 field") {
+#define PBF_TYPE fixed64
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint64_t;
- // Run these tests twice, the second time we basically move the data
- // one byte down in the buffer. It doesn't matter how the data or buffer
- // is aligned before that, in at least one of these cases the int64s will
- // not be aligned properly. So we test that even in that case the int64s
- // will be extracted properly.
-
- for (std::string::size_type n = 0; n < 2; ++n) {
-
- std::string abuffer;
- abuffer.reserve(1000);
- abuffer.append(n, '\0');
-
- SECTION("empty") {
- abuffer.append(load_data("repeated_packed_fixed64/data-empty"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- abuffer.append(load_data("repeated_packed_fixed64/data-one"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_fixed64();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17ULL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- abuffer.append(load_data("repeated_packed_fixed64/data-many"));
- protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_fixed64();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17ULL);
- REQUIRE(*it++ == 0ULL);
- REQUIRE(*it++ == 1ULL);
- REQUIRE(*it++ == std::numeric_limits<uint64_t>::max());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- abuffer.append(load_data("repeated_packed_fixed64/data-many"));
-
- for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
- protozero::pbf_reader item(abuffer.data() + n, i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_fixed64(), protozero::end_of_buffer_exception);
- }
- }
-
- }
-
-}
-
-TEST_CASE("write repeated packed fixed64 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- uint64_t data[] = { 17ULL };
- pw.add_packed_fixed64(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_fixed64/data-empty"));
- }
-
- SECTION("one") {
- uint64_t data[] = { 17ULL };
- pw.add_packed_fixed64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_fixed64/data-one"));
- }
-
- SECTION("many") {
- uint64_t data[] = { 17ULL, 0ULL, 1ULL, std::numeric_limits<uint64_t>::max() };
- pw.add_packed_fixed64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_fixed64/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_fixed64/testcase.cpp b/test/t/repeated_packed_fixed64/testcase.cpp
index 2fc414a..49e9e39 100644
--- a/test/t/repeated_packed_fixed64/testcase.cpp
+++ b/test/t/repeated_packed_fixed64/testcase.cpp
@@ -10,6 +10,7 @@ int main(int c, char *argv[]) {
msg.add_i(17ULL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200UL);
msg.add_i(0ULL);
msg.add_i(1ULL);
msg.add_i(std::numeric_limits<uint64_t>::max());
diff --git a/test/t/repeated_packed_float/test_cases.cpp b/test/t/repeated_packed_float/test_cases.cpp
index 9ae03eb..afc09cd 100644
--- a/test/t/repeated_packed_float/test_cases.cpp
+++ b/test/t/repeated_packed_float/test_cases.cpp
@@ -27,11 +27,11 @@ TEST_CASE("read repeated packed float field") {
protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
REQUIRE(item.next());
- auto it_pair = item.get_packed_float();
+ auto it_range = item.get_packed_float();
REQUIRE(!item.next());
- REQUIRE(*it_pair.first == 17.34f);
- REQUIRE(++it_pair.first == it_pair.second);
+ REQUIRE(*it_range.begin() == 17.34f);
+ REQUIRE(std::next(it_range.begin()) == it_range.end());
}
SECTION("many") {
@@ -39,16 +39,16 @@ TEST_CASE("read repeated packed float field") {
protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
REQUIRE(item.next());
- auto it_pair = item.get_packed_float();
+ auto it_range = item.get_packed_float();
REQUIRE(!item.next());
- auto it = it_pair.first;
+ auto it = it_range.begin();
REQUIRE(*it++ == 17.34f);
REQUIRE(*it++ == 0.0f);
REQUIRE(*it++ == 1.0f);
REQUIRE(*it++ == std::numeric_limits<float>::min());
REQUIRE(*it++ == std::numeric_limits<float>::max());
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
}
SECTION("end_of_buffer") {
diff --git a/test/t/repeated_packed_int32/data-many.pbf b/test/t/repeated_packed_int32/data-many.pbf
index 1b28de6..fe5336a 100644
Binary files a/test/t/repeated_packed_int32/data-many.pbf and b/test/t/repeated_packed_int32/data-many.pbf differ
diff --git a/test/t/repeated_packed_int32/test_cases.cpp b/test/t/repeated_packed_int32/test_cases.cpp
index e27cee9..206c61c 100644
--- a/test/t/repeated_packed_int32/test_cases.cpp
+++ b/test/t/repeated_packed_int32/test_cases.cpp
@@ -1,87 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed int32 field") {
+#define PBF_TYPE int32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_int32/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_int32/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_int32();
- REQUIRE(!item.next());
-
- REQUIRE(it_pair.first != it_pair.second);
- REQUIRE(*it_pair.first == 17L);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_int32/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_int32();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(it != it_pair.second);
- REQUIRE(*it++ == 17L);
- REQUIRE(*it++ == 0L);
- REQUIRE(*it++ == 1L);
- REQUIRE(*it++ == -1L);
- REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_int32/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_int32(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed int32 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int32_t data[] = { 17L };
- pw.add_packed_int32(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_int32/data-empty"));
- }
-
- SECTION("one") {
- int32_t data[] = { 17L };
- pw.add_packed_int32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_int32/data-one"));
- }
-
- SECTION("many") {
- int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
- pw.add_packed_int32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_int32/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_int32/testcase.cpp b/test/t/repeated_packed_int32/testcase.cpp
index 033aa72..ab9c5f7 100644
--- a/test/t/repeated_packed_int32/testcase.cpp
+++ b/test/t/repeated_packed_int32/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17L);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0L);
msg.add_i(1L);
- msg.add_i(-1L);
msg.add_i(std::numeric_limits<int32_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1L);
msg.add_i(std::numeric_limits<int32_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_int64/data-many.pbf b/test/t/repeated_packed_int64/data-many.pbf
index 1a7a7f6..0f71921 100644
Binary files a/test/t/repeated_packed_int64/data-many.pbf and b/test/t/repeated_packed_int64/data-many.pbf differ
diff --git a/test/t/repeated_packed_int64/test_cases.cpp b/test/t/repeated_packed_int64/test_cases.cpp
index 2013a22..d5c2beb 100644
--- a/test/t/repeated_packed_int64/test_cases.cpp
+++ b/test/t/repeated_packed_int64/test_cases.cpp
@@ -1,123 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed int64 field") {
+#define PBF_TYPE int64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_int64/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_int64/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_int64();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17LL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_int64/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_int64();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17LL);
- REQUIRE(*it++ == 0LL);
- REQUIRE(*it++ == 1LL);
- REQUIRE(*it++ == -1LL);
- REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_int64/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_int64(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed int64 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int64_t data[] = { 17LL };
- pw.add_packed_int64(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-empty"));
- }
-
- SECTION("one") {
- int64_t data[] = { 17LL };
- pw.add_packed_int64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-one"));
- }
-
- SECTION("many") {
- int64_t data[] = { 17LL, 0LL, 1LL, -1LL, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
- pw.add_packed_int64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-many"));
- }
-
-}
-
-TEST_CASE("write repeated packed int64 field using packed_field_int64") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty - should do rollback") {
- {
- protozero::packed_field_int64 field{pw, 1};
- }
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-empty"));
- }
-
- SECTION("one") {
- {
- protozero::packed_field_int64 field{pw, 1};
- field.add_element(17LL);
- }
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-one"));
- }
-
- SECTION("many") {
- {
- protozero::packed_field_int64 field{pw, 1};
- field.add_element(17LL);
- field.add_element( 0LL);
- field.add_element( 1LL);
- field.add_element(-1LL);
- field.add_element(std::numeric_limits<int64_t>::max());
- field.add_element(std::numeric_limits<int64_t>::min());
- }
-
- REQUIRE(buffer == load_data("repeated_packed_int64/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_int64/testcase.cpp b/test/t/repeated_packed_int64/testcase.cpp
index e918504..eb4002e 100644
--- a/test/t/repeated_packed_int64/testcase.cpp
+++ b/test/t/repeated_packed_int64/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17LL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0LL);
msg.add_i(1LL);
- msg.add_i(-1LL);
msg.add_i(std::numeric_limits<int64_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1LL);
msg.add_i(std::numeric_limits<int64_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_sfixed32/data-many.pbf b/test/t/repeated_packed_sfixed32/data-many.pbf
index b9ee1b4..e6922e6 100644
Binary files a/test/t/repeated_packed_sfixed32/data-many.pbf and b/test/t/repeated_packed_sfixed32/data-many.pbf differ
diff --git a/test/t/repeated_packed_sfixed32/test_cases.cpp b/test/t/repeated_packed_sfixed32/test_cases.cpp
index 34ace54..c8eb8dd 100644
--- a/test/t/repeated_packed_sfixed32/test_cases.cpp
+++ b/test/t/repeated_packed_sfixed32/test_cases.cpp
@@ -1,86 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed sfixed32 field") {
+#define PBF_TYPE sfixed32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_sfixed32/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_sfixed32/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sfixed32();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17L);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_sfixed32/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sfixed32();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17L);
- REQUIRE(*it++ == 0L);
- REQUIRE(*it++ == 1L);
- REQUIRE(*it++ == -1L);
- REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_sfixed32/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_sfixed32(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed sfixed32 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int32_t data[] = { 17L };
- pw.add_packed_sfixed32(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-empty"));
- }
-
- SECTION("one") {
- int32_t data[] = { 17L };
- pw.add_packed_sfixed32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-one"));
- }
-
- SECTION("many") {
- int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
-
- pw.add_packed_sfixed32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_sfixed32/testcase.cpp b/test/t/repeated_packed_sfixed32/testcase.cpp
index 5177b74..1a84cf5 100644
--- a/test/t/repeated_packed_sfixed32/testcase.cpp
+++ b/test/t/repeated_packed_sfixed32/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0);
msg.add_i(1);
- msg.add_i(-1);
msg.add_i(std::numeric_limits<int32_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1);
msg.add_i(std::numeric_limits<int32_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_sfixed64/data-many.pbf b/test/t/repeated_packed_sfixed64/data-many.pbf
index 9ae5254..5539d4f 100644
Binary files a/test/t/repeated_packed_sfixed64/data-many.pbf and b/test/t/repeated_packed_sfixed64/data-many.pbf differ
diff --git a/test/t/repeated_packed_sfixed64/test_cases.cpp b/test/t/repeated_packed_sfixed64/test_cases.cpp
index ecce063..3597938 100644
--- a/test/t/repeated_packed_sfixed64/test_cases.cpp
+++ b/test/t/repeated_packed_sfixed64/test_cases.cpp
@@ -1,85 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed sfixed64 field") {
+#define PBF_TYPE sfixed64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_sfixed64/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_sfixed64/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sfixed64();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17LL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_sfixed64/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sfixed64();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17LL);
- REQUIRE(*it++ == 0LL);
- REQUIRE(*it++ == 1LL);
- REQUIRE(*it++ == -1LL);
- REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_sfixed64/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_sfixed64(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed sfixed64 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int64_t data[] = { 17L };
- pw.add_packed_sfixed64(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-empty"));
- }
-
- SECTION("one") {
- int64_t data[] = { 17L };
- pw.add_packed_sfixed64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-one"));
- }
-
- SECTION("many") {
- int64_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
- pw.add_packed_sfixed64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_sfixed64/testcase.cpp b/test/t/repeated_packed_sfixed64/testcase.cpp
index 90cafb9..673a5ea 100644
--- a/test/t/repeated_packed_sfixed64/testcase.cpp
+++ b/test/t/repeated_packed_sfixed64/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0);
msg.add_i(1);
- msg.add_i(-1);
msg.add_i(std::numeric_limits<int64_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1);
msg.add_i(std::numeric_limits<int64_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_sint32/data-many.pbf b/test/t/repeated_packed_sint32/data-many.pbf
index 3e0bf6f..e65bbd9 100644
Binary files a/test/t/repeated_packed_sint32/data-many.pbf and b/test/t/repeated_packed_sint32/data-many.pbf differ
diff --git a/test/t/repeated_packed_sint32/test_cases.cpp b/test/t/repeated_packed_sint32/test_cases.cpp
index c2e6c7f..4c6a2d8 100644
--- a/test/t/repeated_packed_sint32/test_cases.cpp
+++ b/test/t/repeated_packed_sint32/test_cases.cpp
@@ -1,85 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed sint32 field") {
+#define PBF_TYPE sint32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_sint32/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_sint32/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sint32();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17L);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_sint32/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sint32();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17L);
- REQUIRE(*it++ == 0L);
- REQUIRE(*it++ == 1L);
- REQUIRE(*it++ == -1L);
- REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_sint32/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_sint32(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed sint32 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int32_t data[] = { 17L };
- pw.add_packed_sint32(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_sint32/data-empty"));
- }
-
- SECTION("one") {
- int32_t data[] = { 17L };
- pw.add_packed_sint32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sint32/data-one"));
- }
-
- SECTION("many") {
- int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
- pw.add_packed_sint32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sint32/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_sint32/testcase.cpp b/test/t/repeated_packed_sint32/testcase.cpp
index 3423f6d..8145e4f 100644
--- a/test/t/repeated_packed_sint32/testcase.cpp
+++ b/test/t/repeated_packed_sint32/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17L);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0L);
msg.add_i(1L);
- msg.add_i(-1L);
msg.add_i(std::numeric_limits<int32_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1L);
msg.add_i(std::numeric_limits<int32_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_sint64/data-many.pbf b/test/t/repeated_packed_sint64/data-many.pbf
index b7eb8bf..89df69a 100644
Binary files a/test/t/repeated_packed_sint64/data-many.pbf and b/test/t/repeated_packed_sint64/data-many.pbf differ
diff --git a/test/t/repeated_packed_sint64/test_cases.cpp b/test/t/repeated_packed_sint64/test_cases.cpp
index 0e06ec2..1a3fb27 100644
--- a/test/t/repeated_packed_sint64/test_cases.cpp
+++ b/test/t/repeated_packed_sint64/test_cases.cpp
@@ -1,123 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed sint64 field") {
+#define PBF_TYPE sint64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_sint64/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_sint64/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sint64();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17LL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_sint64/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_sint64();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17LL);
- REQUIRE(*it++ == 0LL);
- REQUIRE(*it++ == 1LL);
- REQUIRE(*it++ == -1LL);
- REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
- REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_sint64/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_sint64(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed sint64 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- int64_t data[] = { 17L };
- pw.add_packed_sint64(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-empty"));
- }
-
- SECTION("one") {
- int64_t data[] = { 17L };
- pw.add_packed_sint64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-one"));
- }
-
- SECTION("many") {
- int64_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
- pw.add_packed_sint64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-many"));
- }
-
-}
-
-TEST_CASE("write repeated packed sint64 field using packed_field_sint64") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty - should do rollback") {
- {
- protozero::packed_field_sint64 field{pw, 1};
- }
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-empty"));
- }
-
- SECTION("one") {
- {
- protozero::packed_field_sint64 field{pw, 1};
- field.add_element(17L);
- }
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-one"));
- }
-
- SECTION("many") {
- {
- protozero::packed_field_sint64 field{pw, 1};
- field.add_element(17L);
- field.add_element( 0L);
- field.add_element( 1L);
- field.add_element(-1L);
- field.add_element(std::numeric_limits<int64_t>::max());
- field.add_element(std::numeric_limits<int64_t>::min());
- }
-
- REQUIRE(buffer == load_data("repeated_packed_sint64/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_sint64/testcase.cpp b/test/t/repeated_packed_sint64/testcase.cpp
index 22d12e8..920c3be 100644
--- a/test/t/repeated_packed_sint64/testcase.cpp
+++ b/test/t/repeated_packed_sint64/testcase.cpp
@@ -10,10 +10,12 @@ int main(int c, char *argv[]) {
msg.add_i(17LL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200);
msg.add_i(0LL);
msg.add_i(1LL);
- msg.add_i(-1LL);
msg.add_i(std::numeric_limits<int64_t>::max());
+ msg.add_i(-200);
+ msg.add_i(-1LL);
msg.add_i(std::numeric_limits<int64_t>::min());
write_to_file(msg, "data-many.pbf");
}
diff --git a/test/t/repeated_packed_uint32/data-many.pbf b/test/t/repeated_packed_uint32/data-many.pbf
index 47fb203..c58a7e5 100644
Binary files a/test/t/repeated_packed_uint32/data-many.pbf and b/test/t/repeated_packed_uint32/data-many.pbf differ
diff --git a/test/t/repeated_packed_uint32/test_cases.cpp b/test/t/repeated_packed_uint32/test_cases.cpp
index dc70ebe..d68caba 100644
--- a/test/t/repeated_packed_uint32/test_cases.cpp
+++ b/test/t/repeated_packed_uint32/test_cases.cpp
@@ -1,83 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed uint32 field") {
+#define PBF_TYPE uint32
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint32_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_uint32/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_uint32/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_uint32();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17UL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_uint32/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_uint32();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17UL);
- REQUIRE(*it++ == 0UL);
- REQUIRE(*it++ == 1UL);
- REQUIRE(*it++ == std::numeric_limits<uint32_t>::max());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_uint32/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_uint32(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed uint32 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- uint32_t data[] = { 17UL };
- pw.add_packed_uint32(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_uint32/data-empty"));
- }
-
- SECTION("one") {
- uint32_t data[] = { 17UL };
- pw.add_packed_uint32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_uint32/data-one"));
- }
-
- SECTION("many") {
- uint32_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint32_t>::max() };
- pw.add_packed_uint32(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_uint32/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_uint32/testcase.cpp b/test/t/repeated_packed_uint32/testcase.cpp
index d228e5f..ec21b63 100644
--- a/test/t/repeated_packed_uint32/testcase.cpp
+++ b/test/t/repeated_packed_uint32/testcase.cpp
@@ -10,6 +10,7 @@ int main(int c, char *argv[]) {
msg.add_i(17UL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200UL);
msg.add_i(0UL);
msg.add_i(1UL);
msg.add_i(std::numeric_limits<uint32_t>::max());
diff --git a/test/t/repeated_packed_uint64/data-many.pbf b/test/t/repeated_packed_uint64/data-many.pbf
index bbdfbcf..0e1dcc8 100644
Binary files a/test/t/repeated_packed_uint64/data-many.pbf and b/test/t/repeated_packed_uint64/data-many.pbf differ
diff --git a/test/t/repeated_packed_uint64/test_cases.cpp b/test/t/repeated_packed_uint64/test_cases.cpp
index 6880106..7f2e95a 100644
--- a/test/t/repeated_packed_uint64/test_cases.cpp
+++ b/test/t/repeated_packed_uint64/test_cases.cpp
@@ -1,83 +1,9 @@
#include <test.hpp>
-TEST_CASE("read repeated packed uint64 field") {
+#define PBF_TYPE uint64
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint64_t;
- SECTION("empty") {
- const std::string buffer = load_data("repeated_packed_uint64/data-empty");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(!item.next());
- }
-
- SECTION("one") {
- const std::string buffer = load_data("repeated_packed_uint64/data-one");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_uint64();
- REQUIRE(!item.next());
-
- REQUIRE(*it_pair.first == 17ULL);
- REQUIRE(++it_pair.first == it_pair.second);
- }
-
- SECTION("many") {
- const std::string buffer = load_data("repeated_packed_uint64/data-many");
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- auto it_pair = item.get_packed_uint64();
- REQUIRE(!item.next());
-
- auto it = it_pair.first;
- REQUIRE(*it++ == 17ULL);
- REQUIRE(*it++ == 0ULL);
- REQUIRE(*it++ == 1ULL);
- REQUIRE(*it++ == std::numeric_limits<uint64_t>::max());
- REQUIRE(it == it_pair.second);
- }
-
- SECTION("end_of_buffer") {
- const std::string buffer = load_data("repeated_packed_uint64/data-many");
-
- for (std::string::size_type i = 1; i < buffer.size(); ++i) {
- protozero::pbf_reader item(buffer.data(), i);
- REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_packed_uint64(), protozero::end_of_buffer_exception);
- }
- }
-
-}
-
-TEST_CASE("write repeated packed uint64 field") {
-
- std::string buffer;
- protozero::pbf_writer pw(buffer);
-
- SECTION("empty") {
- uint64_t data[] = { 17UL };
- pw.add_packed_uint64(1, std::begin(data), std::begin(data) /* !!!! */);
-
- REQUIRE(buffer == load_data("repeated_packed_uint64/data-empty"));
- }
-
- SECTION("one") {
- uint64_t data[] = { 17UL };
- pw.add_packed_uint64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_uint64/data-one"));
- }
-
- SECTION("many") {
- uint64_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint64_t>::max() };
- pw.add_packed_uint64(1, std::begin(data), std::end(data));
-
- REQUIRE(buffer == load_data("repeated_packed_uint64/data-many"));
- }
-
-}
+#include <packed_access.hpp>
diff --git a/test/t/repeated_packed_uint64/testcase.cpp b/test/t/repeated_packed_uint64/testcase.cpp
index ffefe09..d77795b 100644
--- a/test/t/repeated_packed_uint64/testcase.cpp
+++ b/test/t/repeated_packed_uint64/testcase.cpp
@@ -10,6 +10,7 @@ int main(int c, char *argv[]) {
msg.add_i(17ULL);
write_to_file(msg, "data-one.pbf");
+ msg.add_i(200UL);
msg.add_i(0ULL);
msg.add_i(1ULL);
msg.add_i(std::numeric_limits<uint64_t>::max());
diff --git a/test/t/rollback/test_cases.cpp b/test/t/rollback/test_cases.cpp
index 1223006..99ab6d6 100644
--- a/test/t/rollback/test_cases.cpp
+++ b/test/t/rollback/test_cases.cpp
@@ -51,10 +51,10 @@ TEST_CASE("rollback when using packed_field functions") {
msg.next();
REQUIRE(msg.tag() == 1);
- auto it_pair = msg.get_packed_sint64();
- auto it = it_pair.first;
+ auto it_range = msg.get_packed_sint64();
+ auto it = it_range.begin();
REQUIRE(*it++ == 17L);
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
msg.next();
REQUIRE(msg.tag() == 4);
@@ -86,15 +86,15 @@ TEST_CASE("rollback when using packed_field functions") {
msg.next();
REQUIRE(msg.tag() == 1);
- auto it_pair = msg.get_packed_sint64();
- auto it = it_pair.first;
+ auto it_range = msg.get_packed_sint64();
+ auto it = it_range.begin();
REQUIRE(*it++ == 17L);
REQUIRE(*it++ == 0L);
REQUIRE(*it++ == 1L);
REQUIRE(*it++ == -1L);
REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
- REQUIRE(it == it_pair.second);
+ REQUIRE(it == it_range.end());
msg.next();
REQUIRE(msg.tag() == 4);
diff --git a/test/t/sfixed32/data-neg200.pbf b/test/t/sfixed32/data-neg200.pbf
new file mode 100644
index 0000000..19d26f1
--- /dev/null
+++ b/test/t/sfixed32/data-neg200.pbf
@@ -0,0 +1 @@
+
8���
\ No newline at end of file
diff --git a/test/t/sfixed32/data-pos200.pbf b/test/t/sfixed32/data-pos200.pbf
new file mode 100644
index 0000000..7dc726e
Binary files /dev/null and b/test/t/sfixed32/data-pos200.pbf differ
diff --git a/test/t/sfixed32/testcase.cpp b/test/t/sfixed32/testcase.cpp
index 03be3e0..e9f21ce 100644
--- a/test/t/sfixed32/testcase.cpp
+++ b/test/t/sfixed32/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(-1);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int32_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/sfixed64/data-neg200.pbf b/test/t/sfixed64/data-neg200.pbf
new file mode 100644
index 0000000..6460225
--- /dev/null
+++ b/test/t/sfixed64/data-neg200.pbf
@@ -0,0 +1 @@
+ 8�������
\ No newline at end of file
diff --git a/test/t/sfixed64/data-pos200.pbf b/test/t/sfixed64/data-pos200.pbf
new file mode 100644
index 0000000..be400ca
Binary files /dev/null and b/test/t/sfixed64/data-pos200.pbf differ
diff --git a/test/t/sfixed64/testcase.cpp b/test/t/sfixed64/testcase.cpp
index b2b50d8..aca3093 100644
--- a/test/t/sfixed64/testcase.cpp
+++ b/test/t/sfixed64/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(-1);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int64_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/sint32/data-neg200.pbf b/test/t/sint32/data-neg200.pbf
new file mode 100644
index 0000000..c1add16
--- /dev/null
+++ b/test/t/sint32/data-neg200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/sint32/data-pos200.pbf b/test/t/sint32/data-pos200.pbf
new file mode 100644
index 0000000..2d26c9a
--- /dev/null
+++ b/test/t/sint32/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/sint32/testcase.cpp b/test/t/sint32/testcase.cpp
index cbd7fc1..88f4911 100644
--- a/test/t/sint32/testcase.cpp
+++ b/test/t/sint32/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1L);
write_to_file(msg, "data-pos.pbf");
- msg.set_i(-1L);
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
+ msg.set_i(-1);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int32_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/sint64/data-neg200.pbf b/test/t/sint64/data-neg200.pbf
new file mode 100644
index 0000000..c1add16
--- /dev/null
+++ b/test/t/sint64/data-neg200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/sint64/data-pos200.pbf b/test/t/sint64/data-pos200.pbf
new file mode 100644
index 0000000..2d26c9a
--- /dev/null
+++ b/test/t/sint64/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/sint64/testcase.cpp b/test/t/sint64/testcase.cpp
index 051afd6..fea56c6 100644
--- a/test/t/sint64/testcase.cpp
+++ b/test/t/sint64/testcase.cpp
@@ -11,9 +11,15 @@ int main(int c, char *argv[]) {
msg.set_i(1LL);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200LL);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(-1LL);
write_to_file(msg, "data-neg.pbf");
+ msg.set_i(-200LL);
+ write_to_file(msg, "data-neg200.pbf");
+
msg.set_i(std::numeric_limits<int64_t>::max());
write_to_file(msg, "data-max.pbf");
diff --git a/test/t/skip/test_cases.cpp b/test/t/skip/test_cases.cpp
index f426565..50ea374 100644
--- a/test/t/skip/test_cases.cpp
+++ b/test/t/skip/test_cases.cpp
@@ -1,136 +1,130 @@
#include <test.hpp>
-TEST_CASE("skip") {
-
- SECTION("check that skip() skips the right amount of bytes") {
-
- // These are all the data files which contain exactly one field.
- //
- // Create this list with:
- // cd test/t
- // find . -name data\*pbf -not -empty | sed -e 's/^.\/\(.*\).pbf/"\1",/'
- // and then remove everything manually that contains more than one
- // field.
- const std::vector<std::string> filenames = {
- "bool/data-also-true",
- "bool/data-false",
- "bool/data-still-true",
- "bool/data-true",
- "bytes/data-binary",
- "bytes/data-empty",
- "bytes/data-one",
- "bytes/data-string",
- "double/data-neg",
- "double/data-pos",
- "double/data-zero",
- "enum/data-black",
- "enum/data-blue",
- "fixed32/data-max",
- "fixed32/data-pos",
- "fixed32/data-zero",
- "fixed64/data-max",
- "fixed64/data-pos",
- "fixed64/data-zero",
- "float/data-neg",
- "float/data-pos",
- "float/data-zero",
- "int32/data-max",
- "int32/data-min",
- "int32/data-neg",
- "int32/data-pos",
- "int32/data-zero",
- "int64/data-max",
- "int64/data-min",
- "int64/data-neg",
- "int64/data-pos",
- "int64/data-zero",
- "message/data-message",
- "repeated/data-one",
- "repeated_packed_fixed32/data-many",
- "repeated_packed_fixed32/data-one",
- "repeated_packed_fixed64/data-many",
- "repeated_packed_fixed64/data-one",
- "repeated_packed_int32/data-many",
- "repeated_packed_int32/data-one",
- "repeated_packed_int64/data-many",
- "repeated_packed_int64/data-one",
- "repeated_packed_sfixed32/data-many",
- "repeated_packed_sfixed32/data-one",
- "repeated_packed_sfixed64/data-many",
- "repeated_packed_sfixed64/data-one",
- "repeated_packed_sint32/data-many",
- "repeated_packed_sint32/data-one",
- "repeated_packed_sint64/data-many",
- "repeated_packed_sint64/data-one",
- "repeated_packed_uint32/data-many",
- "repeated_packed_uint32/data-one",
- "repeated_packed_uint64/data-many",
- "repeated_packed_uint64/data-one",
- "sfixed32/data-max",
- "sfixed32/data-min",
- "sfixed32/data-zero",
- "sfixed64/data-max",
- "sfixed64/data-min",
- "sfixed64/data-zero",
- "sint32/data-max",
- "sint32/data-min",
- "sint32/data-neg",
- "sint32/data-pos",
- "sint32/data-zero",
- "sint64/data-max",
- "sint64/data-min",
- "sint64/data-neg",
- "sint64/data-pos",
- "sint64/data-zero",
- "string/data-empty",
- "string/data-one",
- "string/data-string",
- "tags/data-tag-1",
- "tags/data-tag-200000",
- "tags/data-tag-200",
- "tags/data-tag-max",
- "uint32/data-max",
- "uint32/data-pos",
- "uint32/data-zero",
- "uint64/data-max",
- "uint64/data-pos",
- "uint64/data-zero",
- };
-
- for (const auto& filename : filenames) {
- const std::string buffer = load_data(filename);
-
- protozero::pbf_reader item(buffer);
-
- REQUIRE(item.next());
- item.skip();
- REQUIRE(!item);
- }
+TEST_CASE("skip() skips the right amount of bytes") {
+
+ // These are all the data files which contain exactly one field.
+ //
+ // Create this list with:
+ // cd test/t
+ // find . -name data\*pbf -not -empty | sed -e 's/^.\/\(.*\).pbf/"\1",/'
+ // and then remove everything manually that contains more than one
+ // field.
+ const std::vector<std::string> filenames = {
+ "bool/data-also-true",
+ "bool/data-false",
+ "bool/data-still-true",
+ "bool/data-true",
+ "bytes/data-binary",
+ "bytes/data-empty",
+ "bytes/data-one",
+ "bytes/data-string",
+ "double/data-neg",
+ "double/data-pos",
+ "double/data-zero",
+ "enum/data-black",
+ "enum/data-blue",
+ "fixed32/data-max",
+ "fixed32/data-pos",
+ "fixed32/data-zero",
+ "fixed64/data-max",
+ "fixed64/data-pos",
+ "fixed64/data-zero",
+ "float/data-neg",
+ "float/data-pos",
+ "float/data-zero",
+ "int32/data-max",
+ "int32/data-min",
+ "int32/data-neg",
+ "int32/data-pos",
+ "int32/data-zero",
+ "int64/data-max",
+ "int64/data-min",
+ "int64/data-neg",
+ "int64/data-pos",
+ "int64/data-zero",
+ "message/data-message",
+ "repeated/data-one",
+ "repeated_packed_fixed32/data-many",
+ "repeated_packed_fixed32/data-one",
+ "repeated_packed_fixed64/data-many",
+ "repeated_packed_fixed64/data-one",
+ "repeated_packed_int32/data-many",
+ "repeated_packed_int32/data-one",
+ "repeated_packed_int64/data-many",
+ "repeated_packed_int64/data-one",
+ "repeated_packed_sfixed32/data-many",
+ "repeated_packed_sfixed32/data-one",
+ "repeated_packed_sfixed64/data-many",
+ "repeated_packed_sfixed64/data-one",
+ "repeated_packed_sint32/data-many",
+ "repeated_packed_sint32/data-one",
+ "repeated_packed_sint64/data-many",
+ "repeated_packed_sint64/data-one",
+ "repeated_packed_uint32/data-many",
+ "repeated_packed_uint32/data-one",
+ "repeated_packed_uint64/data-many",
+ "repeated_packed_uint64/data-one",
+ "sfixed32/data-max",
+ "sfixed32/data-min",
+ "sfixed32/data-zero",
+ "sfixed64/data-max",
+ "sfixed64/data-min",
+ "sfixed64/data-zero",
+ "sint32/data-max",
+ "sint32/data-min",
+ "sint32/data-neg",
+ "sint32/data-pos",
+ "sint32/data-zero",
+ "sint64/data-max",
+ "sint64/data-min",
+ "sint64/data-neg",
+ "sint64/data-pos",
+ "sint64/data-zero",
+ "string/data-empty",
+ "string/data-one",
+ "string/data-string",
+ "tags/data-tag-1",
+ "tags/data-tag-200000",
+ "tags/data-tag-200",
+ "tags/data-tag-max",
+ "uint32/data-max",
+ "uint32/data-pos",
+ "uint32/data-zero",
+ "uint64/data-max",
+ "uint64/data-pos",
+ "uint64/data-zero",
+ };
+
+ for (const auto& filename : filenames) {
+ const std::string buffer = load_data(filename);
+
+ protozero::pbf_reader item{buffer};
+ REQUIRE(item.next());
+ item.skip();
+ REQUIRE(!item);
}
+}
- SECTION("check that next() throws on unknown field type") {
- std::string buffer;
- protozero::pbf_writer pw(buffer);
- pw.add_fixed32(1, 123);
+TEST_CASE("exceptional cases") {
+
+ std::string buffer;
+ protozero::pbf_writer pw{buffer};
+ pw.add_fixed32(1, 123);
+ SECTION("check that next() throws on unknown field type") {
buffer[0] += 1; // hack to create illegal field type
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE_THROWS_AS(item.next(), protozero::unknown_pbf_wire_type_exception);
}
SECTION("check that skip() throws on short buffer") {
- std::string buffer;
-
- protozero::pbf_writer pw(buffer);
- pw.add_fixed32(1, 123);
-
buffer.resize(buffer.size() - 1); // "remove" last byte from buffer
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
REQUIRE_THROWS_AS(item.skip(), protozero::end_of_buffer_exception);
diff --git a/test/t/string/test_cases.cpp b/test/t/string/test_cases.cpp
index c81d293..050c640 100644
--- a/test/t/string/test_cases.cpp
+++ b/test/t/string/test_cases.cpp
@@ -1,7 +1,7 @@
#include <test.hpp>
-TEST_CASE("read string field") {
+TEST_CASE("read string field using get_string") {
SECTION("empty") {
const std::string buffer = load_data("string/data-empty");
@@ -45,6 +45,53 @@ TEST_CASE("read string field") {
}
+TEST_CASE("read string field using get_view") {
+
+ SECTION("empty") {
+ const std::string buffer = load_data("string/data-empty");
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ auto v = item.get_view();
+ REQUIRE(v.size() == 0);
+ REQUIRE(!item.next());
+ }
+
+ SECTION("one") {
+ const std::string buffer = load_data("string/data-one");
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ auto v = item.get_view();
+ REQUIRE(*v.data() == 'x');
+ REQUIRE(v.size() == 1);
+ REQUIRE(!item.next());
+ }
+
+ SECTION("string") {
+ const std::string buffer = load_data("string/data-string");
+
+ protozero::pbf_reader item(buffer);
+
+ REQUIRE(item.next());
+ REQUIRE(std::string(item.get_view()) == "foobar");
+ REQUIRE(!item.next());
+ }
+
+ SECTION("end_of_buffer") {
+ const std::string buffer = load_data("string/data-string");
+
+ for (std::string::size_type i = 1; i < buffer.size(); ++i) {
+ protozero::pbf_reader item(buffer.data(), i);
+ REQUIRE(item.next());
+ REQUIRE_THROWS_AS(item.get_view(), protozero::end_of_buffer_exception);
+ }
+ }
+
+}
+
TEST_CASE("write string field") {
std::string buffer_test;
diff --git a/test/t/uint32/data-pos200.pbf b/test/t/uint32/data-pos200.pbf
new file mode 100644
index 0000000..d9eeb99
--- /dev/null
+++ b/test/t/uint32/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/uint32/testcase.cpp b/test/t/uint32/testcase.cpp
index 4a6177b..d5533be 100644
--- a/test/t/uint32/testcase.cpp
+++ b/test/t/uint32/testcase.cpp
@@ -11,6 +11,9 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(std::numeric_limits<uint32_t>::max());
write_to_file(msg, "data-max.pbf");
}
diff --git a/test/t/uint64/data-pos200.pbf b/test/t/uint64/data-pos200.pbf
new file mode 100644
index 0000000..d9eeb99
--- /dev/null
+++ b/test/t/uint64/data-pos200.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/uint64/testcase.cpp b/test/t/uint64/testcase.cpp
index 22ee8a8..732bf42 100644
--- a/test/t/uint64/testcase.cpp
+++ b/test/t/uint64/testcase.cpp
@@ -11,6 +11,9 @@ int main(int c, char *argv[]) {
msg.set_i(1);
write_to_file(msg, "data-pos.pbf");
+ msg.set_i(200);
+ write_to_file(msg, "data-pos200.pbf");
+
msg.set_i(std::numeric_limits<uint64_t>::max());
write_to_file(msg, "data-max.pbf");
}
diff --git a/test/t/varint/test_cases.cpp b/test/t/varint/test_cases.cpp
index 3ada39a..be8423e 100644
--- a/test/t/varint/test_cases.cpp
+++ b/test/t/varint/test_cases.cpp
@@ -1,54 +1,142 @@
#include <test.hpp>
+TEST_CASE("max varint length") {
+ REQUIRE(protozero::max_varint_length == 10);
+}
+
TEST_CASE("varint") {
std::string buffer;
- protozero::pbf_writer pw(buffer);
+ protozero::pbf_writer pw{buffer};
SECTION("encode/decode int32") {
pw.add_int32(1, 17);
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE(17 == item.get_int32());
+
+ SECTION("get") {
+ REQUIRE(17 == item.get_int32());
+ }
+
+ SECTION("skip") {
+ item.skip();
+ }
+
+ REQUIRE_FALSE(item.next());
}
SECTION("encode/decode uint32") {
pw.add_uint32(1, 17U);
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE(17U == item.get_uint32());
+
+ SECTION("get") {
+ REQUIRE(17U == item.get_uint32());
+ }
+
+ SECTION("skip") {
+ item.skip();
+ }
+
+ REQUIRE_FALSE(item.next());
}
SECTION("encode/decode uint64") {
pw.add_uint64(1, (1ULL << 40));
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE((1ULL << 40) == item.get_uint64());
+
+ SECTION("get") {
+ REQUIRE((1ULL << 40) == item.get_uint64());
+ }
+
+ SECTION("skip") {
+ item.skip();
+ }
+
+ REQUIRE_FALSE(item.next());
}
SECTION("short buffer while parsing varint") {
pw.add_uint64(1, (1ULL << 40));
buffer.resize(buffer.size() - 1); // "remove" last byte from buffer
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+
+ SECTION("get") {
+ REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+ }
+
+ SECTION("skip") {
+ REQUIRE_THROWS_AS(item.skip(), protozero::end_of_buffer_exception);
+ }
}
SECTION("data corruption in buffer while parsing varint)") {
pw.add_uint64(1, (1ULL << 20));
buffer[buffer.size() - 1] += 0x80; // pretend the varint goes on
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+
+ SECTION("get") {
+ REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+ }
+
+ SECTION("skip") {
+ REQUIRE_THROWS_AS(item.skip(), protozero::end_of_buffer_exception);
+ }
}
SECTION("data corruption in buffer while parsing varint (max length varint)") {
pw.add_uint64(1, std::numeric_limits<uint64_t>::max());
buffer[buffer.size() - 1] += 0x80; // pretend the varint goes on
- protozero::pbf_reader item(buffer);
+ protozero::pbf_reader item{buffer};
+ REQUIRE(item.next());
+
+ SECTION("get") {
+ REQUIRE_THROWS_AS(item.get_uint64(), protozero::varint_too_long_exception);
+ }
+
+ SECTION("skip") {
+ REQUIRE_THROWS_AS(item.skip(), protozero::varint_too_long_exception);
+ }
+ }
+
+}
+
+TEST_CASE("lots of varints back and forth") {
+
+ std::string buffer;
+
+ for (uint32_t n = 0; n < 70000; ++n) {
+ protozero::pbf_writer pw{buffer};
+ pw.add_uint32(1, n);
+ protozero::pbf_reader item{buffer};
+ REQUIRE(item.next());
+ REQUIRE(n == item.get_uint32());
+ REQUIRE(!item.next());
+ buffer.clear();
+ }
+
+ for (int32_t n = -70000; n < 70000; ++n) {
+ protozero::pbf_writer pw{buffer};
+ pw.add_int32(1, n);
+ protozero::pbf_reader item{buffer};
+ REQUIRE(item.next());
+ REQUIRE(n == item.get_int32());
+ REQUIRE(!item.next());
+ buffer.clear();
+ }
+
+ for (int32_t n = -70000; n < 70000; ++n) {
+ protozero::pbf_writer pw{buffer};
+ pw.add_sint32(1, n);
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
- REQUIRE_THROWS_AS(item.get_uint64(), protozero::varint_too_long_exception);
+ REQUIRE(n == item.get_sint32());
+ REQUIRE(!item.next());
+ buffer.clear();
}
}
diff --git a/test/t/vector_tile/test_cases.cpp b/test/t/vector_tile/test_cases.cpp
index 314aeb7..1b31904 100644
--- a/test/t/vector_tile/test_cases.cpp
+++ b/test/t/vector_tile/test_cases.cpp
@@ -28,15 +28,14 @@ std::string get_name(protozero::pbf_reader layer) { // copy!
TEST_CASE("reading vector tiles") {
- SECTION("iterate over message using next()") {
- const std::string buffer = load_data("vector_tile/data.vector");
-
- protozero::pbf_reader item(buffer);
+ const std::string buffer = load_data("vector_tile/data.vector");
+ protozero::pbf_reader item{buffer};
+ std::vector<std::string> layer_names;
- std::vector<std::string> layer_names;
+ SECTION("iterate over message using next()") {
while (item.next()) {
if (item.tag() == 3) { // repeated message Layer
- protozero::pbf_reader layer { item.get_message() };
+ protozero::pbf_reader layer{item.get_message()};
while (layer.next()) {
switch (layer.tag()) {
case 1: // required string name
@@ -55,13 +54,8 @@ TEST_CASE("reading vector tiles") {
}
SECTION("iterate over message using next(type)") {
- const std::string buffer = load_data("vector_tile/data.vector");
-
- protozero::pbf_reader item(buffer);
-
- std::vector<std::string> layer_names;
while (item.next(3)) { // repeated message Layer
- protozero::pbf_reader layermsg { item.get_message() };
+ protozero::pbf_reader layermsg{item.get_message()};
while (layermsg.next(1)) { // required string name
layer_names.push_back(layermsg.get_string());
}
@@ -71,28 +65,25 @@ TEST_CASE("reading vector tiles") {
}
SECTION("iterate over features in road layer") {
- const std::string buffer = load_data("vector_tile/data.vector");
-
- protozero::pbf_reader item(buffer);
int n=0;
while (item.next(3)) { // repeated message Layer
- protozero::pbf_reader layer { item.get_message() };
+ protozero::pbf_reader layer{item.get_message()};
std::string name = get_name(layer);
if (name == "road") {
while (layer.next(2)) { // repeated Feature
++n;
- protozero::pbf_reader feature { layer.get_message() };
+ protozero::pbf_reader feature{layer.get_message()};
while (feature.next()) {
switch (feature.tag()) {
case 1: { // optional uint64 id
- auto id = feature.get_uint64();
+ const auto id = feature.get_uint64();
REQUIRE(id >= 1ULL);
REQUIRE(id <= 504ULL);
break;
}
case 3: { // optional GeomType
- auto geom_type = feature.get_uint32();
+ const auto geom_type = feature.get_uint32();
REQUIRE(geom_type >= 1UL);
REQUIRE(geom_type <= 3UL);
break;
diff --git a/test/t/wrong_type_access/test_cases.cpp b/test/t/wrong_type_access/test_cases.cpp
index 4ebefe2..e372a34 100644
--- a/test/t/wrong_type_access/test_cases.cpp
+++ b/test/t/wrong_type_access/test_cases.cpp
@@ -5,9 +5,9 @@
TEST_CASE("check assert on non-varint access to varint") {
const std::string buffer = load_data("int32/data-zero");
- protozero::pbf_reader item(buffer);
-
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
+
REQUIRE(item.get_int32() == 0);
REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
REQUIRE_THROWS_AS(item.get_string(), assert_error);
@@ -18,9 +18,9 @@ TEST_CASE("check assert on non-varint access to varint") {
TEST_CASE("check assert on non-fixed access to fixed64") {
const std::string buffer = load_data("fixed64/data-zero");
- protozero::pbf_reader item(buffer);
-
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
+
REQUIRE_THROWS_AS(item.get_int32(), assert_error);
REQUIRE(item.get_fixed64() == 0);
REQUIRE_THROWS_AS(item.get_string(), assert_error);
@@ -31,9 +31,9 @@ TEST_CASE("check assert on non-fixed access to fixed64") {
TEST_CASE("check assert on non-string access to string") {
const std::string buffer = load_data("string/data-string");
- protozero::pbf_reader item(buffer);
-
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
+
REQUIRE_THROWS_AS(item.get_int32(), assert_error);
REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
REQUIRE(item.get_string() == "foobar");
@@ -44,9 +44,9 @@ TEST_CASE("check assert on non-string access to string") {
TEST_CASE("check assert on non-fixed access to fixed32") {
const std::string buffer = load_data("fixed32/data-zero");
- protozero::pbf_reader item(buffer);
-
+ protozero::pbf_reader item{buffer};
REQUIRE(item.next());
+
REQUIRE_THROWS_AS(item.get_int32(), assert_error);
REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
REQUIRE_THROWS_AS(item.get_string(), assert_error);
diff --git a/test/t/zigzag/test_cases.cpp b/test/t/zigzag/test_cases.cpp
index 1773100..e877493 100644
--- a/test/t/zigzag/test_cases.cpp
+++ b/test/t/zigzag/test_cases.cpp
@@ -9,43 +9,35 @@ inline int64_t zz64(int64_t val) {
return protozero::decode_zigzag64(protozero::encode_zigzag64(val));
}
-TEST_CASE("zigzag") {
-
- SECTION("some values - 32bit") {
-
- REQUIRE(protozero::encode_zigzag32( 0L) == 0UL);
- REQUIRE(protozero::encode_zigzag32(-1L) == 1UL);
- REQUIRE(protozero::encode_zigzag32( 1L) == 2UL);
- REQUIRE(protozero::encode_zigzag32(-2L) == 3UL);
- REQUIRE(protozero::encode_zigzag32( 2L) == 4UL);
-
- }
-
- SECTION("some values - 64bit") {
-
- REQUIRE(protozero::encode_zigzag64( 0LL) == 0ULL);
- REQUIRE(protozero::encode_zigzag64(-1LL) == 1ULL);
- REQUIRE(protozero::encode_zigzag64( 1LL) == 2ULL);
- REQUIRE(protozero::encode_zigzag64(-2LL) == 3ULL);
- REQUIRE(protozero::encode_zigzag64( 2LL) == 4ULL);
-
- }
+TEST_CASE("zigzag encode some 32 bit values") {
+ REQUIRE(protozero::encode_zigzag32( 0L) == 0UL);
+ REQUIRE(protozero::encode_zigzag32(-1L) == 1UL);
+ REQUIRE(protozero::encode_zigzag32( 1L) == 2UL);
+ REQUIRE(protozero::encode_zigzag32(-2L) == 3UL);
+ REQUIRE(protozero::encode_zigzag32( 2L) == 4UL);
+}
- SECTION("zigzag and back - 32bit") {
- REQUIRE(zz32( 0L) == 0L);
- REQUIRE(zz32( 1L) == 1L);
- REQUIRE(zz32(-1L) == -1L);
- REQUIRE(zz32(std::numeric_limits<int32_t>::max()) == std::numeric_limits<int32_t>::max());
- REQUIRE(zz32(std::numeric_limits<int32_t>::min()) == std::numeric_limits<int32_t>::min());
- }
+TEST_CASE("zigzag encode some 64 bit values") {
+ REQUIRE(protozero::encode_zigzag64( 0LL) == 0ULL);
+ REQUIRE(protozero::encode_zigzag64(-1LL) == 1ULL);
+ REQUIRE(protozero::encode_zigzag64( 1LL) == 2ULL);
+ REQUIRE(protozero::encode_zigzag64(-2LL) == 3ULL);
+ REQUIRE(protozero::encode_zigzag64( 2LL) == 4ULL);
+}
- SECTION("zigzag and back - 64bit") {
- REQUIRE(zz64( 0LL) == 0LL);
- REQUIRE(zz64( 1LL) == 1LL);
- REQUIRE(zz64(-1LL) == -1LL);
- REQUIRE(zz64(std::numeric_limits<int64_t>::max()) == std::numeric_limits<int64_t>::max());
- REQUIRE(zz64(std::numeric_limits<int64_t>::min()) == std::numeric_limits<int64_t>::min());
- }
+TEST_CASE("zigzag and back - 32bit") {
+ REQUIRE(zz32( 0L) == 0L);
+ REQUIRE(zz32( 1L) == 1L);
+ REQUIRE(zz32(-1L) == -1L);
+ REQUIRE(zz32(std::numeric_limits<int32_t>::max()) == std::numeric_limits<int32_t>::max());
+ REQUIRE(zz32(std::numeric_limits<int32_t>::min()) == std::numeric_limits<int32_t>::min());
+}
+TEST_CASE("zigzag and back - 64bit") {
+ REQUIRE(zz64( 0LL) == 0LL);
+ REQUIRE(zz64( 1LL) == 1LL);
+ REQUIRE(zz64(-1LL) == -1LL);
+ REQUIRE(zz64(std::numeric_limits<int64_t>::max()) == std::numeric_limits<int64_t>::max());
+ REQUIRE(zz64(std::numeric_limits<int64_t>::min()) == std::numeric_limits<int64_t>::min());
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/protozero.git
More information about the Pkg-grass-devel
mailing list