Coverage Report

Created: 2026-03-31 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/util/ilist_node.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_NODE_H_
16
#define SOURCE_UTIL_ILIST_NODE_H_
17
18
#include <cassert>
19
20
namespace spvtools {
21
namespace utils {
22
23
template <class NodeType>
24
class IntrusiveList;
25
26
// IntrusiveNodeBase is the base class for nodes in an IntrusiveList.
27
// See the comments in ilist.h on how to use the class.
28
29
template <class NodeType>
30
class IntrusiveNodeBase {
31
 public:
32
  // Creates a new node that is not in a list.
33
  inline IntrusiveNodeBase();
34
  inline IntrusiveNodeBase(const IntrusiveNodeBase&);
35
  inline IntrusiveNodeBase& operator=(const IntrusiveNodeBase&);
36
  inline IntrusiveNodeBase(IntrusiveNodeBase&& that);
37
38
  // Destroys a node.  It is an error to destroy a node that is part of a
39
  // list, unless it is a sentinel.
40
  virtual ~IntrusiveNodeBase();
41
42
  IntrusiveNodeBase& operator=(IntrusiveNodeBase&& that);
43
44
  // Returns true if |this| is in a list.
45
  inline bool IsInAList() const;
46
47
  // Returns the node that comes after the given node in the list, if one
48
  // exists.  If the given node is not in a list or is at the end of the list,
49
  // the return value is nullptr.
50
  inline NodeType* NextNode() const;
51
52
  // Returns the node that comes before the given node in the list, if one
53
  // exists.  If the given node is not in a list or is at the start of the
54
  // list, the return value is nullptr.
55
  inline NodeType* PreviousNode() const;
56
57
  // Inserts the given node immediately before |pos| in the list.
58
  // If the given node is already in a list, it will first be removed
59
  // from that list.
60
  //
61
  // It is assumed that the given node is of type NodeType.  It is an error if
62
  // |pos| is not already in a list.
63
  inline void InsertBefore(NodeType* pos);
64
65
  // Inserts the given node immediately after |pos| in the list.
66
  // If the given node is already in a list, it will first be removed
67
  // from that list.
68
  //
69
  // It is assumed that the given node is of type NodeType.  It is an error if
70
  // |pos| is not already in a list, or if |pos| is equal to |this|.
71
  inline void InsertAfter(NodeType* pos);
72
73
  // Removes the given node from the list.  It is assumed that the node is
74
  // in a list.  Note that this does not free any storage related to the node,
75
  // it becomes the caller's responsibility to free the storage.
76
  inline void RemoveFromList();
77
78
 protected:
79
  // Replaces |this| with |target|.  |this| is a sentinel if and only if
80
  // |target| is also a sentinel.
81
  //
82
  // If neither node is a sentinel, |target| takes
83
  // the place of |this|.  It is assumed that |target| is not in a list.
84
  //
85
  // If both are sentinels, then it will cause all of the
86
  // nodes in the list containing |this| to be moved to the list containing
87
  // |target|.  In this case, it is assumed that |target| is an empty list.
88
  //
89
  // No storage will be deleted.
90
  void ReplaceWith(NodeType* target);
91
92
  // Returns true if |this| is the sentinel node of an empty list.
93
  bool IsEmptyList();
94
95
  // The pointers to the next and previous nodes in the list.
96
  // If the current node is not part of a list, then |next_node_| and
97
  // |previous_node_| are equal to |nullptr|.
98
  NodeType* next_node_;
99
  NodeType* previous_node_;
100
101
  // Only true for the sentinel node stored in the list itself.
102
  bool is_sentinel_;
103
104
  friend IntrusiveList<NodeType>;
105
};
106
107
// Implementation of IntrusiveNodeBase
108
109
template <class NodeType>
110
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase()
111
41.1M
    : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {}
112
113
template <class NodeType>
114
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(
115
2.37M
    const IntrusiveNodeBase&) {
116
2.37M
  next_node_ = nullptr;
117
2.37M
  previous_node_ = nullptr;
118
2.37M
  is_sentinel_ = false;
119
2.37M
}
120
121
template <class NodeType>
122
inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
123
0
    const IntrusiveNodeBase&) {
124
0
  assert(!is_sentinel_);
125
0
  if (IsInAList()) {
126
0
    RemoveFromList();
127
0
  }
128
0
  return *this;
129
0
}
130
131
template <class NodeType>
132
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(IntrusiveNodeBase&& that)
133
    : next_node_(nullptr),
134
      previous_node_(nullptr),
135
      is_sentinel_(that.is_sentinel_) {
136
  if (is_sentinel_) {
137
    next_node_ = this;
138
    previous_node_ = this;
139
  }
140
  that.ReplaceWith(this);
141
}
142
143
template <class NodeType>
144
43.5M
IntrusiveNodeBase<NodeType>::~IntrusiveNodeBase() {
145
43.5M
  assert(is_sentinel_ || !IsInAList());
146
43.5M
}
147
148
template <class NodeType>
149
IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
150
    IntrusiveNodeBase&& that) {
151
  that.ReplaceWith(this);
152
  return *this;
153
}
154
155
template <class NodeType>
156
160M
inline bool IntrusiveNodeBase<NodeType>::IsInAList() const {
157
160M
  return next_node_ != nullptr;
158
160M
}
159
160
template <class NodeType>
161
576M
inline NodeType* IntrusiveNodeBase<NodeType>::NextNode() const {
162
576M
  if (!next_node_->is_sentinel_) return next_node_;
163
78.7M
  return nullptr;
164
576M
}
165
166
template <class NodeType>
167
31.9M
inline NodeType* IntrusiveNodeBase<NodeType>::PreviousNode() const {
168
31.9M
  if (!previous_node_->is_sentinel_) return previous_node_;
169
0
  return nullptr;
170
31.9M
}
171
172
template <class NodeType>
173
33.9M
inline void IntrusiveNodeBase<NodeType>::InsertBefore(NodeType* pos) {
174
33.9M
  assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
175
33.9M
  assert(pos->IsInAList() && "Pos should already be in a list.");
176
33.9M
  if (this->IsInAList()) this->RemoveFromList();
177
178
33.9M
  this->next_node_ = pos;
179
33.9M
  this->previous_node_ = pos->previous_node_;
180
33.9M
  pos->previous_node_ = static_cast<NodeType*>(this);
181
33.9M
  this->previous_node_->next_node_ = static_cast<NodeType*>(this);
182
33.9M
}
183
184
template <class NodeType>
185
0
inline void IntrusiveNodeBase<NodeType>::InsertAfter(NodeType* pos) {
186
0
  assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
187
0
  assert(pos->IsInAList() && "Pos should already be in a list.");
188
0
  assert(this != pos && "Can't insert a node after itself.");
189
190
0
  if (this->IsInAList()) {
191
0
    this->RemoveFromList();
192
0
  }
193
194
0
  this->previous_node_ = pos;
195
0
  this->next_node_ = pos->next_node_;
196
0
  pos->next_node_ = static_cast<NodeType*>(this);
197
0
  this->next_node_->previous_node_ = static_cast<NodeType*>(this);
198
0
}
199
200
template <class NodeType>
201
33.9M
inline void IntrusiveNodeBase<NodeType>::RemoveFromList() {
202
33.9M
  assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
203
33.9M
  assert(this->IsInAList() &&
204
33.9M
         "Cannot remove a node from a list if it is not in a list.");
205
206
33.9M
  this->next_node_->previous_node_ = this->previous_node_;
207
33.9M
  this->previous_node_->next_node_ = this->next_node_;
208
33.9M
  this->next_node_ = nullptr;
209
33.9M
  this->previous_node_ = nullptr;
210
33.9M
}
211
212
template <class NodeType>
213
0
void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) {
214
0
  if (this->is_sentinel_) {
215
0
    assert(target->IsEmptyList() &&
216
0
           "If target is not an empty list, the nodes in that list would not "
217
0
           "be linked to a sentinel.");
218
0
  } else {
219
0
    assert(IsInAList() && "The node being replaced must be in a list.");
220
0
    assert(!target->is_sentinel_ &&
221
0
           "Cannot turn a sentinel node into one that is not.");
222
0
  }
223
0
224
0
  if (!this->IsEmptyList()) {
225
0
    // Link target into the same position that |this| was in.
226
0
    target->next_node_ = this->next_node_;
227
0
    target->previous_node_ = this->previous_node_;
228
0
    target->next_node_->previous_node_ = target;
229
0
    target->previous_node_->next_node_ = target;
230
0
231
0
    // Reset |this| to itself default value.
232
0
    if (!this->is_sentinel_) {
233
0
      // Reset |this| so that it is not in a list.
234
0
      this->next_node_ = nullptr;
235
0
      this->previous_node_ = nullptr;
236
0
    } else {
237
0
      // Set |this| so that it is the head of an empty list.
238
0
      // We cannot treat sentinel nodes like others because it is invalid for
239
0
      // a sentinel node to not be in a list.
240
0
      this->next_node_ = static_cast<NodeType*>(this);
241
0
      this->previous_node_ = static_cast<NodeType*>(this);
242
0
    }
243
0
  } else {
244
0
    // If |this| points to itself, it must be a sentinel node with an empty
245
0
    // list.  Reset |this| so that it is the head of an empty list.  We want
246
0
    // |target| to be the same.  The asserts above guarantee that.
247
0
  }
248
0
}
249
250
template <class NodeType>
251
0
bool IntrusiveNodeBase<NodeType>::IsEmptyList() {
252
0
  if (next_node_ == this) {
253
0
    assert(is_sentinel_ &&
254
0
           "None sentinel nodes should never point to themselves.");
255
0
    assert(previous_node_ == this &&
256
0
           "Inconsistency with the previous and next nodes.");
257
0
    return true;
258
0
  }
259
0
  return false;
260
0
}
261
262
}  // namespace utils
263
}  // namespace spvtools
264
265
#endif  // SOURCE_UTIL_ILIST_NODE_H_