/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (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.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #ifndef DOUBLYLINKEDLIST_H #define DOUBLYLINKEDLIST_H #include "Fundamentals.h" // --- PRIVATE ---------------------------------------------------------------- struct DoublyLinkedNode { DoublyLinkedNode *next; // Link to next node (in DEBUG versions nil if not explicitly set) DoublyLinkedNode *prev; // Link to previous node (in DEBUG versions nil if not explicitly set) void remove(); void substitute(DoublyLinkedNode &src); void insertAfter(DoublyLinkedNode &loc); void insertBefore(DoublyLinkedNode &loc); #ifdef DEBUG void init() {next = 0; prev = 0;} bool isLinked() const {return next && prev;} bool isUnlinked() const {return !next && !prev;} #else void init() {} #endif }; class DoublyLinkedRoot { protected: DoublyLinkedNode root; private: #if 0 DoublyLinkedRoot(const DoublyLinkedRoot &); // Copying forbidden void operator=(const DoublyLinkedRoot &); // Copying forbidden #endif public: DoublyLinkedRoot(const DoublyLinkedRoot &){ PR_ASSERT(0); } // Copying forbidden void operator=(const DoublyLinkedRoot &){ PR_ASSERT(0); } // Copying forbidden DoublyLinkedRoot() {root.next = &root; root.prev = &root;} void init() {root.next = &root; root.prev = &root;} bool empty() const {return root.next == &root;} protected: void removeFirstNode(); }; // --- PUBLIC ----------------------------------------------------------------- // Derive doubly-linked list nodes from this class. N is the node class, which should // be a subclass of this class. template class DoublyLinkedEntry: public DoublyLinkedNode { #ifdef DEBUG DoublyLinkedEntry(const DoublyLinkedEntry &); // Copying forbidden void operator=(const DoublyLinkedEntry &); // Copying forbidden public: DoublyLinkedEntry() {init();} #endif public: // DoublyLinkedNode administration static N &linkOwner(DoublyLinkedNode &l) {return *static_cast(&l);} DoublyLinkedNode &getLinks() {return *this;} }; // Use this class for doubly-linked list containers. N is the node class, which should // inherit from DoublyLinkedEntry. template class DoublyLinkedList: public DoublyLinkedRoot { public: typedef DoublyLinkedNode *iterator; // To iterate forward through a DoublyLinkedList dl, use: // for (DoublyLinkedList::iterator i = dl.begin(); !dl.done(i); i = dl.advance(i)) // ... dl.get(i) ... iterator begin() const {return root.next;} iterator end() const {return root.prev;} NONDEBUG_ONLY(static) iterator location(N &node) {assert(exists(node)); return &node.getLinks();} static N &get(iterator i) {return N::linkOwner(*i);} static iterator advance(iterator i) {return i->next;} static iterator retreat(iterator i) {return i->prev;} bool done(iterator i) const {return i == &root;} #ifdef DEBUG bool validIterator(iterator i) const; #endif Uint32 length() const; bool lengthIs(Uint32 n) const; N &first() const {assert(!empty()); return get(root.next);} N &last() const {assert(!empty()); return get(root.prev);} bool exists(N &node) const; Uint32 index(N &node) const; void addFirst(N &node); void addLast(N &node); NONDEBUG_ONLY(static) void insertBefore(N &node, iterator i); NONDEBUG_ONLY(static) void insertAfter(N &node, iterator i); void removeFirst(); void removeLast(); void clear(); void move(DoublyLinkedList &src); }; template class SortedDoublyLinkedList: public DoublyLinkedList { public: explicit SortedDoublyLinkedList(int (*compare)(const N *elem1, const N *elem2)): compareFunc(compare) {} void insert(N &node); bool isInList(N &node); private: int (*const compareFunc)(const N*, const N*); // Comparison function returns 1 for >, -1 for <, and 0 for ==. }; // --- INLINES ---------------------------------------------------------------- // // Unlink this node from the list into which it is linked. // inline void DoublyLinkedNode::remove() { assert(isLinked() && next != this); DoublyLinkedNode *n = next; DoublyLinkedNode *p = prev; n->prev = p; p->next = n; init(); } // // Insert this node after node loc in a doubly-linked list. // Node loc must be already linked into a list, while // this node should not be already linked into any list. // inline void DoublyLinkedNode::insertAfter(DoublyLinkedNode &loc) { assert(isUnlinked() && loc.isLinked()); next = loc.next; prev = &loc; loc.next->prev = this; loc.next = this; } // // Insert this node before node loc in a doubly-linked list. // Node loc must be already linked into a list, while // this node should not be already linked into any list. // inline void DoublyLinkedNode::insertBefore(DoublyLinkedNode &loc) { assert(isUnlinked() && loc.isLinked()); next = &loc; prev = loc.prev; loc.prev->next = this; loc.prev = this; } // // Unlink src from the list into which it is linked and link this node in src's // place. src must not be the list's root (unless called from the move method below). // inline void DoublyLinkedNode::substitute(DoublyLinkedNode &src) { assert(isUnlinked() && src.isLinked()); DoublyLinkedNode *n = src.next; DoublyLinkedNode *p = src.prev; assert(n != &src && p != &src && n != this && p != this && n->prev == &src && p->next == &src); next = n; prev = p; n->prev = this; p->next = this; src.init(); } // // Return the number of nodes (not including the root) in the doubly-linked list. // template Uint32 DoublyLinkedList::length() const { Uint32 n = 0; for (iterator i = begin(); !done(i); i = advance(i)) n++; return n; } // // Return true if the number of nodes (not including the root) in the doubly-linked list // is equal to n. This is often faster than "length() == n" because if n is small it can // stop iterating through the list as soon as it exceeds n. // template bool DoublyLinkedList::lengthIs(Uint32 n) const { for (iterator i = begin(); !done(i); i = advance(i)) if (n-- == 0) return false; return n == 0; } #ifdef DEBUG // // Return true if the iterator points somewhere within this doubly-linked list // (including its root). // template bool DoublyLinkedList::validIterator(DoublyLinkedNode* i) const { iterator j; for (j = begin(); !done(j); j = advance(j)) if (i == j) return true; return i == j; } #endif // // Return true if node is in the list and false otherwise. // template bool DoublyLinkedList::exists(N &node) const { for (iterator current = this->begin(); !done(current); current = advance(current)) if (&node == &this->get(current)) return true; return false; } // // Return the zero-based index of node in the list or this->length() if // the node is not in the list. // template Uint32 DoublyLinkedList::index(N &node) const { Uint32 index = 0; for (iterator current = this->begin(); !done(current) && &node != &this->get(current); current = advance(current)) index++; return index; } // // Insert node into the doubly-linked list at its beginning. // The node should not be already linked into any list. // template inline void DoublyLinkedList::addFirst(N &node) { node.getLinks().insertAfter(root); } // // Insert node into the doubly-linked list at its end. // The node should not be already linked into any list. // template inline void DoublyLinkedList::addLast(N &node) { node.getLinks().insertBefore(root); } // // Insert node into the doubly-linked list before iterator i. // The node should not be already linked into any list. // template inline void DoublyLinkedList::insertBefore(N &node, DoublyLinkedNode* i) { assert(validIterator(i)); node.getLinks().insertBefore(*i); } // // Insert node into the doubly-linked list after iterator i. // The node should not be already linked into any list. // template inline void DoublyLinkedList::insertAfter(N &node, DoublyLinkedNode* i) { assert(validIterator(i)); node.getLinks().insertAfter(*i); } // // Remove the first node, if any, from this doubly-linked list. // template inline void DoublyLinkedList::removeFirst() { if (!empty()) root.next->remove(); } // // Remove the last node, if any, from this doubly-linked list. // template inline void DoublyLinkedList::removeLast() { if (!empty()) root.prev->remove(); } // // Remove all nodes from this doubly-linked list. // template inline void DoublyLinkedList::clear() { while (!empty()) root.next->remove(); } // // Destructively move the src DoublyLinkedList to this DoublyLinkedList. // The src DoublyLinkedList will subsequently be empty. This DoublyLinkedList // must be empty prior to this call. // template void DoublyLinkedList::move(DoublyLinkedList &src) { assert(empty() && &src != this); if (!src.empty()) { root.init(); // Avoid assert inside substitute. root.substitute(src.root); src.init(); } } // // Uses the specified compare function to search for node. // Returns true if node is in the list and false otherwise. // template bool SortedDoublyLinkedList::isInList(N &node) { iterator current = this->begin(); for (; !done(current) && (compareFunc(&node, &(this->get(current))) == 1); current = advance(current)) {} // do nothing in body if (done(current)) return false; else return (compareFunc(&node, &(this->get(current))) == 0); } // // Inserts node into the sorted list unless it already exists in the list. // template void SortedDoublyLinkedList::insert(N &node) { // loop will terminate with current pointing to the spot where we wish to insert node. // last will be the node before the desired spot. If the element is already in the list then // current will point to it. iterator current = this->begin(); iterator last = current; for(; !done(current) && (compareFunc(&node, &(this->get(current))) == 1); current = advance(current)) last = current; if(this->done(current)) // special case add to end of list addLast(node); else if(compareFunc(&node, &(this->get(current))) != 0) // Do not insert if element is in the list already { // might want this to be users responsibilities if (current == this->begin()) addFirst(node); else { // add to list between last and current DoublyLinkedNode& beforeNode = this->get(last).getLinks(); DoublyLinkedNode& afterNode = this->get(current).getLinks(); beforeNode.next = &(node.getLinks()); node.getLinks().prev = &beforeNode; afterNode.prev = &(node.getLinks()); node.getLinks().next = &afterNode; } } } #endif