/* * Copyright (C) 2018 The Android Open Source Project * * 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 SRC_PROFILING_COMMON_INTERNER_H_ #define SRC_PROFILING_COMMON_INTERNER_H_ #include #include #include #include #include "perfetto/base/logging.h" namespace perfetto { namespace profiling { using InternID = uint32_t; // Interner that hands out refcounted references. template class Interner { private: struct Entry { template Entry(Interner* in, InternID i, U... args) : data(std::forward(args...)), id(i), interner(in) {} bool operator<(const Entry& other) const { return data < other.data; } bool operator==(const Entry& other) const { return data == other.data; } struct Hash { size_t operator()(const Entry& e) const noexcept { return std::hash{}(e.data); } }; const T data; InternID id; size_t ref_count = 0; Interner* interner; }; public: class Interned { public: friend class Interner; explicit Interned(Entry* entry) : entry_(entry) {} Interned(const Interned& other) : entry_(other.entry_) { if (entry_ != nullptr) entry_->ref_count++; } Interned(Interned&& other) noexcept : entry_(other.entry_) { other.entry_ = nullptr; } Interned& operator=(Interned other) noexcept { using std::swap; swap(*this, other); return *this; } const T& data() const { return entry_->data; } InternID id() const { return entry_->id; } ~Interned() { if (entry_ != nullptr) entry_->interner->Return(entry_); } bool operator<(const Interned& other) const { return entry_ < other.entry_; } bool operator==(const Interned& other) const { return entry_ == other.entry_; } const T* operator->() const { return &entry_->data; } private: Interner::Entry* entry_; }; template Interned Intern(U... args) { Entry item(this, next_id, std::forward(args...)); auto it = entries_.find(item); if (it == entries_.cend()) { // This does not invalidate pointers to entries we hold in Interned. See // https://timsong-cpp.github.io/cppwp/n3337/unord.req#8 auto it_and_inserted = entries_.emplace(std::move(item)); next_id++; it = it_and_inserted.first; PERFETTO_DCHECK(it_and_inserted.second); } Entry& entry = const_cast(*it); entry.ref_count++; return Interned(&entry); } ~Interner() { PERFETTO_DCHECK(entries_.empty()); } size_t entry_count_for_testing() { return entries_.size(); } private: void Return(Entry* entry) { if (--entry->ref_count == 0) entries_.erase(*entry); } InternID next_id = 1; std::unordered_set entries_; static_assert(sizeof(Interned) == sizeof(void*), "interned things should be small"); }; template void swap(typename Interner::Interned a, typename Interner::Interned b) { std::swap(a.entry_, b.entry_); } template using Interned = typename Interner::Interned; } // namespace profiling } // namespace perfetto #endif // SRC_PROFILING_COMMON_INTERNER_H_