diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ef9de1..a7c70a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,16 +6,44 @@ cmake_minimum_required(VERSION 3.12) +option(JAM_COMPATIBLE "Build compatible with JAM-codec" OFF) +option(CUSTOM_CONFIG_SUPPORT "Support custom config of streams" OFF) + +option(BUILD_TESTS "Whether to include the test suite in build" OFF) + +if (PACKAGE_MANAGER) + if(PACKAGE_MANAGER NOT MATCHES "^(hunter|vcpkg)$") + message(FATAL_ERROR "PACKAGE_MANAGER must be set to 'hunter', 'vcpkg' or isn't set") + endif () +else () + set(PACKAGE_MANAGER "hunter") + if (CMAKE_TOOLCHAIN_FILE) + get_filename_component(ACTUAL_NAME ${CMAKE_TOOLCHAIN_FILE} NAME) + if(ACTUAL_NAME STREQUAL "vcpkg.cmake") + message(STATUS "vcpkg will be used because vcpkg.cmake has found") + set(PACKAGE_MANAGER "vcpkg") + endif () + endif () +endif () +message(STATUS "Selected package manager: ${PACKAGE_MANAGER}") + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.27") cmake_policy(SET CMP0144 NEW) endif () -include(${CMAKE_CURRENT_LIST_DIR}/cmake/HunterGate.cmake) +if (PACKAGE_MANAGER STREQUAL "hunter") + include(cmake/HunterGate.cmake) + HunterGate( + URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm28.tar.gz + SHA1 a4f1b0f42464e07790b7f90b783a822d71be6c6d + ) +endif () -HunterGate( - URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm25.tar.gz - SHA1 bf5742041306c4b2c8b65b9c2d2af712a36ac3f9 -) +if(BUILD_TESTS) + if (PACKAGE_MANAGER STREQUAL "vcpkg") + list(APPEND VCPKG_MANIFEST_FEATURES scale-tests) + endif() +endif() project(Scale LANGUAGES CXX VERSION 1.1.0) @@ -25,15 +53,16 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -option(JAM_COMPATIBLE "Build compatible with JAM-codec" OFF) -option(CUSTOM_CONFIG_SUPPORT "Support custom config of streams" OFF) - -option(BUILD_TESTS "Whether to include the test suite in build" OFF) - -hunter_add_package(Boost) -find_package(Boost CONFIG REQUIRED) +if (PACKAGE_MANAGER STREQUAL "hunter") + hunter_add_package(Boost) + find_package(Boost) +else() + find_package(Boost CONFIG REQUIRED COMPONENTS endian multiprecision) +endif () -hunter_add_package(qtils) +if (PACKAGE_MANAGER STREQUAL "hunter") + hunter_add_package(qtils) +endif () find_package(qtils CONFIG REQUIRED) set(DEFINITION_PATH "${CMAKE_CURRENT_SOURCE_DIR}/include/scale/definitions.hpp") diff --git a/include/scale/scale_decoder_stream.hpp b/include/scale/scale_decoder_stream.hpp index 7be6465..ed83511 100644 --- a/include/scale/scale_decoder_stream.hpp +++ b/include/scale/scale_decoder_stream.hpp @@ -11,9 +11,15 @@ #include #include #include +#include #include +#ifdef __has_include +#if __has_include() #include +#define USE_BOOST_VARIANT +#endif +#endif #include #include @@ -91,7 +97,29 @@ namespace scale { /** * @brief scale-decoding of variant - * @tparam T enumeration of various types + * @tparam Ts enumeration of various types + * @param v reference to variant + * @return reference to stream + */ + template + ScaleDecoderStream &operator>>(std::variant &v) { + // first byte means type index + uint8_t type_index = 0u; + *this >> type_index; // decode type index + + // ensure that index is in [0, types_count) + if (type_index >= sizeof...(Ts)) { + raise(DecodeError::WRONG_TYPE_INDEX); + } + + tryDecodeAsOneOfVariant<0>(v, type_index); + return *this; + } + +#ifdef USE_BOOST_VARIANT + /** + * @brief scale-decoding of variant + * @tparam Ts enumeration of various types * @param v reference to variant * @return reference to stream */ @@ -109,6 +137,7 @@ namespace scale { tryDecodeAsOneOfVariant<0>(v, type_index); return *this; } +#endif // USE_BOOST_VARIANT /** * @brief scale-decodes shared_ptr value @@ -382,6 +411,22 @@ namespace scale { } } + template + void tryDecodeAsOneOfVariant(std::variant &v, size_t i) { + using T = std::remove_const_t>>; + static_assert(std::is_default_constructible_v); + if (I == i) { + T val; + *this >> val; + v = std::forward(val); + return; + } + if constexpr (sizeof...(Ts) > I + 1) { + tryDecodeAsOneOfVariant(v, i); + } + } + +#ifdef USE_BOOST_VARIANT template void tryDecodeAsOneOfVariant(boost::variant &v, size_t i) { using T = std::remove_const_t>>; @@ -396,6 +441,7 @@ namespace scale { tryDecodeAsOneOfVariant(v, i); } } +#endif // USE_BOOST_VARIANT ByteSpan span_; diff --git a/include/scale/scale_encoder_stream.hpp b/include/scale/scale_encoder_stream.hpp index 25ecf34..ac3f215 100644 --- a/include/scale/scale_encoder_stream.hpp +++ b/include/scale/scale_encoder_stream.hpp @@ -11,9 +11,15 @@ #include #include #include +#include #include +#ifdef __has_include +#if __has_include() #include +#define USE_BOOST_VARIANT +#endif +#endif #include #include @@ -117,6 +123,19 @@ namespace scale { return *this; } + /** + * @brief scale-encodes variant value + * @tparam T type list + * @param v value to encode + * @return reference to stream + */ + template + ScaleEncoderStream &operator<<(const std::variant &v) { + tryEncodeAsOneOfVariant<0>(v); + return *this; + } + +#ifdef USE_BOOST_VARIANT /** * @brief scale-encodes variant value * @tparam T type list @@ -128,6 +147,7 @@ namespace scale { tryEncodeAsOneOfVariant<0>(v); return *this; } +#endif // USE_BOOST_VARIANT /** * @brief scale-encodes sharead_ptr value @@ -260,6 +280,19 @@ namespace scale { } } + template + void tryEncodeAsOneOfVariant(const std::variant &v) { + using T = std::tuple_element_t>; + if (v.index() == I) { + *this << I << std::get(v); + return; + } + if constexpr (sizeof...(Ts) > I + 1) { + tryEncodeAsOneOfVariant(v); + } + } + +#ifdef USE_BOOST_VARIANT template void tryEncodeAsOneOfVariant(const boost::variant &v) { using T = std::tuple_element_t>; @@ -271,6 +304,7 @@ namespace scale { tryEncodeAsOneOfVariant(v); } } +#endif // USE_BOOST_VARIANT /** * @brief scale-encodes any dynamic collection diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1be0a6d..9d420ff 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,9 @@ if (POLICY CMP0076) cmake_policy(SET CMP0076 NEW) endif () -hunter_add_package(GTest) +if (PACKAGE_MANAGER STREQUAL "hunter") + hunter_add_package(GTest) +endif () find_package(GTest CONFIG REQUIRED) function(disable_clang_tidy target) @@ -104,7 +106,8 @@ target_link_libraries(scale_collection_test ) addtest(scale_variant_test - scale_variant_test.cpp + scale_std_variant_test.cpp + scale_boost_variant_test.cpp ) target_link_libraries(scale_variant_test scale diff --git a/test/installation/CMakeLists.txt b/test/installation/CMakeLists.txt index 6eb5124..765c2a6 100644 --- a/test/installation/CMakeLists.txt +++ b/test/installation/CMakeLists.txt @@ -14,8 +14,10 @@ set(CMAKE_CXX_STANDARD 20) list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}/scale-install/") find_package(scale REQUIRED) -hunter_add_package(Boost COMPONENTS random) -find_package(Boost CONFIG REQUIRED random) +if (PACKAGE_MANAGER STREQUAL "hunter") + hunter_add_package(Boost COMPONENTS random) +endif () +find_package(Boost CONFIG REQUIRED COMPONENTS random) add_executable(scale_test scale_test.cpp) target_link_libraries(scale_test diff --git a/test/scale_variant_test.cpp b/test/scale_boost_variant_test.cpp similarity index 89% rename from test/scale_variant_test.cpp rename to test/scale_boost_variant_test.cpp index 54b8f02..87d2aeb 100644 --- a/test/scale_variant_test.cpp +++ b/test/scale_boost_variant_test.cpp @@ -7,13 +7,15 @@ #include #include +#ifdef USE_BOOST_VARIANT + using scale::ByteArray; using scale::decode; using scale::encode; using scale::ScaleDecoderStream; using scale::ScaleEncoderStream; -class VariantFixture +class BoostVariantFixture : public testing::TestWithParam< std::pair, ByteArray>> { protected: @@ -32,14 +34,14 @@ namespace { * @when value is scale-encoded * @then encoded bytes match predefined byte array */ -TEST_P(VariantFixture, EncodeSuccessTest) { +TEST_P(BoostVariantFixture, EncodeSuccessTest) { const auto &[value, match] = GetParam(); ASSERT_NO_THROW(s << value); ASSERT_EQ(s.to_vector(), match); } INSTANTIATE_TEST_SUITE_P(CompactTestCases, - VariantFixture, + BoostVariantFixture, ::testing::Values(make_pair(uint8_t(1), {0, 1}), make_pair(uint32_t(2), {1, 2, 0, 0, 0}))); @@ -51,7 +53,7 @@ INSTANTIATE_TEST_SUITE_P(CompactTestCases, * @then obtained varian has alternative type uint8_t and is equal to encoded * uint8_t value */ -TEST(ScaleVariant, DecodeU8Success) { +TEST(ScaleBoostVariant, DecodeU8Success) { ByteArray match = {0, 1}; // uint8_t{1} ScaleDecoderStream s(match); boost::variant val{}; @@ -66,10 +68,12 @@ TEST(ScaleVariant, DecodeU8Success) { * @then obtained varian has alternative type uint32_t and is equal to encoded * uint32_t value */ -TEST(ScaleVariant, DecodeU32Success) { +TEST(ScaleBoostVariant, DecodeU32Success) { ByteArray match = {1, 1, 0, 0, 0}; // uint32_t{1} ScaleDecoderStream s(match); boost::variant val{}; ASSERT_NO_THROW(s >> val); ASSERT_EQ(boost::get(val), 1); } + +#endif diff --git a/test/scale_std_variant_test.cpp b/test/scale_std_variant_test.cpp new file mode 100644 index 0000000..ed05248 --- /dev/null +++ b/test/scale_std_variant_test.cpp @@ -0,0 +1,74 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +using scale::ByteArray; +using scale::decode; +using scale::encode; +using scale::ScaleDecoderStream; +using scale::ScaleEncoderStream; + +class StdVariantFixture + : public testing::TestWithParam< + std::pair, ByteArray>> { + protected: + ScaleEncoderStream s; +}; +namespace { + std::pair, ByteArray> make_pair( + std::variant v, ByteArray m) { + return {v, std::move(m)}; + } +} // namespace + +/** + * @given variant value and byte array + * @when value is scale-encoded + * @then encoded bytes match predefined byte array + */ +TEST_P(StdVariantFixture, EncodeSuccessTest) { + const auto &[value, match] = GetParam(); + ASSERT_NO_THROW(s << value); + ASSERT_EQ(s.to_vector(), match); +} + +INSTANTIATE_TEST_SUITE_P(CompactTestCases, + StdVariantFixture, + ::testing::Values(make_pair(uint8_t(1), {0, 1}), + make_pair(uint32_t(2), + {1, 2, 0, 0, 0}))); + +/** + * @given byte array of encoded variant of types uint8_t and uint32_t + * containing uint8_t value + * @when variant decoded from scale decoder stream + * @then obtained varian has alternative type uint8_t and is equal to encoded + * uint8_t value + */ +TEST(ScaleStdVariant, DecodeU8Success) { + ByteArray match = {0, 1}; // uint8_t{1} + ScaleDecoderStream s(match); + std::variant val{}; + ASSERT_NO_THROW(s >> val); + ASSERT_EQ(std::get(val), 1); +} + +/** + * @given byte array of encoded variant of types uint8_t and uint32_t + * containing uint32_t value + * @when variant decoded from scale decoder stream + * @then obtained varian has alternative type uint32_t and is equal to encoded + * uint32_t value + */ +TEST(ScaleStdVariant, DecodeU32Success) { + ByteArray match = {1, 1, 0, 0, 0}; // uint32_t{1} + ScaleDecoderStream s(match); + std::variant val{}; + ASSERT_NO_THROW(s >> val); + ASSERT_EQ(std::get(val), 1); +} diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000..1ad42d2 --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,17 @@ +{ + "default-registry": { + "kind": "git", + "baseline": "fe1cde61e971d53c9687cf9a46308f8f55da19fa", + "repository": "https://github.com/microsoft/vcpkg" + }, + "registries": [ + { + "kind": "artifact", + "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", + "name": "microsoft" + } + ], + "overlay-ports": [ + "vcpkg-overlay" + ] +} diff --git a/vcpkg-overlay/qtils/portfile.cmake b/vcpkg-overlay/qtils/portfile.cmake new file mode 100644 index 0000000..e4653e3 --- /dev/null +++ b/vcpkg-overlay/qtils/portfile.cmake @@ -0,0 +1,11 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO qdrvm/qtils + REF de62454d12e250574577232ee7ede4406c4c22cb + SHA512 7b91ea268fb2c9204c1b584d2b433bd93d8d1946ba5d05ddf119b955fdfdb76abbe47c0009a4b186f7968320b99044abfd58315a128c98cf65f57c870ba51112 +) +vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}") +vcpkg_cmake_install() +vcpkg_cmake_config_fixup(PACKAGE_NAME "qtils") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") diff --git a/vcpkg-overlay/qtils/vcpkg.json b/vcpkg-overlay/qtils/vcpkg.json new file mode 100644 index 0000000..a6f24b6 --- /dev/null +++ b/vcpkg-overlay/qtils/vcpkg.json @@ -0,0 +1,19 @@ +{ + "name": "qtils", + "version": "0.0.3", + "dependencies": [ + { "name": "vcpkg-cmake", "host": true }, + { "name": "vcpkg-cmake-config", "host": true }, + "boost-algorithm", + "boost-outcome", + "fmt" + ], + "features": { + "qtils-tests": { + "description": "Test compilable of qtils", + "dependencies": [ + "gtest" + ] + } + } +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..b584d02 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "scale", + "version": "1.1.0", + "dependencies": [ + "qtils", + "boost-multiprecision", + "boost-endian" + ], + "features": { + "scale-tests": { + "description": "Test of scale encoding/decoding", + "dependencies": [ + "gtest" + ] + } + } +}