Coverage Report

Created: 2025-06-12 07:25

/src/duckdb/third_party/skiplist/HeadNode.h
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 *
4
 * Project: skiplist
5
 *
6
 * Created by Paul Ross on 03/12/2015.
7
 *
8
 * Copyright (c) 2015-2023 Paul Ross. All rights reserved.
9
 *
10
 * @code
11
 * MIT License
12
 *
13
 * Copyright (c) 2017-2023 Paul Ross
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a copy
16
 * of this software and associated documentation files (the "Software"), to deal
17
 * in the Software without restriction, including without limitation the rights
18
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
 * copies of the Software, and to permit persons to whom the Software is
20
 * furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in all
23
 * copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
 * SOFTWARE.
32
 * @endcode
33
 */
34
35
#ifndef SkipList_HeadNode_h
36
#define SkipList_HeadNode_h
37
38
#include <functional>
39
//#ifdef SKIPLIST_THREAD_SUPPORT
40
//    #include <mutex>
41
//#endif
42
#include <vector>
43
44
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
45
#include <sstream>
46
#endif // INCLUDE_METHODS_THAT_USE_STREAMS
47
48
#include "IntegrityEnums.h"
49
50
/** HeadNode
51
 *
52
 * @brief A HeadNode is a skip list. This is the single node leading to all other content Nodes.
53
 *
54
 * Example:
55
 *
56
 * @code
57
 *      OrderedStructs::SkipList::HeadNode<double> sl;
58
 *      for (int i = 0; i < 100; ++i) {
59
 *          sl.insert(i * 22.0 / 7.0);
60
 *      }
61
 *      sl.size(); // 100
62
 *      sl.at(50); // Value of 50 pi
63
 *      sl.remove(sl.at(50)); // Remove 50 pi
64
 * @endcode
65
 *
66
 * Created by Paul Ross on 03/12/2015.
67
 *
68
 * Copyright (c) 2015-2023 Paul Ross. All rights reserved.
69
 *
70
 * @tparam T The type of the Skip List Node values.
71
 * @tparam _Compare A comparison function for type T.
72
 */
73
template <typename T, typename _Compare=std::less<T>>
74
class HeadNode {
75
public:
76
    /**
77
     * Constructor for and Empty Skip List.
78
     *
79
     * @param cmp The comparison function for comparing Node values.
80
     */
81
0
    HeadNode(_Compare cmp=_Compare()) : _count(0), _compare(cmp), _pool(cmp) {
82
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
83
        _dot_file_subgraph = 0;
84
#endif
85
0
    }
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, signed char> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, short> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, int> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, long> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, float> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, double> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> >)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::HeadNode(duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> >)
86
    // Const methods
87
    //
88
    // Returns true if the value is present in the skip list.
89
    bool has(const T &value) const;
90
    // Returns the value at the index in the skip list.
91
    // Will throw an OrderedStructs::SkipList::IndexError if index out of range.
92
    const T &at(size_t index) const;
93
    // Find the value at index and write count values to dest.
94
    // Will throw a SkipList::IndexError if any index out of range.
95
    // This is useful for rolling median on even length lists where
96
    // the caller might want to implement the mean of two values.
97
    void at(size_t index, size_t count, std::vector<T> &dest) const;
98
    // Computes index of the first occurrence of a value
99
    // Will throw a ValueError if the value does not exist in the skip list
100
    size_t index(const T& value) const;
101
    // Number of values in the skip list.
102
    size_t size() const;
103
    // Non-const methods
104
    //
105
    // Insert a value.
106
    void insert(const T &value);
107
    // Remove a value and return it.
108
    // Will throw a ValueError is value not present.
109
    T remove(const T &value);
110
111
    // Const methods that are mostly used for debugging and visualisation.
112
    //
113
    // Number of linked lists that are in the skip list.
114
    size_t height() const;
115
    // Number of linked lists that the node at index has.
116
    // Will throw a SkipList::IndexError if idx out of range.
117
    size_t height(size_t idx) const;
118
    // The skip width of the node at index has.
119
    // May throw a SkipList::IndexError
120
    size_t width(size_t idx, size_t level) const;
121
122
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
123
    void dotFile(std::ostream &os) const;
124
    void dotFileFinalise(std::ostream &os) const;
125
#endif // INCLUDE_METHODS_THAT_USE_STREAMS
126
127
    // Returns non-zero if the integrity of this data structure is compromised
128
    // This is a thorough but expensive check!
129
    IntegrityCheck lacksIntegrity() const;
130
    // Estimate of the number of bytes used by the skip list
131
    size_t size_of() const;
132
    virtual ~HeadNode();
133
134
protected:
135
    void _adjRemoveRefs(size_t level, Node<T, _Compare> *pNode);
136
    const Node<T, _Compare> *_nodeAt(size_t idx) const;
137
138
protected:
139
    // Standardised way of throwing a ValueError
140
    void _throwValueErrorNotFound(const T &value) const;
141
    void _throwIfValueDoesNotCompare(const T &value) const;
142
    // Internal integrity checks
143
    IntegrityCheck _lacksIntegrityCyclicReferences() const;
144
    IntegrityCheck _lacksIntegrityWidthAccumulation() const;
145
    IntegrityCheck _lacksIntegrityNodeReferencesNotInList() const;
146
    IntegrityCheck _lacksIntegrityOrder() const;
147
protected:
148
    /// Number of nodes in the list.
149
    size_t _count;
150
    /// My node references, the size of this is the largest height in the list
151
    SwappableNodeRefStack<T, _Compare> _nodeRefs;
152
    /// Comparison function.
153
    _Compare _compare;
154
    typename Node<T, _Compare>::_Pool _pool;
155
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
156
    /// Used to count how many sub-graphs have been plotted
157
    mutable size_t _dot_file_subgraph;
158
#endif
159
160
private:
161
    /// Prevent cctor and operator=
162
    HeadNode(const HeadNode &that);
163
    HeadNode &operator=(const HeadNode &that) const;
164
};
165
166
/**
167
 * Returns true if the value is present in the skip list.
168
 *
169
 * @tparam T Type of the values in the Skip List.
170
 * @tparam _Compare Compare function.
171
 * @param value Value to check if it is in the Skip List.
172
 * @return true if in the Skip List.
173
 */
174
template <typename T, typename _Compare>
175
bool HeadNode<T, _Compare>::has(const T &value) const {
176
    _throwIfValueDoesNotCompare(value);
177
#ifdef SKIPLIST_THREAD_SUPPORT
178
    std::lock_guard<std::mutex> lock(gSkipListMutex);
179
#endif
180
    for (size_t l = _nodeRefs.height(); l-- > 0;) {
181
        assert(_nodeRefs[l].pNode);
182
        if (_nodeRefs[l].pNode->has(value)) {
183
            return true;
184
        }
185
    }
186
    return false;
187
}
188
189
/**
190
 * Returns the value at a particular index.
191
 * Will throw an OrderedStructs::SkipList::IndexError if index out of range.
192
 *
193
 * If @ref SKIPLIST_THREAD_SUPPORT is defined this will block.
194
 *
195
 * See _throw_exceeds_size() that does the throw.
196
 *
197
 * @tparam T Type of the values in the Skip List.
198
 * @tparam _Compare Compare function.
199
 * @param index The index.
200
 * @return The value at that index.
201
 */
202
template <typename T, typename _Compare>
203
const T &HeadNode<T, _Compare>::at(size_t index) const {
204
#ifdef SKIPLIST_THREAD_SUPPORT
205
    std::lock_guard<std::mutex> lock(gSkipListMutex);
206
#endif
207
    const Node<T, _Compare> *pNode = _nodeAt(index);
208
    assert(pNode);
209
    return pNode->value();
210
}
211
212
/**
213
 * Find the count number of value starting at index and write them to dest.
214
 *
215
 * Will throw a OrderedStructs::SkipList::IndexError if any index out of range.
216
 *
217
 * This is useful for rolling median on even length lists where the caller might want to implement the mean of two
218
 * values.
219
 *
220
 * @tparam T Type of the values in the Skip List.
221
 * @tparam _Compare Compare function.
222
 * @param index The index.
223
 * @param count The number of values to retrieve.
224
 * @param dest The vector of values
225
 */
226
template <typename T, typename _Compare>
227
void HeadNode<T, _Compare>::at(size_t index, size_t count,
228
0
                               std::vector<T> &dest) const {
229
#ifdef SKIPLIST_THREAD_SUPPORT
230
    std::lock_guard<std::mutex> lock(gSkipListMutex);
231
#endif
232
0
    dest.clear();
233
0
    const Node<T, _Compare> *pNode = _nodeAt(index);
234
    // _nodeAt will (should) throw an IndexError so this
235
    // assert should always be true
236
0
    assert(pNode);
237
0
    while (count) {
238
0
        if (! pNode) {
239
0
            _throw_exceeds_size(_count);
240
0
        }
241
0
        dest.push_back(pNode->value());
242
0
        pNode = pNode->next();
243
0
        --count;
244
0
    }
245
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, signed char>, std::__1::allocator<std::__1::pair<unsigned long, signed char> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, short>, std::__1::allocator<std::__1::pair<unsigned long, short> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, int>, std::__1::allocator<std::__1::pair<unsigned long, int> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, long>, std::__1::allocator<std::__1::pair<unsigned long, long> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::hugeint_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::hugeint_t> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, float>, std::__1::allocator<std::__1::pair<unsigned long, float> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, double>, std::__1::allocator<std::__1::pair<unsigned long, double> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::interval_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::interval_t> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::string_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::string_t> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::date_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::date_t> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::timestamp_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::timestamp_t> > >&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::at(unsigned long, unsigned long, std::__1::vector<std::__1::pair<unsigned long, duckdb::dtime_t>, std::__1::allocator<std::__1::pair<unsigned long, duckdb::dtime_t> > >&) const
246
247
/**
248
 * Computes index of the first occurrence of a value
249
 * Will throw a OrderedStructs::SkipList::ValueError if the value does not exist in the skip list
250
 * Will throw a OrderedStructs::SkipList::FailedComparison if the value is not comparable.
251
 *
252
 * @tparam T Type of the values in the Skip List.
253
 * @tparam _Compare Compare function.
254
 * @param value The value to search for.
255
 * @return
256
 */
257
template <typename T, typename _Compare>
258
size_t HeadNode<T, _Compare>::index(const T& value) const {
259
    _throwIfValueDoesNotCompare(value);
260
    size_t idx;
261
262
#ifdef SKIPLIST_THREAD_SUPPORT
263
    std::lock_guard<std::mutex> lock(gSkipListMutex);
264
#endif
265
    for (size_t l = _nodeRefs.height(); l-- > 0;) {
266
        assert(_nodeRefs[l].pNode);
267
        if (_nodeRefs[l].pNode->index(value, idx, l)) {
268
            idx += _nodeRefs[l].width;
269
            assert(idx > 0);
270
            return idx - 1;
271
        }
272
    }
273
    _throwValueErrorNotFound(value);
274
    return 0;
275
}
276
277
/**
278
 * Return the number of values in the Skip List.
279
 *
280
 * @tparam T Type of the values in the Skip List.
281
 * @tparam _Compare Compare function.
282
 * @return The number of values in the Skip List.
283
 */
284
template <typename T, typename _Compare>
285
0
size_t HeadNode<T, _Compare>::size() const {
286
0
    return _count;
287
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::size() const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::size() const
288
289
template <typename T, typename _Compare>
290
size_t HeadNode<T, _Compare>::height() const {
291
#ifdef SKIPLIST_THREAD_SUPPORT
292
    std::lock_guard<std::mutex> lock(gSkipListMutex);
293
#endif
294
    size_t val = _nodeRefs.height();
295
    return val;
296
}
297
298
/**
299
 * Return the number of linked lists that the node at index has.
300
 *
301
 * Will throw a OrderedStructs::SkipList::IndexError if the index out of range.
302
 *
303
 * @tparam T Type of the values in the Skip List.
304
 * @tparam _Compare Compare function.
305
 * @param idx The index of the Skip List node.
306
 * @return The number of linked lists that the node at the index has.
307
 */
308
template <typename T, typename _Compare>
309
size_t HeadNode<T, _Compare>::height(size_t idx) const {
310
#ifdef SKIPLIST_THREAD_SUPPORT
311
    std::lock_guard<std::mutex> lock(gSkipListMutex);
312
#endif
313
    const Node<T, _Compare> *pNode = _nodeAt(idx);
314
    assert(pNode);
315
    return pNode->height();
316
}
317
318
/**
319
 * The skip width of the Node at index has at the given level.
320
 * Will throw an IndexError if the index is out of range.
321
 *
322
 * @tparam T Type of the values in the Skip List.
323
 * @tparam _Compare Compare function.
324
 * @param idx The index.
325
 * @param level The level.
326
 * @return Width of Node.
327
 */
328
template <typename T, typename _Compare>
329
size_t HeadNode<T, _Compare>::width(size_t idx, size_t level) const {
330
#ifdef SKIPLIST_THREAD_SUPPORT
331
    std::lock_guard<std::mutex> lock(gSkipListMutex);
332
#endif
333
    // Will throw if out of range.
334
    const Node<T, _Compare> *pNode = _nodeAt(idx);
335
    assert(pNode);
336
    if (level >= pNode->height()) {
337
        _throw_exceeds_size(pNode->height());
338
    }
339
    return pNode->nodeRefs()[level].width;
340
}
341
342
/**
343
 * Find the Node at the given index.
344
 * Will throw an IndexError if the index is out of range.
345
 *
346
 * @tparam T Type of the values in the Skip List.
347
 * @tparam _Compare Compare function.
348
 * @param idx The index.
349
 * @return The Node.
350
 */
351
template <typename T, typename _Compare>
352
0
const Node<T, _Compare> *HeadNode<T, _Compare>::_nodeAt(size_t idx) const {
353
0
    if (idx < _count) {
354
0
        for (size_t l = _nodeRefs.height(); l-- > 0;) {
355
0
            if (_nodeRefs[l].pNode && _nodeRefs[l].width <= idx + 1) {
356
0
                size_t new_index = idx + 1 - _nodeRefs[l].width;
357
0
                const Node<T, _Compare> *pNode = _nodeRefs[l].pNode->at(new_index);
358
0
                if (pNode) {
359
0
                    return pNode;
360
0
                }
361
0
            }
362
0
        }
363
0
    }
364
0
    assert(idx >= _count);
365
0
    _throw_exceeds_size(_count);
366
    // Should not get here as _throw_exceeds_size() will always throw.
367
0
    return NULL;
368
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::_nodeAt(unsigned long) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::_nodeAt(unsigned long) const
369
370
/**
371
 * Insert a value.
372
 *
373
 * @tparam T Type of the values in the Skip List.
374
 * @tparam _Compare Compare function.
375
 * @param value
376
 */
377
template <typename T, typename _Compare>
378
0
void HeadNode<T, _Compare>::insert(const T &value) {
379
#ifdef SKIPLIST_THREAD_SUPPORT
380
    std::lock_guard<std::mutex> lock(gSkipListMutex);
381
#ifdef SKIPLIST_THREAD_SUPPORT_TRACE
382
    std::cout << "HeadNode insert() thread: " << std::this_thread::get_id() << std::endl;
383
#endif
384
#endif
385
0
    Node<T, _Compare> *pNode = nullptr;
386
0
    size_t level = _nodeRefs.height();
387
388
0
    _throwIfValueDoesNotCompare(value);
389
0
    while (level-- > 0) {
390
0
        assert(_nodeRefs[level].pNode);
391
0
        pNode = _nodeRefs[level].pNode->insert(value);
392
0
        if (pNode) {
393
0
            break;
394
0
        }
395
0
    }
396
0
    if (! pNode) {
397
0
        pNode = _pool.Allocate(value);
398
0
        level = 0;
399
0
    }
400
0
    assert(pNode);
401
0
    SwappableNodeRefStack<T, _Compare> &thatRefs = pNode->nodeRefs();
402
0
    if (thatRefs.canSwap()) {
403
        // Expand this to that
404
0
        while (_nodeRefs.height() < thatRefs.height()) {
405
0
            _nodeRefs.push_back(nullptr, _count + 1);
406
0
        }
407
0
        if (level < thatRefs.swapLevel()) {
408
            // Happens when we were originally, say 3 high (max height of any
409
            // previously seen node). Then a node is created
410
            // say 5 high. In that case this will be at level 2 and
411
            // thatRefs.swapLevel() will be 3
412
0
            assert(level + 1 == thatRefs.swapLevel());
413
0
            thatRefs[thatRefs.swapLevel()].width += _nodeRefs[level].width;
414
0
            ++level;
415
0
        }
416
        // Now swap
417
0
        while (level < _nodeRefs.height() && thatRefs.canSwap()) {
418
0
            assert(thatRefs.canSwap());
419
0
            assert(level == thatRefs.swapLevel());
420
0
            _nodeRefs[level].width -= thatRefs[level].width - 1;
421
0
            thatRefs.swap(_nodeRefs);
422
0
            if (thatRefs.canSwap()) {
423
0
                assert(thatRefs[thatRefs.swapLevel()].width == 0);
424
0
                thatRefs[thatRefs.swapLevel()].width = _nodeRefs[level].width;
425
0
            }
426
0
            ++level;
427
0
        }
428
        // Check all references swapped
429
0
        assert(! thatRefs.canSwap());
430
        // Check that all 'this' pointers created on construction have been moved
431
0
        assert(thatRefs.noNodePointerMatches(pNode));
432
0
    }
433
0
    if (level < thatRefs.swapLevel()) {
434
        // Happens when we are, say 5 high then a node is created
435
        // and consumed by the next node say 3 high. In that case this will be
436
        // at level 2 and thatRefs.swapLevel() will be 3
437
0
        assert(level + 1 == thatRefs.swapLevel());
438
0
        ++level;
439
0
    }
440
    // Increment my widths as my references are now going over the top of
441
    // pNode.
442
0
    while (level < _nodeRefs.height() && level >= thatRefs.height()) {
443
0
        _nodeRefs[level++].width += 1;
444
0
    }
445
0
    ++_count;
446
#ifdef SKIPLIST_THREAD_SUPPORT
447
#ifdef SKIPLIST_THREAD_SUPPORT_TRACE
448
    std::cout << "HeadNode insert() thread: " << std::this_thread::get_id() << " DONE" << std::endl;
449
#endif
450
#endif
451
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::insert(std::__1::pair<unsigned long, signed char> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::insert(std::__1::pair<unsigned long, short> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::insert(std::__1::pair<unsigned long, int> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::insert(std::__1::pair<unsigned long, long> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::insert(std::__1::pair<unsigned long, duckdb::hugeint_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::insert(std::__1::pair<unsigned long, float> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::insert(std::__1::pair<unsigned long, double> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::insert(std::__1::pair<unsigned long, duckdb::interval_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::insert(std::__1::pair<unsigned long, duckdb::string_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::insert(std::__1::pair<unsigned long, duckdb::date_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::insert(std::__1::pair<unsigned long, duckdb::timestamp_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::insert(std::__1::pair<unsigned long, duckdb::dtime_t> const&)
452
453
/**
454
 * Adjust references >= level for removal of the node pNode.
455
 *
456
 * @tparam T Type of the values in the Skip List.
457
 * @tparam _Compare Compare function.
458
 * @param level Current level.
459
 * @param pNode Node to swap references with.
460
 */
461
template <typename T, typename _Compare>
462
void HeadNode<T, _Compare>::_adjRemoveRefs(size_t level,
463
0
                                           Node<T, _Compare> *pNode) {
464
0
    assert(pNode);
465
0
    SwappableNodeRefStack<T, _Compare> &thatRefs = pNode->nodeRefs();
466
467
    // Swap all remaining levels
468
    // This assertion checks that if swapping can take place we must be at the
469
    // same level.
470
0
    assert(! thatRefs.canSwap() || level == thatRefs.swapLevel());
471
0
    while (level < _nodeRefs.height() && thatRefs.canSwap()) {
472
0
        assert(level == thatRefs.swapLevel());
473
        // Compute the new width for the new node
474
0
        thatRefs[level].width += _nodeRefs[level].width - 1;
475
0
        thatRefs.swap(_nodeRefs);
476
0
        ++level;
477
0
        if (! thatRefs.canSwap()) {
478
0
            break;
479
0
        }
480
0
    }
481
0
    assert(! thatRefs.canSwap());
482
    // Decrement my widths as my references are now going over the top of
483
    // pNode.
484
0
    while (level < _nodeRefs.height()) {
485
0
        _nodeRefs[level++].width -= 1;
486
0
    }
487
    // Decrement my stack while top has a NULL pointer.
488
0
    while (_nodeRefs.height() && ! _nodeRefs[_nodeRefs.height() - 1].pNode) {
489
0
        _nodeRefs.pop_back();
490
0
    }
491
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >*)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::_adjRemoveRefs(unsigned long, duckdb_skiplistlib::skip_list::Node<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >*)
492
493
/**
494
 * Remove a Node with a value.
495
 * May throw a ValueError if the value is not found.
496
 *
497
 * @tparam T Type of the values in the Skip List.
498
 * @tparam _Compare Compare function.
499
 * @param value The value in the Node to remove.
500
 * @return The value removed.
501
 */
502
template <typename T, typename _Compare>
503
0
T HeadNode<T, _Compare>::remove(const T &value) {
504
#ifdef SKIPLIST_THREAD_SUPPORT
505
    std::lock_guard<std::mutex> lock(gSkipListMutex);
506
#ifdef SKIPLIST_THREAD_SUPPORT_TRACE
507
    std::cout << "HeadNode remove() thread: " << std::this_thread::get_id() << std::endl;
508
#endif
509
#endif
510
0
    Node<T, _Compare> *pNode = nullptr;
511
0
    size_t level;
512
513
0
    _throwIfValueDoesNotCompare(value);
514
0
    for (level = _nodeRefs.height(); level-- > 0;) {
515
0
        assert(_nodeRefs[level].pNode);
516
0
        pNode = _nodeRefs[level].pNode->remove(level, value);
517
0
        if (pNode) {
518
0
            break;
519
0
        }
520
0
    }
521
0
    if (! pNode) {
522
0
        _throwValueErrorNotFound(value);
523
0
    }
524
    // Take swap level as some swaps will have been dealt with by the remove() above.
525
0
    _adjRemoveRefs(pNode->nodeRefs().swapLevel(), pNode);
526
0
    --_count;
527
0
    T ret_val = _pool.Release(pNode);
528
#ifdef SKIPLIST_THREAD_SUPPORT_TRACE
529
    std::cout << "HeadNode remove() thread: " << std::this_thread::get_id() << " DONE" << std::endl;
530
#endif
531
0
    return ret_val;
532
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::remove(std::__1::pair<unsigned long, signed char> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::remove(std::__1::pair<unsigned long, short> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::remove(std::__1::pair<unsigned long, int> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::remove(std::__1::pair<unsigned long, long> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::remove(std::__1::pair<unsigned long, duckdb::hugeint_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::remove(std::__1::pair<unsigned long, float> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::remove(std::__1::pair<unsigned long, double> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::remove(std::__1::pair<unsigned long, duckdb::interval_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::remove(std::__1::pair<unsigned long, duckdb::string_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::remove(std::__1::pair<unsigned long, duckdb::date_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::remove(std::__1::pair<unsigned long, duckdb::timestamp_t> const&)
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::remove(std::__1::pair<unsigned long, duckdb::dtime_t> const&)
533
534
/**
535
 * Throw a ValueError in a consistent fashion.
536
 *
537
 * @tparam T Type of the values in the Skip List.
538
 * @tparam _Compare Compare function.
539
 * @param value The value to put into the ValueError.
540
 */
541
template <typename T, typename _Compare>
542
0
void HeadNode<T, _Compare>::_throwValueErrorNotFound(const T &value) const {
543
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
544
    std::ostringstream oss;
545
    oss << "Value " << value << " not found.";
546
    std::string err_msg = oss.str();
547
#else
548
0
    std::string err_msg = "Value not found.";
549
0
#endif
550
0
    throw ValueError(err_msg);
551
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, signed char> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, short> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, int> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, long> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::hugeint_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, float> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, double> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::interval_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::string_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::date_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::timestamp_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::_throwValueErrorNotFound(std::__1::pair<unsigned long, duckdb::dtime_t> const&) const
552
553
/**
554
 * Checks that the value == value.
555
 * This will throw a FailedComparison if that is not the case, for example NaN.
556
 *
557
 * @note
558
 * The Node class is (should be) not directly accessible by the user so we can just assert(value == value) in Node.
559
 *
560
 * @tparam T Type of the values in the Skip List.
561
 * @tparam _Compare Compare function.
562
 * @param value
563
 */
564
template <typename T, typename _Compare>
565
0
void HeadNode<T, _Compare>::_throwIfValueDoesNotCompare(const T &value) const {
566
0
    if (value != value) {
567
0
        throw FailedComparison(
568
0
            "Can not work with something that does not compare equal to itself.");
569
0
    }
570
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, signed char> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, short> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, int> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, long> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::hugeint_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, float> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, double> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::interval_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::string_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::date_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::timestamp_t> const&) const
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::_throwIfValueDoesNotCompare(std::__1::pair<unsigned long, duckdb::dtime_t> const&) const
571
572
/**
573
 * This tests that at every level >= 0 the sequence of node pointers
574
 * at that level does not contain a cyclic reference.
575
 *
576
 * @tparam T Type of the values in the Skip List.
577
 * @tparam _Compare Compare function.
578
 * @return An IntegrityCheck enum.
579
 */
580
template <typename T, typename _Compare>
581
IntegrityCheck HeadNode<T, _Compare>::_lacksIntegrityCyclicReferences() const {
582
    assert(_nodeRefs.height());
583
    // Check for cyclic references at each level
584
    for (size_t level = 0; level < _nodeRefs.height(); ++level) {
585
        Node<T, _Compare> *p1 = _nodeRefs[level].pNode;
586
        Node<T, _Compare> *p2 = _nodeRefs[level].pNode;
587
        while (p1 && p2) {
588
            p1 = p1->nodeRefs()[level].pNode;
589
            if (p2->nodeRefs()[level].pNode) {
590
                p2 = p2->nodeRefs()[level].pNode->nodeRefs()[level].pNode;
591
            } else {
592
                p2 = nullptr;
593
            }
594
            if (p1 && p2 && p1 == p2) {
595
                return HEADNODE_DETECTS_CYCLIC_REFERENCE;
596
            }
597
        }
598
    }
599
    return INTEGRITY_SUCCESS;
600
}
601
602
/**
603
 * This tests that at every level > 0 the node to node width is the same
604
 * as the accumulated node to node widths at level - 1.
605
 *
606
 * @tparam T Type of the values in the Skip List.
607
 * @tparam _Compare Compare function.
608
 * @return An IntegrityCheck enum.
609
 */
610
template <typename T, typename _Compare>
611
IntegrityCheck HeadNode<T, _Compare>::_lacksIntegrityWidthAccumulation() const {
612
    assert(_nodeRefs.height());
613
    for (size_t level = 1; level < _nodeRefs.height(); ++level) {
614
        const Node<T, _Compare> *pl = _nodeRefs[level].pNode;
615
        const Node<T, _Compare> *pl_1 = _nodeRefs[level - 1].pNode;
616
        assert(pl && pl_1); // No nulls allowed in HeadNode
617
        size_t wl = _nodeRefs[level].width;
618
        size_t wl_1 = _nodeRefs[level - 1].width;
619
        while (true) {
620
            while (pl != pl_1) {
621
                assert(pl_1); // Could only happen if a lower reference was NULL and the higher non-NULL.
622
                wl_1 += pl_1->width(level - 1);
623
                pl_1 = pl_1->pNode(level - 1);
624
            }
625
            if (wl != wl_1) {
626
                return HEADNODE_LEVEL_WIDTHS_MISMATCH;
627
            }
628
            if (pl == nullptr && pl_1 == nullptr) {
629
                break;
630
            }
631
            wl = pl->width(level);
632
            wl_1 = pl_1->width(level - 1);
633
            pl = pl->pNode(level);
634
            pl_1 = pl_1->pNode(level - 1);
635
        }
636
    }
637
    return INTEGRITY_SUCCESS;
638
}
639
640
/**
641
 * This tests the integrity of each Node.
642
 *
643
 * @tparam T Type of the values in the Skip List.
644
 * @tparam _Compare Compare function.
645
 * @return An IntegrityCheck enum.
646
 */
647
template <typename T, typename _Compare>
648
IntegrityCheck HeadNode<T, _Compare>::_lacksIntegrityNodeReferencesNotInList() const {
649
    assert(_nodeRefs.height());
650
651
    IntegrityCheck result;
652
    std::set<const Node<T, _Compare>*> nodeSet;
653
    const Node<T, _Compare> *pNode = _nodeRefs[0].pNode;
654
    assert(pNode);
655
656
    // First gather all nodes, slightly awkward code here is so that
657
    // NULL is always included.
658
    nodeSet.insert(pNode);
659
    do {
660
        pNode = pNode->next();
661
        nodeSet.insert(pNode);
662
    } while (pNode);
663
    assert(nodeSet.size() == _count + 1); // All nodes plus NULL
664
    // Then test each node does not have pointers that are not in nodeSet
665
    pNode = _nodeRefs[0].pNode;
666
    while (pNode) {
667
        result = pNode->lacksIntegrityRefsInSet(nodeSet);
668
        if (result) {
669
            return result;
670
        }
671
        pNode = pNode->next();
672
    }
673
    return INTEGRITY_SUCCESS;
674
}
675
676
/**
677
 * Integrity check. Traverse the lowest level and check that the ordering
678
 * is correct according to the compare function. The HeadNode checks that the
679
 * Node(s) have correctly applied the compare function.
680
 *
681
 * @tparam T Type of the values in the Skip List.
682
 * @tparam _Compare Compare function.
683
 * @return An IntegrityCheck enum.
684
 */
685
template <typename T, typename _Compare>
686
IntegrityCheck HeadNode<T, _Compare>::_lacksIntegrityOrder() const {
687
    if (_nodeRefs.height()) {
688
        // Traverse the lowest level list iteratively deleting as we go
689
        // Doing this recursivley could be expensive as we are at level 0.
690
        const Node<T, _Compare> *node = _nodeRefs[0].pNode;
691
        const Node<T, _Compare> *next;
692
        while (node) {
693
            next = node->next();
694
            if (next && _compare(next->value(), node->value())) {
695
                return HEADNODE_DETECTS_OUT_OF_ORDER;
696
            }
697
            node = next;
698
        }
699
    }
700
    return INTEGRITY_SUCCESS;
701
}
702
703
/**
704
 * Full integrity check.
705
 * This calls the other integrity check functions.
706
 *
707
 * @tparam T Type of the values in the Skip List.
708
 * @tparam _Compare Compare function.
709
 * @return An IntegrityCheck enum.
710
 */
711
template <typename T, typename _Compare>
712
IntegrityCheck HeadNode<T, _Compare>::lacksIntegrity() const {
713
#ifdef SKIPLIST_THREAD_SUPPORT
714
    std::lock_guard<std::mutex> lock(gSkipListMutex);
715
#endif
716
    if (_nodeRefs.height()) {
717
        IntegrityCheck result = _nodeRefs.lacksIntegrity();
718
        if (result) {
719
            return result;
720
        }
721
        if (! _nodeRefs.noNodePointerMatches(nullptr)) {
722
            return HEADNODE_CONTAINS_NULL;
723
        }
724
        // Check all nodes for integrity
725
        const Node<T, _Compare> *pNode = _nodeRefs[0].pNode;
726
        while (pNode) {
727
            result = pNode->lacksIntegrity(_nodeRefs.height());
728
            if (result) {
729
                return result;
730
            }
731
            pNode = pNode->next();
732
        }
733
        // Check count against total number of nodes
734
        pNode = _nodeRefs[0].pNode;
735
        size_t total = 0;
736
        while (pNode) {
737
            total += pNode->nodeRefs()[0].width;
738
            pNode = pNode->next();
739
        }
740
        if (total != _count) {
741
            return HEADNODE_COUNT_MISMATCH;
742
        }
743
        result = _lacksIntegrityWidthAccumulation();
744
        if (result) {
745
            return result;
746
        }
747
        result = _lacksIntegrityCyclicReferences();
748
        if (result) {
749
            return result;
750
        }
751
        result = _lacksIntegrityNodeReferencesNotInList();
752
        if (result) {
753
            return result;
754
        }
755
        result = _lacksIntegrityOrder();
756
        if (result) {
757
            return result;
758
        }
759
    }
760
    return INTEGRITY_SUCCESS;
761
}
762
763
/**
764
 * Returns an estimate of the memory usage of an instance.
765
 *
766
 * @tparam T Type of the values in the Skip List.
767
 * @tparam _Compare Compare function.
768
 * @return The size of the memory estimate.
769
 */
770
template <typename T, typename _Compare>
771
size_t HeadNode<T, _Compare>::size_of() const {
772
#ifdef SKIPLIST_THREAD_SUPPORT
773
    std::lock_guard<std::mutex> lock(gSkipListMutex);
774
#endif
775
    // sizeof(*this) includes the size of _nodeRefs but _nodeRefs.size_of()
776
    // includes sizeof(_nodeRefs) so we need to subtract to avoid double counting
777
    size_t ret_val = sizeof(*this) + _nodeRefs.size_of() - sizeof(_nodeRefs);
778
    if (_nodeRefs.height()) {
779
        const Node<T, _Compare> *node = _nodeRefs[0].pNode;
780
        while (node) {
781
            ret_val += node->size_of();
782
            node = node->next();
783
        }
784
    }
785
    return ret_val;
786
}
787
788
/**
789
 * Destructor.
790
 * This deletes all Nodes.
791
 *
792
 * @tparam T Type of the values in the Skip List.
793
 * @tparam _Compare Compare function.
794
 */
795
template <typename T, typename _Compare>
796
0
HeadNode<T, _Compare>::~HeadNode() {
797
    // Hmm could this deadlock?
798
#ifdef SKIPLIST_THREAD_SUPPORT
799
    std::lock_guard<std::mutex> lock(gSkipListMutex);
800
#endif
801
0
    if (_nodeRefs.height()) {
802
        // Traverse the lowest level list iteratively deleting as we go
803
        // Doing this recursivley could be expensive as we are at level 0.
804
0
        const Node<T, _Compare> *node = _nodeRefs[0].pNode;
805
0
        const Node<T, _Compare> *next;
806
0
        while (node) {
807
0
            next = node->next();
808
0
            delete node;
809
0
            --_count;
810
0
            node = next;
811
0
        }
812
0
    }
813
0
    assert(_count == 0);
814
0
}
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, signed char>, duckdb::SkipLess<std::__1::pair<unsigned long, signed char> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, short>, duckdb::SkipLess<std::__1::pair<unsigned long, short> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, int>, duckdb::SkipLess<std::__1::pair<unsigned long, int> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, long>, duckdb::SkipLess<std::__1::pair<unsigned long, long> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::hugeint_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::hugeint_t> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, float>, duckdb::SkipLess<std::__1::pair<unsigned long, float> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, double>, duckdb::SkipLess<std::__1::pair<unsigned long, double> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::interval_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::interval_t> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::string_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::string_t> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::date_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::date_t> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::timestamp_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::timestamp_t> > >::~HeadNode()
Unexecuted instantiation: duckdb_skiplistlib::skip_list::HeadNode<std::__1::pair<unsigned long, duckdb::dtime_t>, duckdb::SkipLess<std::__1::pair<unsigned long, duckdb::dtime_t> > >::~HeadNode()
815
816
#ifdef INCLUDE_METHODS_THAT_USE_STREAMS
817
818
/**
819
 * Create a DOT file of the internal representation.
820
 *
821
 * @tparam T Type of the values in the Skip List.
822
 * @tparam _Compare Compare function.
823
 * @param os Where to write the DOT file.
824
 */
825
template <typename T, typename _Compare>
826
void HeadNode<T, _Compare>::dotFile(std::ostream &os) const {
827
#ifdef SKIPLIST_THREAD_SUPPORT
828
    std::lock_guard<std::mutex> lock(gSkipListMutex);
829
#endif
830
    if (_dot_file_subgraph == 0) {
831
        os << "digraph SkipList {" << std::endl;
832
        os << "label = \"SkipList.\"" << std::endl;
833
        os << "graph [rankdir = \"LR\"];" << std::endl;
834
        os << "node [fontsize = \"12\" shape = \"ellipse\"];" << std::endl;
835
        os << "edge [];" << std::endl;
836
        os << std::endl;
837
    }
838
    os << "subgraph cluster" << _dot_file_subgraph << " {" << std::endl;
839
    os << "style=dashed" << std::endl;
840
    os << "label=\"Skip list iteration " << _dot_file_subgraph << "\"" << std::endl;
841
    os << std::endl;
842
    os << "\"HeadNode" << _dot_file_subgraph;
843
    os << "\" [" << std::endl;
844
    os << "label = \"";
845
    // Write out the fields
846
    if (_nodeRefs.height()) {
847
        for (size_t level = _nodeRefs.height(); level-- > 0;) {
848
            os << "{ " << _nodeRefs[level].width << " | ";
849
            os << "<f" << level + 1 << "> ";
850
            os << std::hex << _nodeRefs[level].pNode << std::dec;
851
            os << "}";
852
            if (level > 0) {
853
                os << " | ";
854
            }
855
        }
856
    } else {
857
        os << "Empty HeadNode";
858
    }
859
    os << "\"" << std::endl;
860
    os << "shape = \"record\"" << std::endl;
861
    os << "];" << std::endl;
862
    // Edges for head node
863
    for (size_t level = 0; level < _nodeRefs.height(); ++level) {
864
        os << "\"HeadNode";
865
        os << _dot_file_subgraph;
866
        os << "\":f" << level + 1 << " -> ";
867
        _nodeRefs[level].pNode->writeNode(os, _dot_file_subgraph);
868
        os << ":w" << level + 1 << " [];" << std::endl;
869
    }
870
    os << std::endl;
871
    // Now all nodes via level 0, if non-empty
872
    if (_nodeRefs.height()) {
873
        Node<T, _Compare> *pNode = this->_nodeRefs[0].pNode;
874
        pNode->dotFile(os, _dot_file_subgraph);
875
    }
876
    os << std::endl;
877
    // NULL, the sentinal node
878
    if (_nodeRefs.height()) {
879
        os << "\"node";
880
        os << _dot_file_subgraph;
881
        os << "0x0\" [label = \"";
882
        for (size_t level = _nodeRefs.height(); level-- > 0;) {
883
            os << "<w" << level + 1 << "> NULL";
884
            if (level) {
885
                os << " | ";
886
            }
887
        }
888
        os << "\" shape = \"record\"];" << std::endl;
889
    }
890
    // End: "subgraph cluster1 {"
891
    os << "}" << std::endl;
892
    os << std::endl;
893
    _dot_file_subgraph += 1;
894
}
895
896
/**
897
 * Finalise the DOT file of the internal representation.
898
 *
899
 * @tparam T Type of the values in the Skip List.
900
 * @tparam _Compare Compare function.
901
 * @param os Where to write the DOT file.
902
 */
903
template <typename T, typename _Compare>
904
void HeadNode<T, _Compare>::dotFileFinalise(std::ostream &os) const {
905
#ifdef SKIPLIST_THREAD_SUPPORT
906
    std::lock_guard<std::mutex> lock(gSkipListMutex);
907
#endif
908
    if (_dot_file_subgraph > 0) {
909
        // Link the nodes together with an invisible node.
910
        //    node0 [shape=record, label = "<f0> | <f1> | <f2> | <f3> | <f4> | <f5> | <f6> | <f7> | <f8> | ",
911
        //           style=invis,
912
        //           width=0.01];
913
        os << "node0 [shape=record, label = \"";
914
        for (size_t i = 0; i < _dot_file_subgraph; ++i) {
915
            os << "<f" << i << "> | ";
916
        }
917
        os << "\", style=invis, width=0.01];" << std::endl;
918
        // Now:
919
        //    node0:f0 -> HeadNode [style=invis];
920
        //    node0:f1 -> HeadNode1 [style=invis];
921
        for (size_t i = 0; i < _dot_file_subgraph; ++i) {
922
            os << "node0:f" << i << " -> HeadNode" << i;
923
            os << " [style=invis];" << std::endl;
924
        }
925
        _dot_file_subgraph = 0;
926
    }
927
    os << "}" << std::endl;
928
}
929
930
#endif // INCLUDE_METHODS_THAT_USE_STREAMS
931
932
/************************** END: HeadNode *******************************/
933
934
#endif // SkipList_HeadNode_h