Coverage Report

Created: 2026-01-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/util/ilist.h
Line
Count
Source
1
// Copyright (c) 2017 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#ifndef SOURCE_UTIL_ILIST_H_
16
#define SOURCE_UTIL_ILIST_H_
17
18
#include <cassert>
19
#include <memory>
20
#include <type_traits>
21
#include <vector>
22
23
#include "source/util/ilist_node.h"
24
25
namespace spvtools {
26
namespace utils {
27
28
// An IntrusiveList is a generic implementation of a doubly-linked list.  The
29
// intended convention for using this container is:
30
//
31
//      class Node : public IntrusiveNodeBase<Node> {
32
//        // Note that "Node", the class being defined is the template.
33
//        // Must have a default constructor accessible to List.
34
//        // Add whatever data is needed in the node
35
//      };
36
//
37
//      using List = IntrusiveList<Node>;
38
//
39
// You can also inherit from IntrusiveList instead of a typedef if you want to
40
// add more functionality.
41
//
42
// The condition on the template for IntrusiveNodeBase is there to add some type
43
// checking to the container.  The compiler will still allow inserting elements
44
// of type IntrusiveNodeBase<Node>, but that would be an error. This assumption
45
// allows NextNode and PreviousNode to return pointers to Node, and casting will
46
// not be required by the user.
47
48
template <class NodeType>
49
class IntrusiveList {
50
 public:
51
  static_assert(
52
      std::is_base_of<IntrusiveNodeBase<NodeType>, NodeType>::value,
53
      "The type from the node must be derived from IntrusiveNodeBase, with "
54
      "itself in the template.");
55
56
  // Creates an empty list.
57
  inline IntrusiveList();
58
59
  // Moves the contents of the given list to the list being constructed.
60
  IntrusiveList(IntrusiveList&&);
61
62
  // Destroys the list.  Note that the elements of the list will not be deleted,
63
  // but they will be removed from the list.
64
  virtual ~IntrusiveList();
65
66
  // Moves all of the elements in the list on the RHS to the list on the LHS.
67
  IntrusiveList& operator=(IntrusiveList&&);
68
69
  // Basetype for iterators so an IntrusiveList can be traversed like STL
70
  // containers.
71
  template <class T>
72
  class iterator_template {
73
   public:
74
417M
    iterator_template(const iterator_template& i) : node_(i.node_) {}
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::iterator_template(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&)
Line
Count
Source
74
389M
    iterator_template(const iterator_template& i) : node_(i.node_) {}
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::iterator_template(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&)
Line
Count
Source
74
27.1M
    iterator_template(const iterator_template& i) : node_(i.node_) {}
75
76
243M
    iterator_template& operator++() {
77
243M
      node_ = node_->next_node_;
78
243M
      return *this;
79
243M
    }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator++()
Line
Count
Source
76
229M
    iterator_template& operator++() {
77
229M
      node_ = node_->next_node_;
78
229M
      return *this;
79
229M
    }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator++()
Line
Count
Source
76
13.9M
    iterator_template& operator++() {
77
13.9M
      node_ = node_->next_node_;
78
13.9M
      return *this;
79
13.9M
    }
80
81
184M
    iterator_template& operator--() {
82
184M
      node_ = node_->previous_node_;
83
184M
      return *this;
84
184M
    }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator--()
Line
Count
Source
81
86.0M
    iterator_template& operator--() {
82
86.0M
      node_ = node_->previous_node_;
83
86.0M
      return *this;
84
86.0M
    }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator--()
Line
Count
Source
81
98.2M
    iterator_template& operator--() {
82
98.2M
      node_ = node_->previous_node_;
83
98.2M
      return *this;
84
98.2M
    }
85
86
45.8M
    iterator_template& operator=(const iterator_template& i) {
87
45.8M
      node_ = i.node_;
88
45.8M
      return *this;
89
45.8M
    }
90
91
179M
    T& operator*() const { return *node_; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator*() const
Line
Count
Source
91
157M
    T& operator*() const { return *node_; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator*() const
Line
Count
Source
91
22.0M
    T& operator*() const { return *node_; }
92
402M
    T* operator->() const { return node_; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator->() const
Line
Count
Source
92
335M
    T* operator->() const { return node_; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator->() const
Line
Count
Source
92
66.2M
    T* operator->() const { return node_; }
93
94
    friend inline bool operator==(const iterator_template& lhs,
95
378M
                                  const iterator_template& rhs) {
96
378M
      return lhs.node_ == rhs.node_;
97
378M
    }
spvtools::utils::operator==(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&)
Line
Count
Source
95
306M
                                  const iterator_template& rhs) {
96
306M
      return lhs.node_ == rhs.node_;
97
306M
    }
spvtools::utils::operator==(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&)
Line
Count
Source
95
72.3M
                                  const iterator_template& rhs) {
96
72.3M
      return lhs.node_ == rhs.node_;
97
72.3M
    }
98
    friend inline bool operator!=(const iterator_template& lhs,
99
375M
                                  const iterator_template& rhs) {
100
375M
      return !(lhs == rhs);
101
375M
    }
spvtools::utils::operator!=(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&)
Line
Count
Source
99
303M
                                  const iterator_template& rhs) {
100
303M
      return !(lhs == rhs);
101
303M
    }
spvtools::utils::operator!=(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&)
Line
Count
Source
99
72.3M
                                  const iterator_template& rhs) {
100
72.3M
      return !(lhs == rhs);
101
72.3M
    }
102
103
    // Moves the nodes in |list| to the list that |this| points to.  The
104
    // positions of the nodes will be immediately before the element pointed to
105
    // by the iterator.  The return value will be an iterator pointing to the
106
    // first of the newly inserted elements.
107
568k
    iterator_template MoveBefore(IntrusiveList* list) {
108
568k
      if (list->empty()) return *this;
109
110
568k
      NodeType* first_node = list->sentinel_.next_node_;
111
568k
      NodeType* last_node = list->sentinel_.previous_node_;
112
113
568k
      this->node_->previous_node_->next_node_ = first_node;
114
568k
      first_node->previous_node_ = this->node_->previous_node_;
115
116
568k
      last_node->next_node_ = this->node_;
117
568k
      this->node_->previous_node_ = last_node;
118
119
568k
      list->sentinel_.next_node_ = &list->sentinel_;
120
568k
      list->sentinel_.previous_node_ = &list->sentinel_;
121
122
568k
      return iterator(first_node);
123
568k
    }
124
125
    // Define standard iterator types needs so this class can be
126
    // used with <algorithms>.
127
    using iterator_category = std::bidirectional_iterator_tag;
128
    using difference_type = std::ptrdiff_t;
129
    using value_type = T;
130
    using pointer = T*;
131
    using const_pointer = const T*;
132
    using reference = T&;
133
    using const_reference = const T&;
134
    using size_type = size_t;
135
136
   protected:
137
    iterator_template() = delete;
138
432M
    inline iterator_template(T* node) { node_ = node; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::iterator_template(spvtools::opt::Instruction*)
Line
Count
Source
138
314M
    inline iterator_template(T* node) { node_ = node; }
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::iterator_template(spvtools::opt::Instruction const*)
Line
Count
Source
138
117M
    inline iterator_template(T* node) { node_ = node; }
139
    T* node_;
140
141
    friend IntrusiveList;
142
  };
143
144
  using iterator = iterator_template<NodeType>;
145
  using const_iterator = iterator_template<const NodeType>;
146
147
  // Various types of iterators for the start (begin) and one past the end (end)
148
  // of the list.
149
  //
150
  // Decrementing |end()| iterator will give and iterator pointing to the last
151
  // element in the list, if one exists.
152
  //
153
  // Incrementing |end()| iterator will give |begin()|.
154
  //
155
  // Decrementing |begin()| will give |end()|.
156
  //
157
  // TODO: Not marking these functions as noexcept because Visual Studio 2013
158
  // does not support it.  When we no longer care about that compiler, we should
159
  // mark these as noexcept.
160
  iterator begin();
161
  iterator end();
162
  const_iterator begin() const;
163
  const_iterator end() const;
164
  const_iterator cbegin() const;
165
  const_iterator cend() const;
166
167
  // Appends |node| to the end of the list.  If |node| is already in a list, it
168
  // will be removed from that list first.
169
  void push_back(NodeType* node);
170
171
  // Returns true if the list is empty.
172
  bool empty() const;
173
174
  // Makes the current list empty.
175
  inline void clear();
176
177
  // Returns references to the first or last element in the list.  It is an
178
  // error to call these functions on an empty list.
179
  NodeType& front();
180
  NodeType& back();
181
  const NodeType& front() const;
182
  const NodeType& back() const;
183
184
  // Transfers [|first|, |last|) from |other| into the list at |where|.
185
  //
186
  // If |other| is |this|, no change is made.
187
  void Splice(iterator where, IntrusiveList<NodeType>* other, iterator first,
188
              iterator last);
189
190
 protected:
191
  // Doing a deep copy of the list does not make sense if the list does not own
192
  // the data.  It is not clear who will own the newly created data.  Making
193
  // copies illegal for that reason.
194
  IntrusiveList(const IntrusiveList&) = delete;
195
  IntrusiveList& operator=(const IntrusiveList&) = delete;
196
197
  // This function will assert if it finds the list containing |node| is not in
198
  // a valid state.
199
  static void Check(NodeType* node);
200
201
  // A special node used to represent both the start and end of the list,
202
  // without being part of the list.
203
  NodeType sentinel_;
204
};
205
206
// Implementation of IntrusiveList
207
208
template <class NodeType>
209
2.64M
inline IntrusiveList<NodeType>::IntrusiveList() : sentinel_() {
210
2.64M
  sentinel_.next_node_ = &sentinel_;
211
2.64M
  sentinel_.previous_node_ = &sentinel_;
212
2.64M
  sentinel_.is_sentinel_ = true;
213
2.64M
}
214
215
template <class NodeType>
216
IntrusiveList<NodeType>::IntrusiveList(IntrusiveList&& list) : sentinel_() {
217
  sentinel_.next_node_ = &sentinel_;
218
  sentinel_.previous_node_ = &sentinel_;
219
  sentinel_.is_sentinel_ = true;
220
  list.sentinel_.ReplaceWith(&sentinel_);
221
}
222
223
template <class NodeType>
224
2.64M
IntrusiveList<NodeType>::~IntrusiveList() {
225
2.64M
  clear();
226
2.64M
}
227
228
template <class NodeType>
229
IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=(
230
0
    IntrusiveList<NodeType>&& list) {
231
0
  list.sentinel_.ReplaceWith(&sentinel_);
232
0
  return *this;
233
0
}
234
235
template <class NodeType>
236
inline typename IntrusiveList<NodeType>::iterator
237
80.1M
IntrusiveList<NodeType>::begin() {
238
80.1M
  return iterator(sentinel_.next_node_);
239
80.1M
}
240
241
template <class NodeType>
242
inline typename IntrusiveList<NodeType>::iterator
243
232M
IntrusiveList<NodeType>::end() {
244
232M
  return iterator(&sentinel_);
245
232M
}
246
247
template <class NodeType>
248
inline typename IntrusiveList<NodeType>::const_iterator
249
2.87M
IntrusiveList<NodeType>::begin() const {
250
2.87M
  return const_iterator(sentinel_.next_node_);
251
2.87M
}
252
253
template <class NodeType>
254
inline typename IntrusiveList<NodeType>::const_iterator
255
2.87M
IntrusiveList<NodeType>::end() const {
256
2.87M
  return const_iterator(&sentinel_);
257
2.87M
}
258
259
template <class NodeType>
260
inline typename IntrusiveList<NodeType>::const_iterator
261
55.4M
IntrusiveList<NodeType>::cbegin() const {
262
55.4M
  return const_iterator(sentinel_.next_node_);
263
55.4M
}
264
265
template <class NodeType>
266
inline typename IntrusiveList<NodeType>::const_iterator
267
56.5M
IntrusiveList<NodeType>::cend() const {
268
56.5M
  return const_iterator(&sentinel_);
269
56.5M
}
270
271
template <class NodeType>
272
30.0M
void IntrusiveList<NodeType>::push_back(NodeType* node) {
273
30.0M
  node->InsertBefore(&sentinel_);
274
30.0M
}
275
276
template <class NodeType>
277
168M
bool IntrusiveList<NodeType>::empty() const {
278
168M
  return sentinel_.NextNode() == nullptr;
279
168M
}
280
281
template <class NodeType>
282
2.64M
void IntrusiveList<NodeType>::clear() {
283
2.64M
  while (!empty()) {
284
0
    front().RemoveFromList();
285
0
  }
286
2.64M
}
287
288
template <class NodeType>
289
77.5M
NodeType& IntrusiveList<NodeType>::front() {
290
77.5M
  NodeType* node = sentinel_.NextNode();
291
77.5M
  assert(node != nullptr && "Can't get the front of an empty list.");
292
77.5M
  return *node;
293
77.5M
}
294
295
template <class NodeType>
296
94.5k
NodeType& IntrusiveList<NodeType>::back() {
297
94.5k
  NodeType* node = sentinel_.PreviousNode();
298
94.5k
  assert(node != nullptr && "Can't get the back of an empty list.");
299
94.5k
  return *node;
300
94.5k
}
301
302
template <class NodeType>
303
const NodeType& IntrusiveList<NodeType>::front() const {
304
  NodeType* node = sentinel_.NextNode();
305
  assert(node != nullptr && "Can't get the front of an empty list.");
306
  return *node;
307
}
308
309
template <class NodeType>
310
28.7M
const NodeType& IntrusiveList<NodeType>::back() const {
311
28.7M
  NodeType* node = sentinel_.PreviousNode();
312
28.7M
  assert(node != nullptr && "Can't get the back of an empty list.");
313
28.7M
  return *node;
314
28.7M
}
315
316
template <class NodeType>
317
void IntrusiveList<NodeType>::Splice(iterator where,
318
                                     IntrusiveList<NodeType>* other,
319
9.38k
                                     iterator first, iterator last) {
320
9.38k
  if (first == last) return;
321
9.38k
  if (other == this) return;
322
323
9.38k
  NodeType* first_prev = first.node_->previous_node_;
324
9.38k
  NodeType* where_next = where.node_->next_node_;
325
326
  // Attach first.
327
9.38k
  where.node_->next_node_ = first.node_;
328
9.38k
  first.node_->previous_node_ = where.node_;
329
330
  // Attach last.
331
9.38k
  where_next->previous_node_ = last.node_->previous_node_;
332
9.38k
  last.node_->previous_node_->next_node_ = where_next;
333
334
  // Fixup other.
335
9.38k
  first_prev->next_node_ = last.node_;
336
9.38k
  last.node_->previous_node_ = first_prev;
337
9.38k
}
338
339
template <class NodeType>
340
void IntrusiveList<NodeType>::Check(NodeType* start) {
341
  int sentinel_count = 0;
342
  NodeType* p = start;
343
  do {
344
    assert(p != nullptr);
345
    assert(p->next_node_->previous_node_ == p);
346
    assert(p->previous_node_->next_node_ == p);
347
    if (p->is_sentinel_) sentinel_count++;
348
    p = p->next_node_;
349
  } while (p != start);
350
  assert(sentinel_count == 1 && "List should have exactly 1 sentinel node.");
351
  (void)sentinel_count;
352
353
  p = start;
354
  do {
355
    assert(p != nullptr);
356
    assert(p->previous_node_->next_node_ == p);
357
    assert(p->next_node_->previous_node_ == p);
358
    if (p->is_sentinel_) sentinel_count++;
359
    p = p->previous_node_;
360
  } while (p != start);
361
}
362
363
}  // namespace utils
364
}  // namespace spvtools
365
366
#endif  // SOURCE_UTIL_ILIST_H_