xref: /aosp_15_r20/external/perfetto/src/profiling/common/interner.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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