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