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.

571 lines
12KB

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