Qore Programming Language 2.0.0
Loading...
Searching...
No Matches
safe_dslist
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 safe_dslist
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2024 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_SAFE_DSLIST
33
34#define _QORE_SAFE_DSLIST
35
36#include <stdlib.h>
37#include <string.h>
38
39#include <algorithm>
40#include <functional>
41
42//! defines a node in a safe_dslist
43template<typename T> hashdecl _qore_list_node {
44 typedef _qore_list_node self_t;
45 self_t* next;
46 T data;
47
48 DLLLOCAL _qore_list_node(T d, self_t* n) {
49 next = n;
50 data = d;
51 }
52 DLLLOCAL _qore_list_node(T d) {
53 next = 0;
54 data = d;
55 }
56};
57
58//! defines an iterator for a safe_dslist
59template<typename T> hashdecl _qore_list_iterator {
60 typedef _qore_list_node<T> node_t;
61 typedef _qore_list_iterator<T> self_t;
62
63 // data for the node
64 node_t* node;
65
66 DLLLOCAL _qore_list_iterator(node_t* n) {
67 node = n;
68 }
69 DLLLOCAL _qore_list_iterator() {
70 node = nullptr;
71 }
72 DLLLOCAL self_t operator++(int) {
73 self_t _tmp = *this;
74 node = node->next;
75 return _tmp;
76 }
77 DLLLOCAL self_t operator++() {
78 node = node->next;
79 return *this;
80 }
81 DLLLOCAL T operator*() {
82 return node->data;
83 }
84 DLLLOCAL bool operator!=(const self_t& n) {
85 return n.node != node;
86 }
87 DLLLOCAL bool operator==(const self_t& n) {
88 return n.node == node;
89 }
90};
91
92//! defines a const iterator for a safe_dslist
93template<typename T> hashdecl _qore_list_const_iterator {
94 typedef _qore_list_node<T> node_t;
95 typedef _qore_list_const_iterator<T> self_t;
96
97 // data for the node
98 const node_t* node;
99
100 DLLLOCAL _qore_list_const_iterator(_qore_list_iterator<T> i) {
101 node = i.node;
102 }
103 DLLLOCAL _qore_list_const_iterator(const node_t* n) {
104 node = n;
105 }
106 DLLLOCAL _qore_list_const_iterator() {
107 node = nullptr;
108 }
109 DLLLOCAL self_t operator++(int) {
110 self_t _tmp = *this;
111 node = node->next;
112 return _tmp;
113 }
114 DLLLOCAL self_t operator++() {
115 node = node->next;
116 return *this;
117 }
118 DLLLOCAL T operator*() const {
119 return node->data;
120 }
121 DLLLOCAL bool operator!=(const self_t& n) {
122 return n.node != node;
123 }
124 DLLLOCAL bool operator==(const self_t& n) {
125 return n.node == node;
126 }
127};
128
129//! templated class for a double-ended singly-linked list that can be safely read from multiple threads without locking as long as writes are locked
130/**
131 Reading in multiple threads is safe as long as writes (appends at the end or beginning) are locked.
132 Implements a singly-linked list with constant-time inserts at the beginning and end that
133 can be read in a multi-threaded context without locking. Writes must be performed in a lock; however
134 this class does not provide any locking; locking must be provided and performed externally to the
135 class. Provides an STL-like interface.
136*/
137template<typename T> class safe_dslist {
138public:
139 typedef _qore_list_iterator<T> iterator;
140 typedef _qore_list_const_iterator<T> const_iterator;
141 typedef _qore_list_node<T> node_t;
142 typedef safe_dslist<T> self_t;
143
144protected:
145 DLLLOCAL void clear_intern() {
146 node_t* w = head;
147 while (w) {
148 node_t* n = w->next;
149 delete w;
150 w = n;
151 }
152 }
153
154private:
155 node_t* head = nullptr,
156 * tail = nullptr;
157
158public:
159 DLLLOCAL safe_dslist() {
160 }
161
162 DLLLOCAL ~safe_dslist() {
163 clear_intern();
164 }
165
166 //! empties the list
167 DLLLOCAL void clear() {
168 clear_intern();
169 head = tail = nullptr;
170 }
171
172 //! returns an iterator pointing to the first element of the list
173 DLLLOCAL iterator begin() {
174 return head;
175 }
176
177 //! returns an iterator pointing one element from the end of the list
178 DLLLOCAL iterator end() {
179 return nullptr;
180 }
181
182 //! returns an iterator pointing to the first element of the list
183 DLLLOCAL const_iterator begin() const {
184 return head;
185 }
186
187 //! returns an iterator pointing one element from the end of the list
188 DLLLOCAL const_iterator end() const {
189 return nullptr;
190 }
191
192 //! returns an iterator pointing to the last element in the list
193 DLLLOCAL iterator last() {
194 return tail;
195 }
196
197 //! returns an iterator pointing to the last element in the list
198 DLLLOCAL const_iterator last() const {
199 return tail;
200 }
201
202 //! returns an iterator either pointing to the element given if present in the list or pointing to one element from the end of the list if not
203 DLLLOCAL iterator find(T data) {
204 node_t* w = head;
205 while (w) {
206 if (w->data == data)
207 return w;
208 w = w->next;
209 }
210 return nullptr;
211 }
212
213 //! returns an iterator either pointing to the element given if present in the list or pointing to one element from the end of the list if not
214 DLLLOCAL const_iterator find(T data) const {
215 node_t* w = head;
216 while (w) {
217 if (w->data == data)
218 return w;
219 w = w->next;
220 }
221 return nullptr;
222 }
223
224 //! adds an element to the beginning of the list (constant time)
225 DLLLOCAL void push_front(T data) {
226 node_t* n = new node_t(data, head);
227 if (!tail)
228 tail = n;
229 head = n;
230 }
231
232 //! adds an element to the end of the list (constant time)
233 DLLLOCAL void push_back(T data) {
234 node_t* n = new node_t(data);
235 if (tail)
236 tail->next = n;
237 else
238 head = n;
239 tail = n;
240 }
241
242 //! removes an element from the beginning of the list
243 DLLLOCAL void pop_front() {
244 if (!tail)
245 return;
246 node_t* n = head;
247 head = head->next;
248 delete n;
249 if (!head)
250 tail = nullptr;
251 }
252
253 //! concatenates all elements of this list to the end of the list passed
254 DLLLOCAL void populate(self_t& other) {
255 iterator i = begin();
256 while (i != end()) {
257 other.push_back(*i);
258 ++i;
259 }
260 }
261
262 //! concatenates all elements of this list to the end of the list passed
263 DLLLOCAL void populate(self_t* other) {
264 iterator i = begin();
265 while (i != end()) {
266 other->push_back(*i);
267 ++i;
268 }
269 }
270
271 //! returns true if the list is empty
272 DLLLOCAL bool empty() const {
273 return !head;
274 }
275
276 //! returns true if the list contains only one element (constant time)
277 DLLLOCAL bool singular() const {
278 return head && head == tail;
279 }
280
281 //! returns true if the list contains more than one element (constant time)
282 DLLLOCAL bool plural() const {
283 return head != tail;
284 }
285
286 //! deletes the list element after the iterator argument and all other elements to the end of the list
287 /** O(n) where n=length of list after the element given
288 @note does not erase the element given by the iterator argument
289 */
290 DLLLOCAL void erase_to_end(iterator i) {
291 if (!i.node) {
292 clear();
293 return;
294 }
295
296 node_t* w = i.node->next;
297 i.node->next = nullptr;
298 tail = i.node;
299
300 while (w) {
301 node_t* n = w->next;
302 delete w;
303 w = n;
304 }
305 }
306
307 //! deletes the list element given by the iterator argument
308 /** only constant time for the first element in the list, otherwise is O(n), linear with the length of the list
309 */
310 DLLLOCAL void erase(iterator i) {
311 if (i.node == head) {
312 head = i.node->next;
313 if (!head) {
314 tail = nullptr;
315 }
316 } else {
317 // find previous entry
318 node_t* n = head;
319 while (n->next != i.node) {
320 n = n->next;
321 }
322 n->next = i.node->next;
323 if (i.node == tail) {
324 tail = n;
325 }
326 }
327 delete i.node;
328 }
329
330 DLLLOCAL size_t size() const {
331 size_t i = 0;
332 node_t* n = head;
333 while (n) {
334 ++i;
335 n = n->next;
336 }
337 return i;
338 }
339};
340
341#endif