diff --git a/include/daw/json/impl/daw_json_parse_options.h b/include/daw/json/impl/daw_json_parse_options.h new file mode 100644 index 000000000..f93d8d1a9 --- /dev/null +++ b/include/daw/json/impl/daw_json_parse_options.h @@ -0,0 +1,288 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/daw_json_link +// + +#pragma once + +#include "version.h" + +#include <daw/cpp_17.h> +#include <daw/daw_hide.h> +#include <daw/daw_traits.h> + +#include <ciso646> +#include <cstddef> +#include <cstdint> +#include <tuple> +#include <utility> + +namespace daw::json { + inline namespace DAW_JSON_VER { + namespace json_details { + using policy_options_t = std::uint32_t; + + template<typename> + inline constexpr unsigned policy_bits_width = 0; + + template<typename> + inline constexpr auto default_policy_value = [] { + struct unknown_policy {}; + return unknown_policy{ }; + }( ); + + } // namespace json_details + + /*** + * Allow for different optimizations. Currently only the compile_time path + * is fully supported. The others may offer faster parsing. The default is + * compile_time, it provides constexpr parsing and generally is faster + * currently. + */ + enum class ExecModeTypes : unsigned { + compile_time, + runtime, /* testing */ + simd /* testing */ + }; // 2bits + template<> + inline constexpr unsigned json_details::policy_bits_width<ExecModeTypes> = + 2; + + template<> + inline constexpr auto json_details::default_policy_value<ExecModeTypes> = + ExecModeTypes::compile_time; + + /*** + * Input is a zero terminated string. If this cannot be detected, you can + * specify it here. Offers some optimization possibilities in the parser. + * Default is no, to try and detect, but use the safer assumption that the + * buffer does not end in zero. + */ + enum class ZeroTerminatedString : unsigned { no, yes }; // 1bit + template<> + inline constexpr unsigned + json_details::policy_bits_width<ZeroTerminatedString> = 1; + + template<> + inline constexpr auto + json_details::default_policy_value<ZeroTerminatedString> = + ZeroTerminatedString::no; + + /*** + * Allow comments in JSON. The supported modes are none, C++ style + * comments, and # hash style comments. Default is none, no comments + * allowed + */ + enum class PolicyCommentTypes : unsigned { none, cpp, hash }; // 2bits + template<> + inline constexpr unsigned + json_details::policy_bits_width<PolicyCommentTypes> = 2; + + template<> + inline constexpr auto + json_details::default_policy_value<PolicyCommentTypes> = + PolicyCommentTypes::none; + + /*** + * Enable all structure, buffer, and type checking. The default is yes, but + * no still does some checking and can be faster. + */ + enum class CheckedParseMode : unsigned { yes, no }; // 1bit + template<> + inline constexpr unsigned + json_details::policy_bits_width<CheckedParseMode> = 1; + + template<> + inline constexpr auto json_details::default_policy_value<CheckedParseMode> = + CheckedParseMode::yes; + + /*** + * Allow the escape character '\' in names. This forces a slower parser and + * is generally not needed. The default is no, and the end of string + * matching only needs to look for a `"`, not skip `\"` in names. + */ + enum class AllowEscapedNames : unsigned { no, yes }; // 1bit + template<> + inline constexpr unsigned + json_details::policy_bits_width<AllowEscapedNames> = 1; + + template<> + inline constexpr auto + json_details::default_policy_value<AllowEscapedNames> = + AllowEscapedNames::no; + + /*** + * Testing + * Use precise IEEE754 parsing of real numbers. The default is no, and + * results is much faster parsing with very small errors of 0-2ulp. + */ + enum class IEEE754Precise : unsigned { no, yes }; // 1bit + template<> + inline constexpr unsigned json_details::policy_bits_width<IEEE754Precise> = + 1; + + template<> + inline constexpr auto json_details::default_policy_value<IEEE754Precise> = + IEEE754Precise::yes; + + /*** + * If the hashes of all members being looked are unique, the lookup of names + * as they are found in the document stops at hash checking by default. You + * can force a full string check by setting to yes. + */ + enum class ForceFullNameCheck : unsigned { no, yes }; // 1bit + template<> + inline constexpr unsigned + json_details::policy_bits_width<ForceFullNameCheck> = 1; + + template<> + inline constexpr auto + json_details::default_policy_value<ForceFullNameCheck> = + ForceFullNameCheck::no; + + /* ***************************************** + * Implementation details + */ + namespace json_details { + template<typename Policy, typename Policies> + struct policy_bits_start_impl; + + template<typename Policy, typename... Policies> + struct policy_bits_start_impl<Policy, std::tuple<Policies...>> { + static constexpr auto idx = + traits::pack_index_of_v<Policy, Policies...>; + static_assert( idx >= 0, "Policy is not registered" ); + using tp_policies = std::tuple<Policies...>; + + template<std::size_t Pos, int End> + static constexpr unsigned do_step( ) { + if constexpr( Pos >= static_cast<std::size_t>( End ) ) { + return 0U; + } + return policy_bits_width<std::tuple_element_t<Pos, tp_policies>>; + } + + template<std::size_t... Is> + static constexpr unsigned calc( std::index_sequence<Is...> ) { + return ( do_step<Is, idx>( ) + ... ); + } + }; + + using policy_list = + std::tuple<ExecModeTypes, ZeroTerminatedString, PolicyCommentTypes, + CheckedParseMode, AllowEscapedNames, IEEE754Precise, + ForceFullNameCheck>; + + template<typename Policy, typename Policies> + inline constexpr unsigned basic_policy_bits_start = + policy_bits_start_impl<Policy, Policies>::template calc( + std::make_index_sequence<std::tuple_size_v<Policies>>{ } ); + + template<typename Policy> + inline constexpr unsigned policy_bits_start = + basic_policy_bits_start<Policy, policy_list>; + + template<typename Policy> + inline constexpr bool is_policy_flag = policy_bits_width<Policy> > 0; + + template<typename Policy, typename... Policies> + constexpr Policy get_policy_or( std::tuple<Policies...> const &pols ) { + constexpr int const pack_idx = + daw::traits::pack_index_of_v<Policy, Policies...>; + + if constexpr( pack_idx != -1 ) { + return std::get<static_cast<std::size_t>( pack_idx )>( pols ); + } else { + return json_details::default_policy_value<Policy>; + } + } + + template<typename Policy> + constexpr void set_bits( policy_options_t &value, Policy e ) { + static_assert( is_policy_flag<Policy>, + "Only registered policy types are allowed" ); + unsigned new_bits = static_cast<unsigned>( e ); + constexpr unsigned mask = (1U << policy_bits_width<Policy>)-1U; + new_bits &= mask; + new_bits <<= policy_bits_start<Policy>; + value &= ~mask; + value |= new_bits; + } + + template<typename Policy> + constexpr policy_options_t set_bits( policy_options_t const &v, + Policy e ) { + static_assert( is_policy_flag<Policy>, + "Only registered policy types are allowed" ); + auto value = v; + unsigned new_bits = static_cast<unsigned>( e ); + constexpr unsigned mask = (1U << policy_bits_width<Policy>)-1U; + new_bits &= mask; + new_bits <<= policy_bits_start<Policy>; + value &= ~mask; + value |= new_bits; + return value; + } + + template<typename Policy> + constexpr policy_options_t set_bits_for( Policy e ) { + static_assert( is_policy_flag<Policy>, + "Only registered policy types are allowed" ); + policy_options_t new_bits = static_cast<unsigned>( e ); + new_bits <<= policy_bits_start<Policy>; + return new_bits; + } + + template<typename> + struct default_policy_flag_t; + + template<typename... Policies> + struct default_policy_flag_t<std::tuple<Policies...>> { + static constexpr policy_options_t value = + ( set_bits_for<Policies>( default_policy_value<Policies> ) | ... ); + }; + + /*** + * The defaults for all known policies encoded as a policy_optionts_t + */ + inline static constexpr policy_options_t default_policy_flag = + default_policy_flag_t<policy_list>::value; + + template<typename Policy, typename Result = Policy> + constexpr Result get_bits( policy_options_t value ) { + static_assert( is_policy_flag<Policy>, + "Only registered policy types are allowed" ); + constexpr unsigned mask = + ( 1U << (policy_bits_start<Policy> + policy_bits_width<Policy>)) - 1U; + value &= mask; + value >>= policy_bits_start<Policy>; + return static_cast<Result>( Policy{ value } ); + } + + template<std::size_t Idx, typename... Ts> + using switch_t = std::tuple_element_t<Idx, std::tuple<Ts...>>; + } // namespace json_details + // *********************************************** + + /*** + * Create the parser options flag for BasicParsePolicy + * @tparam Policies Policy types that satisfy the `is_policy_flag` trait. + * @param policies A list of parser options to change from the defaults. + * @return A policy_options_t that encodes the options for the parser + */ + template<typename... Policies> + constexpr json_details::policy_options_t + parse_options( Policies... policies ) { + static_assert( ( json_details::is_policy_flag<Policies> and ... ), + "Only registered policy types are allowed" ); + auto result = json_details::default_policy_flag; + if constexpr( sizeof...( Policies ) > 0 ) { + result |= ( json_details::set_bits_for( policies ) | ... ); + } + return result; + } + } // namespace DAW_JSON_VER +} // namespace daw::json diff --git a/include/daw/json/impl/daw_json_parse_policy.h b/include/daw/json/impl/daw_json_parse_policy.h index c1b221601..59229cf61 100644 --- a/include/daw/json/impl/daw_json_parse_policy.h +++ b/include/daw/json/impl/daw_json_parse_policy.h @@ -11,6 +11,7 @@ #include "daw_json_allocator_wrapper.h" #include "daw_json_assert.h" #include "daw_json_parse_common.h" +#include "daw_json_parse_options.h" #include "daw_json_parse_policy_cpp_comments.h" #include "daw_json_parse_policy_hash_comments.h" #include "daw_json_parse_policy_no_comments.h" @@ -29,226 +30,18 @@ namespace daw::json { inline namespace DAW_JSON_VER { - namespace json_details { - using policy_options_t = std::uint32_t; - - template<typename> - inline constexpr unsigned policy_bits_width = 0; - - template<typename> - inline constexpr auto default_policy_value = [] { - struct unknown_policy {}; - return unknown_policy{ }; - }( ); - - } // namespace json_details - - enum class ExecModeTypes : unsigned { - compile_time, - runtime, - simd - }; // 2bits - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<ExecModeTypes> = 2; - - template<> - inline constexpr auto default_policy_value<ExecModeTypes> = - ExecModeTypes::compile_time; - } // namespace json_details - - enum class ZeroTerminatedString : unsigned { no, yes }; // 1bit - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<ZeroTerminatedString> = 1; - - template<> - inline constexpr auto default_policy_value<ZeroTerminatedString> = - ZeroTerminatedString::no; - } // namespace json_details - - enum class PolicyCommentTypes : unsigned { none, cpp, hash }; // 2bits - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<PolicyCommentTypes> = 2; - - template<> - inline constexpr auto default_policy_value<PolicyCommentTypes> = - PolicyCommentTypes::none; - } // namespace json_details - - enum class CheckedParseMode : unsigned { yes, no }; // 1bit - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<CheckedParseMode> = 1; - - template<> - inline constexpr auto default_policy_value<CheckedParseMode> = - CheckedParseMode::yes; - } // namespace json_details - - enum class AllowEscapedNames : unsigned { no, yes }; // 1bit - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<AllowEscapedNames> = 1; - - template<> - inline constexpr auto default_policy_value<AllowEscapedNames> = - AllowEscapedNames::no; - } // namespace json_details - - enum class IEEE754Precise : unsigned { no, yes }; // 1bit - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<IEEE754Precise> = 1; - - template<> - inline constexpr auto default_policy_value<IEEE754Precise> = - IEEE754Precise::no; - } // namespace json_details - - enum class ForceFullNameCheck : unsigned { no, yes }; // 1bit - - namespace json_details { - template<> - inline constexpr unsigned policy_bits_width<ForceFullNameCheck> = 1; - - template<> - inline constexpr auto default_policy_value<ForceFullNameCheck> = - ForceFullNameCheck::no; - - template<typename Policy, typename Policies> - struct policy_bits_start_impl; - - template<typename Policy, typename... Policies> - struct policy_bits_start_impl<Policy, std::tuple<Policies...>> { - static constexpr auto idx = - traits::pack_index_of_v<Policy, Policies...>; - static_assert( idx >= 0, "Policy is not registered" ); - using tp_policies = std::tuple<Policies...>; - - template<std::size_t Pos, int End> - static constexpr unsigned do_step( ) { - if constexpr( Pos >= static_cast<std::size_t>( End ) ) { - return 0U; - } - return policy_bits_width<std::tuple_element_t<Pos, tp_policies>>; - } - - template<std::size_t... Is> - static constexpr unsigned calc( std::index_sequence<Is...> ) { - return ( do_step<Is, idx>( ) + ... ); - } - }; - - using policy_list = - std::tuple<ExecModeTypes, ZeroTerminatedString, PolicyCommentTypes, - CheckedParseMode, AllowEscapedNames, IEEE754Precise, - ForceFullNameCheck>; - - template<typename Policy, typename Policies> - inline constexpr unsigned basic_policy_bits_start = - policy_bits_start_impl<Policy, Policies>::template calc( - std::make_index_sequence<std::tuple_size_v<Policies>>{ } ); - - template<typename Policy> - inline constexpr unsigned policy_bits_start = - basic_policy_bits_start<Policy, policy_list>; - - template<typename Policy> - inline constexpr bool is_policy_flag = policy_bits_width<Policy> > 0; - - // struct is_policy_flag<PolicyCommentTypes> : std::true_type {}; - - } // namespace json_details - - namespace json_details { - template<typename Policy, typename... Policies> - constexpr Policy get_policy_or( std::tuple<Policies...> const &pols ) { - constexpr int const pack_idx = - daw::traits::pack_index_of_v<Policy, Policies...>; - - if constexpr( pack_idx != -1 ) { - return std::get<static_cast<std::size_t>( pack_idx )>( pols ); - } else { - return json_details::default_policy_value<Policy>; - } - } - - template<typename Policy> - constexpr void set_bits( policy_options_t &value, Policy e ) { - static_assert( is_policy_flag<Policy>, - "Only registered policy types are allowed" ); - unsigned new_bits = static_cast<unsigned>( e ); - constexpr unsigned mask = (1U << policy_bits_width<Policy>)-1U; - new_bits &= mask; - new_bits <<= policy_bits_start<Policy>; - value &= ~mask; - value |= new_bits; - } - - template<typename Policy> - constexpr policy_options_t set_bits( policy_options_t const &v, - Policy e ) { - static_assert( is_policy_flag<Policy>, - "Only registered policy types are allowed" ); - auto value = v; - unsigned new_bits = static_cast<unsigned>( e ); - constexpr unsigned mask = (1U << policy_bits_width<Policy>)-1U; - new_bits &= mask; - new_bits <<= policy_bits_start<Policy>; - value &= ~mask; - value |= new_bits; - return value; - } - - template<typename Policy> - constexpr policy_options_t set_bits_for( Policy e ) { - static_assert( is_policy_flag<Policy>, - "Only registered policy types are allowed" ); - policy_options_t new_bits = static_cast<unsigned>( e ); - new_bits <<= policy_bits_start<Policy>; - return new_bits; - } - - template<typename Policy, typename Result = Policy> - constexpr Result get_bits( policy_options_t value ) { - static_assert( is_policy_flag<Policy>, - "Only registered policy types are allowed" ); - constexpr unsigned mask = - ( 1U << (policy_bits_start<Policy> + policy_bits_width<Policy>)) - 1U; - value &= mask; - value >>= policy_bits_start<Policy>; - return static_cast<Result>( Policy{ value } ); - } - - template<std::size_t Idx, typename... Ts> - using switch_t = std::tuple_element_t<Idx, std::tuple<Ts...>>; - } // namespace json_details - - template<typename... Policies> - constexpr json_details::policy_options_t - parse_options( Policies... policies ) { - static_assert( ( json_details::is_policy_flag<Policies> and ... ), - "Invalid policy flag types" ); - if constexpr( sizeof...( Policies ) > 0 ) { - return ( json_details::set_bits_for( policies ) | ... ); - } - return 0; - } - /*** * Handles the bounds and policy items for parsing execution and comments. - * @tparam IsUncheckedInput If true, do not perform all validity checks on - * input. This implies that we can trust the source to be perfect - * @tparam CommentPolicy The policy that handles skipping whitespace where - * comments may or may not be allowed:w - * @tparam ExecMode A Policy type for selecting if we must be constexpr, can - * use C/C++ runtime only methods, or if SIMD intrinsics are allowed - * @tparam AllowEscapedNames Are escapes allowed in member names. When - * true, the slower string parser is used + * @tparam PolicyFlags set via parse_options method to change compile time + * parser options + * @tparam Allocator An optional Allocator to allow for passing to objects + * created while parsing if they support the Allocator protocol of either + * the Allocator argument being last or with a first argument of + * std::allocator_arg_t followed by the allocator.`Thing( args..., alloc )` + * or `Thing( std::allocator_arg, alloc, args... )` */ - template<json_details::policy_options_t PolicyFlags = 0, + template<json_details::policy_options_t PolicyFlags = + json_details::default_policy_flag, typename Allocator = json_details::NoAllocator> struct BasicParsePolicy : json_details::AllocatorWrapper<Allocator> { using iterator = char const *; @@ -262,18 +55,30 @@ namespace daw::json { static constexpr exec_tag_t exec_tag = exec_tag_t{ }; + /*** + * see AllowEscapedNames + */ static constexpr bool allow_escaped_names = json_details::get_bits<AllowEscapedNames>( PolicyFlags ) == AllowEscapedNames::yes; + /*** + * see ForceFullNameCheck + */ static constexpr bool force_name_equal_check = json_details::get_bits<ForceFullNameCheck>( PolicyFlags ) == ForceFullNameCheck::yes; + /*** + * see ZeroTerminatedString + */ static constexpr bool is_zero_terminated_string = json_details::get_bits<ZeroTerminatedString>( PolicyFlags ) == ZeroTerminatedString::yes; + /*** + * See IEEE754Precise + */ static constexpr bool precise_ieee754 = json_details::get_bits<IEEE754Precise>( PolicyFlags ) == IEEE754Precise::yes; @@ -683,7 +488,7 @@ namespace daw::json { } }; - using NoCommentSkippingPolicyChecked = BasicParsePolicy<parse_options( )>; + using NoCommentSkippingPolicyChecked = BasicParsePolicy<>; using NoCommentZeroSkippingPolicyChecked = BasicParsePolicy<parse_options( ZeroTerminatedString::yes )>; diff --git a/include/daw/json/impl/fast_double_parser.h b/include/daw/json/impl/fast_double_parser.h index fb82b34be..268950f2c 100644 --- a/include/daw/json/impl/fast_double_parser.h +++ b/include/daw/json/impl/fast_double_parser.h @@ -1099,8 +1099,10 @@ namespace daw::json { // FASTFLOAT_LARGEST_POWER We recover the mantissa of the power, it // has a leading 1. It is always rounded down. - std::uint64_t factor_mantissa = - math_const::mantissa_64[power - FASTFLOAT_SMALLEST_POWER]; + std::uint64_t factor_mantissa = [&] { + auto const idx = power - FASTFLOAT_SMALLEST_POWER; + return math_const::mantissa_64[idx]; + }( ); // The exponent is 1024 + 63 + power // + floor(log(5**power)/log(2)). @@ -1139,11 +1141,11 @@ namespace daw::json { // this will be non-zero because the most significant bit of i is // 1. - auto [upper, lower] = + auto [lower, upper] = full_multiplication( exec_tag, whole, factor_mantissa ); - // We know that upper has at most one leading zero because - // both i and factor_mantissa have a leading one. This means - // that the result is at least as large as ((1<<63)*(1<<63))/(1<<64). + // We know that upper has at most one leading zero because + // both i and factor_mantissa have a leading one. This means + // that the result is at least as large as ((1<<63)*(1<<63))/(1<<64). // As long as the first 9 bits of "upper" are not "1", then we // know that we have an exact computed value for the leading diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index af972c17a..11118d0fb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -606,11 +606,15 @@ add_executable(daw_json_minify_full src/daw_json_minify_full.cpp) target_link_libraries(daw_json_minify_full json_test) add_dependencies(full daw_json_minify_full) - add_executable(test_array_of_ordered src/test_array_of_ordered.cpp) target_link_libraries(test_array_of_ordered json_test) add_dependencies(full test_array_of_ordered) +add_executable(test_details_parse_options src/test_details_parse_options.cpp) +target_link_libraries(test_details_parse_options json_test) +add_dependencies(full test_details_parse_options) + + # ************************************************** # JSON Benchmark # ************************************************** diff --git a/tests/src/test_details_parse_options.cpp b/tests/src/test_details_parse_options.cpp new file mode 100644 index 000000000..4285c07ae --- /dev/null +++ b/tests/src/test_details_parse_options.cpp @@ -0,0 +1,23 @@ +// Copyright (c) Darrell Wright +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/beached/daw_json_link +// + +// Ensure that parse_options is working as expected +// + +#include <daw/json/impl/daw_json_parse_options.h> + +#include <cassert> + +int main( ) { + using namespace daw::json; + { + constexpr auto precise_opt = parse_options( IEEE754Precise::yes ); + constexpr auto precise_val = json_details::get_bits<IEEE754Precise>( precise_opt ); + static_assert( precise_val == IEEE754Precise::yes ); + } +}