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.

583 lines
13KB

  1. /*
  2. * High-level, real-time safe, templated, C++ doubly-linked list
  3. * Copyright (C) 2013 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 RT_LIST_HPP_INCLUDED
  18. #define RT_LIST_HPP_INCLUDED
  19. #include "CarlaUtils.hpp"
  20. extern "C" {
  21. #include "rtmempool/list.h"
  22. #include "rtmempool/rtmempool.h"
  23. }
  24. // Declare non copyable and prevent heap allocation
  25. #define LIST_DECLARATIONS(ClassName) \
  26. ClassName(ClassName&); \
  27. ClassName(const ClassName&); \
  28. ClassName& operator=(const ClassName&); \
  29. static void* operator new (size_t) { return nullptr; } \
  30. static void operator delete (void*) {}
  31. typedef struct list_head k_list_head;
  32. // -----------------------------------------------------------------------
  33. // Abstract List class
  34. // _allocate() and _deallocate are virtual calls provided by subclasses
  35. template<typename T>
  36. class List
  37. {
  38. protected:
  39. struct Data {
  40. T value;
  41. k_list_head siblings;
  42. };
  43. List()
  44. : fDataSize(sizeof(Data)),
  45. fCount(0)
  46. {
  47. _init();
  48. }
  49. virtual ~List()
  50. {
  51. CARLA_ASSERT(fCount == 0);
  52. }
  53. public:
  54. class Itenerator {
  55. public:
  56. Itenerator(const k_list_head* queue)
  57. : fData(nullptr),
  58. fEntry(queue->next),
  59. fEntry2(fEntry->next),
  60. fQueue(queue)
  61. {
  62. CARLA_ASSERT(fEntry != nullptr);
  63. CARLA_ASSERT(fEntry2 != nullptr);
  64. CARLA_ASSERT(fQueue != nullptr);
  65. }
  66. bool valid() const noexcept
  67. {
  68. return (fEntry != fQueue);
  69. }
  70. void next() noexcept
  71. {
  72. fEntry = fEntry2;
  73. fEntry2 = fEntry->next;
  74. }
  75. T& operator*()
  76. {
  77. fData = list_entry(fEntry, Data, siblings);
  78. CARLA_SAFE_ASSERT_RETURN(fData != nullptr, nullptr);
  79. return fData->value;
  80. }
  81. private:
  82. Data* fData;
  83. k_list_head* fEntry;
  84. k_list_head* fEntry2;
  85. const k_list_head* const fQueue;
  86. friend class List;
  87. };
  88. Itenerator begin() const
  89. {
  90. return Itenerator(&fQueue);
  91. }
  92. void clear()
  93. {
  94. if (fCount != 0)
  95. {
  96. k_list_head* entry;
  97. k_list_head* entry2;
  98. list_for_each_safe(entry, entry2, &fQueue)
  99. {
  100. if (Data* data = list_entry(entry, Data, siblings))
  101. _deallocate(data);
  102. }
  103. }
  104. _init();
  105. }
  106. size_t count() const noexcept
  107. {
  108. return fCount;
  109. }
  110. bool isEmpty() const noexcept
  111. {
  112. return (fCount == 0);
  113. }
  114. bool append(const T& value)
  115. {
  116. if (Data* const data = _allocate())
  117. {
  118. data->value = value;
  119. list_add_tail(&data->siblings, &fQueue);
  120. ++fCount;
  121. return true;
  122. }
  123. return false;
  124. }
  125. bool appendAt(const T& value, const Itenerator& it)
  126. {
  127. if (Data* const data = _allocate())
  128. {
  129. data->value = value;
  130. list_add_tail(&data->siblings, it.fEntry->next);
  131. ++fCount;
  132. return true;
  133. }
  134. return false;
  135. }
  136. bool insert(const T& value)
  137. {
  138. if (Data* const data = _allocate())
  139. {
  140. data->value = value;
  141. list_add(&data->siblings, &fQueue);
  142. ++fCount;
  143. return true;
  144. }
  145. return false;
  146. }
  147. bool insertAt(const T& value, const Itenerator& it)
  148. {
  149. if (Data* const data = _allocate())
  150. {
  151. data->value = value;
  152. list_add(&data->siblings, it.fEntry->prev);
  153. ++fCount;
  154. return true;
  155. }
  156. return false;
  157. }
  158. T& getAt(const size_t index, const bool remove = false)
  159. {
  160. if (fCount == 0 || index >= fCount)
  161. return _getEmpty();
  162. size_t i = 0;
  163. Data* data = nullptr;
  164. k_list_head* entry;
  165. k_list_head* entry2;
  166. list_for_each_safe(entry, entry2, &fQueue)
  167. {
  168. if (index != i++)
  169. continue;
  170. data = list_entry(entry, Data, siblings);
  171. if (remove)
  172. {
  173. --fCount;
  174. list_del(entry);
  175. if (data != nullptr)
  176. _deallocate(data);
  177. }
  178. break;
  179. }
  180. CARLA_ASSERT(data != nullptr);
  181. return (data != nullptr) ? data->value : _getEmpty();
  182. }
  183. T& getFirst(const bool remove = false)
  184. {
  185. return _getFirstOrLast(true, remove);
  186. }
  187. T& getLast(const bool remove = false)
  188. {
  189. return _getFirstOrLast(false, remove);
  190. }
  191. void remove(Itenerator& it)
  192. {
  193. CARLA_ASSERT(it.fEntry != nullptr);
  194. CARLA_ASSERT(it.fData != nullptr);
  195. if (it.fEntry != nullptr && it.fData != nullptr)
  196. {
  197. --fCount;
  198. list_del(it.fEntry);
  199. _deallocate(it.fData);
  200. }
  201. }
  202. bool removeOne(const T& value)
  203. {
  204. Data* data = nullptr;
  205. k_list_head* entry;
  206. k_list_head* entry2;
  207. list_for_each_safe(entry, entry2, &fQueue)
  208. {
  209. data = list_entry(entry, Data, siblings);
  210. CARLA_ASSERT(data != nullptr);
  211. if (data != nullptr && data->value == value)
  212. {
  213. --fCount;
  214. list_del(entry);
  215. _deallocate(data);
  216. break;
  217. }
  218. }
  219. return (data != nullptr);
  220. }
  221. void removeAll(const T& value)
  222. {
  223. Data* data;
  224. k_list_head* entry;
  225. k_list_head* entry2;
  226. list_for_each_safe(entry, entry2, &fQueue)
  227. {
  228. data = list_entry(entry, Data, siblings);
  229. CARLA_ASSERT(data != nullptr);
  230. if (data != nullptr && data->value == value)
  231. {
  232. --fCount;
  233. list_del(entry);
  234. _deallocate(data);
  235. }
  236. }
  237. }
  238. void spliceAppend(List& list, const bool init = true)
  239. {
  240. if (init)
  241. {
  242. list_splice_tail_init(&fQueue, &list.fQueue);
  243. list.fCount += fCount;
  244. fCount = 0;
  245. }
  246. else
  247. {
  248. list_splice_tail(&fQueue, &list.fQueue);
  249. list.fCount += fCount;
  250. }
  251. }
  252. void spliceInsert(List& list, const bool init = true)
  253. {
  254. if (init)
  255. {
  256. list_splice_init(&fQueue, &list.fQueue);
  257. list.fCount += fCount;
  258. fCount = 0;
  259. }
  260. else
  261. {
  262. list_splice(&fQueue, &list.fQueue);
  263. list.fCount += fCount;
  264. }
  265. }
  266. protected:
  267. const size_t fDataSize;
  268. size_t fCount;
  269. k_list_head fQueue;
  270. virtual Data* _allocate() = 0;
  271. virtual void _deallocate(Data* const dataPtr) = 0;
  272. private:
  273. void _init() noexcept
  274. {
  275. fCount = 0;
  276. INIT_LIST_HEAD(&fQueue);
  277. }
  278. T& _getFirstOrLast(const bool first, const bool remove)
  279. {
  280. if (fCount == 0)
  281. return _getEmpty();
  282. k_list_head* const entry = first ? fQueue.next : fQueue.prev;
  283. Data* const data = list_entry(entry, Data, siblings);
  284. CARLA_SAFE_ASSERT_RETURN(data != nullptr, _getEmpty());
  285. T& ret = data->value;
  286. if (remove)
  287. {
  288. --fCount;
  289. list_del(entry);
  290. _deallocate(data);
  291. }
  292. return ret;
  293. }
  294. T& _getEmpty()
  295. {
  296. carla_zeroStruct<T>(fFallbackValue);
  297. return fFallbackValue;
  298. }
  299. T fFallbackValue;
  300. LIST_DECLARATIONS(List)
  301. };
  302. // -----------------------------------------------------------------------
  303. // Realtime safe list
  304. template<typename T>
  305. class RtList : public List<T>
  306. {
  307. public:
  308. // -------------------------------------------------------------------
  309. // RtMemPool C++ class
  310. class Pool
  311. {
  312. public:
  313. Pool(const size_t minPreallocated, const size_t maxPreallocated)
  314. : fHandle(nullptr),
  315. fDataSize(sizeof(typename List<T>::Data))
  316. {
  317. resize(minPreallocated, maxPreallocated);
  318. }
  319. ~Pool()
  320. {
  321. if (fHandle != nullptr)
  322. {
  323. rtsafe_memory_pool_destroy(fHandle);
  324. fHandle = nullptr;
  325. }
  326. }
  327. void* allocate_atomic()
  328. {
  329. return rtsafe_memory_pool_allocate_atomic(fHandle);
  330. }
  331. void* allocate_sleepy()
  332. {
  333. return rtsafe_memory_pool_allocate_sleepy(fHandle);
  334. }
  335. void deallocate(void* const dataPtr)
  336. {
  337. rtsafe_memory_pool_deallocate(fHandle, dataPtr);
  338. }
  339. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  340. {
  341. if (fHandle != nullptr)
  342. {
  343. rtsafe_memory_pool_destroy(fHandle);
  344. fHandle = nullptr;
  345. }
  346. rtsafe_memory_pool_create(&fHandle, nullptr, fDataSize, minPreallocated, maxPreallocated);
  347. CARLA_ASSERT(fHandle != nullptr);
  348. }
  349. bool operator==(const Pool& pool) const noexcept
  350. {
  351. return (fHandle == pool.fHandle && fDataSize == pool.fDataSize);
  352. }
  353. bool operator!=(const Pool& pool) const noexcept
  354. {
  355. return (fHandle != pool.fHandle || fDataSize != pool.fDataSize);
  356. }
  357. private:
  358. RtMemPool_Handle fHandle;
  359. const size_t fDataSize;
  360. };
  361. // -------------------------------------------------------------------
  362. // Now the actual list code
  363. RtList(Pool& memPool)
  364. : fMemPool(memPool)
  365. {
  366. }
  367. ~RtList() override
  368. {
  369. }
  370. void append_sleepy(const T& value)
  371. {
  372. if (typename List<T>::Data* const data = _allocate_sleepy())
  373. {
  374. data->value = value;
  375. list_add_tail(&data->siblings, &this->fQueue);
  376. ++this->fCount;
  377. }
  378. }
  379. void insert_sleepy(const T& value)
  380. {
  381. if (typename List<T>::Data* const data = _allocate_sleepy())
  382. {
  383. data->value = value;
  384. list_add(&data->siblings, &this->fQueue);
  385. ++this->fCount;
  386. }
  387. }
  388. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  389. {
  390. this->clear();
  391. fMemPool.resize(minPreallocated, maxPreallocated);
  392. }
  393. void spliceAppend(RtList& list, const bool init = true)
  394. {
  395. CARLA_ASSERT(fMemPool == list.fMemPool);
  396. List<T>::spliceAppend(list, init);
  397. }
  398. void spliceInsert(RtList& list, const bool init = true)
  399. {
  400. CARLA_ASSERT(fMemPool == list.fMemPool);
  401. List<T>::spliceInsert(list, init);
  402. }
  403. private:
  404. Pool& fMemPool;
  405. typename List<T>::Data* _allocate() override
  406. {
  407. return _allocate_atomic();
  408. }
  409. typename List<T>::Data* _allocate_atomic()
  410. {
  411. return (typename List<T>::Data*)fMemPool.allocate_atomic();
  412. }
  413. typename List<T>::Data* _allocate_sleepy()
  414. {
  415. return (typename List<T>::Data*)fMemPool.allocate_sleepy();
  416. }
  417. void _deallocate(typename List<T>::Data*& dataPtr) override
  418. {
  419. CARLA_ASSERT(dataPtr != nullptr);
  420. fMemPool.deallocate(dataPtr);
  421. dataPtr = nullptr;
  422. }
  423. LIST_DECLARATIONS(RtList)
  424. };
  425. // -----------------------------------------------------------------------
  426. // Non-Realtime list, using malloc/free methods
  427. template<typename T>
  428. class NonRtList : public List<T>
  429. {
  430. public:
  431. NonRtList()
  432. {
  433. }
  434. ~NonRtList() override
  435. {
  436. }
  437. private:
  438. typename List<T>::Data* _allocate() override
  439. {
  440. return (typename List<T>::Data*)std::malloc(this->fDataSize);
  441. }
  442. void _deallocate(typename List<T>::Data*& dataPtr) override
  443. {
  444. CARLA_ASSERT(dataPtr != nullptr);
  445. std::free(dataPtr);
  446. dataPtr = nullptr;
  447. }
  448. LIST_DECLARATIONS(NonRtList)
  449. };
  450. // -----------------------------------------------------------------------
  451. // Non-Realtime list, using new/delete methods
  452. template<typename T>
  453. class NonRtListNew : public List<T>
  454. {
  455. public:
  456. NonRtListNew()
  457. {
  458. }
  459. ~NonRtListNew() override
  460. {
  461. }
  462. private:
  463. typename List<T>::Data* _allocate() override
  464. {
  465. return new typename List<T>::Data;
  466. }
  467. void _deallocate(typename List<T>::Data*& dataPtr) override
  468. {
  469. CARLA_ASSERT(dataPtr != nullptr);
  470. delete dataPtr;
  471. dataPtr = nullptr;
  472. }
  473. LIST_DECLARATIONS(NonRtListNew)
  474. };
  475. #endif // RT_LIST_HPP_INCLUDED