blob: cd7319820356a9a1d09ae5bda855f8a206ff9424 [file]
// 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