/* * 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 thrift { namespace py3 { template struct PyEnumTraits { using NamesMap = std::vector>; static const NamesMap& namesmap(); }; class EnumData { public: using NamePair = std::pair; using FindNameFunc = std::string_view(int); using FindValueFunc = std::optional(std::string_view); EnumData( FindNameFunc* const fn, FindValueFunc* const fv, folly::Range names, const std::vector& nm); virtual ~EnumData() { for (auto i : namesToInstances_) { Py_XDECREF(i.second); } } virtual std::pair> tryGetByName( std::string_view name) const; virtual std::pair tryGetByValue(int value) const; PyObject* tryAddToCache(int value, PyObject* obj); std::size_t size() const { return size_; } std::string_view getPyName(std::string_view name) const { return folly::get_default(origToPyNames_, name, name); } folly::Range getNames() const { return names_; } protected: using NameMap = std::unordered_map; PyObject* findInCache(std::string_view name) const; std::string_view findPyName(int value) const { auto name = findName_(value); return name.empty() ? name : getPyName(name); } FindNameFunc* const findName_; FindValueFunc* const findValue_; const std::size_t size_; std::atomic uncached_; const folly::Range names_; NameMap pyToOrigNames_; NameMap origToPyNames_; std::unordered_map namesToInstances_; mutable folly::SharedMutex cacheMutex_; }; class EnumFlagsData : public EnumData { public: EnumFlagsData( FindNameFunc fn, FindValueFunc fv, folly::Range names, const std::vector& nm, uint32_t maxPossibleFlagValue) : EnumData{fn, fv, names, nm}, maxPossibleFlagValue_{maxPossibleFlagValue}, flagValuesUncached_(1 << names.size()) {} ~EnumFlagsData() override { for (auto i : flagValuesToInstances_) { Py_XDECREF(i.second); } } std::pair tryGetByValue( int value) const override; PyObject* tryAddToFlagValuesCache(uint32_t value, PyObject* obj); std::string getNameForDerivedValue(uint32_t value) const; uint32_t getInvertValue(uint32_t value) const { return maxPossibleFlagValue_ ^ value; } uint32_t convertNegativeValue(uint32_t value) const { return value & maxPossibleFlagValue_; } private: bool isValidFlagValue(uint32_t value) const { return (maxPossibleFlagValue_ | value) == maxPossibleFlagValue_; } PyObject* findInFlagValuesCache(uint32_t value) const; const uint32_t maxPossibleFlagValue_; mutable folly::SharedMutex flagValueCacheMutex_; std::unordered_map flagValuesToInstances_; std::atomic flagValuesUncached_; }; namespace { template std::string_view enumFindNameWrapper(int value) { const char* name = apache::thrift::TEnumTraits::findName(static_cast(value)); return name == nullptr ? std::string_view{} : name; } template std::optional enumFindValueWrapper(const std::string_view name) { T v{}; const auto c = apache::thrift::TEnumTraits::findValue(name.data(), &v); return c ? std::optional{static_cast(v)} : std::nullopt; } } // namespace template EnumData* createEnumData() { using Traits = apache::thrift::TEnumTraits; return new EnumData( enumFindNameWrapper, enumFindValueWrapper, Traits::names, PyEnumTraits::namesmap()); } template EnumFlagsData* createEnumFlagsData() { using Traits = apache::thrift::TEnumTraits; uint32_t s = 0; for (auto value : apache::thrift::TEnumTraits::values) { s |= static_cast(value); } return new EnumFlagsData( enumFindNameWrapper, enumFindValueWrapper, Traits::names, PyEnumTraits::namesmap(), s); } template EnumData* createEnumDataForUnionType() { using type = typename T::Type; using Traits = apache::thrift::TEnumTraits; return new EnumData( enumFindNameWrapper, enumFindValueWrapper, Traits::names, PyEnumTraits::namesmap()); } } // namespace py3 } // namespace thrift