From f3c18b62925183901e3963b2315ee572af34326a Mon Sep 17 00:00:00 2001 From: JaMiT Date: Sat, 4 Jan 2014 16:12:27 -0600 Subject: [PATCH] Implementation of a "smart" linked list. With a standard linked list, iterators are never invalidated unless they point to an element that was erased (or the list is destroyed). This implementation takes that one step further and never invalidates iterators (unless the list is destroyed). From the perspective of the list, erased elements no longer exist, but an iterator that still points to an erased element can be dereferenced, incremented, and decremented. I was having trouble implementing this based on STL and Boost, so I went ahead and coded this from scratch. The standard list interface is implemented, aside from assignment (since I could not reconcile assignment with the goal of not invalidating iterators). --- src/utils/smart_list.hpp | 845 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 845 insertions(+) create mode 100644 src/utils/smart_list.hpp diff --git a/src/utils/smart_list.hpp b/src/utils/smart_list.hpp new file mode 100644 index 000000000000..a007753463f3 --- /dev/null +++ b/src/utils/smart_list.hpp @@ -0,0 +1,845 @@ +/* + Copyright (C) 2014 - 2014 by David White + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + This program 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. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. + + See the COPYING file for more details. +*/ + +/** + * @file A list whose iterators never invalidate. + */ + +#ifndef UTILS_SMART_LIST_HPP_INCLUDED +#define UTILS_SMART_LIST_HPP_INCLUDED + +#include +#include +#include + +namespace utils +{ + + +/* ** smart_list ** */ + +/// This is a variant on a list that never invalidates iterators (unless, of +/// course, the list ceases to exist). In particular, an erase() will only flag +/// an element for removal from the list. Flagged elements are ignored by list +/// functions, but they still exist from the perspective of iterators that had +/// pointed (and still point) to the element. +/// +/// Assignment is incompatible with the goal of preserving iterators, so it is +/// not implemented (declared private though, to emphasize that this is +/// intentional). +/// +/// The insert() and erase() members are static, as they do not require a +/// reference to the list. +/// +/// Instead of being undefined, ++end() is equal to begin(), and --begin() is +/// equal to end(). +template +class smart_list +{ + /// Nodes in the smart list. + struct node_t + { + /// Default constructor. This is for creating the root of a list. + node_t() : dat_ptr(NULL), ref_count(1), next(this), prev(this) + {} + /// Initialized constructor. This is for creating a node in a list. + explicit node_t(const Data & d) : dat_ptr(new Data(d)), ref_count(1), next(NULL), prev(NULL) + {} + /// Destructor. + ~node_t(); + + /// The data of the node. This is NULL for a list's root. + Data * const dat_ptr; + /// ref_count counts 1 for the list and 2 for each iterator pointing to + /// this node. (So nodes are flagged for deletion when ref_count is even.) + mutable size_t ref_count; + // List linking. + node_t * next; + node_t * prev; + + private: // Disallowed (and unneeded) functions (declared but not implemented). + /// No assignments. + node_t & operator=(const node_t &); + /// No copying. + node_t(const node_t & that); + }; + + /// The base for the list's iterator classes. + /// Value is the value type of this iterator. + /// Reversed is true for the reverse iterators. + /// The actual iterators must have neither data nor destructors. + template + class iterator_base + { + friend class smart_list; + typedef typename smart_list::node_t node_t; + + public: + // Types required of an iterator: + typedef Value value_type; + typedef value_type * pointer; + typedef value_type & reference; + typedef std::bidirectional_iterator_tag iterator_category; + typedef ptrdiff_t difference_type; // Needed to use this with BOOST_FOREACH + + protected: // Construct this via derived classes. + /// Default constructor + iterator_base() : ptr_(NULL) {} + /// Initialized constructor + explicit iterator_base(node_t * ptr) : ptr_(ptr) + { skip_flagged(); refer(); } + /// Conversion constructors. + template + iterator_base(const iterator_base & that) : ptr_(that.ptr_) + { refer(); } + /// Copy constructor (the default overrides the conversion template). + iterator_base(const iterator_base & that) : ptr_(that.ptr_) + { refer(); } + public: + /// Destructor + /// (Not virtual, since the derived classes are mere shells.) + ~iterator_base() { unref(ptr_); } + + // Assignment + iterator_base & operator=(const iterator_base & that); + + // Comparison: + bool operator==(const iterator_base & that) const { return ptr_ == that.ptr_; } + bool operator!=(const iterator_base & that) const { return ptr_ != that.ptr_; } + + // Dereference: + reference operator*() const { return *ptr_->dat_ptr; } + pointer operator->() const { return ptr_->dat_ptr; } + + // Increment/decrement: + iterator_base & operator++() { advance(Reversed); return *this; } + iterator_base & operator--() { advance(!Reversed); return *this; } + iterator_base operator++(int); + iterator_base operator--(int); + + /// Test for being in a list, rather than past-the-end (or unassigned). + bool derefable() const { return derefable(ptr_); } + + + private: // functions + /// Test for being in a list, rather than past-the-end (or unassigned). + static bool derefable(node_t * ptr){ return ptr && ptr->dat_ptr; } + /// Advances our pointer to an unflagged node, possibly in the reverse direction. + void advance(bool reverse); + /// Advances our pointer one step, possibly in the reverse direction. + void inc(bool reverse) { ptr_ = reverse ? ptr_->prev : ptr_->next; } + /// Make sure we are not pointing to a flagged node. + void skip_flagged(bool reverse=Reversed); + /// Add a reference. + void refer() const; + /// Remove a reference. + static void unref(node_t * old_ptr); + + private: // data + node_t * ptr_; + }; + template friend class iterator_base; + +public: // The standard list interface, minus assignment. + typedef Data value_type; + typedef value_type * pointer; + typedef value_type & reference; + typedef const value_type & const_reference; + typedef size_t size_type; + + // Making these structs instead of typedefs cuts down on the length of + // compiler messages and helps define which conversions are allowed. + struct iterator : public iterator_base { + /// Default constructor + iterator() : iterator_base() {} + /// Initialized constructor + explicit iterator(node_t * ptr) : iterator_base(ptr) {} + /// Copy constructor. + iterator(const iterator & that) : iterator_base(that) {} + /// Conversion from reverse_iterator. + explicit iterator(const iterator_base & that) : iterator_base(that) {} + }; + struct const_iterator : public iterator_base { + /// Default constructor + const_iterator() : iterator_base() {} + /// Initialized constructor + explicit const_iterator(node_t * ptr) : iterator_base(ptr) {} + /// Copy constructor. + const_iterator(const const_iterator & that) : iterator_base(that) {} + /// Conversion from iterator. + const_iterator(const iterator_base & that) : iterator_base(that) {} + /// Conversion from const_reverse_iterator. + explicit const_iterator(const iterator_base & that) : iterator_base(that) {} + }; + struct reverse_iterator : public iterator_base { + /// Default constructor + reverse_iterator() : iterator_base() {} + /// Initialized constructor + explicit reverse_iterator(node_t * ptr) : iterator_base(ptr) {} + /// Copy constructor. + reverse_iterator(const reverse_iterator & that) : iterator_base(that) {} + /// Conversion from iterator. + explicit reverse_iterator(const iterator_base & that) : iterator_base(that) {} + }; + struct const_reverse_iterator : public iterator_base { + /// Default constructor + const_reverse_iterator() : iterator_base() {} + /// Initialized constructor + explicit const_reverse_iterator(node_t * ptr) : iterator_base(ptr) {} + /// Copy constructor. + const_reverse_iterator(const const_reverse_iterator & that) : iterator_base(that) {} + /// Conversion from reverse_iterator. + const_reverse_iterator(const iterator_base & that) : iterator_base(that) {} + /// Conversion from const_iterator. + explicit const_reverse_iterator(const iterator_base & that) : iterator_base(that) {} + }; + + + smart_list() : root_() {} + smart_list(size_type n); + smart_list(size_type n, const value_type & d); + smart_list(const smart_list & that); + template + smart_list(const InputIterator & f, const InputIterator & l); + ~smart_list() { clear(); } + + iterator begin() { return iterator(root_.next); } + iterator end() { return iterator(&root_); } + const_iterator begin() const { return const_iterator(root_.next); } + const_iterator end() const { return const_iterator(const_cast(&root_)); } + reverse_iterator rbegin() { return reverse_iterator(root_.prev); } + reverse_iterator rend() { return reverse_iterator(&root_); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(root_.prev); } + const_reverse_iterator rend() const { return const_reverse_iterator(const_cast(&root_)); } + + size_type size() const; + size_type max_size() const { return size_type(-1); } + /// Note: if empty() returns true, the list might still contain nodes + /// flagged for deletion. + bool empty() const { return begin() == end(); } + + // Note: these functions use iterators so flagged nodes are skipped. + reference front() { return *begin(); } + const_reference front() const { return *begin(); } + reference back() { return *rbegin(); } + const_reference back() const { return *rbegin(); } + void push_front(const value_type & d) { insert(root_.next, d); } + void push_back(const value_type & d) { insert(&root_, d); } + void pop_front() { erase(begin()); } + void pop_back() { erase(iterator(rbegin())); } + + void swap(smart_list & that); + + static iterator insert(const iterator & pos, const value_type & d); + template + static void insert(const iterator & pos, const InputIterator & f, const InputIterator & l); + static void insert(const iterator & pos, size_type n, const value_type & d); + + static iterator erase(const iterator & pos); + static iterator erase(const iterator & start, const iterator & stop); + void clear() { erase(begin(), end()); } + + void resize(size_type n) { resize(n, value_type()); } + void resize(size_type n, const value_type & d); + + void splice(const iterator & pos, smart_list & L); + void splice(const iterator & pos, smart_list & L, const iterator & i); + void splice(const iterator & pos, smart_list & L, const iterator & f, const iterator & l); + + void remove(const value_type & value); + template + void remove_if(const Predicate & p); + + void unique() { unique(std::equal_to()); } + template + void unique(const BinaryPredicate p); + + void merge(smart_list & L) { merge(L, std::less()); } + template + void merge(smart_list & L, const BinaryPredicate & p); + + void sort() { sort(std::less()); } + template + void sort(const BinaryPredicate & p); + +private: // functions + /// No implementation of operator=() since that would not preserve iterators. + smart_list & operator=(const smart_list &); + + /// Returns true if @a node has been flagged for deletion. + static bool flagged(const node_t & node) { return node.ref_count % 2 == 0; } + /// Flags @a node for deletion. + static void flag(const node_t & node) { node.ref_count &= ~size_t(1); } + + // Low-level implementations. The list is designed so that a reference + // to the list itself is not needed. + static node_t * insert(node_t * const pos, const value_type & d); + static node_t * check_erase(node_t * const pos); + static void link(node_t * const pos, node_t & begin_link, node_t & end_link); + static void unlink(node_t & begin_unlink, node_t & end_unlink); + static void splice(node_t * const pos, node_t & f, node_t & l); + + +private: // data + /// Root of the list. + node_t root_; + // I started doing this using STL and Boost, but it was taking longer than + // it would take for me to write stuff from scratch. +}; + +/** Sized constructor */ +template +inline smart_list::smart_list(size_type n) : root_() +{ + for ( size_type i = 0; i < n; ++i ) + push_back(value_type()); +} +/** Sized constructor with a value */ +template +inline smart_list::smart_list(size_type n, const value_type & d) : root_() +{ + for ( size_type i = 0; i < n; ++i ) + push_back(d); +} +/** Copy constructor */ +template +inline smart_list::smart_list(const smart_list & that) : root_() +{ + // Copy non-flagged nodes. + for ( const_iterator it = that.begin(); it.derefable(); ++it ) + push_back(*it); +} +/** Constructor from a range */ +template template +inline smart_list::smart_list(const InputIterator & f, const InputIterator & l) : + root_() +{ + for ( InputIterator it = f; f != l; ++f ) + push_back(*it); +} + + +/** The size of the list, not counting flagged nodes. */ +template +inline typename smart_list::size_type smart_list::size() const +{ + size_type count = 0; + // Look for nodes not flagged for deletion. + for ( const_iterator it = begin(); it.derefable(); ++it ) + ++count; + return count; +} + +/** Swaps two lists. Done in constant time, with no iterator invalidation. */ +template +inline void smart_list::swap(smart_list & that) +{ + smart_list temp_list; + + // Swap, using splices instead of assignments. + temp_list.splice(temp_list.end(), that); + that.splice(that.end(), *this); + splice(end(), temp_list); +} + +/** Insert a node before @a pos. */ +template +inline typename smart_list::iterator smart_list::insert + (const iterator & pos, const value_type & d) +{ + return iterator(insert(pos.ptr_, d)); +} +/** Insert a range before @a pos. */ +template +template +inline void smart_list::insert(const iterator & pos, const InputIterator & f, + const InputIterator & l) +{ + for ( InputIterator it = f; it != l; ++it ) + insert(pos.ptr_, *it); +} +/** Insert @a n copies of @a d at @a pos. */ +template +inline void smart_list::insert(const iterator & pos, size_type n, + const value_type & d) +{ + for ( size_type i = 0; i < n; ++i ) + insert(pos.ptr_, d); +} + +/** Erase the node at @a pos. */ +template +inline typename smart_list::iterator smart_list::erase + (const iterator & pos) +{ + flag(*pos.ptr_); // We know *pos cannot get deleted yet because pos points to it. + return iterator(pos.ptr_->next); +} +/** Erase a range of nodes. */ +template +inline typename smart_list::iterator smart_list::erase + (const iterator & start, const iterator & stop) +{ + // Loop through all nodes from start to stop. + // We cannot rely on iterators because *stop might be flagged. + node_t * node_ptr = start.ptr_; + while ( node_ptr != stop.ptr_ && iterator::derefable(node_ptr) ) { + flag(*node_ptr); + node_ptr = check_erase(node_ptr); + } + + // node_ptr is more reliable than stop because of the derefable() condition. + return iterator(node_ptr); +} + +/** + * Resize the list. + * This does not count flagged nodes. + */ +template +inline void smart_list::resize(size_type n, const value_type & d) +{ + size_type count = 0; + iterator it = begin(); + iterator _end = end(); + + // See how our current size compares to n. + // (Not calling size(), since that would do at least as much work.) + while ( it != _end && count < n ) { + ++it; + ++count; + } + + if ( count < n ) + // We need more nodes. + insert(_end, n - count, d); + else if ( it != _end ) + // We need to remove nodes. + erase(it, _end); +} + +/** Splice all of @a L into *this before @a pos. */ +template +inline void smart_list::splice(const iterator & pos, smart_list & L) +{ + if ( L.root_.next != &L.root ) // if L has nodes to splice. + splice(pos.ptr_, *L.root_.next, *L.root_.prev); +} +/** Splice the node @a i points to (assumed from @a L) into *this before @a pos. */ +template +inline void smart_list::splice(const iterator & pos, smart_list & /*L*/, + const iterator & i) +{ + if ( i.ptr_.derefable() ) + splice(pos.ptr_, *i.ptr_, *i.ptr_); +} +/** Splice a range (assumed from @a L) into *this before @a pos. */ +template +inline void smart_list::splice(const iterator & pos, smart_list & /*L*/, + const iterator & f, const iterator & l) +{ + // Abort on degenerate cases. + if ( !f.derefable() || f == l ) + return; + + // Splice from f to the node before l. + splice(pos.ptr_, *(f.ptr_), *(l.ptr_->prev)); +} + +/** Remove all nodes whose data equals @a value. */ +template +inline void smart_list::remove(const value_type & value) +{ + // Look for elements to erase. + iterator it = begin(), _end = end(); + while ( it != _end ) + if ( *it == value ) + it = erase(it); + else + ++it; +} +/** Remove all nodes whose data satisfies @a p. */ +template +template +inline void smart_list::remove_if(const Predicate & p) +{ + // Look for elements to erase. + iterator it = begin(), _end = end(); + while ( it != _end() ) + if ( p(*it) ) + it = erase(it); + else + ++it; +} + +/** Remove nodes equal (under @a p) to their immediate predecessor. */ +template +template +inline void smart_list::unique(const BinaryPredicate p) +{ + // Initial state. + iterator _end = end(); + iterator cur = begin(); + iterator prev = cur; + // Look at the second node, making the first node "previous". + if ( cur != _end ) + ++cur; + + // Look for elements equal to their predecessors. + while ( cur != _end ) + if ( p(*prev, *cur) ) + // Duplicate. Remove it. + cur = erase(cur); + else + // Update the tracking of our previous element. + prev = cur++; +} + +/** + * Merge @a L into *this, using @a p to determine order. + * If both lists are sorted under @a p, then so is the merged list. + * This is stable; equivalent nodes retain their order, with nodes of *this + * considered to come before elements of @a L. + */ +template +template +inline void smart_list::merge(smart_list & L, const BinaryPredicate & p) +{ + // Merging a list into itself is a no-op, not a duplication. + if ( this == &L ) + return; + + node_t * dest = root_.next; + node_t * source = L.root_.next; + + // Basic merge loop. Continues until one of the lists is depleted. + while ( iterator::derefable(dest) && iterator::derefable(source) ) { + if ( p(*(source->dat_ptr), *(dest->dat_ptr)) ) { + // We found something to merge in. See if we can merge multiple + // nodes at once. + node_t * end_merge = source->next; + while ( iterator::derefable(end_merge) && p(*(end_merge->dat_ptr), *(dest->dat_ptr)) ) + end_merge = end_merge->next; + // Now end_merge is one past the nodes to merge. + // Move the nodes over. + splice(dest, *source, *(end_merge->prev)); + // Advance the source. + source = end_merge; + } + else + // Advance the destination. + dest = dest->next; + } + + // Append any remaining nodes from L. + if ( iterator::derefable(source) ) // hence, we made it to our end. + splice(&root_, *source, *(L.root_.prev)); +} + +/** + * Sort *this, using the order given by @a p. + * This is stable; equivalent nodes retain their order. + * This is an efficient sort, O(n lg n). + */ +template +template +inline void smart_list::sort(const BinaryPredicate & p) +{ + // We'll leverage merge() to do a merge sort. + + // First, get the real size of the list, so we can allocate memory before + // altering *this (exception safety). + size_type count = 0; + for ( node_t * node_ptr = root_.next; iterator::derefable(node_ptr); node_ptr = node_ptr->next ) + ++count; + // Abort on trivial cases. + if ( count < 2 ) + return; + + // Split *this into 1-length pieces. + std::vector sorter(count); // No memory allocations after this point. + for ( size_type i = 0; i < count; ++i ) + sorter[i].splice(&(sorter[i].root_), *(root_.next), *(root_.next)); + + // At the start of each iteration, step will be the distance between lists + // containing data. (There will be O(lg n) iterations.) + for ( size_type step = 1; step < count; step *= 2 ) + // Merge consecutive data-bearing lists. Summing over all iterations of + // this (inner) loop, there will be O(n) comparisons made. + for ( size_type i = 0; i + step < count; i += 2*step ) + sorter[i].merge(sorter[i+step]); + + // The entire (sorted) list is now in the first element of the vector. + swap(sorter[0]); +} + + +/** + * Low-level insertion. + * The insertion will occur before @a pos. Pass &root_ to append to the list. + */ +template +inline typename smart_list::node_t * smart_list::insert + (node_t * const pos, const value_type & d) +{ + node_t * new_node = new node_t(d); + // The new node is essentially a 1-node list. + link(pos, *new_node, *new_node); + // Return a pointer to the new node. + return new_node; +} +/** + * Low-level erasure. + * The node will only be erased if its ref_count is 0. (So the caller must + * remove their reference before calling this.) + * @returns the next node in the list (could be a flagged node). + */ +template +inline typename smart_list::node_t * smart_list::check_erase + (node_t * const pos) +{ + // Sanity check. + if ( !iterator::derefable(pos) ) + return NULL; + + // Remember our successor. + node_t * ret_val = pos->next; + + // Can we actually erase? + if ( pos->ref_count == 0 ) { + // Disconnect *pos from the list. + unlink(*pos, *pos); + // Now we can delete. + delete pos; + } + + return ret_val; +} + +/** + * Assuming the nodes from @a begin_link to @a end_link are linked, they will + * be inserted into *this before @a pos. (Pass &root_ to append to the list.) + * Note that *end_link is included in the insert; this is not exactly + * analogous to a range of iterators. + * This is a constant-time operation; reference counts are not updated. + */ +template +inline void smart_list::link(node_t * const pos, node_t & begin_link, node_t & end_link) +{ + // Link the new nodes into the list. + begin_link.prev = pos->prev; + end_link.next = pos; + + // Link the list to the new nodes. + begin_link.prev->next = &begin_link; + end_link.next->prev = &end_link; +} +/** + * Assuming @a begin_unlink and @a end_unlink are nodes from *this, with + * @a end_unlink a successor of @a begin_unlink, that chain of nodes will + * be removed from *this. + * Ownership of the removed chain is transferred to the caller, who becomes + * repsonsible for not leaving the nodes in limbo. + * This is a constant-time operation; reference counts are not updated. + */ +template +inline void smart_list::unlink(node_t & begin_unlink, node_t & end_unlink) +{ + // Disconnect the list from the nodes. + begin_unlink.prev->next = end_unlink.next; + end_unlink.next->prev = begin_unlink.prev; + + // Disconnect the nodes from the list. This leaves the nodes in limbo. + end_unlink.next = NULL; + begin_unlink.prev = NULL; +} + +/** + * Low-level splice of the chain from @a b to @a e (including b and e) + * to the spot before @a pos. + */ +template +inline void smart_list::splice(node_t * const pos, node_t & b, node_t & e) +{ + // Remove from the old list. + unlink(b, e); + // Splice into the new list. + link(pos, b, e); +} + + +/** + * Equality, if lists contain equal elements in the same order. + */ +template +inline bool operator==(const smart_list & a, const smart_list & b) +{ + typename smart_list::const_iterator end_a = a.end(); + typename smart_list::const_iterator end_b = b.end(); + typename smart_list::const_iterator it_a = a.begin(); + typename smart_list::const_iterator it_b = b.begin(); + + for ( ; it_a != end_a && it_b != end_b; ++it_a, ++it_b ) + if ( *it_a != *it_b ) + // mismatch + return false; + + // All comparisons were equal, so they are the same if we compared everything. + return it_a == end_a && it_b == end_b; +} +/** + * Lexicographical order. + */ +template +inline bool operator<(const smart_list & a, const smart_list & b) +{ + typename smart_list::const_iterator end_a = a.end(); + typename smart_list::const_iterator end_b = b.end(); + typename smart_list::const_iterator it_a = a.begin(); + typename smart_list::const_iterator it_b = b.begin(); + + for ( ; it_a != end_a && it_b != end_b; ++it_a, ++it_b ) + if ( *it_a < *it_b ) + // a is less than b. + return true; + else if ( *it_b < *it_a ) + // b is less than a. + return false; + + // All comparisons were equal, so a is less than b if a is shorter. + return it_b != end_b; +} + + +/* ** smart_list::node_t ** */ + +template +inline smart_list::node_t::~node_t() +{ + // Some safety checks. + if ( dat_ptr == NULL ) + // Root node: make sure there are no lingering iterators to the list. + assert(next == this); + else { + // Normal node: make sure we are not still in a list. + assert(next == NULL && prev == NULL); + // Make sure no iterators point to us. + assert(ref_count == 0); + } + + delete dat_ptr; +} + + +/* ** smart_list::iterator_base ** */ + +/** Assignment */ +template +template +inline smart_list::iterator_base & + smart_list::iterator_base::operator= + (const iterator_base & that) +{ + // Update our pointer. + node_t * old_ptr = ptr_; + ptr_ = that.ptr_; + + // Update reference counts. + refer(); + unref(old_ptr); + + return *this; +} + +/** Post-increment */ +template +template +inline smart_list::iterator_base + smart_list::iterator_base::operator++(int) +{ + iterator_base retval(*this); + advance(Reversed); + return retval; +} +/** Post-decrement */ +template +template +inline smart_list::iterator_base + smart_list::iterator_base::operator--(int) +{ + iterator_base retval(*this); + advance(!Reversed); + return retval; +} + +/** + * Advances our pointer to an unflagged node, possibly in the reverse direction. + * This will advance at least one step, and will not stop on a flagged node. + * In addition, this takes care of updating reference counts. + * (This is the code shared by increments and decrements.) + */ +template +template +inline void smart_list::iterator_base::advance(bool reverse) +{ + node_t * old_ptr = ptr_; + + // Guarantee a change. + inc(reverse); + // Skip as necessary. + skip_flagged(reverse); + + // Update reference counts. + refer(); + unref(old_ptr); +} + +/** Make sure we are not pointing to a flagged node. */ +template +template +inline void smart_list::iterator_base::skip_flagged + (bool reverse) +{ + while ( derefable() && smart_list::flagged(*ptr_) ) + inc(reverse); +} + +/** Add a reference to that to which we point. */ +template +template +inline void smart_list::iterator_base::refer() const +{ + if ( derefable() ) + ptr_->ref_count += 2; +} + +/** + * Remove a reference. + * May delete old_ptr. So call this after updating ptr_ to a new value. + */ +template +template +inline void smart_list::iterator_base::unref(node_t * old_ptr) +{ + if ( derefable(old_ptr) ) { + // Remove a reference. + old_ptr->ref_count -= 2; + smart_list::check_erase(old_ptr); + } +} + +}// namespace util + +#endif // UTILS_SMART_LIST_HPP_INCLUDED +