/* * 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 #include #include #include #include #include #include namespace apache { namespace thrift { class BinaryProtocolWriter; class CompactProtocolWriter; class SimpleJSONProtocolWriter; namespace op { namespace detail { template inline constexpr bool kIsStrongType = std::is_enum>::value&& type::is_a_v; template inline constexpr bool kIsIntegral = type::is_a_v; using apache::thrift::protocol::TType; template struct TypeTagToTType; template <> struct TypeTagToTType { static constexpr TType value = TType::T_BOOL; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_BYTE; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_I16; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_I32; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_I64; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_FLOAT; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_DOUBLE; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_STRING; }; template <> struct TypeTagToTType { static constexpr TType value = TType::T_STRING; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_LIST; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_SET; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_MAP; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_I32; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_STRUCT; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_STRUCT; }; template struct TypeTagToTType> { static constexpr TType value = TType::T_STRUCT; }; template struct TypeTagToTType> { static constexpr TType value = TypeTagToTType::value; }; template struct TypeTagToTType> { static constexpr TType value = TypeTagToTType::value; }; template inline constexpr apache::thrift::protocol::TType typeTagToTType = detail::TypeTagToTType::value; template struct SerializedSize; template struct SerializedSize { template uint32_t operator()(Protocol& prot, bool t) const { return prot.serializedSizeBool(t); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, int8_t i) const { return prot.serializedSizeByte(i); } template uint32_t operator()(Protocol& prot, uint8_t i) const { return prot.serializedSizeByte(folly::to_signed(i)); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, int16_t i) const { return prot.serializedSizeI16(i); } template uint32_t operator()(Protocol& prot, uint16_t i) const { return prot.serializedSizeI16(folly::to_signed(i)); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, int32_t i) const { return prot.serializedSizeI32(i); } template uint32_t operator()(Protocol& prot, uint32_t i) const { return prot.serializedSizeI32(folly::to_signed(i)); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, int64_t i) const { return prot.serializedSizeI64(i); } template uint32_t operator()(Protocol& prot, uint64_t i) const { return prot.serializedSizeI64(folly::to_signed(i)); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, float i) const { return prot.serializedSizeFloat(i); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, double i) const { return prot.serializedSizeDouble(i); } }; template struct SerializedSize { template uint32_t operator()(Protocol& prot, folly::StringPiece s) const { return prot.serializedSizeString(s); } }; template <> struct SerializedSize { template uint32_t operator()(Protocol& prot, folly::StringPiece s) const { return prot.serializedSizeBinary(s); } template uint32_t operator()(Protocol& prot, const folly::IOBuf& s) const { return prot.serializedSizeBinary(s); } }; template <> struct SerializedSize { template uint32_t operator()(Protocol& prot, folly::StringPiece s) const { return prot.serializedSizeZCBinary(s); } template uint32_t operator()(Protocol& prot, const folly::IOBuf& s) const { return prot.serializedSizeZCBinary(s); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSize(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSizeZC(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSize(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSizeZC(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSize(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.serializedSizeZC(&prot); } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const T& s) const { return prot.serializedSizeI32(static_cast(s)); } }; inline uint32_t checked_container_size(size_t size) { const size_t limit = std::numeric_limits::max(); if (size > limit) { TProtocolException::throwExceededSizeLimit(size, limit); } return static_cast(size); } template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const ListType& list) const { uint32_t xfer = 0; xfer += prot.serializedSizeListBegin( typeTagToTType, checked_container_size(list.size())); for (const auto& elem : list) { xfer += SerializedSize{}(prot, elem); } xfer += prot.serializedSizeListEnd(); return xfer; } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const SetType& set) const { uint32_t xfer = 0; xfer += prot.serializedSizeSetBegin( typeTagToTType, checked_container_size(set.size())); for (const auto& elem : set) { xfer += SerializedSize{}(prot, elem); } xfer += prot.serializedSizeSetEnd(); return xfer; } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const MapType& map) const { uint32_t xfer = 0; xfer += prot.serializedSizeMapBegin( typeTagToTType, typeTagToTType, checked_container_size(map.size())); for (const auto& kv : map) { xfer += SerializedSize{}(prot, kv.first); xfer += SerializedSize{}(prot, kv.second); } xfer += prot.serializedSizeMapEnd(); return xfer; } }; template struct SerializedSize> : SerializedSize { template uint32_t operator()(Protocol& prot, const U& m) const { if constexpr (kIsStrongType) { return SerializedSize{}( prot, static_cast>(m)); } else { return SerializedSize{}(prot, m); } } }; template struct SerializedSize> { template uint32_t operator()(Protocol& prot, const U& m) const { return folly::overload( [&](auto adapter) -> decltype(decltype(adapter):: template serializedSize(prot, m)) { return decltype(adapter)::template serializedSize( prot, m); }, [&](auto...) { return SerializedSize{}(prot, Adapter::toThrift(m)); })(Adapter{}); } }; template struct Encode; template <> struct Encode { template uint32_t operator()(Protocol& prot, bool t) const { return prot.writeBool(t); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, int8_t i) const { return prot.writeByte(i); } template uint32_t operator()(Protocol& prot, uint8_t i) const { return prot.writeByte(folly::to_signed(i)); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, int16_t i) const { return prot.writeI16(i); } template uint32_t operator()(Protocol& prot, uint16_t i) const { return prot.writeI16(folly::to_signed(i)); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, int32_t i) const { return prot.writeI32(i); } template uint32_t operator()(Protocol& prot, uint32_t i) const { return prot.writeI32(folly::to_signed(i)); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, int64_t i) const { return prot.writeI64(i); } template uint32_t operator()(Protocol& prot, uint64_t i) const { return prot.writeI64(folly::to_signed(i)); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, float i) const { return prot.writeFloat(i); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, double i) const { return prot.writeDouble(i); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, folly::StringPiece s) const { return prot.writeString(s); } }; template <> struct Encode { template uint32_t operator()(Protocol& prot, const folly::IOBuf& s) const { return prot.writeBinary(s); } template uint32_t operator()(Protocol& prot, folly::StringPiece s) const { return prot.writeBinary(s); } }; template struct ShouldWrite { template bool operator()(field_ref) const { return true; } template bool operator()(required_field_ref) const { return true; } template bool operator()(optional_field_ref opt) const { return opt.has_value(); } template bool operator()(optional_boxed_field_ref opt) const { return opt.has_value(); } template bool operator()(union_field_ref opt) const { return opt.has_value(); } template bool operator()(terse_field_ref val) const { return !isEmpty(*val); } template bool operator()(terse_intern_boxed_field_ref val) const { return !isEmpty(*val); } template bool operator()(const std::unique_ptr& ptr) const { return ptr != nullptr; } template bool operator()(const std::shared_ptr& ptr) const { return ptr != nullptr; } }; template inline constexpr ShouldWrite should_write{}; template struct StructEncode { template uint32_t operator()(Protocol& prot, const T& t) const { uint32_t s = 0; s += prot.writeStructBegin(op::get_class_name_v.data()); op::for_each_ordinal([&](auto id) { using Id = decltype(id); using Tag = op::get_type_tag; auto&& field = op::get(t); if (!should_write(field)) { return; } s += prot.writeFieldBegin( &*op::get_name_v.begin(), typeTagToTType, folly::to_underlying(op::get_field_id::value)); s += Encode{}(prot, *field); s += prot.writeFieldEnd(); }); s += prot.writeFieldStop(); s += prot.writeStructEnd(); return s; } }; template struct Encode> { template uint32_t operator()(Protocol& prot, const T& t) const { // Is protocol is pre-compiled, use `write` method since it's faster // than `StructEncode`. constexpr bool useWrite = folly::IsOneOf:: value || (std::is_same::value && decltype(apache::thrift::detail::st::struct_private_access:: __fbthrift_cpp2_gen_json())::value); if constexpr (useWrite) { return t.write(&prot); } else { return StructEncode{}(prot, t); } } }; // TODO: Use `union_match` to optimize union serialization template struct Encode> : Encode> {}; template struct Encode> { template uint32_t operator()(Protocol& prot, const T& s) const { return s.write(&prot); } }; template struct Encode> { template uint32_t operator()(Protocol& prot, const T& s) const { return prot.writeI32(static_cast(s)); } }; // TODO: add optimization used in protocol_methods.h template struct ListEncode { template uint32_t operator()(Protocol& prot, const T& list) const { uint32_t xfer = 0; xfer += prot.writeListBegin( typeTagToTType, checked_container_size(list.size())); for (const auto& elem : list) { xfer += Encode{}(prot, elem); } xfer += prot.writeListEnd(); return xfer; } }; template struct Encode> : ListEncode {}; template struct SetEncode { template uint32_t operator()(Protocol& prot, const T& set) const { uint32_t xfer = 0; xfer += prot.writeSetBegin( typeTagToTType, checked_container_size(set.size())); for (const auto& elem : set) { xfer += Encode{}(prot, elem); } xfer += prot.writeSetEnd(); return xfer; } }; template struct Encode> : SetEncode {}; template struct MapEncode { template uint32_t operator()(Protocol& prot, const T& map) const { uint32_t xfer = 0; xfer += prot.writeMapBegin( typeTagToTType, typeTagToTType, checked_container_size(map.size())); for (const auto& kv : map) { xfer += Encode{}(prot, kv.first); xfer += Encode{}(prot, kv.second); } xfer += prot.writeMapEnd(); return xfer; } }; template struct Encode> : MapEncode {}; template struct CppTypeEncode { template uint32_t operator()(Protocol& prot, const U& m) const { if constexpr (kIsStrongType) { return Encode{}(prot, static_cast>(m)); } else { return Encode{}(prot, m); } } }; template struct Encode> : CppTypeEncode {}; template struct AdaptedEncode { template uint32_t operator()(Protocol& prot, const U& m) const { return folly::overload( [&](auto adapter) -> decltype(decltype(adapter)::template encode( prot, m)) { return decltype(adapter)::template encode(prot, m); }, [&](auto...) { return Encode{}(prot, Adapter::toThrift(m)); })( Adapter{}); } }; template struct Encode> : AdaptedEncode {}; template struct Decode; template <> struct Decode { template void operator()(Protocol& prot, bool& b) const { prot.readBool(b); } template void operator()(Protocol& prot, std::vector::reference t) const { bool b; prot.readBool(b); t = b; } }; template <> struct Decode { template void operator()(Protocol& prot, int8_t& i) const { prot.readByte(i); } template void operator()(Protocol& prot, uint8_t& i) const { int8_t tmp; prot.readByte(tmp); i = folly::to_unsigned(tmp); } }; template <> struct Decode { template void operator()(Protocol& prot, int16_t& i) const { prot.readI16(i); } template void operator()(Protocol& prot, uint16_t& i) const { int16_t tmp; prot.readI16(tmp); i = folly::to_unsigned(tmp); } }; template <> struct Decode { template void operator()(Protocol& prot, int32_t& i) const { prot.readI32(i); } template void operator()(Protocol& prot, uint32_t& i) const { int32_t tmp; prot.readI32(tmp); i = folly::to_unsigned(tmp); } }; template <> struct Decode { template void operator()(Protocol& prot, int64_t& i) const { prot.readI64(i); } template void operator()(Protocol& prot, uint64_t& i) const { int64_t tmp; prot.readI64(tmp); i = folly::to_unsigned(tmp); } }; template <> struct Decode { template void operator()(Protocol& prot, float& f) const { prot.readFloat(f); } }; template <> struct Decode { template void operator()(Protocol& prot, double& d) const { prot.readDouble(d); } }; template <> struct Decode { template void operator()(Protocol& prot, StrType& s) const { prot.readString(s); } }; template <> struct Decode { template void operator()(Protocol& prot, StrType& s) const { prot.readBinary(s); } }; template struct Decode> { template void operator()(Protocol& prot, T& s) const { s.read(&prot); } }; template struct Decode> { template void operator()(Protocol& prot, T& s) const { s.read(&prot); } }; template struct Decode> { template void operator()(Protocol& prot, T& s) const { s.read(&prot); } }; template struct Decode> { template void operator()(Protocol& prot, T& t) const { int32_t i; prot.readI32(i); t = static_cast(i); } }; // TODO: add optimization used in protocol_methods.h template struct Decode> { template void operator()(Protocol& prot, ListType& list) const { auto consumeElem = [&] { auto&& elem = apache::thrift::detail::pm::emplace_back_default(list); Decode{}(prot, elem); }; TType t; uint32_t s; prot.readListBegin(t, s); list = ListType(); if (prot.kOmitsContainerSizes()) { // list size unknown, SimpleJSON protocol won't know type, either // so let's just hope that it spits out something that makes sense while (prot.peekList()) { consumeElem(); } } else if (typeTagToTType == t) { apache::thrift::detail::pm::reserve_if_possible(&list, s); while (s--) { consumeElem(); } } else { while (s--) { prot.skip(t); } } prot.readListEnd(); } }; template using emplace_hint_t = decltype(FOLLY_DECLVAL(Container).emplace_hint( FOLLY_DECLVAL(Container).end(), FOLLY_DECLVAL(Args)...)); template using emplace_t = decltype(FOLLY_DECLVAL(Container).emplace( FOLLY_DECLVAL(Container).end(), FOLLY_DECLVAL(Args)...)); template auto& emplace_at_end(Container& container, Key&& key, Value&& val) { if constexpr (folly::is_detected_v) { return container .emplace_hint( container.end(), std::forward(key), std::forward(val)) ->second; } else if constexpr (folly:: is_detected_v) { return container.emplace(std::forward(key), std::forward(val)) .first->second; } else { return container[std::forward(key)]; } } template void emplace_at_end(Container& container, Value&& val) { if constexpr (folly::is_detected_v) { container.emplace_hint(container.end(), std::forward(val)); } else if constexpr (folly::is_detected_v) { container.emplace(std::forward(val)); } else { container.insert(std::forward(val)); } } template struct Decode> { private: // Handles set with sorted_unique property template static std::enable_if_t< apache::thrift::detail::pm::sorted_unique_constructible_v> decode_known_length_set(Protocol& prot, Set& set, std::uint32_t set_size) { if (set_size == 0) { return; } bool sorted = true; typename Set::container_type tmp(set.get_allocator()); apache::thrift::detail::pm::reserve_if_possible(&tmp, set_size); { auto& elem0 = apache::thrift::detail::pm::emplace_back_default(tmp); Decode{}(prot, elem0); } for (size_t i = 1; i < set_size; ++i) { auto& elem = apache::thrift::detail::pm::emplace_back_default(tmp); Decode{}(prot, elem); sorted = sorted && set.key_comp()(tmp[i - 1], elem); } using folly::sorted_unique; set = sorted ? Set(sorted_unique, std::move(tmp)) : Set(std::move(tmp)); } // Handles set without sorted_unique property template static std::enable_if_t< !apache::thrift::detail::pm::sorted_unique_constructible_v> decode_known_length_set(Protocol& prot, Set& set, std::uint32_t set_size) { apache::thrift::detail::pm::reserve_if_possible(&set, set_size); for (auto i = set_size; i > 0; i--) { typename Set::value_type value = apache::thrift::detail::default_set_element(set); Decode{}(prot, value); emplace_at_end(set, std::move(value)); } } public: template void operator()(Protocol& prot, SetType& set) const { auto consumeElem = [&] { typename SetType::value_type value; Decode{}(prot, value); emplace_at_end(set, std::move(value)); }; TType t; uint32_t s; prot.readSetBegin(t, s); set = SetType(); if (prot.kOmitsContainerSizes()) { while (prot.peekSet()) { consumeElem(); } } else if (typeTagToTType == t) { decode_known_length_set(prot, set, s); } else { while (s--) { prot.skip(t); } } prot.readSetEnd(); } }; template struct Decode> { private: // Handles map with sorted_unique property template static std::enable_if_t< apache::thrift::detail::pm::sorted_unique_constructible_v> decode_known_length_map(Protocol& prot, Map& map, std::uint32_t map_size) { if (map_size == 0) { return; } bool sorted = true; typename Map::container_type tmp(map.get_allocator()); apache::thrift::detail::pm::reserve_if_possible(&tmp, map_size); { auto& elem0 = apache::thrift::detail::pm::emplace_back_default_map(tmp, map); Decode{}(prot, elem0.first); Decode{}(prot, elem0.second); } for (size_t i = 1; i < map_size; ++i) { auto& elem = apache::thrift::detail::pm::emplace_back_default_map(tmp, map); Decode{}(prot, elem.first); Decode{}(prot, elem.second); sorted = sorted && map.key_comp()(tmp[i - 1].first, elem.first); } using folly::sorted_unique; map = sorted ? Map(sorted_unique, std::move(tmp)) : Map(std::move(tmp)); } // Handles map without sorted_unique property. template static std::enable_if_t< !apache::thrift::detail::pm::sorted_unique_constructible_v> decode_known_length_map(Protocol& prot, Map& map, std::uint32_t map_size) { apache::thrift::detail::pm::reserve_if_possible(&map, map_size); for (auto i = map_size; i--;) { typename Map::key_type key = apache::thrift::detail::default_map_key(map); typename Map::mapped_type value = apache::thrift::detail::default_map_value(map); Decode{}(prot, key); Decode{}(prot, value); emplace_at_end(map, std::move(key), std::move(value)); } } public: template void operator()(Protocol& prot, MapType& map) const { auto consumeElem = [&] { typename MapType::key_type key; Decode{}(prot, key); auto& val = emplace_at_end(map, std::move(key), typename MapType::mapped_type{}); Decode{}(prot, val); }; TType keyType, valueType; uint32_t s; prot.readMapBegin(keyType, valueType, s); map = MapType(); if (prot.kOmitsContainerSizes()) { while (prot.peekMap()) { consumeElem(); } } else if ( typeTagToTType == keyType && typeTagToTType == valueType) { decode_known_length_map(prot, map, s); } else { while (s--) { prot.skip(keyType); prot.skip(valueType); } } prot.readMapEnd(); } }; template struct Decode> : Decode { template void operator()(Protocol& prot, U& m) const { if constexpr (kIsIntegral) { type::native_type i; Decode::operator()(prot, i); m = static_cast(i); } else { Decode::operator()(prot, m); } } }; template void adapter_clear(U& m) { if (typeTagToTType == TType::T_LIST || typeTagToTType == TType::T_SET || typeTagToTType == TType::T_MAP) { adapt_detail::clear(m); } } template struct Decode> { template void operator()(Protocol& prot, U& m) const { return folly::overload( [&](auto adapter) -> decltype(decltype(adapter)::template decode( prot, m)) { adapter_clear(m); decltype(adapter)::template decode(prot, m); }, [&](auto...) { constexpr bool hasInplaceToThrift = ::apache::thrift::adapt_detail:: has_inplace_toThrift>::value; if constexpr (hasInplaceToThrift) { adapter_clear(m); Decode{}(prot, Adapter::toThrift(m)); } else { type::native_type orig; Decode{}(prot, orig); m = Adapter::fromThrift(std::move(orig)); } })(Adapter{}); } }; template struct Decode< type::field, FieldContext>> { using field_adapted_tag = type::field, FieldContext>; static_assert(type::is_concrete_v, ""); template constexpr adapt_detail:: if_not_field_adapter, Struct, void> operator()(Protocol& prot, U& m, Struct&) const { Decode>{}(prot, m); } template constexpr adapt_detail:: if_field_adapter, Struct, void> operator()(Protocol& prot, U& m, Struct& strct) const { // TODO(dokwon): in-place deserialization support for field adapter. folly::overload( [&](auto adapter) -> decltype(decltype(adapter)::template decode( prot, m)) { adapter_clear(m); decltype(adapter)::template decode(prot, m); }, [&](...) { type::native_type orig; Decode{}(prot, orig); m = adapt_detail::fromThriftField( std::move(orig), strct); })(Adapter{}); } }; } // namespace detail } // namespace op } // namespace thrift } // namespace apache