/[svn]/linuxsampler/trunk/src/common/Pool.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/Pool.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3557 - (show annotations) (download) (as text)
Sun Aug 18 00:06:04 2019 UTC (4 years, 8 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 38305 byte(s)
* NKSP: Introducing 64 bit support for NKSP integer scripts
  variables (declare $foo).
* Require C++11 compiler support.
* Autoconf: Added m4/ax_cxx_compile_stdcxx.m4 macro which is used
  for checking in configure for C++11 support (as mandatory
  requirement) and automatically adds compiler argument if required
  (e.g. -std=C++11).
* Bumped version (2.1.1.svn3).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2019 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifndef __LS_POOL_H__
25 #define __LS_POOL_H__
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 // we just use exceptions for debugging, better not in the final realtime thread !
32 #ifndef CONFIG_RT_EXCEPTIONS
33 # define CONFIG_RT_EXCEPTIONS 0
34 #endif
35
36 #if CONFIG_RT_EXCEPTIONS
37 # include <stdexcept>
38 # include <string>
39 #endif // CONFIG_RT_EXCEPTIONS
40
41 #include <iostream>
42 #include <stdlib.h>
43 #include <stdint.h>
44 #include <stddef.h>
45
46 #if CONFIG_DEVMODE
47 # include <string>
48 # include <stdexcept>
49 const std::string __err_msg_iterator_invalidated = "Pool/RTList iterator invalidated";
50 #endif // CONFIG_DEVMODE
51
52 const std::string __err_msg_resize_while_in_use = "Pool::resizePool() ERROR: elements still in use!";
53
54 /**
55 * Unique numeric ID for exactly one incarnation of one element allocated from
56 * a Pool. As soon as the respective element is once freed back to the Pool,
57 * the ID becomes invalid. Such an ID may be used to safely store an abstract
58 * reference to one Pool element for longer time. Since the Pool classes
59 * automatically detect whether an ID became invalid, using such an ID is thus
60 * safer than storing an Iterator or even a raw pointer in use case scenarios of
61 * storing long term references to Pool elements.
62 *
63 * This ID type must be exactly equal to type vmint, because the latter is used
64 * by the real-time instrument script infrastructure implementation, which in
65 * turn heavily relies on element IDs, and it is using 64-bit for its integer
66 * script variable type.
67 */
68 typedef uint64_t pool_element_id_t;
69
70 // just symbol prototyping
71 template<typename T> class Pool;
72 template<typename T> class RTList;
73
74 template<typename T>
75 class RTListBase {
76 protected:
77 template<typename T1>
78 struct _Node {
79 typedef uint64_t reincarnation_t;
80
81 _Node<T1>* next;
82 _Node<T1>* prev;
83 T1* data;
84 #if CONFIG_DEVMODE
85 RTListBase<T1>* list; // list to which this node currently belongs to
86 #endif // CONFIG_DEVMODE
87 reincarnation_t reincarnation; // just for Pool::fromID()
88
89 _Node() {
90 next = NULL;
91 prev = NULL;
92 data = NULL;
93 #if CONFIG_DEVMODE
94 list = NULL;
95 #endif // CONFIG_DEVMODE
96 reincarnation = 0;
97 }
98
99 inline void bumpReincarnation(uint bits) {
100 reincarnation++;
101 // constrain the bitrange of "reincarnation", because Pool::fromID() will shift up/down for pool_element_id_t and compare this bitwise
102 reincarnation &= ((1 << bits) - 1);
103 }
104 };
105 typedef _Node<T> Node;
106
107 public:
108 /**
109 * Pointer-like object which allows to iterate over elements of a RTList,
110 * similar to iterators of STL container classes. Note that the main
111 * purpose of this class is to access elements of a list / pool i.e.
112 * within a @c while() loop. If you rather want to keep a reference to
113 * one particular element (i.e. for longer time) then you might
114 * consider using @c pool_element_id_t variables instead.
115 */
116 template<typename T1>
117 class _Iterator {
118 public:
119 _Iterator() {
120 current = NULL;
121 fallback = NULL;
122 #if CONFIG_DEVMODE
123 list = NULL;
124 #endif // CONFIG_DEVMODE
125 }
126
127 /// prefix increment op.
128 inline _Iterator& operator++() {
129 #if CONFIG_DEVMODE
130 if (!isValid()) {
131 #if CONFIG_RT_EXCEPTIONS
132 throw std::runtime_error(__err_msg_iterator_invalidated);
133 #else
134 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
135 return *(_Iterator*)NULL; // force segfault if iterator became invalidated
136 #endif // CONFIG_RT_EXCEPTIONS
137 }
138 #endif // CONFIG_DEVMODE
139 fallback = current;
140 current = current->next;
141 return *this;
142 }
143
144 /// postfix increment op.
145 inline _Iterator operator++(int) {
146 _Iterator preval = *this;
147 ++*this; // use prefix operator implementation
148 return preval;
149 }
150
151 /// prefix decrement op.
152 inline _Iterator& operator--() {
153 #if CONFIG_DEVMODE
154 if (!isValid()) {
155 #if CONFIG_RT_EXCEPTIONS
156 throw std::runtime_error(__err_msg_iterator_invalidated);
157 #else
158 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
159 return *(_Iterator*)NULL; // force segfault if iterator became invalidated
160 #endif // CONFIG_RT_EXCEPTIONS
161 }
162 #endif // CONFIG_DEVMODE
163 fallback = current;
164 current = current->prev;
165 return *this;
166 }
167
168 /// postfix decrement op.
169 inline _Iterator operator--(int) {
170 _Iterator preval = *this;
171 --*this; // use prefix operator implementation
172 return preval;
173 }
174
175 inline T1& operator*() {
176 #if CONFIG_DEVMODE
177 if (!isValid()) { // if iterator became invalidated
178 #if CONFIG_RT_EXCEPTIONS
179 throw std::runtime_error(__err_msg_iterator_invalidated);
180 #else
181 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
182 return *((T1*)NULL); // force segfault if iterator became invalidated
183 #endif // CONFIG_RT_EXCEPTIONS
184 }
185 #endif // CONFIG_DEVMODE
186 return *current->data;
187 }
188
189 inline const T1& operator*() const {
190 #if CONFIG_DEVMODE
191 if (!isValid()) { // if iterator became invalidated
192 #if CONFIG_RT_EXCEPTIONS
193 throw std::runtime_error(__err_msg_iterator_invalidated);
194 #else
195 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
196 return *((const T1*)NULL); // force segfault if iterator became invalidated
197 #endif // CONFIG_RT_EXCEPTIONS
198 }
199 #endif // CONFIG_DEVMODE
200 return *current->data;
201 }
202
203 inline T1* operator->() {
204 #if CONFIG_DEVMODE
205 if (!isValid()) { // if iterator became invalidated
206 #if CONFIG_RT_EXCEPTIONS
207 throw std::runtime_error(__err_msg_iterator_invalidated);
208 #else
209 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
210 return (T1*)NULL; // force segfault if iterator became invalidated
211 #endif // CONFIG_RT_EXCEPTIONS
212 }
213 #endif // CONFIG_DEVMODE
214 return current->data;
215 }
216
217 inline const T1* operator->() const {
218 #if CONFIG_DEVMODE
219 if (!isValid()) { // if iterator became invalidated
220 #if CONFIG_RT_EXCEPTIONS
221 throw std::runtime_error(__err_msg_iterator_invalidated);
222 #else
223 std::cerr << __err_msg_iterator_invalidated << std::endl << std::flush;
224 return (const T1*)NULL; // force segfault if iterator became invalidated
225 #endif // CONFIG_RT_EXCEPTIONS
226 }
227 #endif // CONFIG_DEVMODE
228 return current->data;
229 }
230
231 inline bool operator==(const _Iterator<T1> other) const {
232 return current == other.current;
233 }
234
235 inline bool operator!=(const _Iterator<T1> other) const {
236 return current != other.current;
237 }
238
239 inline operator bool() const {
240 return current && current->data;
241 }
242
243 inline bool operator!() const {
244 return !(current && current->data);
245 }
246
247 /**
248 * Moves the element pointed by this Iterator from its current
249 * list to the beginning of the destination list @a pDstList.
250 *
251 * @b CAUTION: When this method returns, this Iterator does
252 * @b NOT point to the element on the new list anymore, instead it
253 * points at a completely different element! In case of a
254 * forward Iterator this Iterator object will point to the
255 * previous element on the source list, in case of a backward
256 * Iterator it will point to the subsequent element on the
257 * source list. This behavior is enforced to avoid breaking an
258 * active loop code working with this Iterator object.
259 *
260 * Thus if you intend to continue working with the same element,
261 * you should do like this:
262 * @code
263 * it = it.moveToEndOf(anotherList);
264 * @endcode
265 *
266 * @param pDstList - destination list
267 * @returns Iterator object pointing at the moved element on
268 * the destination list
269 */
270 inline _Iterator moveToEndOf(RTListBase<T1>* pDstList) {
271 detach();
272 pDstList->append(*this);
273 _Iterator iterOnDstList = _Iterator(current);
274 current = fallback;
275 return iterOnDstList;
276 }
277
278 /**
279 * Moves the element pointed by this Iterator from its current
280 * list to the end of destination list @a pDstList.
281 *
282 * @b CAUTION: When this method returns, this Iterator does
283 * @b NOT point to the element on the new list anymore, instead it
284 * points at a completely different element! In case of a
285 * forward Iterator this Iterator object will point to the
286 * previous element on the source list, in case of a backward
287 * Iterator it will point to the subsequent element on the
288 * source list. This behavior is enforced to avoid breaking an
289 * active loop code working with this Iterator object.
290 *
291 * Thus if you intend to continue working with the same element,
292 * you should do like this:
293 * @code
294 * it = it.moveToBeginOf(anotherList);
295 * @endcode
296 *
297 * @param pDstList - destination list
298 * @returns Iterator object pointing at the moved element on
299 * the destination list
300 */
301 inline _Iterator moveToBeginOf(RTListBase<T1>* pDstList) {
302 detach();
303 pDstList->prepend(*this);
304 _Iterator iterOnDstList = _Iterator(current);
305 current = fallback;
306 return iterOnDstList;
307 }
308
309 /**
310 * Moves the element pointed by this Iterator from its current
311 * position to the position right before @a itDst. That move
312 * may either be from and to the same list, or to a another
313 * list.
314 *
315 * @b CAUTION: When this method returns, this Iterator does
316 * @b NOT point to the element on the new list anymore, instead it
317 * points at a completely different element! In case of a
318 * forward Iterator this Iterator object will point to the
319 * previous element on the source list, in case of a backward
320 * Iterator it will point to the subsequent element on the
321 * source list. This behavior is enforced to avoid breaking an
322 * active loop code working with this Iterator object.
323 *
324 * Thus if you intend to continue working with the same element,
325 * you should do like this:
326 * @code
327 * itSourceElement = itSourceElement.moveBefore(itDestinationElement);
328 * @endcode
329 *
330 * @param itDst - destination element to be inserted before
331 * @returns Iterator object pointing at the moved element on
332 * the destination list
333 */
334 inline _Iterator moveBefore(_Iterator<T1> itDst) {
335 detach();
336 RTList<T1>::prependBefore(*this, itDst);
337 _Iterator iterOnDstList = _Iterator(current);
338 current = fallback;
339 return iterOnDstList;
340 }
341
342 /**
343 * Moves the element pointed by this Iterator from its current
344 * position to the position right after @a itDst. That move
345 * may either be from and to the same list, or to a another
346 * list.
347 *
348 * @b CAUTION: When this method returns, this Iterator does
349 * @b NOT point to the element on the new list anymore, instead it
350 * points at a completely different element! In case of a
351 * forward Iterator this Iterator object will point to the
352 * previous element on the source list, in case of a backward
353 * Iterator it will point to the subsequent element on the
354 * source list. This behavior is enforced to avoid breaking an
355 * active loop code working with this Iterator object.
356 *
357 * Thus if you intend to continue working with the same element,
358 * you should do like this:
359 * @code
360 * itSourceElement = itSourceElement.moveAfter(itDestinationElement);
361 * @endcode
362 *
363 * @param itDst - destination element to be inserted after
364 * @returns Iterator object pointing at the moved element on
365 * the destination list
366 */
367 inline _Iterator moveAfter(_Iterator<T1> itDst) {
368 detach();
369 RTList<T1>::appendAfter(*this, itDst);
370 _Iterator iterOnDstList = _Iterator(current);
371 current = fallback;
372 return iterOnDstList;
373 }
374
375 #if CONFIG_DEVMODE
376 inline bool isValid() const {
377 return current->list == list;
378 }
379 #endif // CONFIG_DEVMODE
380
381 protected:
382 Node* current;
383 Node* fallback;
384 enum dir_t {
385 dir_forward,
386 dir_backward
387 };
388 #if CONFIG_DEVMODE
389 RTListBase<T1>* list;
390 #endif // CONFIG_DEVMODE
391
392 _Iterator(Node* pNode, dir_t direction = dir_forward) {
393 current = pNode;
394 fallback = (direction == dir_forward) ? pNode->prev : pNode->next;
395 #if CONFIG_DEVMODE
396 list = pNode->list;
397 #endif // CONFIG_DEVMODE
398 }
399
400 inline Node* node() {
401 #if CONFIG_DEVMODE
402 #if CONFIG_RT_EXCEPTIONS
403 if (isValid()) return current;
404 else throw std::runtime_error(__err_msg_iterator_invalidated);
405 #else
406 return (isValid()) ? current : (Node*)NULL; // force segfault if iterator became invalidated
407 #endif // CONFIG_RT_EXCEPTIONS
408 #else
409 return current;
410 #endif // CONFIG_DEVMODE
411 }
412
413 inline const Node* node() const {
414 #if CONFIG_DEVMODE
415 #if CONFIG_RT_EXCEPTIONS
416 if (isValid()) return current;
417 else throw std::runtime_error(__err_msg_iterator_invalidated);
418 #else
419 return (isValid()) ? current : (const Node*)NULL; // force segfault if iterator became invalidated
420 #endif // CONFIG_RT_EXCEPTIONS
421 #else
422 return current;
423 #endif // CONFIG_DEVMODE
424 }
425
426 inline void detach() {
427 RTListBase<T1>::detach(*this);
428 }
429
430 friend class RTListBase<T1>;
431 friend class RTList<T1>;
432 friend class Pool<T1>;
433 };
434 typedef _Iterator<T> Iterator;
435
436 inline Iterator first() {
437 return Iterator(_begin.next, Iterator::dir_forward);
438 }
439
440 inline Iterator last() {
441 return Iterator(_end.prev, Iterator::dir_backward);
442 }
443
444 inline Iterator begin() {
445 return Iterator(&_begin, Iterator::dir_forward);
446 }
447
448 inline Iterator end() {
449 return Iterator(&_end, Iterator::dir_backward);
450 }
451
452 inline bool isEmpty() const {
453 return _begin.next == &_end;
454 }
455
456 inline int count() {
457 int elements = 0;
458 for (Iterator it = first(); it != end(); ++it) ++elements;
459 return elements;
460 }
461
462 protected:
463 Node _begin; // fake node (without data) which represents the begin of the list - not the first element!
464 Node _end; // fake node (without data) which represents the end of the list - not the last element!
465
466 RTListBase() {
467 init();
468 }
469
470 void init() {
471 // initialize boundary nodes
472 _begin.prev = &_begin;
473 _begin.next = &_end;
474 _begin.data = NULL;
475 _end.next = &_end;
476 _end.prev = &_begin;
477 _end.data = NULL;
478 #if CONFIG_DEVMODE
479 _begin.list = this;
480 _end.list = this;
481 #endif // CONFIG_DEVMODE
482 }
483
484 inline void append(Iterator itElement) {
485 Node* pNode = itElement.current;
486 Node* last = _end.prev;
487 last->next = pNode;
488 pNode->prev = last; // if a segfault happens here, then because 'itElement' Iterator became invalidated
489 pNode->next = &_end;
490 _end.prev = pNode;
491 #if CONFIG_DEVMODE
492 pNode->list = this;
493 #endif // CONFIG_DEVMODE
494 }
495
496 inline void append(Iterator itFirst, Iterator itLast) {
497 Node* pFirst = itFirst.current;
498 Node* pLast = itLast.current;
499 Node* last = _end.prev;
500 last->next = pFirst;
501 pFirst->prev = last; // if a segfault happens here, then because 'itFirst' Iterator became invalidated
502 pLast->next = &_end; // if a segfault happens here, then because 'itLast' Iterator became invalidated
503 _end.prev = pLast;
504 #if CONFIG_DEVMODE
505 for (Node* pNode = pFirst; true; pNode = pNode->next) {
506 pNode->list = this;
507 if (pNode == pLast) break;
508 }
509 #endif // CONFIG_DEVMODE
510 }
511
512 inline void prepend(Iterator itElement) {
513 Node* pNode = itElement.current;
514 Node* first = _begin.next;
515 _begin.next = pNode;
516 pNode->prev = &_begin; // if a segfault happens here, then because 'itElement' Iterator became invalidated
517 pNode->next = first;
518 first->prev = pNode;
519 #if CONFIG_DEVMODE
520 pNode->list = this;
521 #endif // CONFIG_DEVMODE
522 }
523
524 inline void prepend(Iterator itFirst, Iterator itLast) {
525 Node* pFirst = itFirst.current;
526 Node* pLast = itLast.current;
527 Node* first = _begin.next;
528 _begin.next = pFirst;
529 pFirst->prev = &_begin; // if a segfault happens here, then because 'itFirst' Iterator became invalidated
530 pLast->next = first; // if a segfault happens here, then because 'itLast' Iterator became invalidated
531 first->prev = pLast;
532 #if CONFIG_DEVMODE
533 for (Node* pNode = pFirst; true; pNode = pNode->next) {
534 pNode->list = this;
535 if (pNode == pLast) break;
536 }
537 #endif // CONFIG_DEVMODE
538 }
539
540 static inline void prependBefore(Iterator itSrc, Iterator itDst) {
541 Node* src = itSrc.current;
542 Node* dst = itDst.current;
543 Node* prev = dst->prev;
544 prev->next = src;
545 dst->prev = src;
546 src->prev = prev;
547 src->next = dst;
548 #if CONFIG_DEVMODE
549 src->list = dst->list;
550 #endif // CONFIG_DEVMODE
551 }
552
553 static inline void appendAfter(Iterator itSrc, Iterator itDst) {
554 Node* src = itSrc.current;
555 Node* dst = itDst.current;
556 Node* next = dst->next;
557 next->prev = src;
558 dst->next = src;
559 src->prev = dst;
560 src->next = next;
561 #if CONFIG_DEVMODE
562 src->list = dst->list;
563 #endif // CONFIG_DEVMODE
564 }
565
566 static inline void detach(Iterator itElement) {
567 Node* pNode = itElement.node();
568 Node* prev = pNode->prev; // if a segfault happens here, then because 'itElement' Iterator became invalidated
569 Node* next = pNode->next;
570 prev->next = next;
571 next->prev = prev;
572 }
573
574 static inline void detach(Iterator itFirst, Iterator itLast) {
575 Node* prev = itFirst.node()->prev; // if a segfault happens here, then because 'itFirst' Iterator became invalidated
576 Node* next = itLast.node()->next; // if a segfault happens here, then because 'itLast' Iterator became invalidated
577 prev->next = next;
578 next->prev = prev;
579 }
580
581 friend class _Iterator<T>;
582 friend class RTList<T>;
583 friend class Pool<T>;
584 };
585
586 template<typename T>
587 class RTList : public RTListBase<T> {
588 public:
589 typedef typename RTListBase<T>::Node Node;
590 typedef typename RTListBase<T>::Iterator Iterator;
591
592 /**
593 * Constructor
594 *
595 * @param pPool - pool this list uses for allocation and
596 * deallocation of elements
597 */
598 RTList(Pool<T>* pPool) : RTListBase<T>::RTListBase() {
599 this->pPool = pPool;
600 }
601
602 /**
603 * Copy constructor
604 */
605 RTList(RTList<T>& list) : RTListBase<T>::RTListBase() {
606 this->pPool = list.pPool;
607 Iterator it = list.first();
608 Iterator end = list.end();
609 for(; it != end; ++it) {
610 if (poolIsEmpty()) break;
611 *(allocAppend()) = *it;
612 }
613 }
614
615 virtual ~RTList() {
616 clear();
617 }
618
619 inline bool poolIsEmpty() const {
620 return pPool->poolIsEmpty();
621 }
622
623 inline Iterator allocAppend() {
624 if (pPool->poolIsEmpty()) return RTListBase<T>::begin();
625 Iterator element = pPool->alloc();
626 this->append(element);
627 #if CONFIG_DEVMODE
628 element.list = this;
629 #endif // CONFIG_DEVMODE
630 return element;
631 }
632
633 inline Iterator allocPrepend() {
634 if (pPool->poolIsEmpty()) return RTListBase<T>::end();
635 Iterator element = pPool->alloc();
636 this->prepend(element);
637 #if CONFIG_DEVMODE
638 element.list = this;
639 #endif // CONFIG_DEVMODE
640 return element;
641 }
642
643 inline void free(Iterator& itElement) {
644 itElement.detach();
645 pPool->freeToPool(itElement);
646 itElement.current = itElement.fallback;
647 }
648
649 inline void clear() {
650 if (!RTListBase<T>::isEmpty()) {
651 Node* first = RTListBase<T>::_begin.next;
652 Node* last = RTListBase<T>::_end.prev;
653 RTListBase<T>::detach(first, last);
654 pPool->freeToPool(first, last);
655 }
656 }
657
658 inline pool_element_id_t getID(const T* obj) const {
659 return pPool->getID(obj);
660 }
661
662 inline pool_element_id_t getID(const Iterator& it) const {
663 return pPool->getID(&*it);
664 }
665
666 inline Iterator fromID(pool_element_id_t id) const {
667 return pPool->fromID(id);
668 }
669
670 inline Iterator fromPtr(const T* obj) const {
671 return pPool->fromPtr(obj);
672 }
673
674 protected:
675 Pool<T>* pPool;
676 };
677
678 template<typename T>
679 class Pool : public RTList<T> {
680 public:
681 typedef typename RTList<T>::Node Node;
682 typedef typename RTList<T>::Iterator Iterator;
683
684 Node* nodes;
685 T* data;
686 RTListBase<T> freelist; // not yet allocated elements
687 size_t poolsize;
688 // following 3 used for element ID generation (and vice versa)
689 uint poolsizebits; ///< Amount of bits required to index all elements of this pool (according to current pool size).
690 uint reservedbits; ///< 3rd party reserved bits on the left side of id (default: 0).
691 uint reincarnationbits; ///< Amount of bits allowed for reincarnation counter.
692
693 Pool(int Elements) : RTList<T>::RTList(this), reservedbits(0) {
694 _init(Elements);
695 }
696
697 virtual ~Pool() {
698 if (nodes) delete[] nodes;
699 if (data) delete[] data;
700 }
701
702 /**
703 * Returns true if there is at least one free element that could be
704 * allocated from the pool with i.e. allocAppend() or allocPreprend().
705 *
706 * @see poolHasFreeElements()
707 */
708 inline bool poolIsEmpty() const {
709 return freelist.isEmpty();
710 }
711
712 /**
713 * Returns true if at least the requested amount of free @a elements is
714 * currently available for being allocated from the pool with i.e.
715 * allocAppend() or allocPreprend().
716 *
717 * @see poolIsEmpty()
718 */
719 bool poolHasFreeElements(ssize_t elements) {
720 for (Iterator it = freelist.first(); it != freelist.end() && elements >= 0; ++it)
721 --elements;
722 return elements <= 0;
723 }
724
725 ssize_t countFreeElements() {
726 return freelist.count();
727 }
728
729 /**
730 * Returns the current size of the pool, that is the amount of
731 * pre-allocated elements from the operating system. It equals the
732 * amount of elements given to the constructor unless resizePool()
733 * is called.
734 *
735 * @see resizePool()
736 */
737 size_t poolSize() const {
738 return poolsize;
739 }
740
741 /**
742 * Alters the amount of elements to be pre-allocated from the
743 * operating system for this pool object.
744 *
745 * @e CAUTION: you MUST free all elements in use before calling this
746 * method ( e.g. by calling clear() )! Also make sure that no
747 * references of elements before this call will still be used after this
748 * call, since all elements will be reallocated and their old memory
749 * addresses become invalid!
750 *
751 * @see poolSize()
752 */
753 void resizePool(ssize_t Elements) {
754 if (freelist.count() != poolsize) {
755 #if CONFIG_DEVMODE
756 throw std::runtime_error(__err_msg_resize_while_in_use);
757 #else
758 std::cerr << __err_msg_resize_while_in_use << std::endl << std::flush;
759 // if we're here something's terribly wrong, but we try to do the best
760 RTList<T>::clear();
761 #endif
762 }
763 if (nodes) delete[] nodes;
764 if (data) delete[] data;
765 freelist.init();
766 RTListBase<T>::init();
767 _init(Elements);
768 }
769
770 /**
771 * Sets the amount of bits on the left hand side of pool_element_id_t
772 * numbers to be reserved for 3rd party usage. So if you pass @c 1 for
773 * argument @a bits for example, then all generated element IDs will be
774 * maximum 31 bit large.
775 *
776 * By default there are no reserved bits, and thus by default all IDs
777 * are max. 32 bit large.
778 *
779 * @param bits - amount of bits to reserve on every ID for other purposes
780 * @see pool_element_id_t
781 */
782 void setPoolElementIDsReservedBits(uint bits) {
783 reservedbits = bits;
784 updateReincarnationBits();
785 }
786
787 /**
788 * Returns an abstract, unique numeric ID for the given object of
789 * this pool, it returns 0 in case the passed object is not a member
790 * of this Pool, i.e. because it is simply an invalid pointer or member
791 * of another Pool. The returned ID is unique among all elements of this
792 * Pool and it differs with each reincarnation of an object. That means
793 * each time you free an element to and allocate the same element back
794 * from the Pool, it will have a different ID.
795 *
796 * A valid ID will never be zero, so you may use ID values of 0 in your
797 * data structures for special purposes (i.e. reflecting an invalid
798 * object ID or not yet assigned object).
799 *
800 * Members are always translated both, from Iterators/pointers to IDs,
801 * and from IDs to Iterators/pointers in constant time.
802 *
803 * You might want to use this alternative approach of referencing Pool
804 * members under certain scenarios. For example if you need to expose
805 * an ID to the end user and/or if you want to represent an object of
806 * this pool by a smaller number instead of a native pointer (i.e. 16
807 * bits vs. 64 bits). You can also detect this way whether the object
808 * has already been freed / reallocated from the Pool in the meantime.
809 *
810 * @param obj - raw pointer to a data member of this Pool
811 * @returns unique numeric ID (!= 0) of @a obj or 0 if pointer was invalid
812 */
813 pool_element_id_t getID(const T* obj) const {
814 if (!poolsize) return 0;
815 ssize_t index = ssize_t( obj - &data[0] );
816 if (index < 0 || index >= poolsize) return 0;
817 return ((nodes[index].reincarnation << poolsizebits) | index) + 1;
818 }
819
820 /**
821 * Overridden convenience method, behaves like the method above.
822 */
823 pool_element_id_t getID(const Iterator& it) const {
824 return getID(&*it);
825 }
826
827 /**
828 * Returns an Iterator object of the Pool data member reflected by the
829 * given abstract, unique numeric ID, it returns an invalid Iterator in
830 * case the ID is invalid or if the Pool's data element reflected by
831 * given ID was at least once released/freed back to the Pool in the
832 * meantime.
833 *
834 * Members are always translated both, from Iterators/pointers to IDs,
835 * and from IDs to Iterators/pointers in constant time.
836 *
837 * You might want to use this alternative approach of referencing Pool
838 * members under certain scenarios. For example if you need to expose
839 * an ID to the end user and/or if you want to represent an object of
840 * this pool by a smaller number instead of a native pointer (i.e. 16
841 * bits vs. 64 bits). You can also detect this way whether the object
842 * has already been freed / reallocated from the Pool in the meantime.
843 *
844 * @param id - unique ID (!= 0) of a Pool's data member
845 * @returns Iterator object pointing to Pool's data element, invalid
846 * Iterator in case ID was invalid or data element was freed
847 */
848 Iterator fromID(pool_element_id_t id) const {
849 //TODO: -1 check here is a relict from older versions of Pool.h, once it is certain that no existing code base is still using -1 for "invalid" Pool elements then this -1 check can be removed
850 if (id == 0 || id == -1) return Iterator(); // invalid iterator
851 id--;
852 const uint bits = poolsizebits;
853 size_t index = id & ((1 << bits) - 1);
854 if (index >= poolsize) return Iterator(); // invalid iterator
855 Node* node = &nodes[index];
856 typename Node::reincarnation_t reincarnation = id >> bits;
857 if (reincarnation != node->reincarnation) return Iterator(); // invalid iterator
858 return Iterator(node);
859 }
860
861 /**
862 * Returns an Iterator object for the object pointed by @a obj. This
863 * method will check whether the supplied object is actually part of
864 * this pool, and if it is not part of this pool an invalid Iterator is
865 * returned instead.
866 *
867 * @param obj - raw pointer to an object managed by this pool
868 * @returns Iterator object pointing to the supplied object, invalid
869 * Iterator in case object is not part of this pool
870 */
871 Iterator fromPtr(const T* obj) const {
872 if (!poolsize) return Iterator(); // invalid iterator
873 ssize_t index = ssize_t( obj - &data[0] );
874 if (index < 0 || index >= poolsize) return Iterator(); // invalid iterator
875 return Iterator(&nodes[index]);
876 }
877
878 protected:
879 // caution: assumes pool (that is freelist) is not empty!
880 inline Iterator alloc() {
881 Iterator element = freelist.last();
882 element.detach();
883 return element;
884 }
885
886 inline void freeToPool(Iterator itElement) {
887 itElement.node()->bumpReincarnation(reincarnationbits);
888 freelist.append(itElement);
889 }
890
891 inline void freeToPool(Iterator itFirst, Iterator itLast) {
892 for (Node* n = itFirst.node(); true; n = n->next) {
893 n->bumpReincarnation(reincarnationbits);
894 if (n == itLast.node()) break;
895 }
896 freelist.append(itFirst, itLast);
897 }
898
899 friend class RTList<T>;
900
901 private:
902 void _init(ssize_t Elements) {
903 data = new T[Elements];
904 nodes = new Node[Elements];
905 for (ssize_t i = 0; i < Elements; i++) {
906 nodes[i].data = &data[i];
907 freelist.append(&nodes[i]);
908 }
909 poolsize = Elements;
910 poolsizebits = bitsForSize(poolsize + 1); // +1 here just because IDs are always incremented by one (to avoid them ever being zero)
911 updateReincarnationBits();
912 }
913
914 inline void updateReincarnationBits() {
915 reincarnationbits = sizeof(pool_element_id_t) * 8 - poolsizebits - reservedbits;
916 }
917
918 inline static int bitsForSize(ssize_t size) {
919 if (!size) return 0;
920 size--;
921 int bits = 0;
922 for (; size > 1; bits += 2, size >>= 2);
923 return int(bits + size);
924 }
925 };
926
927 #endif // __LS_POOL_H__

  ViewVC Help
Powered by ViewVC