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.

513 lines
13KB

  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. #else
  64. {
  65. fQueue.next = &fQueue;
  66. fQueue.prev = &fQueue;
  67. }
  68. #endif
  69. public:
  70. virtual ~AbstractLinkedList() noexcept
  71. {
  72. CARLA_SAFE_ASSERT(fCount == 0);
  73. }
  74. class Itenerator {
  75. public:
  76. Itenerator(const ListHead& queue) noexcept
  77. : fEntry(queue.next),
  78. fEntry2(fEntry->next),
  79. kQueue(queue)
  80. {
  81. CARLA_SAFE_ASSERT(fEntry != nullptr);
  82. CARLA_SAFE_ASSERT(fEntry2 != nullptr);
  83. }
  84. bool valid() const noexcept
  85. {
  86. return (fEntry != nullptr && fEntry != &kQueue);
  87. }
  88. void next() noexcept
  89. {
  90. fEntry = fEntry2;
  91. fEntry2 = (fEntry != nullptr) ? fEntry->next : nullptr;
  92. }
  93. T& getValue(T& fallback) const noexcept
  94. {
  95. Data* const data(list_entry(fEntry, Data, siblings));
  96. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  97. return data->value;
  98. }
  99. const T& getValue(const T& fallback) const noexcept
  100. {
  101. const Data* const data(list_entry_const(fEntry, Data, siblings));
  102. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  103. return data->value;
  104. }
  105. void setValue(const T& value) noexcept
  106. {
  107. Data* const data(list_entry(fEntry, Data, siblings));
  108. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  109. data->value = value;
  110. }
  111. // TODO: remove this, fallback should be mandatory
  112. T& getValue() const noexcept
  113. {
  114. Data* const data(list_entry(fEntry, Data, siblings));
  115. return data->value;
  116. }
  117. private:
  118. ListHead* fEntry;
  119. ListHead* fEntry2;
  120. const ListHead& kQueue;
  121. friend class AbstractLinkedList;
  122. };
  123. Itenerator begin() const noexcept
  124. {
  125. return Itenerator(fQueue);
  126. }
  127. void clear() noexcept
  128. {
  129. if (fCount == 0)
  130. return;
  131. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  132. {
  133. Data* const data(list_entry(entry, Data, siblings));
  134. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  135. _deallocate(data);
  136. }
  137. _init();
  138. }
  139. std::size_t count() const noexcept
  140. {
  141. return fCount;
  142. }
  143. bool isEmpty() const noexcept
  144. {
  145. return (fCount == 0);
  146. }
  147. bool append(const T& value) noexcept
  148. {
  149. return _add(value, true, &fQueue);
  150. }
  151. bool appendAt(const T& value, const Itenerator& it) noexcept
  152. {
  153. return _add(value, true, it.fEntry->next);
  154. }
  155. bool insert(const T& value) noexcept
  156. {
  157. return _add(value, false, &fQueue);
  158. }
  159. bool insertAt(const T& value, const Itenerator& it) noexcept
  160. {
  161. return _add(value, false, it.fEntry->prev);
  162. }
  163. T getAt(const std::size_t index, T& fallback, const bool removeObj) noexcept
  164. {
  165. CARLA_SAFE_ASSERT_RETURN(fCount > 0 && index < fCount, fallback);
  166. std::size_t i = 0;
  167. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  168. {
  169. if (index != i++)
  170. continue;
  171. return _get(entry, fallback, removeObj);
  172. }
  173. return fallback;
  174. }
  175. T& getAt(const std::size_t index, T& fallback) const 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);
  184. }
  185. return fallback;
  186. }
  187. const T& getAt(const std::size_t index, const 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& getFirst(const T& fallback) const noexcept
  200. {
  201. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  202. return _get(fQueue.next, fallback);
  203. }
  204. T& getFirst(T& fallback) const noexcept
  205. {
  206. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  207. return _get(fQueue.next, fallback);
  208. }
  209. T getFirst(T& fallback, const bool removeObj) noexcept
  210. {
  211. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  212. return _get(fQueue.next, fallback, removeObj);
  213. }
  214. const T& getLast(const T& fallback) const noexcept
  215. {
  216. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  217. return _get(fQueue.prev, fallback);
  218. }
  219. T& getLast(T& fallback) const noexcept
  220. {
  221. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  222. return _get(fQueue.prev, fallback);
  223. }
  224. T getLast(T& fallback, const bool removeObj) noexcept
  225. {
  226. CARLA_SAFE_ASSERT_RETURN(fCount > 0, fallback);
  227. return _get(fQueue.prev, fallback, removeObj);
  228. }
  229. void remove(Itenerator& it) noexcept
  230. {
  231. CARLA_SAFE_ASSERT_RETURN(it.fEntry != nullptr,);
  232. Data* const data(list_entry(it.fEntry, Data, siblings));
  233. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  234. _delete(it.fEntry, data);
  235. }
  236. bool removeOne(const T& value) noexcept
  237. {
  238. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  239. {
  240. Data* const data = list_entry(entry, Data, siblings);
  241. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  242. if (data->value != value)
  243. continue;
  244. _delete(entry, data);
  245. return true;
  246. }
  247. return false;
  248. }
  249. void removeAll(const T& value) noexcept
  250. {
  251. for (ListHead *entry = fQueue.next, *entry2 = entry->next; entry != &fQueue; entry = entry2, entry2 = entry->next)
  252. {
  253. Data* const data = list_entry(entry, Data, siblings);
  254. CARLA_SAFE_ASSERT_CONTINUE(data != nullptr);
  255. if (data->value != value)
  256. continue;
  257. _delete(entry, data);
  258. }
  259. }
  260. // move data to a new list, and clear ourselves
  261. void moveTo(AbstractLinkedList<T>& list, const bool inTail = true) noexcept
  262. {
  263. CARLA_SAFE_ASSERT_RETURN(fCount > 0,);
  264. if (inTail)
  265. __list_splice_tail(&fQueue, &list.fQueue);
  266. else
  267. __list_splice(&fQueue, &list.fQueue);
  268. //! @a list gets our items
  269. list.fCount += fCount;
  270. //! and we get nothing
  271. _init();
  272. }
  273. protected:
  274. const std::size_t kDataSize;
  275. std::size_t fCount;
  276. ListHead fQueue;
  277. virtual Data* _allocate() noexcept = 0;
  278. virtual void _deallocate(Data* const dataPtr) noexcept = 0;
  279. private:
  280. void _init() noexcept
  281. {
  282. fCount = 0;
  283. fQueue.next = &fQueue;
  284. fQueue.prev = &fQueue;
  285. }
  286. bool _add(const T& value, const bool inTail, ListHead* const queue) noexcept
  287. {
  288. if (Data* const data = _allocate())
  289. return _add_internal(data, value, inTail, queue);
  290. return false;
  291. }
  292. bool _add_internal(Data* const data, const T& value, const bool inTail, ListHead* const queue) noexcept
  293. {
  294. CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
  295. CARLA_SAFE_ASSERT_RETURN(queue != nullptr, false);
  296. CARLA_SAFE_ASSERT_RETURN(queue->prev != nullptr, false);
  297. CARLA_SAFE_ASSERT_RETURN(queue->next != nullptr, false);
  298. data->value = value;
  299. ListHead* const siblings(&data->siblings);
  300. if (inTail)
  301. {
  302. siblings->prev = queue->prev;
  303. siblings->next = queue;
  304. queue->prev->next = siblings;
  305. queue->prev = siblings;
  306. }
  307. else
  308. {
  309. siblings->prev = queue;
  310. siblings->next = queue->next;
  311. queue->next->prev = siblings;
  312. queue->next = siblings;
  313. }
  314. ++fCount;
  315. return true;
  316. }
  317. void _delete(ListHead* const entry, Data* const data) noexcept
  318. {
  319. CARLA_SAFE_ASSERT_RETURN(entry != nullptr,);
  320. CARLA_SAFE_ASSERT_RETURN(entry->prev != nullptr,);
  321. CARLA_SAFE_ASSERT_RETURN(entry->next != nullptr,);
  322. --fCount;
  323. entry->next->prev = entry->prev;
  324. entry->prev->next = entry->next;
  325. entry->next = nullptr;
  326. entry->prev = nullptr;
  327. _deallocate(data);
  328. }
  329. T _get(ListHead* const entry, T& fallback, const bool removeObj) noexcept
  330. {
  331. Data* const data(list_entry(entry, Data, siblings));
  332. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  333. if (! removeObj)
  334. return data->value;
  335. const T value(data->value);
  336. _delete(entry, data);
  337. return value;
  338. }
  339. T& _get(ListHead* const entry, T& fallback) const noexcept
  340. {
  341. Data* const data(list_entry(entry, Data, siblings));
  342. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  343. return data->value;
  344. }
  345. const T& _get(ListHead* const entry, const T& fallback) const noexcept
  346. {
  347. const Data* const data(list_entry_const(entry, Data, siblings));
  348. CARLA_SAFE_ASSERT_RETURN(data != nullptr, fallback);
  349. return data->value;
  350. }
  351. static void __list_splice(ListHead* const list, ListHead* const head) noexcept
  352. {
  353. ListHead* const first = list->next;
  354. ListHead* const last = list->prev;
  355. ListHead* const at = head->next;
  356. first->prev = head;
  357. head->next = first;
  358. last->next = at;
  359. at->prev = last;
  360. }
  361. static void __list_splice_tail(ListHead* const list, ListHead* const head) noexcept
  362. {
  363. ListHead* const first = list->next;
  364. ListHead* const last = list->prev;
  365. ListHead* const at = head->prev;
  366. first->prev = at;
  367. at->next = first;
  368. last->next = head;
  369. head->prev = last;
  370. }
  371. template<typename> friend class RtLinkedList;
  372. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  373. CARLA_DECLARE_NON_COPY_CLASS(AbstractLinkedList)
  374. };
  375. // -----------------------------------------------------------------------
  376. // LinkedList
  377. template<typename T>
  378. class LinkedList : public AbstractLinkedList<T>
  379. {
  380. public:
  381. LinkedList() noexcept {}
  382. protected:
  383. typename AbstractLinkedList<T>::Data* _allocate() noexcept override
  384. {
  385. return (typename AbstractLinkedList<T>::Data*)std::malloc(this->kDataSize);
  386. }
  387. void _deallocate(typename AbstractLinkedList<T>::Data* const dataPtr) noexcept override
  388. {
  389. std::free(dataPtr);
  390. }
  391. CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
  392. CARLA_DECLARE_NON_COPY_CLASS(LinkedList)
  393. };
  394. // -----------------------------------------------------------------------
  395. #endif // LINKED_LIST_HPP_INCLUDED