Files
ios2024/code/lib/list.cc

408 lines
11 KiB
C++

// list.cc
// Routines to manage a singly linked list of "things".
// Lists are implemented as templates so that we can store
// anything on the list in a type-safe manner.
//
// A "ListElement" is allocated for each item to be put on the
// list; it is de-allocated when the item is removed. This means
// we don't need to keep a "next" pointer in every object we
// want to put on a list.
//
// NOTE: Mutual exclusion must be provided by the caller.
// If you want a synchronized list, you must use the routines
// in synchlist.cc.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
//----------------------------------------------------------------------
// ListElement<T>::ListElement
// Initialize a list element, so it can be added somewhere on a list.
//
// "itm" is the thing to be put on the list.
//----------------------------------------------------------------------
template <class T>
ListElement<T>::ListElement(T itm)
{
item = itm;
next = NULL; // always initialize to something!
}
//----------------------------------------------------------------------
// List<T>::List
// Initialize a list, empty to start with.
// Elements can now be added to the list.
//----------------------------------------------------------------------
template <class T>
List<T>::List()
{
first = last = NULL;
numInList = 0;
}
//----------------------------------------------------------------------
// List<T>::~List
// Prepare a list for deallocation.
// This does *NOT* free list elements, nor does it
// free the data those elements point to.
// Normally, the list should be empty when this is called.
//----------------------------------------------------------------------
template <class T>
List<T>::~List()
{
}
//----------------------------------------------------------------------
// List<T>::Append
// Append an "item" to the end of the list.
//
// Allocate a ListElement to keep track of the item.
// If the list is empty, then this will be the only element.
// Otherwise, put it at the end.
//
// "item" is the thing to put on the list.
//----------------------------------------------------------------------
template <class T>
void List<T>::Append(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ASSERT(!this->IsInList(item));
if (IsEmpty())
{ // list is empty
first = element;
last = element;
}
else
{ // else put it after last
last->next = element;
last = element;
}
numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::Prepend
// Same as Append, only put "item" on the front.
//----------------------------------------------------------------------
template <class T>
void List<T>::Prepend(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ASSERT(!this->IsInList(item));
if (IsEmpty())
{ // list is empty
first = element;
last = element;
}
else
{ // else put it before first
element->next = first;
first = element;
}
numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::RemoveFront
// Remove the first "item" from the front of the list.
// List must not be empty.
//
// Returns:
// The removed item.
//----------------------------------------------------------------------
template <class T>
T List<T>::RemoveFront()
{
ListElement<T> *element = first;
T thing;
ASSERT(!IsEmpty());
thing = first->item;
if (first == last)
{ // list had one item, now has none
first = NULL;
last = NULL;
}
else
{
first = element->next;
}
numInList--;
delete element;
return thing;
}
//----------------------------------------------------------------------
// List<T>::Remove
// Remove a specific item from the list. Must be in the list!
//----------------------------------------------------------------------
template <class T>
void List<T>::Remove(T item)
{
ListElement<T> *prev, *ptr;
T removed;
ASSERT(this->IsInList(item));
// if first item on list is match, then remove from front
if (item == first->item)
{
removed = RemoveFront();
ASSERT(item == removed);
}
else
{
prev = first;
for (ptr = first->next; ptr != NULL; prev = ptr, ptr = ptr->next)
{
if (item == ptr->item)
{
prev->next = ptr->next;
if (prev->next == NULL)
{
last = prev;
}
delete ptr;
numInList--;
break;
}
}
ASSERT(ptr != NULL); // should always find item!
}
ASSERT(!this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::IsInList
// Return TRUE if the item is in the list.
//----------------------------------------------------------------------
template <class T>
bool List<T>::IsInList(T item) const
{
ListElement<T> *ptr;
for (ptr = first; ptr != NULL; ptr = ptr->next)
{
if (item == ptr->item)
{
return TRUE;
}
}
return FALSE;
}
//----------------------------------------------------------------------
// List<T>::Apply
// Apply function to every item on a list.
//
// "func" -- the function to apply
//----------------------------------------------------------------------
template <class T>
void List<T>::Apply(void (*func)(T)) const
{
ListElement<T> *ptr;
for (ptr = first; ptr != NULL; ptr = ptr->next)
{
(*func)(ptr->item);
}
}
//----------------------------------------------------------------------
// SortedList::Insert
// Insert an "item" into a list, so that the list elements are
// sorted in increasing order.
//
// Allocate a ListElement to keep track of the item.
// If the list is empty, then this will be the only element.
// Otherwise, walk through the list, one element at a time,
// to find where the new item should be placed.
//
// "item" is the thing to put on the list.
//----------------------------------------------------------------------
template <class T>
void SortedList<T>::Insert(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ListElement<T> *ptr; // keep track
ASSERT(!this->IsInList(item));
if (this->IsEmpty())
{ // if list is empty, put at front
this->first = element;
this->last = element;
}
else if (compare(item, this->first->item) < 0)
{ // item goes at front
element->next = this->first;
this->first = element;
}
else
{ // look for first elt in list bigger than item
for (ptr = this->first; ptr->next != NULL; ptr = ptr->next)
{
if (compare(item, ptr->next->item) < 0)
{
element->next = ptr->next;
ptr->next = element;
this->numInList++;
return;
}
}
this->last->next = element; // item goes at end of list
this->last = element;
}
this->numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List::SanityCheck
// Test whether this is still a legal list.
//
// Tests: do I get to last starting from first?
// does the list have the right # of elements?
//----------------------------------------------------------------------
template <class T>
void List<T>::SanityCheck() const
{
ListElement<T> *ptr;
int numFound;
if (first == NULL)
{
ASSERT((numInList == 0) && (last == NULL));
}
else if (first == last)
{
ASSERT((numInList == 1) && (last->next == NULL));
}
else
{
for (numFound = 1, ptr = first; ptr != last; ptr = ptr->next)
{
numFound++;
ASSERT(numFound <= numInList); // prevent infinite loop
}
ASSERT(numFound == numInList);
ASSERT(last->next == NULL);
}
}
//----------------------------------------------------------------------
// List::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
template <class T>
void List<T>::SelfTest(T *p, int numEntries)
{
int i;
ListIterator<T> *iterator = new ListIterator<T>(this);
SanityCheck();
// check various ways that list is empty
ASSERT(IsEmpty() && (first == NULL));
for (; !iterator->IsDone(); iterator->Next())
{
ASSERTNOTREACHED(); // nothing on list
}
for (i = 0; i < numEntries; i++)
{
Append(p[i]);
ASSERT(this->IsInList(p[i]));
ASSERT(!IsEmpty());
}
SanityCheck();
// should be able to get out everything we put in
for (i = 0; i < numEntries; i++)
{
Remove(p[i]);
ASSERT(!this->IsInList(p[i]));
}
ASSERT(IsEmpty());
SanityCheck();
delete iterator;
}
//----------------------------------------------------------------------
// SortedList::SanityCheck
// Test whether this is still a legal sorted list.
//
// Test: is the list sorted?
//----------------------------------------------------------------------
template <class T>
void SortedList<T>::SanityCheck() const
{
ListElement<T> *prev, *ptr;
List<T>::SanityCheck();
if (this->first != this->last)
{
for (prev = this->first, ptr = this->first->next; ptr != NULL;
prev = ptr, ptr = ptr->next)
{
ASSERT(compare(prev->item, ptr->item) <= 0);
}
}
}
//----------------------------------------------------------------------
// SortedList::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
template <class T>
void SortedList<T>::SelfTest(T *p, int numEntries)
{
int i;
T *q = new T[numEntries];
List<T>::SelfTest(p, numEntries);
for (i = 0; i < numEntries; i++)
{
Insert(p[i]);
ASSERT(this->IsInList(p[i]));
}
SanityCheck();
// should be able to get out everything we put in
for (i = 0; i < numEntries; i++)
{
q[i] = this->RemoveFront();
ASSERT(!this->IsInList(q[i]));
}
ASSERT(this->IsEmpty());
// make sure everything came out in the right order
for (i = 0; i < (numEntries - 1); i++)
{
ASSERT(compare(q[i], q[i + 1]) <= 0);
}
SanityCheck();
delete q;
}