| // Copyright 2021 gRPC authors. |
| // |
| // 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. |
| |
| #ifndef GRPC_SRC_CORE_UTIL_TABLE_H |
| #define GRPC_SRC_CORE_UTIL_TABLE_H |
| |
| #include <grpc/support/port_platform.h> |
| #include <stddef.h> |
| |
| #include <initializer_list> |
| #include <new> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "src/core/util/bitset.h" |
| #include "absl/meta/type_traits.h" |
| #include "absl/utility/utility.h" |
| |
| namespace grpc_core { |
| |
| namespace table_detail { |
| |
| template <size_t I, typename T, bool IsEmpty = std::is_empty<T>::value> |
| struct TableLeaf; |
| |
| template <size_t I, typename T> |
| struct TableLeaf<I, T, false> { |
| struct alignas(T) Data { |
| unsigned char bytes[sizeof(T)]; |
| }; |
| Data x; |
| T* ptr() { return reinterpret_cast<T*>(x.bytes); } |
| const T* ptr() const { return reinterpret_cast<const T*>(x.bytes); } |
| }; |
| |
| template <size_t I, typename T> |
| struct TableLeaf<I, T, true> { |
| T* ptr() { return reinterpret_cast<T*>(this); } |
| const T* ptr() const { return reinterpret_cast<const T*>(this); } |
| }; |
| |
| template <typename IndexSequence, typename... Ts> |
| struct ElementsImpl; |
| |
| template <size_t... Is, typename... Ts> |
| struct ElementsImpl<absl::index_sequence<Is...>, Ts...> : TableLeaf<Is, Ts>... { |
| }; |
| |
| template <typename... Ts> |
| using Elements = ElementsImpl<absl::make_index_sequence<sizeof...(Ts)>, Ts...>; |
| |
| template <typename T, size_t I> |
| struct IndexedType {}; |
| |
| template <typename IndexSequence, typename... Ts> |
| struct IndexMapImpl; |
| |
| template <size_t... Is, typename... Ts> |
| struct IndexMapImpl<absl::index_sequence<Is...>, Ts...> |
| : IndexedType<Ts, Is>... {}; |
| |
| template <typename... Ts> |
| using IndexMap = IndexMapImpl<absl::make_index_sequence<sizeof...(Ts)>, Ts...>; |
| |
| template <typename T, size_t I> |
| constexpr size_t ResolveIndex(IndexedType<T, I>*) { |
| return I; |
| } |
| |
| template <typename T, typename... Ts> |
| constexpr size_t IndexOf() { |
| return ResolveIndex<T>(static_cast<IndexMap<Ts...>*>(nullptr)); |
| } |
| |
| template <size_t I, typename... Ts> |
| using TypeIndex = std::tuple_element_t<I, std::tuple<Ts...>>; |
| |
| template <typename T> |
| void DestructIfNotNull(T* p) { |
| if (p) p->~T(); |
| } |
| |
| } // namespace table_detail |
| |
| // A Table<Ts> is much like a tuple<optional<Ts>...> - a set of values that are |
| // optionally present. Table efficiently packs the presence bits for size, and |
| // provides a slightly more convenient interface. |
| template <typename... Ts> |
| class Table { |
| // Helper - TypeIndex<I> is the type at index I in Ts |
| template <size_t I> |
| using TypeIndex = table_detail::TypeIndex<I, Ts...>; |
| |
| public: |
| // Construct a table with no values set. |
| Table() = default; |
| // Destruct - forwards to the Destruct member with an integer sequence so we |
| // can destruct field-wise. |
| ~Table() { Destruct(absl::make_index_sequence<sizeof...(Ts)>()); } |
| |
| // Copy another table |
| Table(const Table& rhs) { |
| // Since we know all fields are clear initially, pass false for or_clear. |
| Copy<false>(absl::make_index_sequence<sizeof...(Ts)>(), rhs); |
| } |
| |
| // Copy another table |
| Table& operator=(const Table& rhs) { |
| // Since we may not be all clear, pass true for or_clear to have Copy() |
| // clear newly emptied fields. |
| Copy<true>(absl::make_index_sequence<sizeof...(Ts)>(), rhs); |
| return *this; |
| } |
| |
| // Move from another table |
| Table(Table&& rhs) noexcept { |
| // Since we know all fields are clear initially, pass false for or_clear. |
| Move<false>(absl::make_index_sequence<sizeof...(Ts)>(), |
| std::forward<Table>(rhs)); |
| } |
| |
| // Move from another table |
| Table& operator=(Table&& rhs) noexcept { |
| // Since we may not be all clear, pass true for or_clear to have Move() |
| // clear newly emptied fields. |
| Move<true>(absl::make_index_sequence<sizeof...(Ts)>(), |
| std::forward<Table>(rhs)); |
| return *this; |
| } |
| |
| // Check if this table has a value for type T. |
| // Only available if there exists only one T in Ts. |
| template <typename T> |
| bool has() const { |
| return has<index_of<T>()>(); |
| } |
| |
| // Check if this table has index I. |
| template <size_t I> |
| absl::enable_if_t<(I < sizeof...(Ts)), bool> has() const { |
| return present_bits_.is_set(I); |
| } |
| |
| // Return the value for type T, or nullptr if it is un-set. |
| // Only available if there exists only one T in Ts. |
| template <typename T> |
| T* get() { |
| return get<index_of<T>()>(); |
| } |
| |
| // Return the value for type T, or nullptr if it is un-set. |
| // Only available if there exists only one T in Ts. |
| template <typename T> |
| const T* get() const { |
| return get<index_of<T>()>(); |
| } |
| |
| // Return the value for index I, or nullptr if it is un-set. |
| template <size_t I> |
| TypeIndex<I>* get() { |
| if (has<I>()) return element_ptr<I>(); |
| return nullptr; |
| } |
| |
| // Return the value for index I, or nullptr if it is un-set. |
| template <size_t I> |
| const TypeIndex<I>* get() const { |
| if (has<I>()) return element_ptr<I>(); |
| return nullptr; |
| } |
| |
| // Return the value for type T, default constructing it if it is un-set. |
| template <typename T> |
| T* get_or_create() { |
| return get_or_create<index_of<T>()>(); |
| } |
| |
| // Return the value for index I, default constructing it if it is un-set. |
| template <size_t I> |
| TypeIndex<I>* get_or_create() { |
| auto* p = element_ptr<I>(); |
| if (!set_present<I>(true)) { |
| new (p) TypeIndex<I>(); |
| } |
| return element_ptr<I>(); |
| } |
| |
| // Set the value for type T - using Args as construction arguments. |
| template <typename T, typename... Args> |
| T* set(Args&&... args) { |
| return set<index_of<T>()>(std::forward<Args>(args)...); |
| } |
| |
| // Set the value for index I - using Args as construction arguments. |
| template <size_t I, typename... Args> |
| TypeIndex<I>* set(Args&&... args) { |
| auto* p = element_ptr<I>(); |
| if (set_present<I>(true)) { |
| TypeIndex<I> replacement(std::forward<Args>(args)...); |
| *p = std::move(replacement); |
| } else { |
| new (p) TypeIndex<I>(std::forward<Args>(args)...); |
| } |
| return p; |
| } |
| |
| template <size_t I> |
| TypeIndex<I>* set(TypeIndex<I>&& value) { |
| auto* p = element_ptr<I>(); |
| if (set_present<I>(true)) { |
| *p = std::forward<TypeIndex<I>>(value); |
| } else { |
| new (p) TypeIndex<I>(std::forward<TypeIndex<I>>(value)); |
| } |
| return p; |
| } |
| |
| // Clear the value for type T, leaving it un-set. |
| template <typename T> |
| void clear() { |
| clear<index_of<T>()>(); |
| } |
| |
| // Clear the value for index I, leaving it un-set. |
| template <size_t I> |
| void clear() { |
| if (set_present<I>(false)) { |
| using T = TypeIndex<I>; |
| element_ptr<I>()->~T(); |
| } |
| } |
| |
| // Iterate through each set field in the table |
| template <typename F> |
| void ForEach(F f) const { |
| ForEachImpl(std::move(f), absl::make_index_sequence<sizeof...(Ts)>()); |
| } |
| |
| // Iterate through each set field in the table if it exists in Vs, in the |
| // order of Vs. |
| template <typename F, typename... Vs> |
| void ForEachIn(F f) const { |
| ForEachImpl(std::move(f), |
| absl::index_sequence<table_detail::IndexOf<Vs, Ts...>()...>()); |
| } |
| |
| // Iterate through each set field in the table if it exists in Vs, in the |
| // order of Vs. For each existing field, call the filter function. If the |
| // function returns true, keep the field. Otherwise, remove the field. |
| template <typename F, typename... Vs> |
| void FilterIn(F f) { |
| FilterInImpl(std::move(f), |
| absl::index_sequence<table_detail::IndexOf<Vs, Ts...>()...>()); |
| } |
| |
| // Count the number of set fields in the table |
| size_t count() const { return present_bits_.count(); } |
| |
| // Check if the table is completely empty |
| bool empty() const { return present_bits_.none(); } |
| |
| // Clear all elements in the table. |
| void ClearAll() { ClearAllImpl(absl::make_index_sequence<sizeof...(Ts)>()); } |
| |
| private: |
| // Bit field for which elements of the table are set (true) or un-set (false, |
| // the default) -- one bit for each type in Ts. |
| using PresentBits = BitSet<sizeof...(Ts)>; |
| // The tuple-like backing structure for Table. |
| using Elements = table_detail::Elements<Ts...>; |
| |
| // Given a T, return the unambiguous index of it within Ts. |
| template <typename T> |
| static constexpr size_t index_of() { |
| return table_detail::IndexOf<T, Ts...>(); |
| } |
| |
| // Given an index, return a point to the (maybe uninitialized!) data value at |
| // index I. |
| template <size_t I> |
| TypeIndex<I>* element_ptr() { |
| using Leaf = table_detail::TableLeaf<I, TypeIndex<I>>; |
| return static_cast<Leaf*>(&elements_)->ptr(); |
| } |
| |
| // Given an index, return a point to the (maybe uninitialized!) data value at |
| // index I. |
| template <size_t I> |
| const TypeIndex<I>* element_ptr() const { |
| using Leaf = table_detail::TableLeaf<I, TypeIndex<I>>; |
| return static_cast<const Leaf*>(&elements_)->ptr(); |
| } |
| |
| // Set the present bit to value (if true - value is present/set, if false, |
| // value is un-set). Returns the old value so that calling code can note |
| // transition edges. |
| template <size_t I> |
| bool set_present(bool value) { |
| bool out = present_bits_.is_set(I); |
| present_bits_.set(I, value); |
| return out; |
| } |
| |
| // Set the value of index I to the value held in rhs index I if it is set. |
| // If it is unset, if or_clear is true, then clear our value, otherwise do |
| // nothing. |
| template <bool or_clear, size_t I> |
| void CopyIf(const Table& rhs) { |
| if (auto* p = rhs.get<I>()) { |
| set<I>(*p); |
| } else if (or_clear) { |
| clear<I>(); |
| } |
| } |
| |
| // Set the value of index I to the value moved from rhs index I if it was set. |
| // If it is unset, if or_clear is true, then clear our value, otherwise do |
| // nothing. |
| template <bool or_clear, size_t I> |
| void MoveIf(Table&& rhs) { |
| if (auto* p = rhs.get<I>()) { |
| set<I>(std::move(*p)); |
| } else if (or_clear) { |
| clear<I>(); |
| } |
| } |
| |
| // Call (*f)(value) if that value is in the table. |
| template <size_t I, typename F> |
| void CallIf(F* f) const { |
| if (auto* p = get<I>()) { |
| (*f)(*p); |
| } |
| } |
| |
| // Call (*f)(value) if that value is in the table. |
| // If the value is present in the table and (*f)(value) returns false, remove |
| // the value from the table. |
| template <size_t I, typename F> |
| void FilterIf(F* f) { |
| if (auto* p = get<I>()) { |
| if (!(*f)(*p)) { |
| clear<I>(); |
| } |
| } |
| } |
| |
| // For each field (element I=0, 1, ...) if that field is present, call its |
| // destructor. |
| template <size_t... I> |
| void Destruct(absl::index_sequence<I...>) { |
| (table_detail::DestructIfNotNull(get<I>()), ...); |
| } |
| |
| // For each field (element I=0, 1, ...) copy that field into this table - |
| // or_clear as per CopyIf(). |
| template <bool or_clear, size_t... I> |
| void Copy(absl::index_sequence<I...>, const Table& rhs) { |
| (CopyIf<or_clear, I>(rhs), ...); |
| } |
| |
| // For each field (element I=0, 1, ...) move that field into this table - |
| // or_clear as per MoveIf(). |
| template <bool or_clear, size_t... I> |
| void Move(absl::index_sequence<I...>, Table&& rhs) { |
| (MoveIf<or_clear, I>(std::forward<Table>(rhs)), ...); |
| } |
| |
| // For each field (element I=0, 1, ...) if that field is present, call f. |
| template <typename F, size_t... I> |
| void ForEachImpl(F f, absl::index_sequence<I...>) const { |
| (CallIf<I>(&f), ...); |
| } |
| |
| // For each field (element I=0, 1, ...) if that field is present, call f. If |
| // f returns false, remove the field from the table. |
| template <typename F, size_t... I> |
| void FilterInImpl(F f, absl::index_sequence<I...>) { |
| (FilterIf<I>(&f), ...); |
| } |
| |
| template <size_t... I> |
| void ClearAllImpl(absl::index_sequence<I...>) { |
| (clear<I>(), ...); |
| } |
| |
| // Bit field indicating which elements are set. |
| PresentBits present_bits_; |
| // The memory to store the elements themselves. |
| Elements elements_; |
| }; |
| |
| } // namespace grpc_core |
| |
| #endif // GRPC_SRC_CORE_UTIL_TABLE_H |