diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index 1b11ed0ec..4fb8b09d9 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -125,6 +125,7 @@ jobs: ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY + ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=vectors_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 40ab577d1..4a1696d11 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -67,6 +67,7 @@ jobs: ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY + ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=vectors_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 14ca7b2fc..be3e5e54d 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -66,6 +66,7 @@ jobs: ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=licenses_write >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY + ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=vectors_read >> $GITHUB_STEP_SUMMARY ./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=points_read >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY diff --git a/benchmarks/all/vectors_read.cpp b/benchmarks/all/vectors_read.cpp new file mode 100644 index 000000000..c6d8a45fe --- /dev/null +++ b/benchmarks/all/vectors_read.cpp @@ -0,0 +1,265 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vectors_read { + +struct FloatVector { + std::vector values; + + bool operator==(const FloatVector&) const = default; +}; + +struct Data { + std::vector vectors; +}; + +static Data make_data(std::size_t count, std::size_t values_per_vector) { + Data data; + data.vectors.reserve(count); + for (std::size_t i = 0; i < count; ++i) { + FloatVector vector; + vector.values.reserve(values_per_vector); + for (std::size_t j = 0; j < values_per_vector; ++j) { + const auto index = i * values_per_vector + j; + const auto value = static_cast(index % 65536) - 32768; + vector.values.push_back(static_cast(value) * 0.125f); + } + data.vectors.push_back(std::move(vector)); + } + return data; +} + +template +static void run(benchmark::State& state, Write write, Read read) { + // Compare one large vector with many short vectors. The object wrappers + // allow the same workload to be used with BSON, TOML and XML as well. + const auto input = make_data(static_cast(state.range(0)), + static_cast(state.range(1))); + const auto data = write(input); + { + const auto result = read(data); + if (!result) { + state.SkipWithError(result.error().what()); + return; + } + if (result->vectors != input.vectors) { + state.SkipWithError("Vector values changed during round trip"); + return; + } + } + + for (auto _ : state) { + auto result = read(data); + if (!result) { + state.SkipWithError(result.error().what()); + break; + } + benchmark::DoNotOptimize(result); + } + state.SetItemsProcessed(state.iterations() * state.range(0) * state.range(1)); + state.SetBytesProcessed(state.iterations() * + static_cast(data.size())); + state.counters["serialized_bytes"] = static_cast(data.size()); +} + +static void BM_vectors_read_reflect_cpp_avro(benchmark::State& state) { + const auto schema = rfl::avro::to_schema(); + run( + state, + [&schema](const Data& value) { return rfl::avro::write(value, schema); }, + [&schema](const auto& data) { + return rfl::avro::read(data, schema); + }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_avro) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_boost_serialization( + benchmark::State& state) { + run( + state, + [](const Data& value) { return rfl::boost_serialization::write(value); }, + [](const auto& data) { + return rfl::boost_serialization::read(data); + }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_boost_serialization) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_bson(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::bson::write(value); }, + [](const auto& data) { return rfl::bson::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_bson) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_capnproto(benchmark::State& state) { + const auto schema = rfl::capnproto::to_schema(); + run( + state, + [&schema](const Data& value) { + return rfl::capnproto::write(value, schema); + }, + [&schema](const auto& data) { + return rfl::capnproto::read(data, schema); + }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_capnproto) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_cbor(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::cbor::write(value); }, + [](const auto& data) { return rfl::cbor::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_cbor) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_cereal(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::cereal::write(value); }, + [](const auto& data) { return rfl::cereal::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_cereal) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_flexbuf(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::flexbuf::write(value); }, + [](const auto& data) { return rfl::flexbuf::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_flexbuf) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_json(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::json::write(value); }, + [](const auto& data) { return rfl::json::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_json) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_msgpack(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::msgpack::write(value); }, + [](const auto& data) { return rfl::msgpack::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_msgpack) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_toml(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::toml::write(value); }, + [](const auto& data) { return rfl::toml::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_toml) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_ubjson(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::ubjson::write(value); }, + [](const auto& data) { return rfl::ubjson::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_ubjson) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_xml(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::xml::write(value); }, + [](const auto& data) { return rfl::xml::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_xml) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_yaml(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::yaml::write(value); }, + [](const auto& data) { return rfl::yaml::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_yaml) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +static void BM_vectors_read_reflect_cpp_yas(benchmark::State& state) { + run( + state, [](const Data& value) { return rfl::yas::write(value); }, + [](const auto& data) { return rfl::yas::read(data); }); +} +BENCHMARK(BM_vectors_read_reflect_cpp_yas) + ->Args({1, 1000}) + ->Args({1, 1000000}) + ->Args({10000, 64}) + ->ArgNames({"vectors", "values"}) + ->Unit(benchmark::kMillisecond); + +} // namespace vectors_read diff --git a/include/rfl/avro/Reader.hpp b/include/rfl/avro/Reader.hpp index 747bb0d0c..f9cda4c43 100644 --- a/include/rfl/avro/Reader.hpp +++ b/include/rfl/avro/Reader.hpp @@ -217,6 +217,11 @@ struct Reader { return Error(std::string(__FUNCTION__) + " error(" + std::to_string(res) + "): " + avro_strerror()); } + if constexpr (requires { _array_reader.reserve(size); }) { + if (const auto err = _array_reader.reserve(size)) { + return err; + } + } for (size_t ix = 0; ix < size; ++ix) { avro_value_t element; res = avro_value_get_by_index(_arr.val_, ix, &element, nullptr); diff --git a/include/rfl/boost_serialization/Reader.hpp b/include/rfl/boost_serialization/Reader.hpp index ec15eb0ae..ae5aad515 100644 --- a/include/rfl/boost_serialization/Reader.hpp +++ b/include/rfl/boost_serialization/Reader.hpp @@ -139,6 +139,11 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.size); }) { + if (const auto err = _array_reader.reserve(_arr.size)) { + return err; + } + } for (size_t i = 0; i < _arr.size; ++i) { auto var = InputVarType{_arr.ar}; const auto err = _array_reader.read(var); diff --git a/include/rfl/capnproto/Reader.hpp b/include/rfl/capnproto/Reader.hpp index eda33add4..c0ad2bc1f 100644 --- a/include/rfl/capnproto/Reader.hpp +++ b/include/rfl/capnproto/Reader.hpp @@ -149,6 +149,11 @@ class RFL_API Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.val_.size()); }) { + if (const auto err = _array_reader.reserve(_arr.val_.size())) { + return err; + } + } for (auto element : _arr.val_) { const auto err = _array_reader.read(InputVarType{std::move(element)}); if (err) { diff --git a/include/rfl/cbor/Reader.hpp b/include/rfl/cbor/Reader.hpp index 8a6fd8511..d9ddf237f 100644 --- a/include/rfl/cbor/Reader.hpp +++ b/include/rfl/cbor/Reader.hpp @@ -198,6 +198,11 @@ class Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.val_->size()); }) { + if (const auto err = _array_reader.reserve(_arr.val_->size())) { + return err; + } + } for (auto& val : _arr.val_->array_range()) { const auto err = _array_reader.read(InputVarType{&val}); if (err) { diff --git a/include/rfl/cereal/Reader.hpp b/include/rfl/cereal/Reader.hpp index 85d1ab09d..4f02b91f3 100644 --- a/include/rfl/cereal/Reader.hpp +++ b/include/rfl/cereal/Reader.hpp @@ -126,6 +126,11 @@ struct Reader { try { size_t size; (*_arr.archive_)(::cereal::make_size_tag(size)); + if constexpr (requires { _array_reader.reserve(size); }) { + if (const auto err = _array_reader.reserve(size)) { + return err; + } + } for (size_t i = 0; i < size; ++i) { const auto err = _array_reader.read(InputVarType{_arr.archive_}); if (err) { diff --git a/include/rfl/flexbuf/Reader.hpp b/include/rfl/flexbuf/Reader.hpp index 854f8e95e..07428b396 100644 --- a/include/rfl/flexbuf/Reader.hpp +++ b/include/rfl/flexbuf/Reader.hpp @@ -103,6 +103,11 @@ struct Reader { std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { const auto size = _arr.size(); + if constexpr (requires { _array_reader.reserve(size); }) { + if (const auto err = _array_reader.reserve(size)) { + return err; + } + } for (size_t i = 0; i < size; ++i) { const auto err = _array_reader.read(InputVarType(_arr[i])); if (err) { diff --git a/include/rfl/generic/Reader.hpp b/include/rfl/generic/Reader.hpp index 2d6f32405..c56cdd9e7 100644 --- a/include/rfl/generic/Reader.hpp +++ b/include/rfl/generic/Reader.hpp @@ -86,6 +86,11 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.size()); }) { + if (const auto err = _array_reader.reserve(_arr.size())) { + return err; + } + } for (const auto& v : _arr) { const auto err = _array_reader.read(InputVarType(v)); if (err) { diff --git a/include/rfl/json/Reader.hpp b/include/rfl/json/Reader.hpp index 0c2f7ad35..31c943fbe 100644 --- a/include/rfl/json/Reader.hpp +++ b/include/rfl/json/Reader.hpp @@ -98,6 +98,12 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + const auto size = yyjson_arr_size(_arr.val_); + if constexpr (requires { _array_reader.reserve(size); }) { + if (const auto err = _array_reader.reserve(size)) { + return err; + } + } yyjson_val* val; yyjson_arr_iter iter; yyjson_arr_iter_init(_arr.val_, &iter); diff --git a/include/rfl/msgpack/Reader.hpp b/include/rfl/msgpack/Reader.hpp index 18309e448..e797ce09b 100644 --- a/include/rfl/msgpack/Reader.hpp +++ b/include/rfl/msgpack/Reader.hpp @@ -170,6 +170,11 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.size); }) { + if (const auto err = _array_reader.reserve(_arr.size)) { + return err; + } + } for (uint32_t i = 0; i < _arr.size; ++i) { const auto err = _array_reader.read(_arr.ptr[i]); if (err) { diff --git a/include/rfl/parsing/IsReader.hpp b/include/rfl/parsing/IsReader.hpp index 315e01fd6..482fd6d13 100644 --- a/include/rfl/parsing/IsReader.hpp +++ b/include/rfl/parsing/IsReader.hpp @@ -69,6 +69,8 @@ concept IsReader = /// Iterates through an array and writes the contained vars into /// an array reader. + /// An array reader may optionally provide reserve(size_t), returning + /// std::optional, to receive the element count before reading. { r.read_array(array_reader, arr) } -> std::same_as>; /// Iterates through an object and writes the key-value pairs into an diff --git a/include/rfl/parsing/VectorReader.hpp b/include/rfl/parsing/VectorReader.hpp index c7abd90d3..a1b0be310 100644 --- a/include/rfl/parsing/VectorReader.hpp +++ b/include/rfl/parsing/VectorReader.hpp @@ -1,6 +1,8 @@ #ifndef RFL_PARSING_VECTORREADER_HPP_ #define RFL_PARSING_VECTORREADER_HPP_ +#include +#include #include #include #include @@ -30,6 +32,19 @@ class VectorReader { ~VectorReader() = default; + std::optional reserve(const std::size_t _size) const noexcept { + if constexpr (requires { vec_->reserve(_size); } && + !is_inplace_vector_v && !is_set_like_v && + !is_map_like_v) { + try { + vec_->reserve(_size); + } catch (const std::exception& e) { + return Error(e.what()); + } + } + return std::nullopt; + } + /** * @brief Reads a single variable from the input. * diff --git a/include/rfl/toml/Reader.hpp b/include/rfl/toml/Reader.hpp index f704879dc..ac87aaf1e 100644 --- a/include/rfl/toml/Reader.hpp +++ b/include/rfl/toml/Reader.hpp @@ -120,6 +120,11 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr->size()); }) { + if (const auto err = _array_reader.reserve(_arr->size())) { + return err; + } + } for (auto& node : *_arr) { const auto err = _array_reader.read(&node); if (err) { diff --git a/include/rfl/ubjson/Reader.hpp b/include/rfl/ubjson/Reader.hpp index 308011f9b..3c0f5fa1b 100644 --- a/include/rfl/ubjson/Reader.hpp +++ b/include/rfl/ubjson/Reader.hpp @@ -148,6 +148,11 @@ class Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.val_->size()); }) { + if (const auto err = _array_reader.reserve(_arr.val_->size())) { + return err; + } + } for (auto& val : _arr.val_->array_range()) { const auto err = _array_reader.read(InputVarType{&val}); if (err) { diff --git a/include/rfl/yaml/Reader.hpp b/include/rfl/yaml/Reader.hpp index 3ff3ac044..48560a007 100644 --- a/include/rfl/yaml/Reader.hpp +++ b/include/rfl/yaml/Reader.hpp @@ -159,6 +159,11 @@ struct Reader { template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { + if constexpr (requires { _array_reader.reserve(_arr.node_.size()); }) { + if (const auto err = _array_reader.reserve(_arr.node_.size())) { + return err; + } + } for (size_t i = 0; i < _arr.node_.size(); ++i) { const auto err = _array_reader.read(_arr.node_[i]); if (err) { diff --git a/include/rfl/yas/Reader.hpp b/include/rfl/yas/Reader.hpp index bf1b652cd..fd1273955 100644 --- a/include/rfl/yas/Reader.hpp +++ b/include/rfl/yas/Reader.hpp @@ -120,6 +120,11 @@ struct Reader { try { std::size_t size; (*_arr.ar) & size; + if constexpr (requires { _array_reader.reserve(size); }) { + if (const auto err = _array_reader.reserve(size)) { + return err; + } + } for (std::size_t i = 0; i < size; ++i) { const auto err = _array_reader.read(InputVarType{_arr.ar}); if (err) { diff --git a/tests/avro/test_vector_reserve.cpp b/tests/avro/test_vector_reserve.cpp new file mode 100644 index 000000000..806bc352d --- /dev/null +++ b/tests/avro/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(avro, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::avro::write(value); }, + [](const auto& data) { return rfl::avro::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/boost_serialization/test_vector_reserve.cpp b/tests/boost_serialization/test_vector_reserve.cpp new file mode 100644 index 000000000..b639ce43e --- /dev/null +++ b/tests/boost_serialization/test_vector_reserve.cpp @@ -0,0 +1,15 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(boost_serialization, test_vector_reserve) { + check_moves( + [](const Data& value) { return rfl::boost_serialization::write(value); }, + [](const auto& data) { + return rfl::boost_serialization::read(data); + }); +} + +} // namespace test_vector_reserve diff --git a/tests/capnproto/test_vector_reserve.cpp b/tests/capnproto/test_vector_reserve.cpp new file mode 100644 index 000000000..2a83a28e8 --- /dev/null +++ b/tests/capnproto/test_vector_reserve.cpp @@ -0,0 +1,13 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(capnproto, test_vector_reserve) { + check_moves( + [](const Data& value) { return rfl::capnproto::write(value); }, + [](const auto& data) { return rfl::capnproto::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/cbor/test_vector_reserve.cpp b/tests/cbor/test_vector_reserve.cpp new file mode 100644 index 000000000..4d8c0a8f1 --- /dev/null +++ b/tests/cbor/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(cbor, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::cbor::write(value); }, + [](const auto& data) { return rfl::cbor::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/cereal/test_vector_reserve.cpp b/tests/cereal/test_vector_reserve.cpp new file mode 100644 index 000000000..d6083fea3 --- /dev/null +++ b/tests/cereal/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(cereal, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::cereal::write(value); }, + [](const auto& data) { return rfl::cereal::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/check_vector_reserve.hpp b/tests/check_vector_reserve.hpp new file mode 100644 index 000000000..97c8543de --- /dev/null +++ b/tests/check_vector_reserve.hpp @@ -0,0 +1,57 @@ +#ifndef CHECK_VECTOR_RESERVE_HPP_ +#define CHECK_VECTOR_RESERVE_HPP_ + +#include + +#include +#include + +namespace test_vector_reserve { + +struct Value { + using ReflectionType = float; + + inline static std::size_t moves = 0; + + explicit Value(float value) : value_(value) {} + Value(const Value&) = delete; + Value(Value&& other) noexcept : value_(other.value_) { ++moves; } + + const float& reflection() const { return value_; } + + bool operator==(const Value& other) const { return value_ == other.value_; } + + private: + float value_; +}; + +struct Data { + std::vector values; +}; + +template +void check_moves(Write write, Read read) { + std::size_t moves_per_element = 0; + for (const auto size : {0, 1, 65}) { + SCOPED_TRACE(size); + Data input; + for (int i = 0; i < size; ++i) { + input.values.emplace_back(static_cast(i) * 0.5f); + } + const auto data = write(input); + Value::moves = 0; + const auto result = read(data); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->values, input.values); + if (size == 1) { + moves_per_element = Value::moves; + EXPECT_GT(moves_per_element, 0u); + } + // Growing the vector would add moves of previously parsed elements. + EXPECT_EQ(Value::moves, size * moves_per_element); + } +} + +} // namespace test_vector_reserve + +#endif diff --git a/tests/flexbuffers/test_vector_reserve.cpp b/tests/flexbuffers/test_vector_reserve.cpp new file mode 100644 index 000000000..1798f47f1 --- /dev/null +++ b/tests/flexbuffers/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(flexbuffers, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::flexbuf::write(value); }, + [](const auto& data) { return rfl::flexbuf::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/generic/test_vector_reserve.cpp b/tests/generic/test_vector_reserve.cpp new file mode 100644 index 000000000..03bb59b68 --- /dev/null +++ b/tests/generic/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(generic, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::to_generic(value); }, + [](const auto& data) { return rfl::from_generic(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/json/test_vector_reserve.cpp b/tests/json/test_vector_reserve.cpp new file mode 100644 index 000000000..c53b5b96e --- /dev/null +++ b/tests/json/test_vector_reserve.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(json, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::json::write(value); }, + [](const auto& data) { return rfl::json::read(data); }); +} + +TEST(json, test_vector_reserve_excessive_size) { + std::vector values; + const rfl::json::Reader reader; + const rfl::parsing::VectorReader, rfl::Processors<>> + vector_reader(&reader, &values); + const auto error = + vector_reader.reserve(std::numeric_limits::max()); + EXPECT_TRUE(error.has_value()); + EXPECT_TRUE(values.empty()); +} + +} // namespace test_vector_reserve diff --git a/tests/msgpack/test_vector_reserve.cpp b/tests/msgpack/test_vector_reserve.cpp new file mode 100644 index 000000000..880e2e228 --- /dev/null +++ b/tests/msgpack/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(msgpack, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::msgpack::write(value); }, + [](const auto& data) { return rfl::msgpack::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/toml/test_vector_reserve.cpp b/tests/toml/test_vector_reserve.cpp new file mode 100644 index 000000000..3ed424fd8 --- /dev/null +++ b/tests/toml/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(toml, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::toml::write(value); }, + [](const auto& data) { return rfl::toml::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/ubjson/test_vector_reserve.cpp b/tests/ubjson/test_vector_reserve.cpp new file mode 100644 index 000000000..d266ba178 --- /dev/null +++ b/tests/ubjson/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(ubjson, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::ubjson::write(value); }, + [](const auto& data) { return rfl::ubjson::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/yaml/test_vector_reserve.cpp b/tests/yaml/test_vector_reserve.cpp new file mode 100644 index 000000000..9f3f2ef56 --- /dev/null +++ b/tests/yaml/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(yaml, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::yaml::write(value); }, + [](const auto& data) { return rfl::yaml::read(data); }); +} + +} // namespace test_vector_reserve diff --git a/tests/yas/test_vector_reserve.cpp b/tests/yas/test_vector_reserve.cpp new file mode 100644 index 000000000..925fd5bc2 --- /dev/null +++ b/tests/yas/test_vector_reserve.cpp @@ -0,0 +1,12 @@ +#include + +#include "../check_vector_reserve.hpp" + +namespace test_vector_reserve { + +TEST(yas, test_vector_reserve) { + check_moves([](const Data& value) { return rfl::yas::write(value); }, + [](const auto& data) { return rfl::yas::read(data); }); +} + +} // namespace test_vector_reserve