• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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