/* 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 "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