Thorin 1.9.0
The Higher ORder INtermediate representation
Loading...
Searching...
No Matches
pool.h
Go to the documentation of this file.
1#pragma once
2
3#include <absl/container/flat_hash_set.h>
4#include <fe/arena.h>
5
6#include "thorin/util/util.h"
7
8// TODO move to fe
9
10namespace thorin {
11
12// get rid of clang warnings
13template<class T>
14inline static constexpr size_t SizeOf = sizeof(std::conditional_t<std::is_pointer_v<T>, uintptr_t, T>);
15
16template<class T> class Pool;
17
18/// Ordered set maintained in a consecutive buffer and unified in Pool.
19template<class T> class PooledSet {
20public:
21 struct Data {
22 constexpr Data() noexcept = default;
23 constexpr Data(size_t size) noexcept
24 : size(size) {}
25
26 size_t size = 0;
27 T elems[];
28
29 struct Equal {
30 constexpr bool operator()(const Data* d1, const Data* d2) const {
31 bool res = d1->size == d2->size;
32 for (size_t i = 0, e = d1->size; res && i != e; ++i) res &= d1->elems[i] == d2->elems[i];
33 return res;
34 }
35 };
36
37 template<class H> friend constexpr H AbslHashValue(H h, const Data* d) {
38 if (!d) return H::combine(std::move(h), 0);
39 return H::combine_contiguous(std::move(h), d->elems, d->size);
40 }
41 };
42
43 static_assert(sizeof(Data) == sizeof(size_t), "Data.elems should be 0");
44
45private:
46 /// @name Construction & Destruction
47 ///@{
48 constexpr PooledSet(const Data* data) noexcept
49 : data_(data) {}
50
51public:
52 constexpr PooledSet() noexcept = default;
53 constexpr PooledSet(const PooledSet&) noexcept = default;
54 constexpr PooledSet& operator=(const PooledSet&) noexcept = default;
55 constexpr void clear() noexcept { data_ = nullptr; }
56 ///@}
57
58 /// @name Getters
59 ///@{
60 constexpr explicit operator bool() const noexcept { return data_; } ///< Is not empty?
61 constexpr bool empty() const noexcept { return data_ == nullptr; }
62 constexpr size_t size() const noexcept { return empty() ? 0 : data_->size; }
63 constexpr const T& operator[](size_t i) const { return data_->elems[i]; }
64 constexpr const T& front() const { return (*this)[0]; }
65 constexpr const T* elems() const { return data_ ? data_->elems : nullptr; }
66 constexpr bool contains(const T& elem) const { return binary_find(begin(), end(), elem, GIDLt<T>()) != end(); }
67 ///@}
68
69 /// @name Comparisons
70 ///@{
71 constexpr bool operator==(PooledSet<T> other) const noexcept { return this->data_ == other.data_; }
72 constexpr bool operator!=(PooledSet<T> other) const noexcept { return this->data_ != other.data_; }
73 ///@}
74
75 /// @name Iterators
76 ///@{
77 constexpr auto begin() const noexcept { return elems(); }
78 constexpr auto end() const noexcept { return elems() + size(); } // note: you can add 0 to nullptr
79 constexpr auto cbegin() const noexcept { return elems(); }
80 constexpr auto cend() const noexcept { return end(); }
81 constexpr auto rbegin() const noexcept { return std::reverse_iterator(end()); }
82 constexpr auto rend() const noexcept { return std::reverse_iterator(begin()); }
83 constexpr auto crbegin() const noexcept { return rbegin(); }
84 constexpr auto crend() const noexcept { return rend(); }
85 ///@}
86
87private:
88 const Data* data_ = nullptr;
89
90 template<class H> friend H AbslHashValue(H h, PooledSet<T> set) { return H::combine(std::move(h), set.data_); }
91 friend class Pool<T>;
92};
93
94#ifndef DOXYGEN
95} // namespace thorin
96
97template<class T> struct std::hash<thorin::PooledSet<T>> {
98 constexpr size_t operator()(thorin::PooledSet<T> set) const { return std::hash<uintptr_t>()((uintptr_t)set.data_); }
99};
100
101namespace thorin {
102#endif
103
104/// Maintains PooledSet%s within a `fe::Arena` and unifies them in a `absl::flat_hash_set`.
105template<class T> class Pool {
106 using Data = typename PooledSet<T>::Data;
107
108public:
109 /// @name Construction & Destruction
110 ///@{
111 Pool& operator=(const Pool&) = delete;
112
113 Pool() = default;
114 Pool(const Pool&) = delete;
115 Pool(Pool&& other)
116 : Pool() {
117 swap(*this, other);
118 }
119 ///@}
120
121 /// @name Set Operations
122 ///@{
123 /// @note All operations do **not** modify the input set(s); they create a **new** PooledSet.
124
125 /// Create a PooledSet wih a single @p elem%ent: @f$\{elem\}@f$.
126 [[nodiscard]] PooledSet<T> singleton(T elem) {
127 auto [data, state] = allocate(1);
128 data->elems[0] = elem;
129 return unify(data, state);
130 }
131
132 /// Yields @f$a \cup b@f$.
134 if (a == b || !b) return a;
135 if (!a) return b;
136
137 auto size = a.size() + b.size(); // max space needed - may be less; see actual_size below
138 auto [data, state] = allocate(size);
139
140 auto di = data->elems;
141 auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end();
142
143 *di = (*ai)->gid() < (*bi)->gid() ? *ai++ : *bi++; // di points to the last valid elem, so init first one
144
145 while (ai != ae && bi != be)
146 if ((*ai)->gid() < (*bi)->gid())
147 copy_if_unique_and_inc(di, ai);
148 else
149 copy_if_unique_and_inc(di, bi);
150
151 // copy the rest of a/b (if needed):
152 while (ai != ae) copy_if_unique_and_inc(di, ai);
153 while (bi != be) copy_if_unique_and_inc(di, bi);
154
155 auto actual_size = std::distance(data->elems, di + 1);
156 data->size = actual_size; // correct size
157 return unify(data, state, size - actual_size);
158 }
159
160 /// Yields @f$a \cup \{elem\}@f$.
161 [[nodiscard]] PooledSet<T> insert(PooledSet<T> a, const T& elem) { return merge(a, singleton(elem)); }
162
163 /// Yields @f$a \setminus b@f$.
164 [[nodiscard]] PooledSet<T> erase(PooledSet<T> set, const T& elem) {
165 if (!set) return set;
166 auto i = binary_find(set.begin(), set.end(), elem, GIDLt<T>());
167 if (i == set.end()) return set;
168
169 auto size = set.size() - 1;
170 if (size == 0) return PooledSet<T>(); // empty Set is not hashed
171
172 auto [data, state] = allocate(size);
173 std::copy(i + 1, set.end(), std::copy(set.begin(), i, data->elems)); // copy over, skip i
174 return unify(data, state);
175 }
176
177 /// Is @f$a \cup b \neq \emptyset@f$?
178 [[nodiscard]] bool has_intersection(PooledSet<T> a, PooledSet<T> b) {
179 if (a == b) return true;
180 if (!a || !b) return false;
181
182 for (auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end(); ai != ae && bi != be;) {
183 if (*ai == *bi) return true;
184
185 if ((*ai)->gid() < (*bi)->gid())
186 ++ai;
187 else
188 ++bi;
189 }
190
191 return false;
192 }
193 ///@}
194
195 friend void swap(Pool& p1, Pool& p2) {
196 using std::swap;
197 swap(p1.arena_, p2.arena_);
198 swap(p1.pool_, p2.pool_);
199 }
200
201private:
202 std::pair<Data*, fe::Arena::State> allocate(size_t size) {
203 auto bytes = sizeof(Data) + size * SizeOf<T>;
204 auto state = arena_.state();
205 auto buf = arena_.allocate(bytes);
206 auto data = new (buf) Data(size);
207 return {data, state};
208 }
209
210 PooledSet<T> unify(Data* data, fe::Arena::State state, size_t excess = 0) {
211 auto [i, ins] = pool_.emplace(data);
212 if (ins) {
213 arena_.deallocate(excess * SizeOf<T>); // release excess memory
214 return PooledSet<T>(data);
215 }
216
217 arena_.deallocate(state);
218 return PooledSet<T>(*i);
219 }
220
221 void copy_if_unique_and_inc(T*& i, const T*& ai) {
222 if ((*i)->gid() != (*ai)->gid()) *++i = *ai;
223 ++ai;
224 }
225
226 fe::Arena arena_;
227 absl::flat_hash_set<const Data*, absl::Hash<const Data*>, typename Data::Equal> pool_;
228};
229
230} // namespace thorin
Maintains PooledSets within a fe::Arena and unifies them in a absl::flat_hash_set.
Definition pool.h:105
Pool & operator=(const Pool &)=delete
friend void swap(Pool &p1, Pool &p2)
Definition pool.h:195
Pool(const Pool &)=delete
PooledSet< T > erase(PooledSet< T > set, const T &elem)
Yields .
Definition pool.h:164
PooledSet< T > insert(PooledSet< T > a, const T &elem)
Yields .
Definition pool.h:161
PooledSet< T > singleton(T elem)
Create a PooledSet wih a single element: .
Definition pool.h:126
bool has_intersection(PooledSet< T > a, PooledSet< T > b)
Is ?
Definition pool.h:178
Pool(Pool &&other)
Definition pool.h:115
Pool()=default
PooledSet< T > merge(PooledSet< T > a, PooledSet< T > b)
Yields .
Definition pool.h:133
Ordered set maintained in a consecutive buffer and unified in Pool.
Definition pool.h:19
constexpr auto rbegin() const noexcept
Definition pool.h:81
constexpr const T * elems() const
Definition pool.h:65
constexpr auto cbegin() const noexcept
Definition pool.h:79
constexpr void clear() noexcept
Definition pool.h:55
constexpr auto end() const noexcept
Definition pool.h:78
constexpr auto rend() const noexcept
Definition pool.h:82
constexpr PooledSet() noexcept=default
friend H AbslHashValue(H h, PooledSet< T > set)
Definition pool.h:90
constexpr bool contains(const T &elem) const
Definition pool.h:66
constexpr const T & operator[](size_t i) const
Definition pool.h:63
constexpr bool empty() const noexcept
Definition pool.h:61
constexpr const T & front() const
Definition pool.h:64
constexpr auto cend() const noexcept
Definition pool.h:80
constexpr bool operator!=(PooledSet< T > other) const noexcept
Definition pool.h:72
constexpr bool operator==(PooledSet< T > other) const noexcept
Definition pool.h:71
constexpr auto crbegin() const noexcept
Definition pool.h:83
constexpr auto begin() const noexcept
Definition pool.h:77
constexpr auto crend() const noexcept
Definition pool.h:84
constexpr size_t size() const noexcept
Definition pool.h:62
Definition cfg.h:11
I binary_find(I begin, I end, T val, Cmp cmp)
Definition util.h:58
static constexpr size_t SizeOf
Definition pool.h:14
constexpr bool operator()(const Data *d1, const Data *d2) const
Definition pool.h:30
friend constexpr H AbslHashValue(H h, const Data *d)
Definition pool.h:37
constexpr Data() noexcept=default