Fcitx
intrusivelist.h
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_UTILS_INSTRUSIVELIST_H_
8 #define _FCITX_UTILS_INSTRUSIVELIST_H_
9 
10 #include <cassert>
11 #include <cstddef>
12 #include <iterator>
13 #include <type_traits>
14 #include <utility>
15 #include <fcitx-utils/macros.h>
16 #include <fcitx-utils/misc.h>
17 
18 namespace fcitx {
19 
20 class IntrusiveListBase;
21 
23  friend class IntrusiveListBase;
24 
25 public:
26  IntrusiveListNode() = default;
27  IntrusiveListNode(const IntrusiveListNode &) = delete;
28  virtual ~IntrusiveListNode() { remove(); }
29 
30  bool isInList() const { return !!list_; }
31  bool isInList(const IntrusiveListBase *list) const { return list == list_; }
32  void remove();
33  IntrusiveListNode *prev() const { return prev_; }
34  IntrusiveListNode *next() const { return next_; }
35 
36 private:
37  IntrusiveListBase *list_ = nullptr;
38  IntrusiveListNode *prev_ = nullptr;
39  IntrusiveListNode *next_ = nullptr;
40 };
41 
43  friend class IntrusiveListNode;
44 
45 protected:
46  IntrusiveListBase() noexcept { root_.prev_ = root_.next_ = &root_; }
47  IntrusiveListBase(IntrusiveListBase &&other) noexcept
48  : IntrusiveListBase() {
49  operator=(std::forward<IntrusiveListBase>(other));
50  }
51 
52  virtual ~IntrusiveListBase() { removeAll(); }
53 
54  IntrusiveListBase &operator=(IntrusiveListBase &&other) noexcept {
55  using std::swap;
56  // no need to swap empty list.
57  if (size_ == 0 && other.size_ == 0) {
58  return *this;
59  }
60 
61  // clear current one.
62  removeAll();
63  while (other.size_) {
64  auto *node = other.root_.prev_;
65  // pop_back
66  other.remove(other.root_.prev_);
67  // push_front
68  prepend(node, root_.next_);
69  }
70 
71  return *this;
72  }
73 
74  void insertBetween(IntrusiveListNode *add, IntrusiveListNode *prev,
75  IntrusiveListNode *next) noexcept {
76  next->prev_ = add;
77  prev->next_ = add;
78  add->next_ = next;
79  add->prev_ = prev;
80  add->list_ = this;
81  size_++;
82  }
83 
84  void append(IntrusiveListNode *add, IntrusiveListNode *pos) noexcept {
85  add->remove();
86  return insertBetween(add, pos, pos->next_);
87  }
88 
89  void prepend(IntrusiveListNode *add, IntrusiveListNode *pos) noexcept {
90  add->remove();
91  return insertBetween(add, pos->prev_, pos);
92  }
93 
94  void remove(IntrusiveListNode *pos) noexcept {
95  auto *next_ = pos->next_;
96  auto *prev_ = pos->prev_;
97  prev_->next_ = next_;
98  next_->prev_ = prev_;
99 
100  pos->next_ = nullptr;
101  pos->prev_ = nullptr;
102  pos->list_ = nullptr;
103 
104  size_--;
105  }
106 
107  void removeAll() {
108  // remove everything from list, since we didn't own anything, then we
109  // are good.
110  while (size_) {
111  remove(root_.prev_);
112  }
113  }
114 
115  IntrusiveListNode root_;
116  std::size_t size_ = 0;
117 };
118 
119 inline void IntrusiveListNode::remove() {
120  if (list_) {
121  list_->remove(this);
122  }
123 }
124 
125 template <typename T>
127  static_assert(std::is_base_of<IntrusiveListNode, T>::value,
128  "T must be a descendant of IntrusiveListNode");
129 
130  static IntrusiveListNode &toNode(T &value) noexcept {
131  return *static_cast<IntrusiveListNode *>(&value);
132  }
133 
134  static T &toValue(IntrusiveListNode &node) noexcept {
135  return *static_cast<T *>(&node);
136  }
137 
138  static const IntrusiveListNode &toNode(const T &value) noexcept {
139  return *static_cast<const IntrusiveListNode *>(&value);
140  }
141 
142  static const T &toValue(const IntrusiveListNode &node) noexcept {
143  return *static_cast<const T *>(&node);
144  }
145 };
146 
147 template <typename T, IntrusiveListNode T::*ptrToNode>
149  static IntrusiveListNode &toNode(T &value) noexcept {
150  return value.*ptrToNode;
151  }
152 
153  static T &toValue(IntrusiveListNode &node) noexcept {
154  return *parentFromMember(&node, ptrToNode);
155  }
156 
157  static const IntrusiveListNode &toNode(const T &value) noexcept {
158  return value.*ptrToNode;
159  }
160 
161  static const T &toValue(const IntrusiveListNode &node) noexcept {
162  return *parentFromMember(&node, ptrToNode);
163  }
164 };
165 
166 template <typename T, typename NodeGetter>
168 
169 template <typename T, typename NodeGetter, bool isConst>
172  using node_ptr = std::conditional_t<isConst, const IntrusiveListNode *,
173  IntrusiveListNode *>;
174  struct enabler {};
175 
176 public:
177  using iterator_category = std::bidirectional_iterator_tag;
178  using value_type = T;
179  using difference_type = std::ptrdiff_t;
180  using reference =
181  std::conditional_t<isConst, typename list_type::const_reference,
182  typename list_type::reference>;
183  using pointer =
184  std::conditional_t<isConst, typename list_type::const_pointer,
185  typename list_type::pointer>;
186 
187  IntrusiveListIterator() : node(nullptr), nodeGetter(nullptr) {}
188  IntrusiveListIterator(node_ptr node_, const NodeGetter &nodeGetter_)
189  : node(node_), nodeGetter(&nodeGetter_) {}
190 
191  // Enable non-const to const conversion.
192  template <bool fromConst>
195  std::enable_if_t<isConst && !fromConst, enabler> /*unused*/ = enabler())
196  : IntrusiveListIterator(other.pointed_node(), other.get_nodeGetter()) {}
197 
198  FCITX_INLINE_DEFINE_DEFAULT_DTOR_AND_COPY(IntrusiveListIterator)
199 
200  bool operator==(const IntrusiveListIterator &other) const noexcept {
201  return node == other.node;
202  }
203  bool operator!=(const IntrusiveListIterator &other) const noexcept {
204  return !operator==(other);
205  }
206  IntrusiveListIterator &operator++() {
207  node = node->next();
208  return *this;
209  }
210 
211  IntrusiveListIterator operator++(int) {
212  auto *old = node;
213  ++(*this);
214  return {old, *nodeGetter};
215  }
216 
217  reference operator*() { return nodeGetter->toValue(*node); }
218 
219  pointer operator->() { return &nodeGetter->toValue(*node); }
220 
221  node_ptr pointed_node() const { return node; }
222 
223  const NodeGetter &get_nodeGetter() const { return *nodeGetter; }
224 
225 private:
226  node_ptr node;
227  const NodeGetter *nodeGetter;
228 };
229 
230 template <typename T, typename NodeGetter = IntrusiveListTrivialNodeGetter<T>>
231 class IntrusiveList : public IntrusiveListBase {
232 public:
233  using value_type = T;
234  using pointer = value_type *;
235  using const_pointer = const value_type *;
236  using reference = value_type &;
237  using const_reference = const value_type &;
239  using const_iterator = IntrusiveListIterator<T, NodeGetter, true>;
240  using reverse_iterator = std::reverse_iterator<iterator>;
241  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
242  using size_type = std::size_t;
243 
244  IntrusiveList(NodeGetter nodeGetter_ = NodeGetter())
245  : nodeGetter(nodeGetter_) {}
246 
247  FCITX_INLINE_DEFINE_DEFAULT_DTOR_AND_MOVE(IntrusiveList)
248 
249  iterator begin() { return {root_.next(), nodeGetter}; }
250  iterator end() { return {&root_, nodeGetter}; }
251 
252  const_iterator begin() const {
253  return const_iterator{root_.next(), nodeGetter};
254  }
255 
256  const_iterator end() const { return const_iterator{&root_, nodeGetter}; }
257 
258  const_iterator cbegin() const { return {root_.next(), nodeGetter}; }
259 
260  const_iterator cend() const { return {&root_, nodeGetter}; }
261 
262  reference front() { return *begin(); }
263 
264  const_reference front() const { return *cbegin(); }
265 
266  reference back() { return *iterator{root_.prev(), nodeGetter}; }
267 
268  const_reference back() const {
269  return *const_iterator{root_.prev(), nodeGetter};
270  }
271 
272  iterator iterator_to(reference value) {
273  return iterator(&nodeGetter.toNode(value), nodeGetter);
274  }
275 
276  const_iterator iterator_to(const_reference value) const {
277  return const_iterator(&nodeGetter.toNode(value), nodeGetter);
278  }
279 
280  bool isInList(const_reference value) const {
281  return nodeGetter.toNode(value).isInList(this);
282  }
283 
284  void push_back(reference value) {
285  auto &node = nodeGetter.toNode(value);
286  prepend(&node, &root_);
287  }
288 
289  void pop_back() { remove(root_.prev()); }
290 
291  void push_front(reference value) { insert(begin(), value); }
292 
293  void pop_front() { erase(begin()); }
294 
295  iterator erase(iterator pos) {
296  auto node = pos.pointed_node();
297  auto next = node->next();
298  remove(node);
299  return {next, nodeGetter};
300  }
301 
302  iterator erase(iterator start, iterator end) {
303  if (start == end) {
304  return {start->pointed_node(), nodeGetter};
305  }
306 
307  iterator iter;
308  while ((iter = erase(start)) != end) {
309  }
310  return iter;
311  }
312 
313  size_type size() const { return size_; }
314 
315  bool empty() const { return root_.next() == &root_; }
316 
317  iterator insert(iterator pos, reference value) {
318  // insert value before pos.
319  prepend(&nodeGetter.toNode(value), pos.pointed_node());
320  return {pos.pointed_node()->prev(), nodeGetter};
321  }
322 
323 private:
324  NodeGetter nodeGetter;
325 };
326 
327 template <typename T>
329 } // namespace fcitx
330 
331 #endif // _FCITX_UTILS_INSTRUSIVELIST_H_
Definition: action.cpp:17