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