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