Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

533 lines
14KB

  1. /*
  2. * High-level, templated, C++ doubly-linked list
  3. * Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
  4. *
  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.
  9. *
  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.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #ifndef LINKED_LIST_HPP_INCLUDED
  18. #define LINKED_LIST_HPP_INCLUDED
  19. #include "CarlaUtils.hpp"
  20. // -----------------------------------------------------------------------
  21. // Define list_entry and list_entry_const
  22. #ifndef offsetof
  23. # define offsetof(TYPE, MEMBER) ((std::size_t) &((TYPE*)nullptr)->MEMBER)
  24. #endif
  25. #if (defined(__GNUC__) || defined(__clang__)) && ! defined(__STRICT_ANSI__)
  26. # define container_of(ptr, type, member) ({ \
  27. typeof( ((type*)nullptr)->member ) *__mptr = (ptr); \
  28. (type*)( (char*)__mptr - offsetof(type, member) );})
  29. # define container_of_const(ptr, type, member) ({ \
  30. const typeof( ((type*)nullptr)->member ) *__mptr = (ptr); \
  31. (const type*)( (const char*)__mptr - offsetof(type, member) );})
  32. # define list_entry(ptr, type, member) \
  33. container_of(ptr, type, member)
  34. # define list_entry_const(ptr, type, member) \
  35. container_of_const(ptr, type, member)
  36. #else
  37. # define list_entry(ptr, type, member) \
  38. ((type*)((char*)(ptr)-offsetof(type, member)))
  39. # define list_entry_const(ptr, type, member) \
  40. ((const type*)((const char*)(ptr)-offsetof(type, member)))
  41. #endif
  42. // -----------------------------------------------------------------------
  43. // Abstract Linked List class
  44. // _allocate() and _deallocate are virtual calls provided by subclasses
  45. // NOTE: this class is meant for non-polymorphic data types only!
  46. template<typename T>
  47. class AbstractLinkedList
  48. {
  49. protected:
  50. struct ListHead {
  51. ListHead* next;
  52. ListHead* prev;
  53. };
  54. struct Data {
  55. T value;
  56. ListHead siblings;
  57. };
  58. AbstractLinkedList() noexcept
  59. : kDataSize(sizeof(Data)),
  60. fCount(0),
  61. #ifdef CARLA_PROPER_CPP11_SUPPORT
  62. fQueue({&fQueue, &fQueue}),
  63. #endif
  64. fUsingItenerator(false)
  65. {
  66. #ifndef CARLA_PROPER_CPP11_SUPPORT
  67. fQueue.next = &fQueue;
  68. fQueue.prev = &fQueue;
  69. #endif
  70. }
  71. public:
  72. virtual ~AbstractLinkedList() noexcept
  73. {
  74. CARLA_SAFE_ASSERT(fCount == 0);
  75. }
  76. class Itenerator {
  77. public:
  78. Itenerator(const ListHead& queue, bool* usingItenerator) noexcept
  79. : fEntry(queue.next),
  80. fEntry2(fEntry->next),
  81. kQueue(queue),
  82. fUsingItenerator(usingItenerator)
  83. {
  84. CARLA_SAFE_ASSERT(fEntry != nullptr);
  85. CARLA_SAFE_ASSERT(fEntry2 != nullptr);
  86. }
  87. ~Itenerator()
  88. {
  89. if (fUsingItenerator != nullptr)
  90. *fUsingItenerator = false;
  91. }
  92. bool valid() const noexcept
  93. {
  94. return (fEntry != nullptr && fEntry != &kQueue);
  95. }
  96. void next() noexcept
  97. {
  98. fEntry = fEntry2;
  99. fEntry2 = (fEntry != nullptr) ? fEntry->next : nullptr;
  100. }
  101. T& getValue(T& fallback) const noexcept
  102. {
  103. Data* const data(list_entry(fEntry, Data, siblings));
  104. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  105. return data->value;
  106. }
  107. const T& getValue(const T& fallback) const noexcept
  108. {
  109. const Data* const data(list_entry_const(fEntry, Data, siblings));
  110. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  111. return data->value;
  112. }
  113. void setValue(const T& value) noexcept
  114. {
  115. Data* const data(list_entry(fEntry, Data, siblings));
  116. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  117. data->value = value;
  118. }
  119. // TODO: remove this, fallback should be mandatory
  120. T& getValue() const noexcept
  121. {
  122. Data* const data(list_entry(fEntry, Data, siblings));
  123. return data->value;
  124. }
  125. private:
  126. ListHead* fEntry;
  127. ListHead* fEntry2;
  128. const ListHead& kQueue;
  129. bool* const fUsingItenerator;
  130. friend class AbstractLinkedList;
  131. };
  132. Itenerator begin() const noexcept
  133. {
  134. static const ListHead fallback = { nullptr, nullptr };
  135. CARLA_SAFE_ASSERT_RETURN(! fUsingItenerator, Itenerator(fallback, nullptr));
  136. fUsingItenerator = true;
  137. return Itenerator(fQueue, &fUsingItenerator);
  138. }
  139. void clear() noexcept
  140. {
  141. if (fCount == 0)
  142. return;
  143. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  144. {
  145. Data* const data(list_entry(entry, Data, siblings));
  146. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  147. _deallocate(data);
  148. }
  149. _init();
  150. }
  151. std::size_t count() const noexcept
  152. {
  153. return fCount;
  154. }
  155. bool isEmpty() const noexcept
  156. {
  157. return (fCount == 0);
  158. }
  159. bool append(const T& value) noexcept
  160. {
  161. return _add(value, true, &fQueue);
  162. }
  163. bool appendAt(const T& value, const Itenerator& it) noexcept
  164. {
  165. return _add(value, true, it.fEntry->next);
  166. }
  167. bool insert(const T& value) noexcept
  168. {
  169. return _add(value, false, &fQueue);
  170. }
  171. bool insertAt(const T& value, const Itenerator& it) noexcept
  172. {
  173. return _add(value, false, it.fEntry->prev);
  174. }
  175. T getAt(const std::size_t index, T& fallback, const bool removeObj) noexcept
  176. {
  177. CARLA_SAFE_ASSERT_RETURN(fCount > 0 && index < fCount, fallback);
  178. std::size_t i = 0;
  179. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  180. {
  181. if (index != i++)
  182. continue;
  183. return _get(entry, fallback, removeObj);
  184. }
  185. return fallback;
  186. }
  187. T& getAt(const std::size_t index, T& fallback) const noexcept
  188. {
  189. CARLA_SAFE_ASSERT_RETURN(fCount > 0 && index < fCount, fallback);
  190. std::size_t i = 0;
  191. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  192. {
  193. if (index != i++)
  194. continue;
  195. return _get(entry, fallback);
  196. }
  197. return fallback;
  198. }
  199. const T& getAt(const std::size_t index, const T& fallback) const noexcept
  200. {
  201. CARLA_SAFE_ASSERT_RETURN(fCount > 0 && index < fCount, fallback);
  202. std::size_t i = 0;
  203. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  204. {
  205. if (index != i++)
  206. continue;
  207. return _get(entry, fallback);
  208. }
  209. return fallback;
  210. }
  211. const T& getFirst(const T& fallback) const noexcept
  212. {
  213. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  214. return _get(fQueue.next, fallback);
  215. }
  216. T& getFirst(T& fallback) const noexcept
  217. {
  218. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  219. return _get(fQueue.next, fallback);
  220. }
  221. T getFirst(T& fallback, const bool removeObj) noexcept
  222. {
  223. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  224. return _get(fQueue.next, fallback, removeObj);
  225. }
  226. const T& getLast(const T& fallback) const noexcept
  227. {
  228. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  229. return _get(fQueue.prev, fallback);
  230. }
  231. T& getLast(T& fallback) const noexcept
  232. {
  233. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  234. return _get(fQueue.prev, fallback);
  235. }
  236. T getLast(T& fallback, const bool removeObj) noexcept
  237. {
  238. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  239. return _get(fQueue.prev, fallback, removeObj);
  240. }
  241. void remove(Itenerator& it) noexcept
  242. {
  243. CARLA_SAFE_ASSERT_RETURN(it.fEntry != nullptr,);
  244. Data* const data(list_entry(it.fEntry, Data, siblings));
  245. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  246. _delete(it.fEntry, data);
  247. }
  248. bool removeOne(const T& value) noexcept
  249. {
  250. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  251. {
  252. Data* const data = list_entry(entry, Data, siblings);
  253. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  254. if (data->value != value)
  255. continue;
  256. _delete(entry, data);
  257. return true;
  258. }
  259. return false;
  260. }
  261. void removeAll(const T& value) noexcept
  262. {
  263. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  264. {
  265. Data* const data = list_entry(entry, Data, siblings);
  266. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  267. if (data->value != value)
  268. continue;
  269. _delete(entry, data);
  270. }
  271. }
  272. // move data to a new list, and clear ourselves
  273. bool moveTo(AbstractLinkedList<T>& list, const bool inTail = true) noexcept
  274. {
  275. CARLA_SAFE_ASSERT_RETURN(fCount > 0, false);
  276. CARLA_SAFE_ASSERT_RETURN(! fUsingItenerator, false);
  277. CARLA_SAFE_ASSERT_RETURN(! list.fUsingItenerator, false);
  278. if (inTail)
  279. __list_splice_tail(&fQueue, &list.fQueue);
  280. else
  281. __list_splice(&fQueue, &list.fQueue);
  282. //! @a list gets our items
  283. list.fCount += fCount;
  284. //! and we get nothing
  285. _init();
  286. return true;
  287. }
  288. protected:
  289. const std::size_t kDataSize;
  290. std::size_t fCount;
  291. ListHead fQueue;
  292. mutable bool fUsingItenerator;
  293. virtual Data* _allocate() noexcept = 0;
  294. virtual void _deallocate(Data* const dataPtr) noexcept = 0;
  295. private:
  296. void _init() noexcept
  297. {
  298. fCount = 0;
  299. fQueue.next = &fQueue;
  300. fQueue.prev = &fQueue;
  301. }
  302. bool _add(const T& value, const bool inTail, ListHead* const queue) noexcept
  303. {
  304. if (Data* const data = _allocate())
  305. return _add_internal(data, value, inTail, queue);
  306. return false;
  307. }
  308. bool _add_internal(Data* const data, const T& value, const bool inTail, ListHead* const queue) noexcept
  309. {
  310. CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
  311. CARLA_SAFE_ASSERT_RETURN(queue != nullptr, false);
  312. CARLA_SAFE_ASSERT_RETURN(queue->prev != nullptr, false);
  313. CARLA_SAFE_ASSERT_RETURN(queue->next != nullptr, false);
  314. data->value = value;
  315. ListHead* const siblings(&data->siblings);
  316. if (inTail)
  317. {
  318. siblings->prev = queue->prev;
  319. siblings->next = queue;
  320. queue->prev->next = siblings;
  321. queue->prev = siblings;
  322. }
  323. else
  324. {
  325. siblings->prev = queue;
  326. siblings->next = queue->next;
  327. queue->next->prev = siblings;
  328. queue->next = siblings;
  329. }
  330. ++fCount;
  331. return true;
  332. }
  333. void _delete(ListHead* const entry, Data* const data) noexcept
  334. {
  335. CARLA_SAFE_ASSERT_RETURN(entry != nullptr,);
  336. CARLA_SAFE_ASSERT_RETURN(entry->prev != nullptr,);
  337. CARLA_SAFE_ASSERT_RETURN(entry->next != nullptr,);
  338. --fCount;
  339. entry->next->prev = entry->prev;
  340. entry->prev->next = entry->next;
  341. entry->next = nullptr;
  342. entry->prev = nullptr;
  343. _deallocate(data);
  344. }
  345. T _get(ListHead* const entry, T& fallback, const bool removeObj) noexcept
  346. {
  347. Data* const data(list_entry(entry, Data, siblings));
  348. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  349. if (! removeObj)
  350. return data->value;
  351. const T value(data->value);
  352. _delete(entry, data);
  353. return value;
  354. }
  355. T& _get(ListHead* const entry, T& fallback) const noexcept
  356. {
  357. Data* const data(list_entry(entry, Data, siblings));
  358. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  359. return data->value;
  360. }
  361. const T& _get(ListHead* const entry, const T& fallback) const noexcept
  362. {
  363. const Data* const data(list_entry_const(entry, Data, siblings));
  364. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  365. return data->value;
  366. }
  367. static void __list_splice(ListHead* const list, ListHead* const head) noexcept
  368. {
  369. ListHead* const first = list->next;
  370. ListHead* const last = list->prev;
  371. ListHead* const at = head->next;
  372. first->prev = head;
  373. head->next = first;
  374. last->next = at;
  375. at->prev = last;
  376. }
  377. static void __list_splice_tail(ListHead* const list, ListHead* const head) noexcept
  378. {
  379. ListHead* const first = list->next;
  380. ListHead* const last = list->prev;
  381. ListHead* const at = head->prev;
  382. first->prev = at;
  383. at->next = first;
  384. last->next = head;
  385. head->prev = last;
  386. }
  387. template<typename> friend class RtLinkedList;
  388. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  389. CARLA_DECLARE_NON_COPY_CLASS(AbstractLinkedList)
  390. };
  391. // -----------------------------------------------------------------------
  392. // LinkedList
  393. template<typename T>
  394. class LinkedList : public AbstractLinkedList<T>
  395. {
  396. public:
  397. LinkedList() noexcept {}
  398. protected:
  399. typename AbstractLinkedList<T>::Data* _allocate() noexcept override
  400. {
  401. return (typename AbstractLinkedList<T>::Data*)std::malloc(this->kDataSize);
  402. }
  403. void _deallocate(typename AbstractLinkedList<T>::Data* const dataPtr) noexcept override
  404. {
  405. std::free(dataPtr);
  406. }
  407. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  408. CARLA_DECLARE_NON_COPY_CLASS(LinkedList)
  409. };
  410. // -----------------------------------------------------------------------
  411. #endif // LINKED_LIST_HPP_INCLUDED