mCRL2
Loading...
Searching...
No Matches
bucket_list.h
Go to the documentation of this file.
1// Author(s): Maurice Laveaux
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8
9#ifndef MCRL2_UTILITIES_DETAIL_BUCKETLIST_H_
10#define MCRL2_UTILITIES_DETAIL_BUCKETLIST_H_
11
12#include <cassert>
13#include <atomic>
14#include <cstddef>
15#include <iterator>
16#include <memory>
17
18namespace mcrl2
19{
20namespace utilities
21{
22namespace detail
23{
24
25struct Sentinel{};
26
28static constexpr Sentinel EndIterator = {};
29
31template<typename Allocator, typename ...Args>
32inline auto allocate(Allocator& allocator, const Args&... args) -> decltype(allocator.allocate_args(args...))
33{
34 return allocator.allocate_args(args...);
35}
36
37template<typename Allocator, typename ...Args>
38inline auto allocate(Allocator& allocator, const Args&...) -> decltype(allocator.allocate(1))
39{
40 return allocator.allocate(1);
41}
42
46template<typename Key, typename Allocator>
48{
49public:
50
54 {
55 public:
56 node_base() = default;
57 node_base(node_base&& other) noexcept
58 {
59 m_next.store(other.m_next.load());
60 other.m_next = nullptr;
61 }
62
64 node_base* next() const noexcept { return m_next.load(std::memory_order_acquire); }
65
67 bool has_next() const noexcept { return m_next.load(std::memory_order_relaxed) != nullptr; }
68
70 void set_next(node_base* next) noexcept { m_next.store(next, std::memory_order_relaxed); }
71
73 bool exchange(node_base*& expected, node_base* value) { return m_next.compare_exchange_weak(expected, value, std::memory_order_acq_rel); }
74 protected:
75
77 std::atomic<node_base*> m_next = nullptr;
78 };
79
81 class node final : public node_base
82 {
83 public:
84
86 template<typename ...Args>
87 explicit node(Args&&... args)
88 : m_key(std::forward<Args>(args)...)
89 {}
90
92 Key& key() noexcept { return m_key; }
93 const Key& key() const noexcept { return m_key; }
94
96 operator Key&() { return m_key; }
97 private:
99 Key m_key;
100 };
101
103 template<bool Constant = true>
105 {
106 friend class bucket_list;
107
108 public:
109 using value_type = typename std::conditional<Constant, const Key, Key>::type;
110 using reference = typename std::conditional<Constant, const Key&, Key&>::type;
111 using pointer = typename std::conditional<Constant, const Key*, Key*>::type;
112 using difference_type = std::ptrdiff_t;
113 using iterator_category = std::forward_iterator_tag;
114
117
119 operator key_iterator<true>() const
120 {
122 }
123
125 {
126 m_current_node = reinterpret_cast<node*>(m_current_node->next());
127 return *this;
128 }
129
131 {
132 return m_current_node->key();
133 }
134
136 {
137 return &m_current_node->key();
138 }
139
140 bool operator== (const key_iterator& it) const noexcept
141 {
142 return !(*this != it);
143 }
144
145 bool operator!= (const key_iterator& it) const noexcept
146 {
147 return m_current_node != it.m_current_node;
148 }
149
150 bool operator== (Sentinel) const noexcept
151 {
152 return !(*this != Sentinel());
153 }
154
155 bool operator!= (Sentinel) const noexcept
156 {
157 return m_current_node != nullptr;
158 }
159
160 private:
161 explicit key_iterator(node_base* current)
162 : m_current_node(reinterpret_cast<node*>(current))
163 {}
164
165 node* get_node() noexcept
166 {
167 return m_current_node;
168 }
169
170 const node* get_node() const noexcept
171 {
172 return m_current_node;
173 }
174
176 };
177
178public:
182
184 using NodeAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<Bucket::node>;
185
187 Key& front() { return reinterpret_cast<node&>(*m_head.next()).key(); }
188 const Key& front() const { return reinterpret_cast<const node&>(*m_head.next()).key(); }
189
192 iterator end() { return iterator(); }
193
196
199 const_iterator end() const { return const_iterator(); }
200
202 const_iterator cend() const { return const_iterator(); }
203
205 const_iterator before_begin() const { return const_iterator(const_cast<node_base*>(&m_head)); }
206
208 bool empty() const { return !m_head.has_next(); }
209
211 void clear(NodeAllocator& allocator)
212 {
213 while(!empty())
214 {
215 erase_after(before_begin(), allocator);
216 }
217 }
218
220 template<typename ...Args>
221 void emplace_front(NodeAllocator& allocator, Args&& ...args)
222 {
223 // Allocate a new node.
224 node* new_node = allocate(allocator, std::forward<Args>(args)...);
225 std::allocator_traits<NodeAllocator>::construct(allocator, new_node, std::forward<Args>(args)...);
226
227 // Ensure that the previous front is set behind this node.
228 new_node->set_next(m_head.next());
229
230 // Change the head to the newly allocated node.
231 m_head.set_next(new_node);
232 }
233
237 template<typename ...Args,
238 typename Equals>
239 std::pair<iterator, bool> emplace_front_unique(NodeAllocator& allocator, const Equals& equals, Args&& ...args)
240 {
241 // Allocate a new node.
242 node* new_node = allocate(allocator, std::forward<Args>(args)...);
243 std::allocator_traits<NodeAllocator>::construct(allocator, new_node, std::forward<Args>(args)...);
244
245 // This was the first and last when we started the operation.
246 node_base* old_head = m_head.next();
247 iterator old_end;
248
249 // Change the head to the newly allocated node.
250 do
251 {
252 // Ensure that the previous front is set behind this node.
253 new_node->set_next(old_head);
254
255 // Check whether the new node is not already contained in the bucket list.
256 for (auto it = iterator(old_head); it != old_end; ++it)
257 {
258 if (equals(*it, std::forward<Args>(args)...))
259 {
260 // Clean up new node and leave bucket as is.
261 std::allocator_traits<NodeAllocator>::destroy(allocator, new_node);
262 std::allocator_traits<NodeAllocator>::deallocate(allocator, new_node, 1);
263 return std::make_pair(it, false);
264 }
265 }
266
267 // Next iteration we only have to look at newly inserted nodes.
268 old_end = iterator(old_head);
269 }
270 while(!m_head.exchange(old_head, new_node));
271
272 return std::make_pair(iterator(new_node), true);
273 }
274
277 {
278 node* current_node = const_cast<node*>(it.get_node());
279 assert(current_node->next() != nullptr); // Cannot erase after the last node.
280
281 // Keep track of the node that we should remove.
282 node* erased_node = reinterpret_cast<node*>(current_node->next());
283 node_base* next_node = erased_node->next();
284
285 // Update the next pointer of the current node.
286 current_node->set_next(next_node);
287
288 // Clean up the old node.
289 std::allocator_traits<NodeAllocator>::destroy(allocator, erased_node);
290 std::allocator_traits<NodeAllocator>::deallocate(allocator, erased_node, 1);
291
292 return iterator(next_node);
293 }
294
297 {
298 if (!other.empty())
299 {
300 assert(begin() != other.begin());
301 node* current_node = const_cast<node*>(pos.get_node());
302
303 // Increment the position now as we are going to change the current_node.
304 ++pos;
305
306 // Sets the current position to be followed by the other bucket list.
307 current_node->set_next(other.m_head.next());
308
309 node* after_pos_node = const_cast<node*>(pos.get_node());
310 if (after_pos_node != nullptr)
311 {
312 // Find the last node of the other list.
313 iterator before_end;
314 for (iterator it = other.begin(); it != other.end(); ++it)
315 {
316 before_end = it;
317 }
318
319 // Set the last element of other to the position after pos.
320 node* last = before_end.get_node();
321 last->set_next(after_pos_node);
322 }
323
324 // Make the other bucket empty, all elements belong to this bucket now.
325 other.m_head.set_next(nullptr);
326 }
327 }
328
332 {
333 if (!other.empty())
334 {
335 // Sets the current position to be followed by the other bucket list.
336 node* current_node = const_cast<node*>(pos.get_node());
337 node_base* next_node = current_node->next();
338
339 // Make the other head node the start of our bucket after pos.
340 node_base* head_node = other.begin().get_node();
341 current_node->set_next(head_node);
342
343 // Make the next position the position after the head, but keep track of the current next node.
344 node_base* next_head_node = head_node->next();
345 head_node->set_next(next_node);
346
347 // Make the other head point to the next element.
348 other.m_head.set_next(next_head_node);
349 }
350 }
351
352private:
353
356 {
357 node_base* element = &m_head;
358 while(element->has_next())
359 {
360 if (node == element->next())
361 {
362 return true;
363 }
364
365 element = element->next();
366 }
367
368 return false;
369 }
370
373};
374
375} // namespace detail
376} // namespace utilities
377} // namespace mcrl2
378
379#endif // MCRL2_UTILITIES_DETAIL_BUCKETLIST_H_
Iterator over all keys in a bucket list.
bool operator!=(const key_iterator &it) const noexcept
typename std::conditional< Constant, const Key *, Key * >::type pointer
bool operator==(const key_iterator &it) const noexcept
typename std::conditional< Constant, const Key, Key >::type value_type
typename std::conditional< Constant, const Key &, Key & >::type reference
The nodes of the bucket list without carrying any additional informations. Used to make no different ...
Definition bucket_list.h:54
std::atomic< node_base * > m_next
Pointer to the next node.
Definition bucket_list.h:77
void set_next(node_base *next) noexcept
Set the next pointer to the given next pointer.
Definition bucket_list.h:70
bool exchange(node_base *&expected, node_base *value)
Definition bucket_list.h:73
The nodes of the bucket list.
Definition bucket_list.h:82
node(Args &&... args)
Constructs a key by using the given arguments.
Definition bucket_list.h:87
const Key & key() const noexcept
Definition bucket_list.h:93
This essentially implements the std::forward_list, with the difference that it does not own the nodes...
Definition bucket_list.h:48
void splice_after(const_iterator pos, bucket_list &other)
Moves the elements from other into this bucket after the given position.
iterator erase_after(NodeAllocator &allocator, const_iterator it)
Removes the element after the given iterator from the list. The returned iterator.
void clear(NodeAllocator &allocator)
Empties the bucket list.
const_iterator before_begin() const
std::pair< iterator, bool > emplace_front_unique(NodeAllocator &allocator, const Equals &equals, Args &&...args)
Constructs an element using the allocator with the given arguments and insert it in the front of the ...
void splice_front(const_iterator pos, bucket_list &other)
Moves the first node from the given bucket into this bucket after the given position.
typename std::allocator_traits< Allocator >::template rebind_alloc< Bucket::node > NodeAllocator
Rebind the passed to allocator to a bucket list node allocator.
node_base m_head
The first node in the bucket list.
void emplace_front(NodeAllocator &allocator, Args &&...args)
Constructs an element using the allocator with the given arguments and insert it in the front.
static constexpr Sentinel EndIterator
A end of the iterator sentinel.
Definition bucket_list.h:28
auto allocate(Allocator &allocator, const Args &... args) -> decltype(allocator.allocate_args(args...))
A compile time check for allocate_args in the given allocator, calls allocate(1) otherwise.
Definition bucket_list.h:32
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
Definition indexed_set.h:72
STL namespace.