// Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/json/ #ifndef TAO_JSON_OPERATORS_HPP #define TAO_JSON_OPERATORS_HPP #include "basic_value.hpp" #include "internal/format.hpp" namespace tao::json { template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator==( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { if( rhs.is_value_ptr() ) { return lhs == rhs.skip_value_ptr(); } if( lhs.type() != rhs.type() ) { switch( lhs.type() ) { case type::VALUE_PTR: return lhs.skip_value_ptr() == rhs; case type::SIGNED: if( rhs.type() == type::UNSIGNED ) { const auto v = lhs.get_signed(); return ( v >= 0 ) && ( static_cast< std::uint64_t >( v ) == rhs.get_unsigned() ); } if( rhs.type() == type::DOUBLE ) { return lhs.get_signed() == rhs.get_double(); } break; case type::UNSIGNED: if( rhs.type() == type::SIGNED ) { const auto v = rhs.get_signed(); return ( v >= 0 ) && ( lhs.get_unsigned() == static_cast< std::uint64_t >( v ) ); } if( rhs.type() == type::DOUBLE ) { return lhs.get_unsigned() == rhs.get_double(); } break; case type::DOUBLE: if( rhs.type() == type::SIGNED ) { return lhs.get_double() == rhs.get_signed(); } if( rhs.type() == type::UNSIGNED ) { return lhs.get_double() == rhs.get_unsigned(); } break; case type::STRING: if( rhs.type() == type::STRING_VIEW ) { return lhs.get_string() == rhs.get_string_view(); } break; case type::STRING_VIEW: if( rhs.type() == type::STRING ) { return lhs.get_string_view() == rhs.get_string(); } break; case type::BINARY: if( rhs.type() == type::BINARY_VIEW ) { return tao::internal::binary_equal( lhs.get_binary(), rhs.get_binary_view() ); } break; case type::BINARY_VIEW: if( rhs.type() == type::BINARY ) { return tao::internal::binary_equal( lhs.get_binary_view(), rhs.get_binary() ); } break; case type::OPAQUE_PTR: assert( lhs.type() != type::OPAQUE_PTR ); break; default: break; } assert( rhs.type() != type::OPAQUE_PTR ); return false; } switch( lhs.type() ) { case type::UNINITIALIZED: return true; case type::NULL_: return true; case type::BOOLEAN: return lhs.get_boolean() == rhs.get_boolean(); case type::SIGNED: return lhs.get_signed() == rhs.get_signed(); case type::UNSIGNED: return lhs.get_unsigned() == rhs.get_unsigned(); case type::DOUBLE: return lhs.get_double() == rhs.get_double(); case type::STRING: return lhs.get_string() == rhs.get_string(); case type::STRING_VIEW: return lhs.get_string_view() == rhs.get_string_view(); case type::BINARY: return lhs.get_binary() == rhs.get_binary(); case type::BINARY_VIEW: return tao::internal::binary_equal( lhs.get_binary_view(), rhs.get_binary_view() ); case type::ARRAY: return lhs.get_array() == rhs.get_array(); case type::OBJECT: return lhs.get_object() == rhs.get_object(); case type::VALUE_PTR: assert( lhs.type() != type::VALUE_PTR ); break; // LCOV_EXCL_LINE case type::OPAQUE_PTR: assert( lhs.type() != type::OPAQUE_PTR ); break; // LCOV_EXCL_LINE case type::VALUELESS_BY_EXCEPTION: assert( lhs.type() != type::VALUELESS_BY_EXCEPTION ); break; // LCOV_EXCL_LINE } // LCOV_EXCL_START assert( false ); return false; // LCOV_EXCL_STOP } template< template< typename... > class Traits, int = 1 > // work-around for Visual C++ [[nodiscard]] bool operator==( const basic_value< Traits >& lhs, tao::internal::identity_t< basic_value< Traits > > rhs ) noexcept { return lhs == rhs; } template< template< typename... > class Traits, int = 2 > // work-around for Visual C++ [[nodiscard]] bool operator==( tao::internal::identity_t< basic_value< Traits > > lhs, const basic_value< Traits >& rhs ) noexcept { return lhs == rhs; } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator==( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { static_assert( noexcept( Traits< std::optional< T > >::equal( lhs, rhs ) ), "equal must be noexcept" ); return Traits< std::optional< T > >::equal( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator==( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { return rhs == lhs; } template< template< typename... > class Traits, typename T > [[nodiscard]] auto operator==( const basic_value< Traits >& lhs, const T& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::equal( lhs, rhs ) ) { using D = std::decay_t< T >; static_assert( noexcept( Traits< D >::equal( lhs, rhs ) ), "equal must be noexcept" ); return Traits< D >::equal( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] auto operator==( const T& lhs, const basic_value< Traits >& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::equal( rhs, lhs ) ) { return rhs == lhs; } template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator!=( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { return !( lhs == rhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator!=( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { return !( lhs == rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator!=( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { return !( rhs == lhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator!=( const basic_value< Traits >& lhs, const T& rhs ) noexcept { return !( lhs == rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator!=( const T& lhs, const basic_value< Traits >& rhs ) noexcept { return !( lhs == rhs ); } template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator<( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { if( rhs.is_value_ptr() ) { return lhs < rhs.skip_value_ptr(); } if( lhs.type() != rhs.type() ) { switch( lhs.type() ) { case type::VALUE_PTR: return lhs.skip_value_ptr() < rhs; case type::SIGNED: if( rhs.type() == type::UNSIGNED ) { const auto v = lhs.get_signed(); return ( v < 0 ) || ( static_cast< std::uint64_t >( v ) < rhs.get_unsigned() ); } if( rhs.type() == type::DOUBLE ) { return lhs.get_signed() < rhs.get_double(); } break; case type::UNSIGNED: if( rhs.type() == type::SIGNED ) { const auto v = rhs.get_signed(); return ( v >= 0 ) && ( lhs.get_unsigned() < static_cast< std::uint64_t >( v ) ); } if( rhs.type() == type::DOUBLE ) { return lhs.get_unsigned() < rhs.get_double(); } break; case type::DOUBLE: if( rhs.type() == type::SIGNED ) { return lhs.get_double() < rhs.get_signed(); } if( rhs.type() == type::UNSIGNED ) { return lhs.get_double() < rhs.get_unsigned(); } break; case type::STRING: if( rhs.type() == type::STRING_VIEW ) { return lhs.get_string() < rhs.get_string_view(); } break; case type::STRING_VIEW: if( rhs.type() == type::STRING ) { return lhs.get_string_view() < rhs.get_string(); } break; case type::BINARY: if( rhs.type() == type::BINARY_VIEW ) { return tao::internal::binary_less( lhs.get_binary(), rhs.get_binary_view() ); } break; case type::BINARY_VIEW: if( rhs.type() == type::BINARY ) { return tao::internal::binary_less( lhs.get_binary_view(), rhs.get_binary() ); } break; case type::OPAQUE_PTR: assert( lhs.type() != type::OPAQUE_PTR ); break; default: break; } assert( rhs.type() != type::OPAQUE_PTR ); return lhs.type() < rhs.type(); } switch( lhs.type() ) { case type::UNINITIALIZED: return false; case type::NULL_: return false; case type::BOOLEAN: return lhs.get_boolean() < rhs.get_boolean(); case type::SIGNED: return lhs.get_signed() < rhs.get_signed(); case type::UNSIGNED: return lhs.get_unsigned() < rhs.get_unsigned(); case type::DOUBLE: return lhs.get_double() < rhs.get_double(); case type::STRING: return lhs.get_string() < rhs.get_string(); case type::STRING_VIEW: return lhs.get_string_view() < rhs.get_string_view(); case type::BINARY: return lhs.get_binary() < rhs.get_binary(); case type::BINARY_VIEW: return tao::internal::binary_less( lhs.get_binary_view(), rhs.get_binary_view() ); case type::ARRAY: return lhs.get_array() < rhs.get_array(); case type::OBJECT: return lhs.get_object() < rhs.get_object(); case type::VALUE_PTR: assert( lhs.type() != type::VALUE_PTR ); break; // LCOV_EXCL_LINE case type::OPAQUE_PTR: assert( lhs.type() != type::OPAQUE_PTR ); break; // LCOV_EXCL_LINE case type::VALUELESS_BY_EXCEPTION: assert( lhs.type() != type::VALUELESS_BY_EXCEPTION ); break; // LCOV_EXCL_LINE } // LCOV_EXCL_START assert( false ); return false; // LCOV_EXCL_STOP } template< template< typename... > class Traits, int = 1 > // work-around for Visual C++ [[nodiscard]] bool operator<( const basic_value< Traits >& lhs, tao::internal::identity_t< basic_value< Traits > > rhs ) noexcept { return lhs < rhs; } template< template< typename... > class Traits, int = 2 > // work-around for Visual C++ [[nodiscard]] bool operator<( const tao::internal::identity_t< basic_value< Traits > > lhs, const basic_value< Traits >& rhs ) noexcept { return lhs < rhs; } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator<( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { static_assert( noexcept( Traits< std::optional< T > >::less_than( lhs, rhs ) ), "less_than must be noexcept" ); return Traits< std::optional< T > >::less_than( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator<( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { static_assert( noexcept( Traits< std::optional< T > >::greater_than( rhs, lhs ) ), "greater_than must be noexcept" ); return Traits< std::optional< T > >::greater_than( rhs, lhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] auto operator<( const basic_value< Traits >& lhs, const T& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::less_than( lhs, rhs ) ) { using D = std::decay_t< T >; static_assert( noexcept( Traits< D >::less_than( lhs, rhs ) ), "less_than must be noexcept" ); return Traits< D >::less_than( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] auto operator<( const T& lhs, const basic_value< Traits >& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::greater_than( rhs, lhs ) ) { using D = std::decay_t< T >; static_assert( noexcept( Traits< D >::greater_than( rhs, lhs ) ), "greater_than must be noexcept" ); return Traits< D >::greater_than( rhs, lhs ); } template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator>( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { return rhs < lhs; } template< template< typename... > class Traits > [[nodiscard]] bool operator>( const basic_value< Traits >& lhs, tao::internal::identity_t< basic_value< Traits > > rhs ) noexcept { return rhs < lhs; } template< template< typename... > class Traits > [[nodiscard]] bool operator>( tao::internal::identity_t< basic_value< Traits > > lhs, const basic_value< Traits >& rhs ) noexcept { return rhs < lhs; } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator>( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { static_assert( noexcept( Traits< std::optional< T > >::greater_than( lhs, rhs ) ), "greater_than must be noexcept" ); return Traits< std::optional< T > >::greater_than( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator>( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { static_assert( noexcept( Traits< std::optional< T > >::less_than( rhs, lhs ) ), "less_than must be noexcept" ); return Traits< std::optional< T > >::less_than( rhs, lhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] auto operator>( const basic_value< Traits >& lhs, const T& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::greater_than( lhs, rhs ) ) { using D = std::decay_t< T >; static_assert( noexcept( Traits< D >::greater_than( lhs, rhs ) ), "greater_than must be noexcept" ); return Traits< D >::greater_than( lhs, rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] auto operator>( const T& lhs, const basic_value< Traits >& rhs ) noexcept -> decltype( Traits< std::decay_t< T > >::less_than( rhs, lhs ) ) { using D = std::decay_t< T >; static_assert( noexcept( Traits< D >::less_than( rhs, lhs ) ), "less_than must be noexcept" ); return Traits< D >::less_than( rhs, lhs ); } template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator<=( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { return !( lhs > rhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator<=( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { return !( lhs > rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator<=( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { return !( lhs > rhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator<=( const basic_value< Traits >& lhs, const T& rhs ) noexcept { return !( lhs > rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator<=( const T& lhs, const basic_value< Traits >& rhs ) noexcept { return !( lhs > rhs ); } template< template< typename... > class TraitsL, template< typename... > class TraitsR > [[nodiscard]] bool operator>=( const basic_value< TraitsL >& lhs, const basic_value< TraitsR >& rhs ) noexcept { return !( lhs < rhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator>=( const basic_value< Traits >& lhs, const std::optional< T >& rhs ) noexcept { return !( lhs < rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator>=( const std::optional< T >& lhs, const basic_value< Traits >& rhs ) noexcept { return !( lhs < rhs ); } template< template< typename... > class Traits, typename T > [[nodiscard]] bool operator>=( const basic_value< Traits >& lhs, const T& rhs ) noexcept { return !( lhs < rhs ); } template< typename T, template< typename... > class Traits > [[nodiscard]] bool operator>=( const T& lhs, const basic_value< Traits >& rhs ) noexcept { return !( lhs < rhs ); } } // namespace tao::json #endif