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.

590 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 GPL.txt file
  16. */
  17. #ifndef __RT_LIST_HPP__
  18. #define __RT_LIST_HPP__
  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. : kDataSize(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. : kQueue(queue),
  58. fEntry(queue->next),
  59. fEntry2(fEntry->next),
  60. fData(nullptr)
  61. {
  62. CARLA_ASSERT(kQueue != nullptr);
  63. CARLA_ASSERT(fEntry != nullptr);
  64. CARLA_ASSERT(fEntry2 != nullptr);
  65. }
  66. bool valid()
  67. {
  68. return (fEntry != kQueue);
  69. }
  70. void next()
  71. {
  72. fEntry = fEntry2;
  73. fEntry2 = fEntry->next;
  74. }
  75. T& operator*()
  76. {
  77. fData = list_entry(fEntry, Data, siblings);
  78. CARLA_ASSERT(fData != nullptr);
  79. return fData->value;
  80. }
  81. private:
  82. const k_list_head* const kQueue;
  83. k_list_head* fEntry;
  84. k_list_head* fEntry2;
  85. Data* fData;
  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
  107. {
  108. return fCount;
  109. }
  110. bool isEmpty() const
  111. {
  112. return (fCount == 0);
  113. }
  114. bool append(const T& value)
  115. {
  116. if (Data* const data = _allocate())
  117. {
  118. std::memcpy(&data->value, &value, sizeof(T));
  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. std::memcpy(&data->value, &value, sizeof(T));
  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. std::memcpy(&data->value, &value, sizeof(T));
  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. std::memcpy(&data->value, &value, sizeof(T));
  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 = false)
  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 = false)
  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 kDataSize;
  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()
  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_ASSERT(data != nullptr);
  285. if (data == nullptr)
  286. return _getEmpty();
  287. T& ret = data->value;
  288. if (data != nullptr && remove)
  289. {
  290. --fCount;
  291. list_del(entry);
  292. _deallocate(data);
  293. }
  294. return ret;
  295. }
  296. T& _getEmpty()
  297. {
  298. static T value;
  299. static bool reset = true;
  300. if (reset)
  301. {
  302. reset = false;
  303. carla_zeroStruct<T>(value);
  304. }
  305. return value;
  306. }
  307. LIST_DECLARATIONS(List)
  308. };
  309. // -----------------------------------------------------------------------
  310. // Realtime safe list
  311. template<typename T>
  312. class RtList : public List<T>
  313. {
  314. public:
  315. // -------------------------------------------------------------------
  316. // RtMemPool C++ class
  317. class Pool
  318. {
  319. public:
  320. Pool(const size_t minPreallocated, const size_t maxPreallocated)
  321. : fHandle(nullptr),
  322. kDataSize(sizeof(typename List<T>::Data))
  323. {
  324. resize(minPreallocated, maxPreallocated);
  325. }
  326. ~Pool()
  327. {
  328. if (fHandle != nullptr)
  329. {
  330. rtsafe_memory_pool_destroy(fHandle);
  331. fHandle = nullptr;
  332. }
  333. }
  334. void* allocate_atomic()
  335. {
  336. return rtsafe_memory_pool_allocate_atomic(fHandle);
  337. }
  338. void* allocate_sleepy()
  339. {
  340. return rtsafe_memory_pool_allocate_sleepy(fHandle);
  341. }
  342. void deallocate(void* const dataPtr)
  343. {
  344. rtsafe_memory_pool_deallocate(fHandle, dataPtr);
  345. }
  346. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  347. {
  348. if (fHandle != nullptr)
  349. {
  350. rtsafe_memory_pool_destroy(fHandle);
  351. fHandle = nullptr;
  352. }
  353. rtsafe_memory_pool_create(&fHandle, nullptr, kDataSize, minPreallocated, maxPreallocated);
  354. CARLA_ASSERT(fHandle != nullptr);
  355. }
  356. bool operator==(const Pool& pool) const
  357. {
  358. return (fHandle == pool.fHandle && kDataSize == pool.kDataSize);
  359. }
  360. bool operator!=(const Pool& pool) const
  361. {
  362. return (fHandle != pool.fHandle || kDataSize != pool.kDataSize);
  363. }
  364. private:
  365. RtMemPool_Handle fHandle;
  366. const size_t kDataSize;
  367. };
  368. // -------------------------------------------------------------------
  369. // Now the actual list code
  370. RtList(Pool& memPool)
  371. : fMemPool(memPool)
  372. {
  373. }
  374. ~RtList() override
  375. {
  376. }
  377. void append_sleepy(const T& value)
  378. {
  379. if (typename List<T>::Data* const data = _allocate_sleepy())
  380. {
  381. std::memcpy(&data->value, &value, sizeof(T));
  382. list_add_tail(&data->siblings, &this->fQueue);
  383. ++this->fCount;
  384. }
  385. }
  386. void insert_sleepy(const T& value)
  387. {
  388. if (typename List<T>::Data* const data = _allocate_sleepy())
  389. {
  390. std::memcpy(&data->value, &value, sizeof(T));
  391. list_add(&data->siblings, &this->fQueue);
  392. ++this->fCount;
  393. }
  394. }
  395. void resize(const size_t minPreallocated, const size_t maxPreallocated)
  396. {
  397. this->clear();
  398. fMemPool.resize(minPreallocated, maxPreallocated);
  399. }
  400. void spliceAppend(RtList& list, const bool init = false)
  401. {
  402. CARLA_ASSERT(fMemPool == list.fMemPool);
  403. List<T>::spliceAppend(list, init);
  404. }
  405. void spliceInsert(RtList& list, const bool init = false)
  406. {
  407. CARLA_ASSERT(fMemPool == list.fMemPool);
  408. List<T>::spliceInsert(list, init);
  409. }
  410. private:
  411. Pool& fMemPool;
  412. typename List<T>::Data* _allocate() override
  413. {
  414. return _allocate_atomic();
  415. }
  416. typename List<T>::Data* _allocate_atomic()
  417. {
  418. return (typename List<T>::Data*)fMemPool.allocate_atomic();
  419. }
  420. typename List<T>::Data* _allocate_sleepy()
  421. {
  422. return (typename List<T>::Data*)fMemPool.allocate_sleepy();
  423. }
  424. void _deallocate(typename List<T>::Data* const dataPtr) override
  425. {
  426. fMemPool.deallocate(dataPtr);
  427. }
  428. LIST_DECLARATIONS(RtList)
  429. };
  430. // -----------------------------------------------------------------------
  431. // Non-Realtime list, using malloc/free methods
  432. template<typename T>
  433. class NonRtList : public List<T>
  434. {
  435. public:
  436. NonRtList()
  437. {
  438. }
  439. ~NonRtList() override
  440. {
  441. }
  442. private:
  443. typename List<T>::Data* _allocate() override
  444. {
  445. return (typename List<T>::Data*)std::malloc(this->kDataSize);
  446. }
  447. void _deallocate(typename List<T>::Data* const dataPtr) override
  448. {
  449. CARLA_ASSERT(dataPtr != nullptr);
  450. std::free(dataPtr);
  451. }
  452. LIST_DECLARATIONS(NonRtList)
  453. };
  454. // -----------------------------------------------------------------------
  455. // Non-Realtime list, using new/delete methods
  456. template<typename T>
  457. class NonRtListNew : public List<T>
  458. {
  459. public:
  460. NonRtListNew()
  461. {
  462. }
  463. ~NonRtListNew() override
  464. {
  465. }
  466. private:
  467. typename List<T>::Data* _allocate() override
  468. {
  469. return new typename List<T>::Data;
  470. }
  471. void _deallocate(typename List<T>::Data* const dataPtr) override
  472. {
  473. CARLA_ASSERT(dataPtr != nullptr);
  474. delete dataPtr;
  475. }
  476. LIST_DECLARATIONS(NonRtListNew)
  477. };
  478. // -----------------------------------------------------------------------
  479. #endif // __RT_LIST_HPP__