/* * 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 #include #include #include #include #include #include namespace apache { namespace thrift { namespace detail { using FieldID = std::int16_t; // MSVC cannot reinterpret_cast an overloaded function to another function // pointer, but piping the function through an identity function before // reinterpret_cast works. template FOLLY_ERASE constexpr T identity(T t) { return t; } template using enable_if_smart_ptr_t = std::enable_if_t, U>; template using enable_if_not_smart_ptr_t = std::enable_if_t, U>; template struct maybe_get_element_type { using type = T; }; template struct maybe_get_element_type> : maybe_get_element_type> {}; template using maybe_get_element_type_t = typename maybe_get_element_type::type; enum class StringFieldType { String, StringView, BinaryStringView, Binary, IOBuf, IOBufPtr, IOBufObj, }; // A required field is handled as an unqualified field, because the required // qualifier is deprecated and has same serialization semantic with the // unqualified qualifier. enum class FieldQualifier { Unqualified, Optional, Terse, }; struct FieldInfo { // Field id in thrift definition. FieldID id; // Unqualified fields need to be differentiated from optional fields to always // write unqualified fields despite the value of __isset. FieldQualifier qualifier; const char* name; // Offset into the data member of the field in the struct. ptrdiff_t memberOffset; // 0 means that the field does not have __isset. ptrdiff_t issetOffset; const TypeInfo* typeInfo; }; struct UnionExt { // Clear union before setting a field. VoidFuncPtr clear; ptrdiff_t unionTypeOffset; int (*getActiveId)(const void* /* object */); void (*setActiveId)(void* /* object */, int /* fieldId */); // Value initialized using placement new into the member. // Generated code should order this list by fields key order. VoidFuncPtr initMember[]; }; // Templatized version to const initialize with the exact array length. template struct UnionExtN { VoidFuncPtr clear; ptrdiff_t unionTypeOffset; int (*getActiveId)(const void*); void (*setActiveId)(void*, int); VoidFuncPtr initMember[NumFields]; }; struct StructInfo { /** * Number of fields in `fieldInfos`. */ std::int16_t numFields; const char* name; // This should be set to nullptr when not a union. const UnionExt* unionExt = nullptr; bool (*getIsset)(const void* /* object */, ptrdiff_t /* offset */); void (*setIsset)(void* /* object */, ptrdiff_t /* offset */, bool /*set */); // Use for other languages to pass in additional information. const void* customExt; /** * Holds `numFields` entries. * * The memory for these entries is sequentially allocated with instances of * `StructInfo`, so this field MUST be the last in this struct. */ FieldInfo fieldInfos[]; }; // Templatized version to const initialize with the exact array length. template struct StructInfoN { std::int16_t numFields = NumFields; const char* name; const void* unionExt = nullptr; bool (*getIsset)(const void*, ptrdiff_t); void (*setIsset)(void*, ptrdiff_t, bool); const void* customExt; FieldInfo fieldInfos[NumFields]; }; template FOLLY_ERASE const StructInfo& toStructInfo( const StructInfoN& templatizedInfo) { return reinterpret_cast(templatizedInfo); } struct ListFieldExt { const TypeInfo* valInfo; std::uint32_t (*size)(const void* object); void (*clear)(void* object); void (*consumeElem)( const void* context, void* object, void (*reader)(const void* context, void* val)); void (*readList)( const void* context, void* object, std::uint32_t listSize, void (*reader)(const void* context, void* val)); size_t (*writeList)( const void* context, const void* val, size_t (*writer)(const void* context, const void* val)); }; struct SetFieldExt { const TypeInfo* valInfo; std::uint32_t (*size)(const void* object); void (*clear)(void* object); void (*consumeElem)( const void* context, void* object, void (*reader)(const void* context, void* val)); void (*readSet)( const void* context, void* object, std::uint32_t setSize, void (*reader)(const void* context, void* val)); size_t (*writeSet)( const void* context, const void* object, bool protocolSortKeys, size_t (*writer)(const void* context, const void* val)); }; struct MapFieldExt { const TypeInfo* keyInfo; const TypeInfo* valInfo; std::uint32_t (*size)(const void* object); void (*clear)(void* object); void (*consumeElem)( const void* context, void* object, void (*keyReader)(const void* context, void* key), void (*valueReader)(const void* context, void* val)); void (*readMap)( const void* context, void* object, std::uint32_t mapSize, void (*keyReader)(const void* context, void* key), void (*valueReader)(const void* context, void* val)); size_t (*writeMap)( const void* context, const void* object, bool protocolSortKeys, size_t (*writer)( const void* context, const void* keyElem, const void* valueElem)); }; template void clearUnion(void* object) { apache::thrift::clear(*reinterpret_cast(object)); } union ThriftValue { explicit ThriftValue(bool value) : boolValue(value) {} explicit ThriftValue(std::int8_t value) : int8Value(value) {} explicit ThriftValue(std::int16_t value) : int16Value(value) {} explicit ThriftValue(std::int32_t value) : int32Value(value) {} explicit ThriftValue(std::int64_t value) : int64Value(value) {} explicit ThriftValue(float value) : floatValue(value) {} explicit ThriftValue(double value) : doubleValue(value) {} explicit ThriftValue(const void* value) : object(value) {} explicit ThriftValue(folly::IOBuf* value) : iobuf(value) {} explicit ThriftValue(folly::StringPiece value) : stringViewValue(value) {} bool boolValue; std::int8_t int8Value; std::int16_t int16Value; std::int32_t int32Value; std::int64_t int64Value; float floatValue; double doubleValue; const void* object; folly::IOBuf* iobuf; folly::StringPiece stringViewValue; }; using OptionalThriftValue = folly::Optional; template enable_if_not_smart_ptr_t get( const void* object, const TypeInfo& /* typeInfo */) { return folly::make_optional( static_cast(*static_cast(object))); } template enable_if_smart_ptr_t get( const void* object, const TypeInfo& /* typeInfo */) { if (const auto* ptr = static_cast(object)->get()) { return folly::make_optional(static_cast(*ptr)); } return folly::none; } template enable_if_smart_ptr_t get( const void* object, const TypeInfo& /* typeInfo */) { if (const void* ptr = static_cast(object)->get()) { return folly::make_optional(ptr); } return folly::none; } template = 0> constexpr auto getDerefFuncPtr() { return nullptr; } template = 0> constexpr auto getDerefFuncPtr() { return get; } template enable_if_not_smart_ptr_t set(void* object) { *static_cast(object) = ObjectType(); return object; } template std::enable_if_t, void*> set(void* object) { using Element = typename PtrType::element_type; auto& ptr = *static_cast(object); ptr = std::make_shared(); return const_cast*>(ptr.get()); } template std::enable_if_t, void*> set(void* object) { using Element = typename PtrType::element_type; auto& ptr = *static_cast(object); ptr = std::make_unique(); return const_cast*>(ptr.get()); } template enable_if_not_smart_ptr_t set(void* object, PrimitiveType val) { *static_cast(object) = static_cast(val); } template std::enable_if_t> set( void* object, PrimitiveType val) { using Element = typename PtrType::element_type; *static_cast(object) = std::make_unique(static_cast(val)); } template std::enable_if_t> set( void* object, PrimitiveType val) { using Element = typename PtrType::element_type; *static_cast(object) = std::make_shared(static_cast(val)); } template enable_if_not_smart_ptr_t placementNewUnionValue(void* object) { ::new (object) ValueType(); } template enable_if_smart_ptr_t placementNewUnionValue(void* object) { ::new (object) PtrType(new typename PtrType::element_type()); } template size_t writeList( const void* context, const void* object, size_t (*writer)(const void* /*context*/, const void* /*val*/)) { const List& out = *static_cast(object); size_t written = 0; for (auto& elem : out) { written += writer(context, &elem); } return written; } template size_t writeSet( const void* context, const void* object, bool protocolSortKeys, size_t (*writer)(const void* /*context*/, const void* /*val*/)) { const Set& out = *static_cast(object); size_t written = 0; if (!folly::is_detected_v< ::apache::thrift::detail::pm::detect_key_compare, Set> && protocolSortKeys) { std::vector iters; iters.reserve(out.size()); for (auto it = out.begin(); it != out.end(); ++it) { iters.push_back(it); } std::sort( iters.begin(), iters.end(), [](auto a, auto b) { return *a < *b; }); for (auto it : iters) { written += writer(context, &(*it)); } } else { // Support containers with defined but non-FIFO iteration order. auto get_view = folly::order_preserving_reinsertion_view_or_default; for (const auto& elem : get_view(out)) { written += writer(context, &elem); } } return written; } template size_t writeMap( const void* context, const void* object, bool protocolSortKeys, size_t (*writer)( const void* /*context*/, const void* /*keyElem*/, const void* /*valueElem*/)) { const Map& out = *static_cast(object); size_t written = 0; if (!folly::is_detected_v< ::apache::thrift::detail::pm::detect_key_compare, Map> && protocolSortKeys) { std::vector iters; iters.reserve(out.size()); for (auto it = out.begin(); it != out.end(); ++it) { iters.push_back(it); } std::sort(iters.begin(), iters.end(), [](auto a, auto b) { return a->first < b->first; }); for (auto it : iters) { written += writer(context, &it->first, &it->second); } } else { // Support containers with defined but non-FIFO iteration order. auto get_view = folly::order_preserving_reinsertion_view_or_default; for (auto& elem_pair : get_view(out)) { written += writer(context, &elem_pair.first, &elem_pair.second); } } return written; } template std::uint32_t containerSize(const void* object) { return folly::to_narrow( folly::to_unsigned(static_cast(object)->size())); } template void containerClear(void* object) { static_cast(object)->clear(); } template void consumeMapElem( const void* context, void* object, void (*keyReader)(const void* /*context*/, void* /*key*/), void (*valueReader)(const void* /*context*/, void* /*val*/)) { Map& out = *static_cast(object); typename Map::key_type key; keyReader(context, &key); valueReader(context, &out[key]); } template void readMap( const void* context, void* object, std::uint32_t mapSize, void (*keyReader)(const void* /*context*/, void* /*key*/), void (*valueReader)(const void* /*context*/, void* /*val*/)) { Map& out = *static_cast(object); ::apache::thrift::detail::pm::reserve_if_possible(&out, mapSize); for (auto i = mapSize; i--;) { typename Map::key_type key; keyReader(context, &key); valueReader(context, &out[key]); } } template void consumeListElem( const void* context, void* object, void (*reader)(const void* /*context*/, void* /*val*/)) { List& out = *static_cast(object); out.emplace_back(); reader(context, &out.back()); } template void consumeSetElem( const void* context, void* object, void (*reader)(const void* /*context*/, void* /*val*/)) { Set& out = *static_cast(object); typename Set::value_type tmp; reader(context, &tmp); out.insert(std::move(tmp)); } template void readKnownLengthSet( const void* context, void* object, std::uint32_t setSize, void (*reader)(const void* /*context*/, void* /*val*/)) { ::apache::thrift::detail::pm::reserve_if_possible( static_cast(object), setSize); while (setSize--) { consumeSetElem(context, object, reader); } } template void readList( const void* context, void* object, std::uint32_t listSize, void (*reader)(const void* /*context*/, void* /*val*/)) { List& out = *static_cast(object); using traits = std::iterator_traits; using cat = typename traits::iterator_category; if (::apache::thrift::detail::pm::reserve_if_possible(&out, listSize) || std::is_same::value) { while (listSize--) { consumeListElem(context, object, reader); } } else { out.resize(listSize); for (auto& elem : out) { reader(context, &elem); } } } #define THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( \ TypeClass, Type, ThriftType, TTypeValue) \ template <> \ struct TypeToInfo { \ using underlying_type = ThriftType; \ static const TypeInfo typeInfo; \ } // Specializations for numbers. THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::int8_t, std::int8_t, T_BYTE); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::int16_t, std::int16_t, T_I16); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::int32_t, std::int32_t, T_I32); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::int64_t, std::int64_t, T_I64); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::uint8_t, std::int8_t, T_BYTE); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::uint16_t, std::int16_t, T_I16); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::uint32_t, std::int32_t, T_I32); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO( integral, std::uint64_t, std::int64_t, T_I64); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO(integral, bool, bool, T_BOOL); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO(floating_point, float, float, T_FLOAT); THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO(floating_point, double, double, T_DOUBLE); #undef THRIFT_DEFINE_PRIMITIVE_TYPE_TO_INFO // Specialization for string. #define THRIFT_DEFINE_STRING_TYPE_TO_INFO(TypeClass, T, ExtVal) \ template <> \ struct TypeToInfo { \ static const StringFieldType ext; \ static const TypeInfo typeInfo; \ } THRIFT_DEFINE_STRING_TYPE_TO_INFO(string, std::string, StringFieldType::String); THRIFT_DEFINE_STRING_TYPE_TO_INFO( string, folly::fbstring, StringFieldType::String); THRIFT_DEFINE_STRING_TYPE_TO_INFO(binary, std::string, StringFieldType::String); THRIFT_DEFINE_STRING_TYPE_TO_INFO( binary, folly::fbstring, StringFieldType::String); THRIFT_DEFINE_STRING_TYPE_TO_INFO(binary, folly::IOBuf, StringFieldType::IOBuf); THRIFT_DEFINE_STRING_TYPE_TO_INFO( binary, std::unique_ptr, StringFieldType::IOBufPtr); #undef THRIFT_DEFINE_STRING_TYPE_TO_INFO // Specialization for set. template struct TypeToInfo, T> { using set_type = maybe_get_element_type_t; static const SetFieldExt ext; static const TypeInfo typeInfo; }; template const SetFieldExt TypeToInfo, T>::ext = { /* .valInfo */ &TypeToInfo:: typeInfo, /* .size */ containerSize, /* .clear */ containerClear, /* .consumeElem */ consumeSetElem, /* .readSet */ readKnownLengthSet, /* .writeSet */ writeSet, }; template const TypeInfo TypeToInfo, T>::typeInfo = { /* .type */ protocol::TType::T_SET, /* .get */ getDerefFuncPtr(), /* .set */ reinterpret_cast(set), /* .typeExt */ &TypeToInfo, T>::ext, }; // Specialization for list. template struct TypeToInfo, T> { using list_type = maybe_get_element_type_t; static const ListFieldExt ext; static const TypeInfo typeInfo; }; template const ListFieldExt TypeToInfo, T>::ext = { /* .valInfo */ &TypeToInfo:: typeInfo, /* .size */ containerSize, /* .clear */ containerClear, /* .consumeElem */ consumeListElem, /* .readList */ readList, /* .writeList */ writeList, }; template const TypeInfo TypeToInfo, T>::typeInfo = { /* .type */ protocol::TType::T_LIST, /* .get */ getDerefFuncPtr(), /* .set */ reinterpret_cast(set), /* .typeExt */ &ext, }; // Specialization for map. template struct TypeToInfo, T> { using map_type = maybe_get_element_type_t; static const MapFieldExt ext; static const TypeInfo typeInfo; }; template const MapFieldExt TypeToInfo, T>::ext = { /* .keyInfo */ &TypeToInfo:: typeInfo, /* .valInfo */ &TypeToInfo::typeInfo, /* .size */ containerSize, /* .clear */ containerClear, /* .consumeElem */ consumeMapElem, /* .readMap */ readMap, /* .writeMap */ writeMap, }; template const TypeInfo TypeToInfo, T>::typeInfo = { /* .type */ protocol::TType::T_MAP, /* .get */ getDerefFuncPtr(), /* .set */ reinterpret_cast(set), /* .typeExt */ &ext, }; // Specialization for smart pointers of type class struct and union. #define THRIFT_DEFINE_STRUCT_PTR_TYPE_INFO(TypeClass) \ template \ struct TypeToInfo> { \ using struct_type = std::remove_cv_t; \ static const TypeInfo typeInfo; \ }; \ \ template \ const TypeInfo TypeToInfo< \ type_class::TypeClass, \ T, \ enable_if_smart_ptr_t>::typeInfo = { \ TypeToInfo::typeInfo.type, \ get, \ reinterpret_cast(set), \ TypeToInfo::typeInfo.typeExt, \ } THRIFT_DEFINE_STRUCT_PTR_TYPE_INFO(structure); THRIFT_DEFINE_STRUCT_PTR_TYPE_INFO(variant); #undef THRIFT_DEFINE_STRUCT_PTR_TYPE_INFO // Specialization for smart pointers of numerical types. #define THRIFT_DEFINE_NUMERIC_PTR_TYPE_INFO(TypeClass) \ template \ struct TypeToInfo> { \ using numeric_type = std::remove_cv_t; \ using underlying_type = \ typename TypeToInfo:: \ underlying_type; \ static const TypeInfo typeInfo; \ }; \ \ template \ const TypeInfo \ TypeToInfo>:: \ typeInfo = { \ TypeToInfo::typeInfo.type, \ get, \ reinterpret_cast(set), \ nullptr, \ } THRIFT_DEFINE_NUMERIC_PTR_TYPE_INFO(integral); THRIFT_DEFINE_NUMERIC_PTR_TYPE_INFO(floating_point); #undef THRIFT_DEFINE_NUMERIC_PTR_TYPE_INFO template constexpr ptrdiff_t fieldOffset(std::int16_t fieldIndex); template constexpr ptrdiff_t issetOffset(std::int16_t fieldIndex); template constexpr ptrdiff_t unionTypeOffset(); template void read(Protocol_* iprot, const StructInfo& structInfo, void* object); template size_t write( Protocol_* iprot, const StructInfo& structInfo, const void* object); } // namespace detail } // namespace thrift } // namespace apache