/* * 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 #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; /** * If the owning `StructInfo` specifies a `getIsset` and/or `setIsset` * function(s), then this value will be passed as the `offset` argument. * * For either operation, if the owning `StructInfo` does not have a function * pointer set, then this field should hold the offset (in bytes) from the * beginning of the corresponding struct object, of memory that can be * reintepreted as a `bool` holding the "isset" information for this field. * * If the owning `StructInfo` is a union, this field is set to 0 (and, in * practice, never used). */ ptrdiff_t issetOffset; const TypeInfo* typeInfo; }; struct UnionExt { /** * Clears the given union data object. * * Should be called prior to setting any field. */ VoidFuncPtr clear; /** * Offset (in bytes) from the start of the corresponding union data object to * the `int` memory holding the ID of the currently active (i.e., present) * field. * * This is used (and should be non-zero) iff `getActiveId` or `setActiveId` * below are nullptr, in which case the value of the active field ID will be * written and read directly into that memory. */ ptrdiff_t unionTypeOffset; /** * Returns the ID of the currently active field for the given union data * object, or 0 if none. * * If this (or `setActiveId`) are non-nullptr, `unionTypeOffset` is not used. */ int (*getActiveId)(const void* /* object */); /** * Sets the active field ID of the union data `object` to the given value. * * If this (or `getActiveId`) are non-nullptr, `unionTypeOffset` is not used. */ void (*setActiveId)(void* /* object */, int /* fieldId */); FOLLY_PUSH_WARNING FOLLY_CLANG_DISABLE_WARNING("-Wc99-extensions") // Value initialized using placement new into the member. // Generated code should order this list by fields key order. VoidFuncPtr initMember[]; FOLLY_POP_WARNING }; // 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; /** * Returns the value of the "isset" flag at the given `offset` for the given * `object`. * * @param object * @param offset of the isset flag to check for a given field, corresponding * to the value of `FieldInfo::issetOffset` for that field. */ 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; FOLLY_PUSH_WARNING FOLLY_CLANG_DISABLE_WARNING("-Wc99-extensions") /** * 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[]; FOLLY_POP_WARNING }; // 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); folly::reserve_if_available(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*/)) { Set& out = *static_cast(object); folly::reserve_if_available(out, 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 (folly::reserve_if_available(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