mirror of https://github.com/stella-emu/stella.git
269 lines
8.1 KiB
C++
269 lines
8.1 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//============================================================================
|
|
|
|
#ifndef LINKED_OBJECT_POOL_HXX
|
|
#define LINKED_OBJECT_POOL_HXX
|
|
|
|
#include <list>
|
|
#include "bspf.hxx"
|
|
|
|
/**
|
|
A fixed-size object-pool based doubly-linked list that makes use of
|
|
multiple STL lists, to reduce frequent (de)allocations.
|
|
|
|
This structure can be used as either a stack or queue, but also allows
|
|
for removal at any location in the list.
|
|
|
|
There are two internal lists; one stores active nodes, and the other
|
|
stores pool nodes that have been 'deleted' from the active list (note
|
|
that no actual deletion takes place; nodes are simply moved from one list
|
|
to another). Similarly, when a new node is added to the active list, it
|
|
is simply moved from the pool list to the active list.
|
|
|
|
In all cases, the variable 'myCurrent' is updated to point to the
|
|
current node.
|
|
|
|
NOTE: You must always call 'currentIsValid()' before calling 'current()',
|
|
to make sure that the return value is a valid reference.
|
|
|
|
In the case of methods which wrap the C++ 'splice()' method, the
|
|
semantics of splice are followed wrt invalid/out-of-range/etc
|
|
iterators. See the applicable C++ STL documentation for such
|
|
behaviour.
|
|
|
|
@author Stephen Anthony
|
|
*/
|
|
namespace Common {
|
|
|
|
template <class T, uInt32 CAPACITY = 100>
|
|
class LinkedObjectPool
|
|
{
|
|
public:
|
|
using iter = typename std::list<T>::iterator;
|
|
using const_iter = typename std::list<T>::const_iterator;
|
|
|
|
/*
|
|
Create a pool of size CAPACITY; the active list starts out empty.
|
|
*/
|
|
LinkedObjectPool<T, CAPACITY>() : myCurrent(myList.end()), myCapacity(0) {
|
|
resize(CAPACITY);
|
|
}
|
|
|
|
/**
|
|
Return node data that the 'current' iterator points to.
|
|
Note that this returns a valid value only in the case where the list
|
|
is non-empty (at least one node has been added to the active list).
|
|
|
|
Make sure to call 'currentIsValid()' before accessing this method.
|
|
*/
|
|
T& current() const { return *myCurrent; }
|
|
|
|
/**
|
|
Returns current's position in the list
|
|
|
|
SLOW, but only required for messages
|
|
*/
|
|
uInt32 currentIdx() const {
|
|
if(empty())
|
|
return 0;
|
|
|
|
iter it = myCurrent;
|
|
uInt32 idx = 1;
|
|
|
|
while(it-- != myList.begin()) idx++;
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
Does the 'current' iterator point to a valid node in the active list?
|
|
This must be called before 'current()' is called.
|
|
*/
|
|
bool currentIsValid() const { return myCurrent != myList.end(); }
|
|
|
|
/**
|
|
Advance 'current' iterator to previous position in the active list.
|
|
If we go past the beginning, it is reset to one past the end (indicates nullptr).
|
|
*/
|
|
void moveToPrevious() {
|
|
if(currentIsValid())
|
|
myCurrent = myCurrent == myList.begin() ? myList.end() : std::prev(myCurrent, 1);
|
|
}
|
|
|
|
/**
|
|
Advance 'current' iterator to next position in the active list.
|
|
If we go past the last node, it will point to one past the end (indicates nullptr).
|
|
*/
|
|
void moveToNext() {
|
|
if(currentIsValid())
|
|
myCurrent = std::next(myCurrent, 1);
|
|
}
|
|
|
|
/**
|
|
Return an iterator to the first node in the active list.
|
|
*/
|
|
const_iter first() const { return myList.begin(); }
|
|
|
|
/**
|
|
Return an iterator to the last node in the active list.
|
|
*/
|
|
const_iter last() const { return std::prev(myList.end(), 1); }
|
|
|
|
/**
|
|
Return an iterator to the previous node of 'i' in the active list.
|
|
*/
|
|
const_iter previous(const_iter i) const { return std::prev(i, 1); }
|
|
|
|
/**
|
|
Return an iterator to the next node to 'current' in the active list.
|
|
*/
|
|
const_iter next(const_iter i) const { return std::next(i, 1); }
|
|
|
|
/**
|
|
Canonical iterators from C++ STL.
|
|
*/
|
|
const_iter cbegin() const { return myList.cbegin(); }
|
|
const_iter cend() const { return myList.cend(); }
|
|
|
|
/**
|
|
Answer whether 'current' is at the specified iterator.
|
|
*/
|
|
bool atFirst() const { return myCurrent == first(); }
|
|
bool atLast() const { return myCurrent == last(); }
|
|
|
|
/**
|
|
Add a new node at the beginning of the active list, and update 'current'
|
|
to point to that node.
|
|
*/
|
|
void addFirst() {
|
|
myList.splice(myList.begin(), myPool, myPool.begin());
|
|
myCurrent = myList.begin();
|
|
}
|
|
|
|
/**
|
|
Add a new node at the end of the active list, and update 'current'
|
|
to point to that node.
|
|
*/
|
|
void addLast() {
|
|
myList.splice(myList.end(), myPool, myPool.begin());
|
|
myCurrent = std::prev(myList.end(), 1);
|
|
}
|
|
|
|
/**
|
|
Remove the first node of the active list, updating 'current' if it
|
|
happens to be the one removed.
|
|
*/
|
|
void removeFirst() {
|
|
const_iter i = myList.cbegin();
|
|
myPool.splice(myPool.end(), myList, i);
|
|
if(myCurrent == i) // did we just invalidate 'current'
|
|
moveToNext(); // if so, move to the next node
|
|
}
|
|
|
|
/**
|
|
Remove the last node of the active list, updating 'current' if it
|
|
happens to be the one removed.
|
|
*/
|
|
void removeLast() {
|
|
const_iter i = std::prev(myList.end(), 1);
|
|
myPool.splice(myPool.end(), myList, i);
|
|
if(myCurrent == i) // did we just invalidate 'current'
|
|
moveToPrevious(); // if so, move to the previous node
|
|
}
|
|
|
|
/**
|
|
Remove a single element from the active list at position of the iterator.
|
|
*/
|
|
void remove(const_iter i) {
|
|
myPool.splice(myPool.end(), myList, i);
|
|
}
|
|
|
|
/**
|
|
Remove a single element from the active list by index, offset from
|
|
the beginning of the list. (ie, '0' means first element, '1' is second,
|
|
and so on).
|
|
*/
|
|
void remove(uInt32 index) {
|
|
myPool.splice(myPool.end(), myList, std::next(myList.begin(), index));
|
|
}
|
|
|
|
/**
|
|
Remove range of elements from the beginning of the active list to
|
|
the 'current' node.
|
|
*/
|
|
void removeToFirst() {
|
|
myPool.splice(myPool.end(), myList, myList.begin(), myCurrent);
|
|
}
|
|
|
|
/**
|
|
Remove range of elements from the node after 'current' to the end of the
|
|
active list.
|
|
*/
|
|
void removeToLast() {
|
|
myPool.splice(myPool.end(), myList, std::next(myCurrent, 1), myList.end());
|
|
}
|
|
|
|
/**
|
|
Resize the pool to specified size, invalidating the list in the process
|
|
(ie, the list essentially becomes empty again).
|
|
*/
|
|
void resize(uInt32 capacity) {
|
|
if(myCapacity != capacity) // only resize when necessary
|
|
{
|
|
myList.clear(); myPool.clear();
|
|
myCurrent = myList.end();
|
|
myCapacity = capacity;
|
|
|
|
for(uInt32 i = 0; i < myCapacity; ++i)
|
|
myPool.emplace_back(T());
|
|
}
|
|
}
|
|
|
|
/**
|
|
Erase entire contents of active list.
|
|
*/
|
|
void clear() {
|
|
myPool.splice(myPool.end(), myList, myList.begin(), myList.end());
|
|
myCurrent = myList.end();
|
|
}
|
|
|
|
uInt32 capacity() const { return myCapacity; }
|
|
|
|
uInt32 size() const { return uInt32(myList.size()); }
|
|
bool empty() const { return size() == 0; }
|
|
bool full() const { return size() >= capacity(); }
|
|
|
|
private:
|
|
std::list<T> myList, myPool;
|
|
|
|
// Current position in the active list (end() indicates an invalid position)
|
|
iter myCurrent;
|
|
|
|
// Total capacity of the pool
|
|
uInt32 myCapacity;
|
|
|
|
private:
|
|
// Following constructors and assignment operators not supported
|
|
LinkedObjectPool(const LinkedObjectPool&) = delete;
|
|
LinkedObjectPool(LinkedObjectPool&&) = delete;
|
|
LinkedObjectPool& operator=(const LinkedObjectPool&) = delete;
|
|
LinkedObjectPool& operator=(LinkedObjectPool&&) = delete;
|
|
};
|
|
|
|
} // Namespace Common
|
|
|
|
#endif
|