1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker *
4*6dbdd20aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker *
8*6dbdd20aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker *
10*6dbdd20aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker */
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker #ifndef SRC_PROFILING_COMMON_INTERNER_H_
18*6dbdd20aSAndroid Build Coastguard Worker #define SRC_PROFILING_COMMON_INTERNER_H_
19*6dbdd20aSAndroid Build Coastguard Worker
20*6dbdd20aSAndroid Build Coastguard Worker #include <stddef.h>
21*6dbdd20aSAndroid Build Coastguard Worker #include <stdint.h>
22*6dbdd20aSAndroid Build Coastguard Worker #include <functional>
23*6dbdd20aSAndroid Build Coastguard Worker #include <unordered_set>
24*6dbdd20aSAndroid Build Coastguard Worker
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
26*6dbdd20aSAndroid Build Coastguard Worker
27*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
28*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
29*6dbdd20aSAndroid Build Coastguard Worker
30*6dbdd20aSAndroid Build Coastguard Worker using InternID = uint32_t;
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker // Interner that hands out refcounted references.
33*6dbdd20aSAndroid Build Coastguard Worker template <typename T>
34*6dbdd20aSAndroid Build Coastguard Worker class Interner {
35*6dbdd20aSAndroid Build Coastguard Worker private:
36*6dbdd20aSAndroid Build Coastguard Worker struct Entry {
37*6dbdd20aSAndroid Build Coastguard Worker template <typename... U>
EntryEntry38*6dbdd20aSAndroid Build Coastguard Worker Entry(Interner<T>* in, InternID i, U... args)
39*6dbdd20aSAndroid Build Coastguard Worker : data(std::forward<U...>(args...)), id(i), interner(in) {}
40*6dbdd20aSAndroid Build Coastguard Worker
41*6dbdd20aSAndroid Build Coastguard Worker bool operator<(const Entry& other) const { return data < other.data; }
42*6dbdd20aSAndroid Build Coastguard Worker bool operator==(const Entry& other) const { return data == other.data; }
43*6dbdd20aSAndroid Build Coastguard Worker
44*6dbdd20aSAndroid Build Coastguard Worker struct Hash {
operatorEntry::Hash45*6dbdd20aSAndroid Build Coastguard Worker size_t operator()(const Entry& e) const noexcept {
46*6dbdd20aSAndroid Build Coastguard Worker return std::hash<T>{}(e.data);
47*6dbdd20aSAndroid Build Coastguard Worker }
48*6dbdd20aSAndroid Build Coastguard Worker };
49*6dbdd20aSAndroid Build Coastguard Worker
50*6dbdd20aSAndroid Build Coastguard Worker const T data;
51*6dbdd20aSAndroid Build Coastguard Worker InternID id;
52*6dbdd20aSAndroid Build Coastguard Worker size_t ref_count = 0;
53*6dbdd20aSAndroid Build Coastguard Worker Interner<T>* interner;
54*6dbdd20aSAndroid Build Coastguard Worker };
55*6dbdd20aSAndroid Build Coastguard Worker
56*6dbdd20aSAndroid Build Coastguard Worker public:
57*6dbdd20aSAndroid Build Coastguard Worker class Interned {
58*6dbdd20aSAndroid Build Coastguard Worker public:
59*6dbdd20aSAndroid Build Coastguard Worker friend class Interner<T>;
Interned(Entry * entry)60*6dbdd20aSAndroid Build Coastguard Worker explicit Interned(Entry* entry) : entry_(entry) {}
Interned(const Interned & other)61*6dbdd20aSAndroid Build Coastguard Worker Interned(const Interned& other) : entry_(other.entry_) {
62*6dbdd20aSAndroid Build Coastguard Worker if (entry_ != nullptr)
63*6dbdd20aSAndroid Build Coastguard Worker entry_->ref_count++;
64*6dbdd20aSAndroid Build Coastguard Worker }
65*6dbdd20aSAndroid Build Coastguard Worker
Interned(Interned && other)66*6dbdd20aSAndroid Build Coastguard Worker Interned(Interned&& other) noexcept : entry_(other.entry_) {
67*6dbdd20aSAndroid Build Coastguard Worker other.entry_ = nullptr;
68*6dbdd20aSAndroid Build Coastguard Worker }
69*6dbdd20aSAndroid Build Coastguard Worker
70*6dbdd20aSAndroid Build Coastguard Worker Interned& operator=(Interned other) noexcept {
71*6dbdd20aSAndroid Build Coastguard Worker using std::swap;
72*6dbdd20aSAndroid Build Coastguard Worker swap(*this, other);
73*6dbdd20aSAndroid Build Coastguard Worker return *this;
74*6dbdd20aSAndroid Build Coastguard Worker }
75*6dbdd20aSAndroid Build Coastguard Worker
data()76*6dbdd20aSAndroid Build Coastguard Worker const T& data() const { return entry_->data; }
77*6dbdd20aSAndroid Build Coastguard Worker
id()78*6dbdd20aSAndroid Build Coastguard Worker InternID id() const { return entry_->id; }
79*6dbdd20aSAndroid Build Coastguard Worker
~Interned()80*6dbdd20aSAndroid Build Coastguard Worker ~Interned() {
81*6dbdd20aSAndroid Build Coastguard Worker if (entry_ != nullptr)
82*6dbdd20aSAndroid Build Coastguard Worker entry_->interner->Return(entry_);
83*6dbdd20aSAndroid Build Coastguard Worker }
84*6dbdd20aSAndroid Build Coastguard Worker
85*6dbdd20aSAndroid Build Coastguard Worker bool operator<(const Interned& other) const {
86*6dbdd20aSAndroid Build Coastguard Worker return entry_ < other.entry_;
87*6dbdd20aSAndroid Build Coastguard Worker }
88*6dbdd20aSAndroid Build Coastguard Worker
89*6dbdd20aSAndroid Build Coastguard Worker bool operator==(const Interned& other) const {
90*6dbdd20aSAndroid Build Coastguard Worker return entry_ == other.entry_;
91*6dbdd20aSAndroid Build Coastguard Worker }
92*6dbdd20aSAndroid Build Coastguard Worker
93*6dbdd20aSAndroid Build Coastguard Worker const T* operator->() const { return &entry_->data; }
94*6dbdd20aSAndroid Build Coastguard Worker
95*6dbdd20aSAndroid Build Coastguard Worker private:
96*6dbdd20aSAndroid Build Coastguard Worker Interner::Entry* entry_;
97*6dbdd20aSAndroid Build Coastguard Worker };
98*6dbdd20aSAndroid Build Coastguard Worker
99*6dbdd20aSAndroid Build Coastguard Worker template <typename... U>
Intern(U...args)100*6dbdd20aSAndroid Build Coastguard Worker Interned Intern(U... args) {
101*6dbdd20aSAndroid Build Coastguard Worker Entry item(this, next_id, std::forward<U...>(args...));
102*6dbdd20aSAndroid Build Coastguard Worker auto it = entries_.find(item);
103*6dbdd20aSAndroid Build Coastguard Worker if (it == entries_.cend()) {
104*6dbdd20aSAndroid Build Coastguard Worker // This does not invalidate pointers to entries we hold in Interned. See
105*6dbdd20aSAndroid Build Coastguard Worker // https://timsong-cpp.github.io/cppwp/n3337/unord.req#8
106*6dbdd20aSAndroid Build Coastguard Worker auto it_and_inserted = entries_.emplace(std::move(item));
107*6dbdd20aSAndroid Build Coastguard Worker next_id++;
108*6dbdd20aSAndroid Build Coastguard Worker it = it_and_inserted.first;
109*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(it_and_inserted.second);
110*6dbdd20aSAndroid Build Coastguard Worker }
111*6dbdd20aSAndroid Build Coastguard Worker Entry& entry = const_cast<Entry&>(*it);
112*6dbdd20aSAndroid Build Coastguard Worker entry.ref_count++;
113*6dbdd20aSAndroid Build Coastguard Worker return Interned(&entry);
114*6dbdd20aSAndroid Build Coastguard Worker }
115*6dbdd20aSAndroid Build Coastguard Worker
~Interner()116*6dbdd20aSAndroid Build Coastguard Worker ~Interner() { PERFETTO_DCHECK(entries_.empty()); }
117*6dbdd20aSAndroid Build Coastguard Worker
entry_count_for_testing()118*6dbdd20aSAndroid Build Coastguard Worker size_t entry_count_for_testing() { return entries_.size(); }
119*6dbdd20aSAndroid Build Coastguard Worker
120*6dbdd20aSAndroid Build Coastguard Worker private:
Return(Entry * entry)121*6dbdd20aSAndroid Build Coastguard Worker void Return(Entry* entry) {
122*6dbdd20aSAndroid Build Coastguard Worker if (--entry->ref_count == 0)
123*6dbdd20aSAndroid Build Coastguard Worker entries_.erase(*entry);
124*6dbdd20aSAndroid Build Coastguard Worker }
125*6dbdd20aSAndroid Build Coastguard Worker
126*6dbdd20aSAndroid Build Coastguard Worker InternID next_id = 1;
127*6dbdd20aSAndroid Build Coastguard Worker std::unordered_set<Entry, typename Entry::Hash> entries_;
128*6dbdd20aSAndroid Build Coastguard Worker static_assert(sizeof(Interned) == sizeof(void*),
129*6dbdd20aSAndroid Build Coastguard Worker "interned things should be small");
130*6dbdd20aSAndroid Build Coastguard Worker };
131*6dbdd20aSAndroid Build Coastguard Worker
132*6dbdd20aSAndroid Build Coastguard Worker template <typename T>
swap(typename Interner<T>::Interned a,typename Interner<T>::Interned b)133*6dbdd20aSAndroid Build Coastguard Worker void swap(typename Interner<T>::Interned a, typename Interner<T>::Interned b) {
134*6dbdd20aSAndroid Build Coastguard Worker std::swap(a.entry_, b.entry_);
135*6dbdd20aSAndroid Build Coastguard Worker }
136*6dbdd20aSAndroid Build Coastguard Worker
137*6dbdd20aSAndroid Build Coastguard Worker template <typename T>
138*6dbdd20aSAndroid Build Coastguard Worker using Interned = typename Interner<T>::Interned;
139*6dbdd20aSAndroid Build Coastguard Worker
140*6dbdd20aSAndroid Build Coastguard Worker } // namespace profiling
141*6dbdd20aSAndroid Build Coastguard Worker } // namespace perfetto
142*6dbdd20aSAndroid Build Coastguard Worker
143*6dbdd20aSAndroid Build Coastguard Worker #endif // SRC_PROFILING_COMMON_INTERNER_H_
144