#ifndef QPID_ILIST_H #define QPID_ILIST_H /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ #include #include #include #include namespace qpid { template class IList; /** * Base class providing links for a node in an IList. * * Derived classes can inheirt multiple IListNode bases provided each * has a unique value for N. This allows objects to be linked in * multiple independent lists. For example: * * @code * class Foo : public IListNode<0>, public IListNode<1> {}; * IList - uses the 0 links * IList - uses the 1 links *@endcode * *@param T The derived class. *@param N ID for multiple inheritance. */ template class IListNode : public virtual RefCounted { intrusive_ptr prev; intrusive_ptr next; void insert(IListNode*); void erase(); virtual T* self() const { // Virutal so anchor can hide. return boost::polymorphic_downcast(const_cast(this)); } friend class IList; public: typedef IList IListType; IListNode(IListNode* p=0) : prev(p), next(p) {} T* getNext() { return next ? next->self() : 0; } T* getPrev() { return prev ? prev->self() : 0; } const T* getNext() const { return next ? next->self() : 0; } const T* getPrev() const { return prev ? prev->self() : 0; } }; /** * Intrusive doubly linked list of reference counted nodes. * *@param T must inherit from IListNode *@param N Identifies which links to use. @see IListNode. */ template class IList { private: typedef IListNode Node; template class Iterator : public boost::iterator_facade, R, boost::bidirectional_traversal_tag> { Node* ptr; // Iterators do not own their pointees. void increment() { assert(ptr); ptr = ptr->next.get(); } void decrement() { assert(ptr); ptr = ptr->prev.get(); } R& dereference() const { assert(ptr); return *boost::polymorphic_downcast(ptr); } bool equal(const Iterator& x) const { return ptr == x.ptr; } Node* cast(const Node* p) { return const_cast(p); } explicit Iterator(const Node* p=0) : ptr(cast(p)) {} explicit Iterator(const T* p=0) : ptr(cast(p)) {} friend class boost::iterator_core_access; friend class IList; public: template Iterator(const Iterator& x) : ptr(x.ptr) {} }; public: IList() {} ~IList() { clear(); anchor.erase(); } typedef size_t size_type; typedef T value_type; /// pointer type is an intrusive_ptr typedef intrusive_ptr pointer; /// pointer type is an intrusive_ptr typedef intrusive_ptr const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef Iterator iterator; typedef Iterator const_iterator; iterator begin() { return iterator(anchor.next.get()); } iterator end() { return iterator(&anchor); } const_iterator begin() const { return const_iterator(anchor.next.get()); } const_iterator end() const { return const_iterator(&anchor); } /// Iterator to the last element or end() if empty iterator last() { return iterator(anchor.prev.get()); } /// Iterator to the last element or end() if empty const_iterator last() const { return const_iterator(anchor.prev.get()); } /// Note: takes a non-const reference, unlike standard containers. void insert(iterator pos, reference x) { x.Node::insert(pos.ptr); } void insert(iterator pos, pointer x) { x->Node::insert(pos.ptr); } void erase(iterator pos) { pos.ptr->erase(); } void swap(IList &x) { anchor.swap(x.anchor); } reference back() { assert(!empty()); return *last(); } const_reference back() const { assert(!empty()); return *last(); } void pop_back() { assert(!empty()); erase(last()); } /// Note: takes a non-const reference, unlike standard containers. void push_back(reference x) { insert(end(), x); } void push_back(pointer x) { insert(end(), x); } reference front() { assert(!empty()); return *begin(); } const_reference front() const { assert(!empty()); return *begin(); } void pop_front() { assert(!empty()); erase(begin()); } /// Note: takes a non-const reference, unlike standard containers. void push_front(reference x) { insert(begin(), x); } void push_front(pointer x) { insert(begin(), x); } bool empty() const { return begin() == end(); } void clear() { while (!empty()) { pop_front(); } } size_type size() const { size_type s=0; for (const_iterator i = begin(); i != end(); ++i) ++s; return s; } iterator erase(iterator from, iterator to) { while(from != to) erase(from); } private: // The list is circular and always contains an anchor node. // end() points to the anchor, anchor.next is the first // iterator, anchor.prev is the last iterator. struct Anchor : public Node { Anchor() : Node(this) {} // Suppress refcounting for the anchor node. void release() const {} // Hide anchor from public get functions. T* self() const { return 0; } } anchor; }; template void IListNode::insert(IListNode* node) { assert(!next && !prev); // Not already in a list. assert(node); assert(node->next && node->prev); next=node; prev=node->prev; prev->next = this; next->prev = this; } template void IListNode::erase() { assert(prev && next); intrusive_ptr save(this); // Protect till exit. prev->next = next; next->prev = prev; prev = next = 0; } } // namespace qpid #endif /*!QPID_ILIST_H*/