417 lines
8.5 KiB
C++
417 lines
8.5 KiB
C++
/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
|
|
*
|
|
* Om is free software; you can redistribute it and/or modify it under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* Om is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#ifndef LIST_H
|
|
#define LIST_H
|
|
|
|
#include <cassert>
|
|
#include "util/types.h"
|
|
#include "MaidObject.h"
|
|
|
|
|
|
/** A node in a List.
|
|
*
|
|
* This class is (unusually) exposed to the user to allow list operations
|
|
* to be realtime safe (ie so events can allocate list nodes in other threads
|
|
* and then insert them in the realtime thread.
|
|
*/
|
|
template <typename T>
|
|
class ListNode : public MaidObject
|
|
{
|
|
public:
|
|
ListNode(T elem) : m_elem(elem), m_next(NULL), m_prev(NULL) {}
|
|
virtual ~ListNode() {}
|
|
|
|
ListNode* next() const { return m_next; }
|
|
void next(ListNode* ln) { m_next = ln; }
|
|
ListNode* prev() const { return m_prev; }
|
|
void prev(ListNode* ln) { m_prev = ln; }
|
|
T& elem() { return m_elem;}
|
|
const T& elem() const { return m_elem; }
|
|
|
|
private:
|
|
// Prevent copies (undefined)
|
|
ListNode(const ListNode& copy);
|
|
ListNode& operator=(const ListNode& copy);
|
|
|
|
T m_elem;
|
|
ListNode* m_next;
|
|
ListNode* m_prev;
|
|
};
|
|
|
|
|
|
|
|
/** A realtime safe, (partially) thread safe doubly linked list.
|
|
*
|
|
* Elements can be added safely while another thread is reading the list. See
|
|
* documentation for specific functions for realtime/thread safeness.
|
|
*/
|
|
template <typename T>
|
|
class List : public MaidObject
|
|
{
|
|
public:
|
|
List() : m_head(NULL), m_tail(NULL), m_size(0), m_end_iter(this), m_const_end_iter(this)
|
|
{
|
|
m_end_iter.m_listnode = NULL;
|
|
m_end_iter.m_next = NULL;
|
|
m_const_end_iter.m_listnode = NULL;
|
|
m_const_end_iter.m_next = NULL;
|
|
}
|
|
~List();
|
|
|
|
void push_back(ListNode<T>* elem);
|
|
ListNode<T>* remove(const T elem);
|
|
|
|
void clear();
|
|
size_t size() const { return m_size; }
|
|
|
|
class iterator;
|
|
|
|
/** Realtime safe const iterator for a List. */
|
|
class const_iterator
|
|
{
|
|
public:
|
|
const_iterator(const List<T>* const list);
|
|
const_iterator(const iterator& i)
|
|
: m_list(i.m_list), m_listnode(i.m_listnode), m_next(i.m_next) {}
|
|
|
|
inline const T& operator*();
|
|
inline const_iterator& operator++();
|
|
inline bool operator!=(const const_iterator& iter) const;
|
|
inline bool operator!=(const iterator& iter) const;
|
|
|
|
friend class List<T>;
|
|
|
|
private:
|
|
const List<T>* const m_list;
|
|
const ListNode<T>* m_listnode;
|
|
const ListNode<T>* m_next; // use this instead of m_listnode->next() to allow deleting
|
|
};
|
|
|
|
|
|
/** Realtime safe iterator for a List. */
|
|
class iterator
|
|
{
|
|
public:
|
|
iterator(List<T>* const list);
|
|
|
|
inline T& operator*();
|
|
inline iterator& operator++();
|
|
inline bool operator!=(const iterator& iter) const;
|
|
inline bool operator!=(const const_iterator& iter) const;
|
|
|
|
friend class List<T>;
|
|
friend class List<T>::const_iterator;
|
|
|
|
private:
|
|
const List<T>* m_list;
|
|
ListNode<T>* m_listnode;
|
|
ListNode<T>* m_next; // use this instead of m_listnode->next() to allow deleting
|
|
};
|
|
|
|
|
|
ListNode<T>* remove(const iterator iter);
|
|
|
|
iterator begin();
|
|
const iterator end() const;
|
|
|
|
const_iterator begin() const;
|
|
//const_iterator end() const;
|
|
|
|
private:
|
|
// Prevent copies (undefined)
|
|
List(const List& copy);
|
|
List& operator=(const List& copy);
|
|
|
|
ListNode<T>* m_head;
|
|
ListNode<T>* m_tail;
|
|
size_t m_size;
|
|
iterator m_end_iter;
|
|
const_iterator m_const_end_iter;
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
List<T>::~List<T>()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
|
|
/** Clear the list, deleting all ListNodes contained (but NOT their contents!)
|
|
*
|
|
* Not realtime safe.
|
|
*/
|
|
template <typename T>
|
|
void
|
|
List<T>::clear()
|
|
{
|
|
if (m_head == NULL) return;
|
|
|
|
ListNode<T>* node = m_head;
|
|
ListNode<T>* next = NULL;
|
|
|
|
while (node != NULL) {
|
|
next = node->next();
|
|
delete node;
|
|
node = next;
|
|
}
|
|
m_tail = m_head = NULL;
|
|
m_size = 0;
|
|
}
|
|
|
|
|
|
/** Add an element to the list.
|
|
*
|
|
* This method can be called while another thread is reading the list.
|
|
* Realtime safe.
|
|
*/
|
|
template <typename T>
|
|
void
|
|
List<T>::push_back(ListNode<T>* const ln)
|
|
{
|
|
assert(ln != NULL);
|
|
|
|
ln->next(NULL);
|
|
// FIXME: atomicity? relevant?
|
|
if (m_head == NULL) {
|
|
ln->prev(NULL);
|
|
m_head = m_tail = ln;
|
|
} else {
|
|
ln->prev(m_tail);
|
|
m_tail->next(ln);
|
|
m_tail = ln;
|
|
}
|
|
++m_size;
|
|
}
|
|
|
|
|
|
/** Remove an element from the list.
|
|
*
|
|
* This function is realtime safe - it is the caller's responsibility to
|
|
* delete the returned ListNode, or there will be a leak.
|
|
*/
|
|
template <typename T>
|
|
ListNode<T>*
|
|
List<T>::remove(const T elem)
|
|
{
|
|
// FIXME: atomicity?
|
|
ListNode<T>* n = m_head;
|
|
while (n != NULL) {
|
|
if (n->elem() == elem)
|
|
break;
|
|
n = n->next();
|
|
}
|
|
if (n != NULL) {
|
|
if (n == m_head) m_head = m_head->next();
|
|
if (n == m_tail) m_tail = m_tail->prev();
|
|
if (n->prev() != NULL)
|
|
n->prev()->next(n->next());
|
|
if (n->next() != NULL)
|
|
n->next()->prev(n->prev());
|
|
--m_size;
|
|
if (m_size == 0) m_head = m_tail = NULL; // FIXME: Shouldn't be necessary
|
|
return n;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Remove an element from the list using an iterator.
|
|
*
|
|
* This function is realtime safe - it is the caller's responsibility to
|
|
* delete the returned ListNode, or there will be a leak.
|
|
*/
|
|
template <typename T>
|
|
ListNode<T>*
|
|
List<T>::remove(const iterator iter)
|
|
{
|
|
ListNode<T>* n = iter.m_listnode;
|
|
if (n != NULL) {
|
|
if (n == m_head) m_head = m_head->next();
|
|
if (n == m_tail) m_tail = m_tail->prev();
|
|
if (n->prev() != NULL)
|
|
n->prev()->next(n->next());
|
|
if (n->next() != NULL)
|
|
n->next()->prev(n->prev());
|
|
--m_size;
|
|
if (m_size == 0) m_head = m_tail = NULL; // FIXME: Shouldn't be necessary
|
|
return n;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//// Iterator stuff ////
|
|
|
|
template <typename T>
|
|
List<T>::iterator::iterator(List<T>* list)
|
|
: m_list(list),
|
|
m_listnode(NULL),
|
|
m_next(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
T&
|
|
List<T>::iterator::operator*()
|
|
{
|
|
assert(m_listnode != NULL);
|
|
return m_listnode->elem();
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline typename List<T>::iterator&
|
|
List<T>::iterator::operator++()
|
|
{
|
|
assert(m_listnode != NULL);
|
|
m_listnode = m_next;
|
|
if (m_next != NULL)
|
|
m_next = m_next->next();
|
|
else
|
|
m_next = NULL;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline bool
|
|
List<T>::iterator::operator!=(const iterator& iter) const
|
|
{
|
|
return (m_listnode != iter.m_listnode);
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline bool
|
|
List<T>::iterator::operator!=(const const_iterator& iter) const
|
|
{
|
|
return (m_listnode != iter.m_listnode);
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline typename List<T>::iterator
|
|
List<T>::begin()
|
|
{
|
|
typename List<T>::iterator iter(this);
|
|
iter.m_listnode = m_head;
|
|
if (m_head != NULL)
|
|
iter.m_next = m_head->next();
|
|
else
|
|
iter.m_next = NULL;
|
|
return iter;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline const typename List<T>::iterator
|
|
List<T>::end() const
|
|
{
|
|
/*typename List<T>::iterator iter(this);
|
|
iter.m_listnode = NULL;
|
|
iter.m_next = NULL;
|
|
return iter;*/
|
|
return m_end_iter;
|
|
}
|
|
|
|
|
|
|
|
/// const_iterator stuff ///
|
|
|
|
|
|
template <typename T>
|
|
List<T>::const_iterator::const_iterator(const List<T>* const list)
|
|
: m_list(list),
|
|
m_listnode(NULL),
|
|
m_next(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
const T&
|
|
List<T>::const_iterator::operator*()
|
|
{
|
|
assert(m_listnode != NULL);
|
|
return m_listnode->elem();
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline typename List<T>::const_iterator&
|
|
List<T>::const_iterator::operator++()
|
|
{
|
|
assert(m_listnode != NULL);
|
|
m_listnode = m_next;
|
|
if (m_next != NULL)
|
|
m_next = m_next->next();
|
|
else
|
|
m_next = NULL;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline bool
|
|
List<T>::const_iterator::operator!=(const const_iterator& iter) const
|
|
{
|
|
return (m_listnode != iter.m_listnode);
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline bool
|
|
List<T>::const_iterator::operator!=(const iterator& iter) const
|
|
{
|
|
return (m_listnode != iter.m_listnode);
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
inline typename List<T>::const_iterator
|
|
List<T>::begin() const
|
|
{
|
|
typename List<T>::const_iterator iter(this);
|
|
iter.m_listnode = m_head;
|
|
if (m_head != NULL)
|
|
iter.m_next = m_head->next();
|
|
else
|
|
iter.m_next = NULL;
|
|
return iter;
|
|
}
|
|
|
|
#if 0
|
|
template <typename T>
|
|
inline typename List<T>::const_iterator
|
|
List<T>::end() const
|
|
{
|
|
/*typename List<T>::const_iterator iter(this);
|
|
iter.m_listnode = NULL;
|
|
iter.m_next = NULL;
|
|
return iter;*/
|
|
return m_const_end_iter;
|
|
}
|
|
#endif
|
|
|
|
#endif // LIST_H
|