/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include namespace apache { namespace thrift { namespace adapt_detail { // Identical to std::declval. template const T& cr(); template using is_mutable_ref = folly::Conjunction< std::is_reference, folly::Negation>>>; // The type returned by the adapter for the given thrift type. template using adapted_t = decltype(Adapter::fromThrift(std::declval())); // Used to detect if Adapter has the fromThriftField function which takes an // additional FieldContext argument. template using FromThriftFieldIdType = decltype(Adapter::fromThriftField( std::declval(), std::declval>())); template using FromThriftFieldType = decltype(Adapter::fromThriftField( std::declval(), std::declval>())); template constexpr bool is_field_adapter_v = folly::is_detected_v; template < typename Adapter, int16_t FieldId, typename ThriftT, typename Struct, typename R = FromThriftFieldIdType> using if_field_adapter = std::enable_if_t, R>; template < typename Adapter, typename ThriftT, typename Struct, typename R = adapted_t> using if_not_field_adapter = std::enable_if_t, R>; // Used to detect if Adapter has a construct function override. template using ConstructType = decltype(Adapter::construct( std::declval(), std::declval())); template constexpr bool is_ctor_adapter_v = folly::is_detected_v; template using if_ctor_adapter = std::enable_if_t>; template using if_not_ctor_adapter = std::enable_if_t>; // Used to detect if Adapter has a clear function override. template using ClearType = decltype(Adapter::clear(std::declval())); template constexpr bool is_clear_adapter_v = folly::is_detected_v; template using if_clear_adapter = std::enable_if_t, R>; template using if_not_clear_adapter = std::enable_if_t, R>; // Used to detect if Adapter has an isEmpty function override. template using IsEmptyType = decltype(Adapter::isEmpty(std::declval())); template constexpr bool is_empty_adapter_v = folly::is_detected_v; template using if_is_empty_adapter = std::enable_if_t, bool>; template using if_not_is_empty_adapter = std::enable_if_t, bool>; // Converts a Thrift field value into an adapted type via Adapter. // This overload passes additional context containing the reference to the // Thrift object containing the field and the field ID as a second argument // to Adapter::fromThriftField. template constexpr if_field_adapter fromThriftField( ThriftT&& value, Struct& object) { return Adapter::fromThriftField( std::forward(value), FieldContext{object}); } // Converts a Thrift field value into an adapted type via Adapter. // This overloads does the conversion via Adapter::fromThrift and is used when // Adapter::fromThriftField is unavailable. template constexpr if_not_field_adapter fromThriftField( ThriftT&& value, Struct&) { return Adapter::fromThrift(std::forward(value)); } // The type returned by the adapter for the given thrift type of a struct field. template using adapted_field_t = decltype(fromThriftField( std::declval(), std::declval())); // The type returned by the adapter for the given adapted type. template using thrift_t = decltype(Adapter::toThrift(std::declval())); // If the adapter exposes access to the standard thrift value // from the toThrift method. template using has_inplace_toThrift = is_mutable_ref>; template void fromThrift(AdaptedT& adapted, ThriftT&& value) { adapted = Adapter::fromThrift(std::forward(value)); } // Called during the construction of a Thrift object to perform any additional // initialization of an adapted type. This overload passes a context containing // the reference to the Thrift object containing the field and the field ID as // a second argument to Adapter::construct. template constexpr if_ctor_adapter> construct(AdaptedT& field, Struct& object) { Adapter::construct(field, FieldContext{object}); } template constexpr if_not_ctor_adapter> construct(AdaptedT&, Struct&) {} // Clear op based on the adapter, with a fallback to calling the default // constructor and Adapter::construct for context population. template constexpr if_clear_adapter clear(AdaptedT& field) { Adapter::clear(field); } template constexpr if_not_clear_adapter clear(AdaptedT& field) { field = AdaptedT(); } // Clear op based on the field adapter, with a fallback to calling the default // constructor and Adapter::construct for context population. template constexpr if_clear_adapter clear(AdaptedT& field, Struct&) { Adapter::clear(field); } template constexpr if_not_clear_adapter clear( AdaptedT& field, Struct& object) { field = AdaptedT(); construct(field, object); } // Equal op based on the thrift types. template struct thrift_equal { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return Adapter::toThrift(lhs) == Adapter::toThrift(rhs); } }; // Equal op based on the adapted types, with a fallback on thrift_equal. template struct adapted_equal : thrift_equal {}; template struct adapted_equal< Adapter, AdaptedT, folly::void_t() == cr())>> { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return lhs == rhs; } }; // Equal op based on the adapter, with a fallback on adapted_equal. template struct adapter_equal : adapted_equal {}; template struct adapter_equal< Adapter, AdaptedT, folly::void_t(), cr()))>> { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return Adapter::equal(lhs, rhs); } }; // Less op based on the thrift types. template struct thrift_less { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return Adapter::toThrift(lhs) < Adapter::toThrift(rhs); } }; // Less op based on the adapted types, with a fallback on thrift_less. template struct adapted_less : thrift_less {}; template struct adapted_less< Adapter, AdaptedT, folly::void_t() < cr())>> { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return lhs < rhs; } }; // Less op based on the adapter, with a fallback on adapted_less. template struct adapter_less : adapted_less {}; template struct adapter_less< Adapter, AdaptedT, folly::void_t(), cr()))>> { constexpr bool operator()(const AdaptedT& lhs, const AdaptedT& rhs) const { return Adapter::less(lhs, rhs); } }; // Hash based on the thrift type. template struct thrift_hash { constexpr size_t operator()(const AdaptedT& value) const { auto&& tvalue = Adapter::toThrift(value); return std::hash>()(tvalue); } }; // Hash based on the adapted types, with a fallback on thrift_hash. template struct adapted_hash : thrift_hash {}; template struct adapted_hash< Adapter, AdaptedT, folly::void_t>())>> : std::hash> {}; // Hash based on the adapter, with a fallback on adapted_hash. template struct adapter_hash : adapted_hash {}; template struct adapter_hash< Adapter, AdaptedT, folly::void_t()))>> { constexpr size_t operator()(const AdaptedT& value) const { return Adapter::hash(value); } }; template constexpr bool equal(const AdaptedT& lhs, const AdaptedT& rhs) { return adapter_equal()(lhs, rhs); } // Helper for optional fields. template constexpr bool equal_opt(const FieldRefT& lhs, const FieldRefT& rhs) { using AdaptedT = decltype(lhs.value()); return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || equal(lhs.value(), rhs.value())); } template constexpr bool not_equal(const AdaptedT& lhs, const AdaptedT& rhs) { return !adapter_equal()(lhs, rhs); } // Helper for optional fields. template constexpr bool not_equal_opt(const FieldRefT& lhs, const FieldRefT& rhs) { return !equal_opt(lhs, rhs); } template constexpr bool less(const AdaptedT& lhs, const AdaptedT& rhs) { return adapter_less()(lhs, rhs); } // A less comparision when the values are already known to be not equal. // Helper for optional fields. template constexpr bool neq_less_opt(const FieldRefT& lhs, const FieldRefT& rhs) { using AdaptedT = decltype(lhs.value()); return !lhs.has_value() || (rhs.has_value() && adapter_less()(lhs.value(), rhs.value())); } template constexpr size_t hash(const AdaptedT& value) { return adapter_hash()(value); } // Helpers replace less, hash, equal_to functions // for a set, with the appropriate adapted versions. template < typename Adapter, template class SetT, typename Key, typename Less, typename Allocator> SetT, Allocator> resolveSetForAdapated(const SetT&); template < typename Adapter, template class SetT, typename Key, typename Hash, typename KeyEqual, typename Allocator> SetT< Key, adapt_detail::adapted_hash, adapt_detail::adapted_equal, Allocator> resolveSetForAdapated(const SetT&); template using adapt_set_key_t = decltype(resolveSetForAdapated(std::declval())); // Helpers to set the appropriate less, hash, equal_to functions // for a map with an adapted key type. template < typename Adapter, template class MapT, typename Key, typename Value, typename Less, typename Allocator> MapT, Allocator> resolveMapForAdapated(const MapT&); template < typename Adapter, template class MapT, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator> MapT< Key, Value, adapt_detail::adapted_hash, adapt_detail::adapted_equal, Allocator> resolveMapForAdapated(const MapT&); template using adapt_map_key_t = decltype(resolveMapForAdapated(std::declval())); // Validates an adapter. // Checking decltype(equal(...)) is not sufficient for validation. template void validate() { const auto adapted = AdaptedT(); equal(adapted, adapted); not_equal(adapted, adapted); // less and hash are not validated because not all adapters provide it. } template void validateAdapter() { validate>(); } template void validateFieldAdapter() { validate>(); } template < bool ZeroCopy, typename Tag, typename Adapter, typename AdaptedT, typename Protocol, typename FallbackF, typename = void> struct adapter_serialized_size { uint32_t operator()(Protocol&, const AdaptedT&, FallbackF f) { return f(); } }; template using serialized_size_type = decltype(Adapter::template serializedSize( std::declval(), std::declval())); template < bool ZeroCopy, typename Tag, typename Adapter, typename AdaptedT, typename Protocol, typename FallbackF> struct adapter_serialized_size< ZeroCopy, Tag, Adapter, AdaptedT, Protocol, FallbackF, folly::void_t>> { uint32_t operator()(Protocol& prot, const AdaptedT& val, FallbackF) { return Adapter::template serializedSize(prot, val); } }; template uint32_t serializedSizeFixed(Protocol& protocol, bool) { return protocol.serializedSizeBool(); } template uint32_t serializedSizeFixed(Protocol& protocol, int8_t) { return protocol.serializedSizeByte(); } template uint32_t serializedSizeFixed(Protocol& protocol, int16_t) { return protocol.serializedSizeI16(); } template uint32_t serializedSizeFixed(Protocol& protocol, int32_t) { return protocol.serializedSizeI32(); } template uint32_t serializedSizeFixed(Protocol& protocol, int64_t) { return protocol.serializedSizeI64(); } template uint32_t serializedSizeFixed(Protocol& protocol, double) { return protocol.serializedSizeDouble(); } template uint32_t serializedSizeFixed(Protocol& protocol, float) { return protocol.serializedSizeFloat(); } template < bool ZeroCopy, typename Tag, typename Adapter, typename AdaptedT, typename Protocol, typename FallbackF> struct adapter_serialized_size< ZeroCopy, Tag, Adapter, AdaptedT, Protocol, FallbackF, std::enable_if_t< !folly::is_detected_v< serialized_size_type, Tag, Adapter, AdaptedT, Protocol> && std::is_arithmetic()))>::value>> { uint32_t operator()(Protocol& prot, const AdaptedT&, FallbackF) { return serializedSizeFixed( prot, decltype(Adapter::toThrift(std::declval()))(0)); } }; template < bool ZeroCopy, typename Tag, typename Adapter, typename AdaptedT, typename Protocol, typename FallbackF> uint32_t serializedSize(Protocol& prot, const AdaptedT& val, FallbackF f) { return adapter_serialized_size< ZeroCopy, Tag, Adapter, AdaptedT, Protocol, FallbackF>()(prot, val, f); } } // namespace adapt_detail } // namespace thrift } // namespace apache