Fix last commit
[carla.git] / source / utils / LinkedList.hpp
blob451cb1f761a1a156f7cf158a4794c943d03324cf
1 /*
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
26 #ifndef offsetof
27 # define offsetof(TYPE, MEMBER) ((std::size_t) &((TYPE*)nullptr)->MEMBER)
28 #endif
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) );})
37 #else
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)))
42 #endif
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!
50 template<typename T>
51 class AbstractLinkedList
53 protected:
54 struct ListHead {
55 ListHead* next;
56 ListHead* prev;
59 struct Data {
60 T value;
61 ListHead siblings;
64 AbstractLinkedList() noexcept
65 : kDataSize(sizeof(Data)),
66 #ifdef CARLA_PROPER_CPP11_SUPPORT
67 fQueue({&fQueue, &fQueue}),
68 #endif
69 fCount(0)
71 #ifndef CARLA_PROPER_CPP11_SUPPORT
72 fQueue.next = &fQueue;
73 fQueue.prev = &fQueue;
74 #endif
77 public:
78 virtual ~AbstractLinkedList() noexcept
80 CARLA_SAFE_ASSERT(fCount == 0);
83 class Itenerator {
84 public:
85 Itenerator(const ListHead& queue) noexcept
86 : fEntry(queue.next),
87 fEntry2(fEntry->next),
88 kQueue(queue)
90 CARLA_SAFE_ASSERT(fEntry != nullptr);
91 CARLA_SAFE_ASSERT(fEntry2 != nullptr);
94 bool valid() const noexcept
96 return (fEntry != nullptr && fEntry != &kQueue);
99 void next() noexcept
101 fEntry = fEntry2;
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);
110 return data->value;
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);
118 return data->value;
121 void setValue(const T& value) noexcept
123 Data* const data(list_entry(fEntry, Data, siblings));
124 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
126 data->value = value;
129 private:
130 ListHead* fEntry;
131 ListHead* fEntry2;
132 const ListHead& kQueue;
134 friend class AbstractLinkedList;
137 class AutoItenerator {
138 public:
139 AutoItenerator(const ListHead* entry) noexcept
140 : fEntry(entry),
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
157 fEntry = fEntry2;
158 fEntry2 = (fEntry != nullptr) ? fEntry->next : nullptr;
159 return *this;
162 #if 0
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);
170 return data->value;
172 #endif
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);
181 return data->value;
184 private:
185 const ListHead* fEntry;
186 const ListHead* fEntry2;
188 static T& _getFallback()
190 static T data;
191 carla_zeroStruct(data);
192 return 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
213 if (fCount == 0)
214 return;
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);
221 _deallocate(data);
224 _init();
227 inline std::size_t count() const noexcept
229 return fCount;
232 inline bool isEmpty() const noexcept
234 return fCount == 0;
237 inline bool isNotEmpty() const noexcept
239 return fCount != 0;
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);
267 std::size_t i = 0;
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)
335 continue;
337 _delete(entry, data);
339 return true;
342 return false;
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)
353 continue;
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);
364 if (inTail)
365 __list_splice_tail(&fQueue, &list.fQueue);
366 else
367 __list_splice(&fQueue, &list.fQueue);
369 //! @a list gets our items
370 list.fCount += fCount;
372 //! and we get nothing
373 _init();
375 return true;
378 protected:
379 const std::size_t kDataSize;
381 ListHead fQueue;
382 std::size_t fCount;
384 virtual Data* _allocate() noexcept = 0;
385 virtual void _deallocate(Data* const dataPtr) noexcept = 0;
387 private:
388 void _init() noexcept
390 fCount = 0;
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);
399 return false;
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);
409 data->value = value;
411 ListHead* const siblings(&data->siblings);
413 if (inTail)
415 siblings->prev = queue->prev;
416 siblings->next = queue;
418 queue->prev->next = siblings;
419 queue->prev = siblings;
421 else
423 siblings->prev = queue;
424 siblings->next = queue->next;
426 queue->next->prev = siblings;
427 queue->next = siblings;
430 ++fCount;
431 return true;
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,);
440 --fCount;
442 entry->next->prev = entry->prev;
443 entry->prev->next = entry->next;
445 entry->next = nullptr;
446 entry->prev = nullptr;
448 _deallocate(data);
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);
456 if (! removeObj)
457 return data->value;
459 const T value(data->value);
461 _delete(entry, data);
463 return value;
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);
471 return data->value;
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);
479 return data->value;
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;
488 first->prev = head;
489 head->next = first;
491 last->next = at;
492 at->prev = last;
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;
501 first->prev = at;
502 at->next = first;
504 last->next = head;
505 head->prev = last;
508 template<typename> friend class RtLinkedList;
510 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
511 CARLA_DECLARE_NON_COPYABLE(AbstractLinkedList)
514 // -----------------------------------------------------------------------
515 // LinkedList
517 template<typename T>
518 class LinkedList : public AbstractLinkedList<T>
520 public:
521 LinkedList() noexcept {}
523 protected:
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
531 std::free(dataPtr);
534 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
535 CARLA_DECLARE_NON_COPYABLE(LinkedList)
538 // -----------------------------------------------------------------------
540 #endif // LINKED_LIST_HPP_INCLUDED