mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 715405 - Add a type-safe linked list class. r=waldo
--HG-- extra : rebase_source : c1a829b9c1264c6b07a10b1ff7a8d0a64e3125f7
This commit is contained in:
parent
09e70c8a76
commit
fc0cb053c5
400
mfbt/LinkedList.h
Normal file
400
mfbt/LinkedList.h
Normal file
@ -0,0 +1,400 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 tw=80 et cin:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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 Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Justin Lebar <justin.lebar@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/* A type-safe doubly-linked list class. */
|
||||
|
||||
/*
|
||||
* The classes LinkedList<T> and LinkedListElement<T> together form a
|
||||
* convenient, type-safe doubly-linked list implementation.
|
||||
*
|
||||
* The class T which will be inserted into the linked list must inherit from
|
||||
* LinkedListElement<T>. A given object may be in only one linked list at a
|
||||
* time.
|
||||
*
|
||||
* For example, you might use LinkedList in a simple observer list class as
|
||||
* follows.
|
||||
*
|
||||
* class Observer : public LinkedListElement<Observer>
|
||||
* {
|
||||
* void observe(char* topic) { ... }
|
||||
* };
|
||||
*
|
||||
* class ObserverContainer
|
||||
* {
|
||||
* private:
|
||||
* LinkedList<ElemType> list;
|
||||
*
|
||||
* public:
|
||||
*
|
||||
* void addObserver(Observer* observer)
|
||||
* {
|
||||
* // Will assert if |observer| is part of another list.
|
||||
* list.insertBack(observer);
|
||||
* }
|
||||
*
|
||||
* void removeObserver(Observer* observer)
|
||||
* {
|
||||
* // Will assert if |observer| is not part of some list.
|
||||
* observer.remove();
|
||||
* }
|
||||
*
|
||||
* void notifyObservers(char* topic)
|
||||
* {
|
||||
* for (Observer* o = list.getFirst();
|
||||
* o != NULL;
|
||||
* o = o->getNext()) {
|
||||
* o->Observe(topic);
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef mozilla_LinkedList_h_
|
||||
#define mozilla_LinkedList_h_
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<typename T>
|
||||
class LinkedList;
|
||||
|
||||
template<typename T>
|
||||
class LinkedListElement
|
||||
{
|
||||
/*
|
||||
* It's convenient that we return NULL when getNext() or getPrevious() hits
|
||||
* the end of the list, but doing so costs an extra word of storage in each
|
||||
* linked list node (to keep track of whether |this| is the sentinel node)
|
||||
* and a branch on this value in getNext/getPrevious.
|
||||
*
|
||||
* We could get rid of the extra word of storage by shoving the "is
|
||||
* sentinel" bit into one of the pointers, although this would, of course,
|
||||
* have performance implications of its own.
|
||||
*
|
||||
* But the goal here isn't to win an award for the fastest or slimmest
|
||||
* linked list; rather, we want a *convenient* linked list. So we won't
|
||||
* waste time guessing which micro-optimization strategy is best.
|
||||
*/
|
||||
|
||||
private:
|
||||
LinkedListElement* next;
|
||||
LinkedListElement* prev;
|
||||
const bool isSentinel;
|
||||
|
||||
public:
|
||||
LinkedListElement()
|
||||
: next(this)
|
||||
, prev(this)
|
||||
, isSentinel(false)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next element in the list, or NULL if this is the last element in
|
||||
* the list.
|
||||
*/
|
||||
T* getNext()
|
||||
{
|
||||
return next->asT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the previous element in the list, or NULL if this is the first element
|
||||
* in the list.
|
||||
*/
|
||||
T* getPrevious()
|
||||
{
|
||||
return prev->asT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert elem after this element in the list. |this| must be part of a
|
||||
* linked list when you call setNext(); otherwise, this method will assert.
|
||||
*/
|
||||
void setNext(T* elem)
|
||||
{
|
||||
MOZ_ASSERT(isInList());
|
||||
setNextUnsafe(elem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert elem before this element in the list. |this| must be part of a
|
||||
* linked list when you call setPrevious(); otherwise, this method will
|
||||
* assert.
|
||||
*/
|
||||
void setPrevious(T* elem)
|
||||
{
|
||||
MOZ_ASSERT(isInList());
|
||||
setPreviousUnsafe(elem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove this element from the list which contains it. If this element is
|
||||
* not currently part of a linked list, this method asserts.
|
||||
*/
|
||||
void remove()
|
||||
{
|
||||
MOZ_ASSERT(isInList());
|
||||
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
next = this;
|
||||
prev = this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if |this| part is of a linked list, and false otherwise.
|
||||
*/
|
||||
bool isInList()
|
||||
{
|
||||
MOZ_ASSERT((next == this) == (prev == this));
|
||||
return next != this;
|
||||
}
|
||||
|
||||
private:
|
||||
LinkedListElement& operator=(const LinkedList<T>& other) MOZ_DELETE;
|
||||
LinkedListElement(const LinkedList<T>& other) MOZ_DELETE;
|
||||
|
||||
friend class LinkedList<T>;
|
||||
|
||||
enum NodeKind {
|
||||
NODE_TYPE_NORMAL,
|
||||
NODE_TYPE_SENTINEL
|
||||
};
|
||||
|
||||
LinkedListElement(NodeKind nodeType)
|
||||
: next(this)
|
||||
, prev(this)
|
||||
, isSentinel(nodeType == NODE_TYPE_SENTINEL)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Return |this| cast to T* if we're a normal node, or return NULL if we're
|
||||
* a sentinel node.
|
||||
*/
|
||||
T* asT()
|
||||
{
|
||||
if (isSentinel)
|
||||
return NULL;
|
||||
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert elem after this element, but don't check that this element is in
|
||||
* the list. This is called by LinkedList::insertFront().
|
||||
*/
|
||||
void setNextUnsafe(T* elem)
|
||||
{
|
||||
LinkedListElement *listElem = static_cast<LinkedListElement*>(elem);
|
||||
MOZ_ASSERT(!listElem->isInList());
|
||||
|
||||
listElem->next = this->next;
|
||||
listElem->prev = this;
|
||||
this->next->prev = listElem;
|
||||
this->next = listElem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert elem before this element, but don't check that this element is in
|
||||
* the list. This is called by LinkedList::insertBack().
|
||||
*/
|
||||
void setPreviousUnsafe(T* elem)
|
||||
{
|
||||
LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(elem);
|
||||
MOZ_ASSERT(!listElem->isInList());
|
||||
|
||||
listElem->next = this;
|
||||
listElem->prev = this->prev;
|
||||
this->prev->next = listElem;
|
||||
this->prev = listElem;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LinkedList
|
||||
{
|
||||
private:
|
||||
LinkedListElement<T> sentinel;
|
||||
|
||||
public:
|
||||
LinkedList& operator=(const LinkedList<T>& other) MOZ_DELETE;
|
||||
LinkedList(const LinkedList<T>& other) MOZ_DELETE;
|
||||
|
||||
LinkedList()
|
||||
: sentinel(LinkedListElement<T>::NODE_TYPE_SENTINEL)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Add elem to the front of the list.
|
||||
*/
|
||||
void insertFront(T* elem)
|
||||
{
|
||||
/* Bypass setNext()'s this->isInList() assertion. */
|
||||
sentinel.setNextUnsafe(elem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add elem to the back of the list.
|
||||
*/
|
||||
void insertBack(T* elem)
|
||||
{
|
||||
sentinel.setPreviousUnsafe(elem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the first element of the list, or NULL if the list is empty.
|
||||
*/
|
||||
T* getFirst()
|
||||
{
|
||||
return sentinel.getNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the last element of the list, or NULL if the list is empty.
|
||||
*/
|
||||
T* getLast()
|
||||
{
|
||||
return sentinel.getPrevious();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get and remove the first element of the list. If the list is empty,
|
||||
* return NULL.
|
||||
*/
|
||||
T* popFirst()
|
||||
{
|
||||
T* ret = sentinel.getNext();
|
||||
if (ret)
|
||||
static_cast<LinkedListElement<T>*>(ret)->remove();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get and remove the last element of the list. If the list is empty,
|
||||
* return NULL.
|
||||
*/
|
||||
T* popLast()
|
||||
{
|
||||
T* ret = sentinel.getPrevious();
|
||||
if (ret)
|
||||
static_cast<LinkedListElement<T>*>(ret)->remove();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the list is empty, or false otherwise.
|
||||
*/
|
||||
bool isEmpty()
|
||||
{
|
||||
return !sentinel.isInList();
|
||||
}
|
||||
|
||||
/*
|
||||
* In a debug build, make sure that the list is sane (no cycles, consistent
|
||||
* next/prev pointers, only one sentinel). Has no effect in release builds.
|
||||
*/
|
||||
void debugAssertIsSane()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Check for cycles in the forward singly-linked list using the
|
||||
* tortoise/hare algorithm.
|
||||
*/
|
||||
for (LinkedListElement<T>* slow = sentinel.next,
|
||||
* fast1 = sentinel.next->next,
|
||||
* fast2 = sentinel.next->next->next;
|
||||
slow != sentinel && fast1 != sentinel && fast2 != sentinel;
|
||||
slow = slow->next,
|
||||
fast1 = fast2->next,
|
||||
fast2 = fast1->next) {
|
||||
|
||||
MOZ_ASSERT(slow != fast1);
|
||||
MOZ_ASSERT(slow != fast2);
|
||||
}
|
||||
|
||||
/* Check for cycles in the backward singly-linked list. */
|
||||
for (LinkedListElement<T>* slow = sentinel.prev,
|
||||
* fast1 = sentinel.prev->prev,
|
||||
* fast2 = sentinel.prev->prev->prev;
|
||||
slow != sentinel && fast1 != sentinel && fast2 != sentinel;
|
||||
slow = slow->prev,
|
||||
fast1 = fast2->prev,
|
||||
fast2 = fast1->prev) {
|
||||
|
||||
MOZ_ASSERT(slow != fast1);
|
||||
MOZ_ASSERT(slow != fast2);
|
||||
}
|
||||
|
||||
/* Check that |sentinel| is the only root in the list. */
|
||||
for (LinkedListElement<T>* elem = sentinel.next;
|
||||
elem != sentinel;
|
||||
elem = elem->next) {
|
||||
|
||||
MOZ_ASSERT(!elem->isSentinel);
|
||||
}
|
||||
|
||||
/* Check that the next/prev pointers match up. */
|
||||
LinkedListElement<T>* prev = sentinel;
|
||||
LinkedListElement<T>* cur = sentinel.next;
|
||||
do {
|
||||
MOZ_ASSERT(cur->prev == prev);
|
||||
MOZ_ASSERT(prev->next == cur);
|
||||
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
} while (cur != sentinel);
|
||||
#endif /* ifdef DEBUG */
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* ifdef __cplusplus */
|
||||
#endif /* ifdef mozilla_LinkedList_h_ */
|
@ -45,6 +45,7 @@ EXPORTS_mozilla += \
|
||||
Assertions.h \
|
||||
Attributes.h \
|
||||
GuardObjects.h \
|
||||
LinkedList.h \
|
||||
MSStdInt.h \
|
||||
RangedPtr.h \
|
||||
RefPtr.h \
|
||||
|
Loading…
Reference in New Issue
Block a user