2 * High-level, templated, C++ doubly-linked list
3 * Copyright (C) 2013-2022 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef LINKED_LIST_HPP_INCLUDED
19 #define LINKED_LIST_HPP_INCLUDED
21 #include "CarlaUtils.hpp"
23 // -----------------------------------------------------------------------
24 // Define list_entry and list_entry_const
27 # define offsetof(TYPE, MEMBER) ((std::size_t) &((TYPE*)nullptr)->MEMBER)
30 #if (defined(__GNUC__) || defined(__clang__)) && ! defined(__STRICT_ANSI__)
31 # define list_entry(ptr, type, member) ({ \
32 typeof( ((type*)nullptr)->member ) *__mptr = (ptr); \
33 (type*)( (char*)__mptr - offsetof(type, member) );})
34 # define list_entry_const(ptr, type, member) ({ \
35 const typeof( ((type*)nullptr)->member ) *__mptr = (ptr); \
36 (const type*)( (const char*)__mptr - offsetof(type, member) );})
38 # define list_entry(ptr, type, member) \
39 ((type*)((char*)(ptr)-offsetof(type, member)))
40 # define list_entry_const(ptr, type, member) \
41 ((const type*)((const char*)(ptr)-offsetof(type, member)))
44 // -----------------------------------------------------------------------
45 // Abstract Linked List class
46 // _allocate() and _deallocate are virtual calls provided by subclasses
48 // NOTE: this class is meant for non-polymorphic data types only!
51 class AbstractLinkedList
64 AbstractLinkedList() noexcept
65 : kDataSize(sizeof(Data
)),
66 #ifdef CARLA_PROPER_CPP11_SUPPORT
67 fQueue({&fQueue
, &fQueue
}),
71 #ifndef CARLA_PROPER_CPP11_SUPPORT
72 fQueue
.next
= &fQueue
;
73 fQueue
.prev
= &fQueue
;
78 virtual ~AbstractLinkedList() noexcept
80 CARLA_SAFE_ASSERT(fCount
== 0);
85 Itenerator(const ListHead
& queue
) noexcept
87 fEntry2(fEntry
->next
),
90 CARLA_SAFE_ASSERT(fEntry
!= nullptr);
91 CARLA_SAFE_ASSERT(fEntry2
!= nullptr);
94 bool valid() const noexcept
96 return (fEntry
!= nullptr && fEntry
!= &kQueue
);
102 fEntry2
= (fEntry
!= nullptr) ? fEntry
->next
: nullptr;
105 T
& getValue(T
& fallback
) const noexcept
107 Data
* const data(list_entry(fEntry
, Data
, siblings
));
108 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
113 const T
& getValue(const T
& fallback
) const noexcept
115 const Data
* const data(list_entry_const(fEntry
, Data
, siblings
));
116 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
121 void setValue(const T
& value
) noexcept
123 Data
* const data(list_entry(fEntry
, Data
, siblings
));
124 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr,);
132 const ListHead
& kQueue
;
134 friend class AbstractLinkedList
;
137 class AutoItenerator
{
139 AutoItenerator(const ListHead
* entry
) noexcept
141 fEntry2(entry
!= nullptr ? entry
->next
: nullptr)
143 CARLA_SAFE_ASSERT(fEntry
!= nullptr);
144 CARLA_SAFE_ASSERT(fEntry2
!= nullptr);
147 bool operator!=(AutoItenerator
& it
) const noexcept
149 CARLA_SAFE_ASSERT_RETURN(fEntry
!= nullptr, false);
150 CARLA_SAFE_ASSERT_RETURN(it
.fEntry
!= nullptr, false);
152 return fEntry
!= it
.fEntry
;
155 AutoItenerator
& operator++() noexcept
158 fEntry2
= (fEntry
!= nullptr) ? fEntry
->next
: nullptr;
163 T
& operator*() noexcept
165 static T
& fallback(_getFallback());
167 Data
* const data(list_entry(fEntry
, Data
, siblings
));
168 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
174 const T
& operator*() const noexcept
176 static const T
& fallback(_getFallback());
178 const Data
* const data(list_entry_const(fEntry
, Data
, siblings
));
179 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
185 const ListHead
* fEntry
;
186 const ListHead
* fEntry2
;
188 static T
& _getFallback()
191 carla_zeroStruct(data
);
196 Itenerator
begin2() const noexcept
198 return Itenerator(fQueue
);
201 AutoItenerator
begin() const noexcept
203 return AutoItenerator(fQueue
.next
);
206 AutoItenerator
end() const noexcept
208 return AutoItenerator(&fQueue
);
211 void clear() noexcept
216 for (ListHead
*entry
= fQueue
.next
, *entry2
= entry
->next
; entry
!= &fQueue
; entry
= entry2
, entry2
= entry
->next
)
218 Data
* const data(list_entry(entry
, Data
, siblings
));
219 CARLA_SAFE_ASSERT_CONTINUE(data
!= nullptr);
227 inline std::size_t count() const noexcept
232 inline bool isEmpty() const noexcept
237 inline bool isNotEmpty() const noexcept
242 bool append(const T
& value
) noexcept
244 return _add(value
, true, &fQueue
);
247 bool appendAt(const T
& value
, const Itenerator
& it
) noexcept
249 return _add(value
, true, it
.fEntry
->next
);
252 bool insert(const T
& value
) noexcept
254 return _add(value
, false, &fQueue
);
257 bool insertAt(const T
& value
, const Itenerator
& it
) noexcept
259 return _add(value
, false, it
.fEntry
->prev
);
262 // NOTE: do not use this function unless strictly needed. it can be very expensive if the list is big
263 const T
& getAt(const std::size_t index
, const T
& fallback
) const noexcept
265 CARLA_SAFE_ASSERT_UINT2_RETURN(fCount
> 0 && index
< fCount
, index
, fCount
, fallback
);
268 ListHead
* entry
= fQueue
.next
;
270 for (; i
++ != index
; entry
= entry
->next
) {}
272 return _get(entry
, fallback
);
275 T
getFirst(T
& fallback
, const bool removeObj
) noexcept
277 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
279 return _get(fQueue
.next
, fallback
, removeObj
);
282 T
& getFirst(T
& fallback
) const noexcept
284 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
286 return _get(fQueue
.next
, fallback
);
289 const T
& getFirst(const T
& fallback
) const noexcept
291 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
293 return _get(fQueue
.next
, fallback
);
296 T
getLast(T
& fallback
, const bool removeObj
) noexcept
298 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
300 return _get(fQueue
.prev
, fallback
, removeObj
);
303 T
& getLast(T
& fallback
) const noexcept
305 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
307 return _get(fQueue
.prev
, fallback
);
310 const T
& getLast(const T
& fallback
) const noexcept
312 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, fallback
);
314 return _get(fQueue
.prev
, fallback
);
317 void remove(Itenerator
& it
) noexcept
319 CARLA_SAFE_ASSERT_RETURN(it
.fEntry
!= nullptr,);
321 Data
* const data(list_entry(it
.fEntry
, Data
, siblings
));
322 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr,);
324 _delete(it
.fEntry
, data
);
327 bool removeOne(const T
& value
) noexcept
329 for (ListHead
*entry
= fQueue
.next
, *entry2
= entry
->next
; entry
!= &fQueue
; entry
= entry2
, entry2
= entry
->next
)
331 Data
* const data
= list_entry(entry
, Data
, siblings
);
332 CARLA_SAFE_ASSERT_CONTINUE(data
!= nullptr);
334 if (data
->value
!= value
)
337 _delete(entry
, data
);
345 void removeAll(const T
& value
) noexcept
347 for (ListHead
*entry
= fQueue
.next
, *entry2
= entry
->next
; entry
!= &fQueue
; entry
= entry2
, entry2
= entry
->next
)
349 Data
* const data
= list_entry(entry
, Data
, siblings
);
350 CARLA_SAFE_ASSERT_CONTINUE(data
!= nullptr);
352 if (data
->value
!= value
)
355 _delete(entry
, data
);
359 // move data to a new list, and clear ourselves
360 virtual bool moveTo(AbstractLinkedList
<T
>& list
, const bool inTail
= true) noexcept
362 CARLA_SAFE_ASSERT_RETURN(fCount
> 0, false);
365 __list_splice_tail(&fQueue
, &list
.fQueue
);
367 __list_splice(&fQueue
, &list
.fQueue
);
369 //! @a list gets our items
370 list
.fCount
+= fCount
;
372 //! and we get nothing
379 const std::size_t kDataSize
;
384 virtual Data
* _allocate() noexcept
= 0;
385 virtual void _deallocate(Data
* const dataPtr
) noexcept
= 0;
388 void _init() noexcept
391 fQueue
.next
= &fQueue
;
392 fQueue
.prev
= &fQueue
;
395 bool _add(const T
& value
, const bool inTail
, ListHead
* const queue
) noexcept
397 if (Data
* const data
= _allocate())
398 return _add_internal(data
, value
, inTail
, queue
);
402 bool _add_internal(Data
* const data
, const T
& value
, const bool inTail
, ListHead
* const queue
) noexcept
404 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, false);
405 CARLA_SAFE_ASSERT_RETURN(queue
!= nullptr, false);
406 CARLA_SAFE_ASSERT_RETURN(queue
->prev
!= nullptr, false);
407 CARLA_SAFE_ASSERT_RETURN(queue
->next
!= nullptr, false);
411 ListHead
* const siblings(&data
->siblings
);
415 siblings
->prev
= queue
->prev
;
416 siblings
->next
= queue
;
418 queue
->prev
->next
= siblings
;
419 queue
->prev
= siblings
;
423 siblings
->prev
= queue
;
424 siblings
->next
= queue
->next
;
426 queue
->next
->prev
= siblings
;
427 queue
->next
= siblings
;
434 void _delete(ListHead
* const entry
, Data
* const data
) noexcept
436 CARLA_SAFE_ASSERT_RETURN(entry
!= nullptr,);
437 CARLA_SAFE_ASSERT_RETURN(entry
->prev
!= nullptr,);
438 CARLA_SAFE_ASSERT_RETURN(entry
->next
!= nullptr,);
442 entry
->next
->prev
= entry
->prev
;
443 entry
->prev
->next
= entry
->next
;
445 entry
->next
= nullptr;
446 entry
->prev
= nullptr;
451 T
_get(ListHead
* const entry
, T
& fallback
, const bool removeObj
) noexcept
453 Data
* const data(list_entry(entry
, Data
, siblings
));
454 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
459 const T
value(data
->value
);
461 _delete(entry
, data
);
466 T
& _get(ListHead
* const entry
, T
& fallback
) const noexcept
468 Data
* const data(list_entry(entry
, Data
, siblings
));
469 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
474 const T
& _get(const ListHead
* const entry
, const T
& fallback
) const noexcept
476 const Data
* const data(list_entry_const(entry
, Data
, siblings
));
477 CARLA_SAFE_ASSERT_RETURN(data
!= nullptr, fallback
);
482 static void __list_splice(ListHead
* const list
, ListHead
* const head
) noexcept
484 ListHead
* const first
= list
->next
;
485 ListHead
* const last
= list
->prev
;
486 ListHead
* const at
= head
->next
;
495 static void __list_splice_tail(ListHead
* const list
, ListHead
* const head
) noexcept
497 ListHead
* const first
= list
->next
;
498 ListHead
* const last
= list
->prev
;
499 ListHead
* const at
= head
->prev
;
508 template<typename
> friend class RtLinkedList
;
510 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
511 CARLA_DECLARE_NON_COPYABLE(AbstractLinkedList
)
514 // -----------------------------------------------------------------------
518 class LinkedList
: public AbstractLinkedList
<T
>
521 LinkedList() noexcept
{}
524 typename AbstractLinkedList
<T
>::Data
* _allocate() noexcept override
526 return (typename AbstractLinkedList
<T
>::Data
*)std::malloc(this->kDataSize
);
529 void _deallocate(typename AbstractLinkedList
<T
>::Data
* const dataPtr
) noexcept override
534 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
535 CARLA_DECLARE_NON_COPYABLE(LinkedList
)
538 // -----------------------------------------------------------------------
540 #endif // LINKED_LIST_HPP_INCLUDED