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
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 mim
96
97template<class T> struct std::hash<mim::PooledSet<T>> {
98 constexpr size_t operator()(mim::PooledSet<T> set) const { return std::hash<uintptr_t>()((uintptr_t)set.data_); }
99};
100
101namespace mim {
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 /// @note All operations do **not** modify the input set(s); they create a **new** PooledSet.
123 ///@{
124 /// Create a PooledSet wih a single @p elem%ent: @f$\{elem\}@f$.
125 [[nodiscard]] PooledSet<T> singleton(T elem) {
126 auto [data, state] = allocate(1);
127 data->elems[0] = elem;
128 return unify(data, state);
129 }
130
131 /// Yields @f$a \cup b@f$.
133 if (a == b || !b) return a;
134 if (!a) return b;
135
136 auto size = a.size() + b.size(); // max space needed - may be less; see actual_size below
137 auto [data, state] = allocate(size);
138
139 auto di = data->elems;
140 auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end();
141
142 *di = (*ai)->gid() < (*bi)->gid() ? *ai++ : *bi++; // di points to the last valid elem, so init first one
143
144 while (ai != ae && bi != be)
145 if ((*ai)->gid() < (*bi)->gid())
146 copy_if_unique_and_inc(di, ai);
147 else
148 copy_if_unique_and_inc(di, bi);
149
150 // copy the rest of a/b (if needed):
151 while (ai != ae) copy_if_unique_and_inc(di, ai);
152 while (bi != be) copy_if_unique_and_inc(di, bi);
153
154 auto actual_size = std::distance(data->elems, di + 1);
155 data->size = actual_size; // correct size
156 return unify(data, state, size - actual_size);
157 }
158
159 /// Yields @f$a \cup \{elem\}@f$.
160 [[nodiscard]] PooledSet<T> insert(PooledSet<T> a, const T& elem) { return merge(a, singleton(elem)); }
161
162 /// Yields @f$a \setminus b@f$.
163 [[nodiscard]] PooledSet<T> erase(PooledSet<T> set, const T& elem) {
164 if (!set) return set;
165 auto i = binary_find(set.begin(), set.end(), elem, GIDLt<T>());
166 if (i == set.end()) return set;
167
168 auto size = set.size() - 1;
169 if (size == 0) return PooledSet<T>(); // empty Set is not hashed
170
171 auto [data, state] = allocate(size);
172 std::copy(i + 1, set.end(), std::copy(set.begin(), i, data->elems)); // copy over, skip i
173 return unify(data, state);
174 }
175
176 /// Is @f$a \cup b \neq \emptyset@f$?
177 [[nodiscard]] bool has_intersection(PooledSet<T> a, PooledSet<T> b) {
178 if (a == b) return true;
179 if (!a || !b) return false;
180
181 for (auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end(); ai != ae && bi != be;) {
182 if (*ai == *bi) return true;
183
184 if ((*ai)->gid() < (*bi)->gid())
185 ++ai;
186 else
187 ++bi;
188 }
189
190 return false;
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:105
Pool(Pool &&other)
Definition pool.h:115
PooledSet< T > singleton(T elem)
Definition pool.h:125
PooledSet< T > merge(PooledSet< T > a, PooledSet< T > b)
Yields .
Definition pool.h:132
PooledSet< T > erase(PooledSet< T > set, const T &elem)
Yields .
Definition pool.h:163
bool has_intersection(PooledSet< T > a, PooledSet< T > b)
Is ?
Definition pool.h:177
friend void swap(Pool &p1, Pool &p2) noexcept
Definition pool.h:194
Pool(const Pool &)=delete
PooledSet< T > insert(PooledSet< T > a, const T &elem)
Yields .
Definition pool.h:160
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:80
constexpr bool operator==(PooledSet< T > other) const noexcept
Definition pool.h:71
constexpr bool operator!=(PooledSet< T > other) const noexcept
Definition pool.h:72
constexpr auto crbegin() const noexcept
Definition pool.h:83
constexpr auto end() const noexcept
Definition pool.h:78
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:84
constexpr PooledSet() noexcept=default
constexpr bool contains(const T &elem) const
Definition pool.h:66
constexpr auto begin() const noexcept
Definition pool.h:77
constexpr auto rbegin() const noexcept
Definition pool.h:81
friend H AbslHashValue(H h, PooledSet< T > set)
Definition pool.h:90
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:82
constexpr auto cbegin() const noexcept
Definition pool.h:79
constexpr const T & front() const
Definition pool.h:64
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