diff --git a/.drone.star b/.drone.star index 7c79089a9..09071d05f 100644 --- a/.drone.star +++ b/.drone.star @@ -19,7 +19,7 @@ def main(ctx): linux_cxx("Clang 12 arm64", "clang++-12", packages="clang-12 libstdc++-9-dev", llvm_os="focal", llvm_ver="12", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'clang-12', 'B2_CXXSTD': '17,20', 'DRONE_JOB_UUID': '7719a1c783m'}, arch="arm64", globalenv=globalenv), linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '0716d9708dm'}, arch="arm64", globalenv=globalenv), linux_cxx("docs", "g++", packages="docbook docbook-xml docbook-xsl python3-jinja2 xsltproc flex libfl-dev bison unzip rsync", buildtype="docs", buildscript="drone", image="cppalliance/droneubuntu1804:1", environment={'COMMENT': 'docs', 'DRONE_JOB_UUID': 'b6589fc6ab'}, globalenv=globalenv), - linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'LCOV_BRANCH_COVERAGE': '0', 'B2_CXXSTD': '11', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_JSON_EXPENSIVE_TESTS BOOST_NO_STRESS_TEST=1', 'CODECOV_TOKEN': {'from_secret': 'codecov_token'}, 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv), + linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'LCOV_BRANCH_COVERAGE': '0', 'B2_CXXSTD': '17', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_JSON_EXPENSIVE_TESTS BOOST_NO_STRESS_TEST=1', 'CODECOV_TOKEN': {'from_secret': 'codecov_token'}, 'B2_FLAGS': 'warnings=extra warnings-as-errors=on linkflags=-lstdc++fs', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv), linux_cxx("Valgrind", "clang++-14", packages="clang-14 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="jammy", llvm_ver="14", buildscript="drone", buildtype="valgrind", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'valgrind', 'B2_TOOLSET': 'clang-14', 'B2_CXXSTD': '11,14,17', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_VARIANT': 'debug', 'B2_TESTFLAGS': 'testing.launcher=valgrind', 'VALGRIND_OPTS': '--error-exitcode=1'}, globalenv=globalenv), linux_cxx("ASan GCC", "g++-12", packages="g++-12", buildscript="drone", buildtype="boost", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-12', 'B2_CXXSTD': '11,14,17', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True'}, globalenv=globalenv, privileged=True), linux_cxx("ASan Clang", "clang++-14", packages="clang-14 libstdc++-10-dev", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'clang-14', 'B2_CXXSTD': '11,14,17', 'B2_ASAN': '1', 'B2_DEFINES': 'define=BOOST_NO_STRESS_TEST=1'}, globalenv=globalenv, privileged=True), diff --git a/fuzzing/fuzz_direct_parse.cpp b/fuzzing/fuzz_direct_parse.cpp index 8c9ab7e33..0924c67c1 100644 --- a/fuzzing/fuzz_direct_parse.cpp +++ b/fuzzing/fuzz_direct_parse.cpp @@ -6,6 +6,18 @@ // Official repository: https://github.com/boostorg/json // +#include + +#if !defined(BOOST_DESCRIBE_CXX14) + +#include + +BOOST_PRAGMA_MESSAGE( "This example requires C++14" ) + +int main() {} + +#else + #include #include #include @@ -82,3 +94,4 @@ LLVMFuzzerTestOneInput( return 0; } +#endif // !defined(BOOST_DESCRIBE_CXX14) diff --git a/include/boost/json/detail/parse_into.hpp b/include/boost/json/detail/parse_into.hpp index 8349d4ca5..3f7418a2c 100644 --- a/include/boost/json/detail/parse_into.hpp +++ b/include/boost/json/detail/parse_into.hpp @@ -711,7 +711,7 @@ struct handler_tuple; template< class P, template class L, class... V, std::size_t... I > struct handler_tuple< P, L, mp11::index_sequence > - : handler_tuple_element< I, get_handler > + : handler_tuple_element ... { handler_tuple( handler_tuple const& ) = delete; @@ -719,11 +719,11 @@ struct handler_tuple< P, L, mp11::index_sequence > template< class Access, class T > handler_tuple( Access access, T* pv, P* pp ) - : handler_tuple_element< I, get_handler >( - access( pv, mp11::mp_int() ), + : handler_tuple_element( + access( pv, mp11::mp_size_t() ), pp ) ... - { } + {} }; #if defined(BOOST_MSVC) && BOOST_MSVC < 1910 @@ -803,10 +803,17 @@ class converting_handler { private: + using ElementTypes = tuple_element_list; + + template + using ElementHandler = get_handler; + using InnerHandlers = mp11::mp_transform; + using HandlerTuple = handler_tuple; + T* value_; P* parent_; - handler_tuple< converting_handler, tuple_element_list > handlers_; + HandlerTuple handlers_; int inner_active_ = -1; public: @@ -874,7 +881,7 @@ class converting_handler struct do_on_array_begin { - handler_tuple< converting_handler, tuple_element_list >& handlers; + HandlerTuple& handlers; system::error_code& ec; template< class I > @@ -905,7 +912,7 @@ class converting_handler struct do_on_array_end { - handler_tuple< converting_handler, tuple_element_list >& handlers; + HandlerTuple& handlers; system::error_code& ec; template< class I > @@ -966,6 +973,13 @@ using struct_element_list = mp11::mp_transform_q< struct struct_accessor { + template< class T > + auto operator()( T*, mp11::mp_size< described_members > ) const + -> void* + { + return nullptr; + } + template< class T, class I > auto operator()( T* t, I ) const -> described_member_t, I> >* @@ -976,16 +990,132 @@ struct struct_accessor } }; -template< class F > struct struct_key_searcher { - F fn; + string_view key; + int& found; + int index = 0; + + struct_key_searcher(string_view key, int& found) noexcept + : key(key), found(found) + {} template< class D > void - operator()( D ) const + operator()( D ) { - fn( D::name ) ; + if( key == D::name ) + found = index; + ++index; + } +}; + +template +struct ignoring_handler +{ + P* parent_; + std::size_t array_depth_ = 0; + std::size_t object_depth_ = 0; + + ignoring_handler(ignoring_handler const&) = delete; + ignoring_handler& operator=(ignoring_handler const&) = delete; + + ignoring_handler(void*, P* p) noexcept + : parent_(p) + {} + + bool on_object_begin(system::error_code&) + { + ++object_depth_; + return true; + } + + bool on_object_end(system::error_code& ec) + { + BOOST_ASSERT( object_depth_ > 0 ); + --object_depth_; + + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_array_begin(system::error_code&) + { + ++array_depth_; + return true; + } + + bool on_array_end(system::error_code& ec) + { + BOOST_ASSERT( array_depth_ > 0 ); + --array_depth_; + + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_end(ec); + return true; + } + + bool on_key_part(system::error_code&, string_view) + { + return true; + } + + bool on_key(system::error_code&, string_view) + { + return true; + } + + bool on_string_part(system::error_code&, string_view) + { + return true; + } + + bool on_string(system::error_code& ec, string_view) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_number_part(system::error_code&) + { + return true; + } + + bool on_int64(system::error_code& ec, std::int64_t) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_uint64(system::error_code& ec, std::uint64_t) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_double(system::error_code& ec, double) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_bool(system::error_code& ec, bool) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_null(system::error_code& ec) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; } }; @@ -1000,14 +1130,22 @@ class converting_handler #else private: + using Dm = described_members; + using Dt = struct_element_list; + + template + using MemberHandler = get_handler; + using InnerHandlers = mp11::mp_push_back< + mp11::mp_transform, + ignoring_handler >; + using InnerCount = mp11::mp_size; + V* value_; P* parent_; std::string key_; - using Dm = described_members; - - handler_tuple< converting_handler, struct_element_list > handlers_; + handler_tuple handlers_; int inner_active_ = -1; std::size_t activated_ = 0; @@ -1019,13 +1157,17 @@ class converting_handler : value_(v), parent_(p), handlers_(struct_accessor(), v, this) {} - struct is_optional_checker + struct is_required_checker { + bool operator()( mp11::mp_size
) const noexcept + { + return false; + } + template< class I > - bool operator()( I ) const noexcept + auto operator()( I ) const noexcept { - using L = struct_element_list; - using T = mp11::mp_at; + using T = mp11::mp_at; return !is_optional_like::value; } }; @@ -1033,9 +1175,9 @@ class converting_handler bool signal_value(system::error_code&) { BOOST_ASSERT( inner_active_ >= 0 ); - bool required_member = mp11::mp_with_index< mp11::mp_size >( + bool required_member = mp11::mp_with_index( inner_active_, - is_optional_checker{}); + is_required_checker{}); if( required_member ) ++activated_; @@ -1060,7 +1202,7 @@ class converting_handler auto f = [&](auto& handler) { return handler.fn ; }; \ using F = decltype(f); \ using H = decltype(handlers_); \ - return mp11::mp_with_index< mp11::mp_size >( \ + return mp11::mp_with_index( \ inner_active_, \ tuple_handler_op_invoker{handlers_, f} ); @@ -1076,9 +1218,8 @@ class converting_handler { if( inner_active_ < 0 ) { - using L = struct_element_list; - using C = mp11::mp_count_if; - constexpr int N = mp11::mp_size::value - C::value; + using C = mp11::mp_count_if; + constexpr int N = mp11::mp_size
::value - C::value; if( activated_ < N ) { BOOST_JSON_FAIL( ec, error::size_mismatch ); @@ -1129,24 +1270,8 @@ class converting_handler key = key_; } - int i = 0; - - auto f = [&](char const* name) - { - if( key == name ) - inner_active_ = i; - ++i; - }; - - mp11::mp_for_each( - struct_key_searcher{f} ); - - if( inner_active_ < 0 ) - { - BOOST_JSON_FAIL(ec, error::unknown_name); - return false; - } - + inner_active_ = InnerCount::value - 1; + mp11::mp_for_each( struct_key_searcher(key, inner_active_) ); return true; } diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index fe3bba023..29ae444f9 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -346,17 +346,13 @@ value_to_impl( *arr, ctx, boost::mp11::make_index_sequence()); } -template< class Ctx, class T, bool non_throwing = true > +template< class Ctx, class T > struct to_described_member { using Ds = described_members; - using result_type = mp11::mp_eval_if_c< - !non_throwing, T, system::result, T>; - - result_type& res; + system::result& res; object const& obj; - std::size_t count; Ctx const& ctx; template< class I > @@ -375,7 +371,7 @@ struct to_described_member BOOST_IF_CONSTEXPR( !is_optional_like::value ) { system::error_code ec; - BOOST_JSON_FAIL(ec, error::unknown_name); + BOOST_JSON_FAIL(ec, error::size_mismatch); res = {boost::system::in_place_error, ec}; } return; @@ -391,10 +387,7 @@ struct to_described_member # pragma GCC diagnostic pop #endif if( member_res ) - { (*res).* D::pointer = std::move(*member_res); - ++count; - } else res = {boost::system::in_place_error, member_res.error()}; } @@ -421,7 +414,7 @@ value_to_impl( return res; } - to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx }; + to_described_member member_converter{res, *obj, ctx}; using Ds = typename decltype(member_converter)::Ds; constexpr std::size_t N = mp11::mp_size::value; @@ -430,14 +423,6 @@ value_to_impl( if( !res ) return res; - if( member_converter.count != obj->size() ) - { - system::error_code ec; - BOOST_JSON_FAIL(ec, error::size_mismatch); - res = {boost::system::in_place_error, ec}; - return res; - } - return res; } diff --git a/test/parse_into.cpp b/test/parse_into.cpp index c3c94b6f5..404c9d9e4 100644 --- a/test/parse_into.cpp +++ b/test/parse_into.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -86,14 +87,16 @@ class parse_into_test { public: - template void testParseInto( T const& t ) + template + void testParseIntoValue( value const& jv ) { #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif - T t1( t ); - std::string json = serialize( value_from( t1 ) ); + T t1 = value_to(jv); + (void)t1; // older GCC thinks t1 can be unused + std::string json = serialize(jv); T t2{}; system::error_code jec; @@ -148,6 +151,12 @@ class parse_into_test #endif } + template + void testParseInto( T const& t ) + { + testParseIntoValue( value_from(t) ); + } + template void testParseIntoErrors( error e, value const& sample ) @@ -380,10 +389,18 @@ class parse_into_test testParseInto( { {1, 3.14f, "hello"}, true } ); testParseIntoErrors( error::not_object, 1 ); - testParseIntoErrors( - error::unknown_name, - { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", 0} } ); testParseIntoErrors( error::size_mismatch, { {"a", 1} } ); + + object jo{ {"a", 1}, {"b", 3.14f}, {"c", "hello"} }; + jo["e1"] = array{1, ULONG_MAX, "three", array{}, true, .5, nullptr}; + jo["e2"] = object{ {"one", 1}, {"two", 2}, {"three", object{}} }; + jo["e3"] = "third extra element"; + jo["e4"] = 100; + jo["e5"] = ULONG_MAX; + jo["e6"] = 12.4; + jo["e7"] = false; + jo["e8"] = nullptr; + testParseIntoValue(jo); #endif } @@ -395,6 +412,7 @@ class parse_into_test testParseInto( E::z ); testParseIntoErrors< E >( error::not_string, (int)(E::y) ); + testParseIntoErrors< E >( error::unknown_name, "zoom" ); #endif // BOOST_DESCRIBE_CXX14 } @@ -441,6 +459,9 @@ class parse_into_test #ifndef BOOST_NO_CXX17_HDR_OPTIONAL testParseInto< std::optional >( std::nullopt ); testParseInto< std::optional >( 1 ); + testParseInto< std::optional >( ULONG_MAX ); + testParseInto< std::optional >( true ); + testParseInto< std::optional >( 33.77 ); testParseInto< std::optional> >( std::nullopt ); @@ -452,6 +473,9 @@ class parse_into_test testParseInto< std::optional> >( std::vector{"1", "2", "3"} ); + testParseInto< std::optional< std::map > >( + std::map{ {"1", 1}, {"2", 2}, {"3", 3} } ); + testParseInto< std::vector< std::optional > >( {1, 2, 3, std::nullopt, 5, std::nullopt, std::nullopt, 8}); #endif diff --git a/test/value_to.cpp b/test/value_to.cpp index 854642178..5d6fcc34a 100644 --- a/test/value_to.cpp +++ b/test/value_to.cpp @@ -431,8 +431,9 @@ class value_to_test BOOST_TEST( res->d == 0.125 ); jv.as_object()["x"] = 0; - BOOST_TEST_THROWS_WITH_LOCATION( - value_to<::value_to_test_ns::T6>( jv )); + res = try_value_to<::value_to_test_ns::T6>( + jv, ctx... ); + BOOST_TEST( res ); } { value jv = {{"n", 1}, {"d", 2}, {"s", "xyz"}, {"b", true}}; @@ -479,8 +480,9 @@ class value_to_test BOOST_TEST( std::nullopt == res->opt_s ); jv.as_object()["x"] = 0; - BOOST_TEST_THROWS_WITH_LOCATION( - value_to<::value_to_test_ns::T8>( jv, ctx... )); + res = try_value_to<::value_to_test_ns::T8>( + jv, ctx... ); + BOOST_TEST( res ); #endif // BOOST_NO_CXX17_HDR_OPTIONAL } @@ -562,6 +564,8 @@ class value_to_test BOOST_TEST( paths == (Paths{ "from/here", "to/there", "", "c:/" , "..", "../"}) ); + BOOST_TEST_THROWS_WITH_LOCATION( + value_to( value(1), ctx... )); #endif // BOOST_NO_CXX17_HDR_FILESYSTEM }