Sources/CollectionClasses.cp

/*
    File:       CollectionClasses.cp
 
    Contains:   The following collection classes are implemented: TLinkedList, TStack, (TQueue, TDeQueue),
                THashTable.
                CollectionClasses.cp contains the collection class member functions. 
 
    Written by: Kent Sandvik    
 
    Copyright:  Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                8/18/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#ifndef _COLLECTION_
#include "CollectionClasses.h"
#endif
 
 
// _________________________________________________________________________________________________________ //
//  TLINKEDLIST Class definitions.
 
//  CONSTRUCTORS AND DESTRUCTORS
#pragma segment Collections
TLinkedList::TLinkedList(short max)
// Create a Linked list class (max entries pre-defined in a constant in the header file).
{
    fMaxCollectionSize = max;                   // keep track of how many entries we could add to the collection
    fCollectionSize = 0;                        // no entries yet
    // Fix the head and the rest of the pointer hooks.
    fHead = new fNODE;                          // create nodes
    fTail = new fNODE;
    fLastEntry = new fNODE;
    fCurrentNode = new fNODE;
 
    fHead->fNext = fTail;                       // default head points at tail, then we will push in nodes between
    fHead->fPrevious = fHead;                   // bit your own tail
    fTail->fNext = fTail;                       // bite your own tail
    fTail->fPrevious = fHead;                   // hook tail with head (double linked list)
    fTail->fKey = NULL;                         // mark the last entry as NULL
    fHead->fKey = NULL;                         // mark the first entry as NULL
    fLastEntry->fPrevious = fHead;              // hook the ptr node between the head
    fLastEntry->fNext = fTail;                  // Éand the tail
}
 
 
#pragma segment Collections
TLinkedList::~TLinkedList()
// Destruct the structures required for the former linked list.
{
    struct fNODE* temp = fHead;                 // create a temp node
 
    while (temp != fTail)
        // while we have entries, delete them from head to tail
        {
            fHead = temp;                       // copy temp node to the head position
            temp = temp->fNext;                 // and make temp point at the next one
            delete fHead;                       // meanwhile delete the head, and continue copying tmp to head
        }
}
 
 
//  MAIN INTERFACES
 
#pragma segment Collections
Boolean TLinkedList::Append(const TItemtype item)
// Append entry to the linked list (at the end).
{
    if (fCollectionSize < fMaxCollectionSize)
    {
        struct fNODE*        temp = new fNODE;  // create a new node
        temp->fKey = item;
 
        // Hook the temp node between the last entry and the tail       
        if (fCollectionSize == 0)               // first entry?
        {
            fHead->fNext = temp;                // hook it just after the head
            temp->fNext = fTail;                // and before the tail
            fLastEntry = temp;                  // and keep track of it!
        }
        else                                    // OK now we will track of the fCurrentNode
            {
                temp->fNext = fTail;            // stuck it just before the tail
                fLastEntry->fNext = temp;       // and after the current node
                fLastEntry = temp;              // and mark it the current node
            }
 
        fCollectionSize++;                      // keep a count on the size of the linked list
        goto AppendOK;
    }
    ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the LinkedList");
    return false;
AppendOK:return true;
}
 
 
#pragma segment Collections
Boolean TLinkedList::Remove(const TItemtype item)
// Remove defined entry from the linked list.
{
    // First find the item, if found remove it, connect the links and decrease the collection size count
    this->Reset();                              // get back to the first entry
    struct fNODE*            temp = fHead;      // keep a temp pointer to earlier node passed
 
    do                                          // walk along the list  
    {
        if (fCurrentNode->fKey == item)         // found a hit? 
        {
            // yes!
            temp->fNext = fCurrentNode->fNext;  // short circuit the current node
            delete fCurrentNode;                // delete this node
            fCollectionSize--;                  // decreate the collection count
            goto OK;                            // and jump out 
        }
 
        temp = fCurrentNode;                    // keep track of the just visited node
        fCurrentNode = fCurrentNode->fNext;     // Éand step forward
    } while (fCurrentNode != fTail);            // as long as we are inside the linked list 
 
    return false;                               // nothing happenedÉ
OK: return true;                                // OK, we were able to delete the entry
}
 
 
#pragma segment Collections
Boolean TLinkedList::IsEmpty()
// Check if the collection is empty (head points at tail).
{
    return (fHead->fNext == fTail);             // check if the next pointer is pointing on the tail
}
 
 
#pragma segment Collections
Boolean TLinkedList::Find(const TItemtype item)
// Find if we have the itemtype in the 'queue'.
{
    TItemtype tempVal;
    this->Reset();                              // get back to the first entry
 
    while ((tempVal = this->Next()) != NULL)
        // get all entries one at a time
        {
            if (tempVal == item)                // if we found a one-to-one relationship
                return true;                    // signal OK
        }
    return false;                               // otherwise signal false
}
 
 
#pragma segment Collections
Size TLinkedList::GetSize() const
// Return the internal count of entries in collection.
{
    return fCollectionSize;
}
 
 
// ITERATORS
#pragma segment Collections
TItemtype TLinkedList::First()
// Get the first entry in the collection.
{
    if (fCollectionSize != 0)
        return fHead->fNext->fKey;              // return what FHead->fNext has (the first 'real' node)
    else
        return NULL;
}
 
 
#pragma segment Collections
TItemtype TLinkedList::Next()
// Return next entry in the list.
{
    TItemtype item;
    item = fCurrentNode->fKey;                  // get current item
    fCurrentNode = fCurrentNode->fNext;         // then increase the node to the next entry
 
    return item;                                // return item
}
 
 
#pragma segment Collections
TItemtype TLinkedList::Last()
// Return last entry in the list.
{
    // because we cached the first entry, it's easy to return it
    // we might have problems with a real de-queue later if 
    // we don't revise this scheme (but most likely we will override Last() with new behavior
    return fLastEntry->fKey;                    // the fLastNode is always pointing at the last entry
}
 
 
#pragma segment Collections
void TLinkedList::Reset()
// Make the current node point at the first entry from beginning (reset).
{
    fCurrentNode = fHead->fNext;                // reset the fCurrentNode pointer to the first entry
}
 
 
// _________________________________________________________________________________________________________ //
//  TSTACK Class definitions.
 
//  CONSTRUCTORS AND DESTRUCTORS
#pragma segment Collections
TStack::TStack(short max)
// Create a stack class (max pre-defined in the header files as a constant).
{
    fMaxCollectionSize = max;                   // keep track of how big we could grow the stack into
    fCollectionSize = 0;                        // no entries yet
}
 
 
#pragma segment Collections
TStack::~TStack()
// Default constructor -- not used for the time being.
{
}
 
 
//  MAIN INTERFACES
#pragma segment Collections
Boolean TStack::Push(const TItemtype item)
// Push entry into the stack (beginning).
{
    if (fCollectionSize < fMaxCollectionSize)   // yes, we bail out and don't add more entries
    {
        struct fNODE* temp = new fNODE;         // create temp node
        temp->fKey = item;                      // store value in temp node
        temp->fNext = fHead->fNext;             // make temp point at the real next block 
        fHead->fNext = temp;                    // and make temp suddenly the real head (stack head!)
        fCollectionSize++;                      // increase the stack size
        if (fCollectionSize == 1)               // first entry?
            fLastEntry = temp;                  // store it for fast access later (cache the ptr)
 
        goto PushOK;
    }
    ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the Stack");// signal about a possible problem
    return false;
PushOK:return true;
}
 
 
#pragma segment Collections
TItemtype TStack::Pop()
// Pop entry from the stack (beginning).
{
    TItemtype item;
    struct fNODE* temp = fHead->fNext;          // create temp node with the header next pointer values
    fHead->fNext = temp->fNext;                 // copy back the following pointer from temp to the head
    item = temp->fKey;                          // get the temp item
 
    delete temp;                                // delete the suddenly non-needed node
    fCollectionSize--;                          // decrease the stack
    return item;                                // return the asked value
}
 
 
#pragma segment Collections
Boolean TStack::Append(const TItemtype item)
// Append entry, same as Push, but we included it.
{
    return (this->Push(item));                  // call the one and only method supported with stack insertion
}
 
 
#pragma segment Collections
Boolean TStack::Remove(const TItemtype          /*item*/)
// Remove is not implemented, but we had to include it because we are subclassing from a TLinkedList.
{
    return false;                               // not supported
}
 
 
// _________________________________________________________________________________________________________ //
//  TQUEUE
//  CONSTRUCTORS AND DESTRUCTORS
#pragma segment Collections
TQueue::TQueue(short max)
// Create a queue class (max pre-defined in the header files as a constant).
{
    fMaxCollectionSize = max;                   // keep track of how big we could grow the stack into
    fCollectionSize = 0;                        // no entries yet
}
 
 
#pragma segment Collections
TQueue::~TQueue()
// Default destructor, not used for the time being.
{
}
 
 
//  MAIN INTERFACE
#pragma segment Collections
Boolean TQueue::Put(const TItemtype item)
// Put entry into the queue (beginning).
{
    if (fCollectionSize < fMaxCollectionSize)   // no problems?
    {
        struct fNODE* temp = new fNODE;         // create a new node
        temp->fKey = item;                      // add value to the node bucket 
 
        // place it in the beginning of the queue
        temp->fNext = fHead->fNext;             // push it into the queue
        fHead->fNext = temp;                    // now it's there
 
        // update the tail pointer
        if (fCollectionSize == 0)               // first entry?
        {
            fTail->fPrevious = temp;            // hook fTail to the entry
        }
        temp->fPrevious = fHead;                // and hook it back to head
        fLastEntry->fPrevious = temp;           // and as well as to the current first entry
        fLastEntry = temp;                      // and mark it as the current first entry   
 
        fCollectionSize++;                      // increase the collection count
        goto QueueOK;
    }
    ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of TQueue");
    return false;
QueueOK:return true;
}
 
 
#pragma segment Collections
Boolean TQueue::Append(const TItemtype item)
// Append is the same as Put.
{
    return (this->Put(item));                   // call TQueue.Put
}
 
 
#pragma segment Collections
TItemtype TQueue::Get()
// Get entry from the end of the list (queue).
{
    TItemtype item;
 
    // get the node and the value       
    struct fNODE* temp = fTail->fPrevious;      // get the last before fTail
    item = temp->fKey;                          // get the item 
 
 
    // change the pointers
    fTail->fPrevious = temp->fPrevious;         // hook fTail to the previous entry
    temp->fPrevious->fNext = fTail;             // hook the node to tail as well    
 
    delete temp;                                // delete the entry 
 
    fCollectionSize--;                          // decrease the collection count    
    return item;                                // return the item
}
 
 
#pragma segment Collections
TItemtype TQueue::Last()
// Get last entry from queue.
{
    return fTail->fPrevious->fKey;              // get the last entry
}
 
 
#pragma segment Collections
Boolean TQueue::Remove(const TItemtype          /*item*/)
// Remove is not supported, but we needed to include this because TQueue is subclassed from TLinkedList
{
    return false;                               // not supported
}
 
 
 
// _________________________________________________________________________________________________________ //
// TDEQUE
 
//  CONSTRUCTORS AND DESTRUCTORS
#pragma segment Collections
TDeque::TDeque(short max)
// Create dequeue class (max pre-defined in the header files as a constant).
{
    fMaxCollectionSize = max;                   // keep track of how big we could grow the stack into
    fCollectionSize = 0;                        // no entries yet
}
 
 
#pragma segment Collections
TDeque::~TDeque()
// Default destructor, not used for the time being.
{
}
 
#pragma segment Collections
Boolean TDeque::Push(const TItemtype item)
// Push entry into queue (beginning).
{
    return (TQueue::Put(item));                 // call inherited Put
}
 
 
#pragma segment Collections
Boolean TDeque::PutAtEnd(const TItemtype item)
// Place entry at end of the deque.
{
    if (fCollectionSize < fMaxCollectionSize)   // bail out if we reach the limit
    {
        struct fNODE*     temp = new fNODE;     // create temp node
        temp->fKey = item;                      // store the value
        temp->fNext = fTail;                    // point at tail
        temp->fPrevious = fTail->fPrevious;     // make the backwards ptr hooked to the new entry
        fTail->fPrevious->fNext = temp;         // and hook the old last one into this one 
        fTail->fPrevious = temp;                // make the tail point at the new entry
 
        fCollectionSize++;                      // increase the counter
        goto PutAtEndOK;
    }
    ASSERT(fCollectionSize < fMaxCollectionSize, "\pOverflow of the TDeque");
    return false;
PutAtEndOK:return true;
}
 
 
TItemtype TDeque::Pop()
// Pop entry from the queue end.
{
    TItemtype item;
    struct fNODE* temp = fHead->fNext;          // hook to the beginning of the deque
    fHead->fNext = temp->fNext;                 // hook head to the following node
    temp->fNext->fPrevious = fHead;             // and the next one should point back at the head
 
    item = temp->fKey;                          // get the stored value
    delete temp;                                // remove node
    fCollectionSize--;                          // decrease the counter
 
    return item;                                // and return the item
}
 
 
// _________________________________________________________________________________________________________ //
// THASHTABLE 
 
//  CONSTRUCTORS AND DESTRUCTORS
THashTable::THashTable()
// Create a hash table class.
{
    // Clear the array bucket.
    for (long i = 0; i < NBUCKETS; i++)
        fBucket[i] = 0;
}
 
 
THashTable::~THashTable()
// Delete any memory structures required for the hashtable class.
{
    // Delete the entries in the array bucket.  
    for (long i = 0; i < NBUCKETS; i++)
    {
        THashEntryPtr p = fBucket[i];           // get a ptr to the entry
        while (p)
            // while a valid ptr
            {
                fBucket[i] = p->fNext;          // install next entry in array
                delete p;                       // delete the current node
                p = fBucket[i];                 // ptr back to the new value
            }
    }
}
 
 
//  MAIN INTERFACE
#pragma segment Collections
Boolean THashTable::Add(THashKey key,
                        TItemtype value)
// Add entries to the hash table.
{
    THashEntryPtr p;
    THashKey whichItem = this->Hash(key);       // hash out the real value
 
    cout << " whichItem = " << whichItem << "\n";
 
    p = FindInBucket(fBucket[whichItem], key);
    if (p)                                      // have already an entry?
        goto problem;                           // signal the failure
 
    p = this->AddElement(key);                  // add our entry
    p->fValue = value;                          // add the item value to the entry
    return true;
 
problem:return false;
}
 
 
#pragma segment Collections
Boolean THashTable::Remove(THashKey key)
// Remove entries from the hash table.
{
    THashKey whichItem = this->Hash(key);       // hash out the real value
    THashEntryPtr p = FindInBucket(fBucket[whichItem], key);
    if (!p)                                     // no valid ptr?
        goto problem;
 
    if (fBucket[whichItem] == p)
        fBucket[whichItem] = p->fNext;          // connect to next node
 
    delete p;                                   // delete current node
    return true;
 
problem:return false;
}
 
 
 
#pragma segment Collections
TItemtype THashTable::Find(THashKey key)
// Find entries in the hash table.
{
    THashKey whichItem = this->Hash(key);       // hash out the real value
    THashEntryPtr p = this->FindInBucket(fBucket[whichItem], key);
    if (!p)
        return 0;                               // return 0
    else
        return p->fValue;                       // return valid value
}
 
 
void THashTable::MapCar(MapFun function)
// Run a function on each entry in the hash table.
{
    for (long i = 0; i < NBUCKETS; i++)         // iterate through all the entries
    {
        THashEntryPtr p = fBucket[i];           // get the ptr to the entry
 
        while (p)
            // while we have a nice value
            {
                function(p);                    // call the function on p
                p = p->fNext;                   // get the next ptr
            }
    }
}
 
 
// PRIVATE INTERNAL FUNCTIONS
THashEntryPtr THashTable::AddElement(THashKey key)
// Add an element to the internal structure.
{
    THashKey whichItem = this->Hash(key);
    THashEntryPtr p = new THashEntry(key);
 
    p->fNext = fBucket[whichItem];              // get ptr to next entry
    if (fBucket[whichItem])                     // OK?
        fBucket[whichItem]->fPrevious = p;      // establish ptrs backwards
 
    fBucket[whichItem] = p;                     // as well as the current one
 
    return p;
}
 
 
THashKey THashTable::Hash(THashKey key)
// Hash out a key to be used with the buckets. Override for your own preferences.
{
    key &= kResourceIDMask;
    return ((THashKey)(0x7FF & (key ^ (key >> 11))) % NBUCKETS);
    // Note: if you write your own hash function, don't forget modula NBUCKETS to get
    // down the size of the key so it hooks to the buckets.
}
 
 
THashEntryPtr THashTable::FindInBucket(THashEntryPtr p,
                                       THashKey key)
// Find bucket where entry is located.
{
    while (p)
        // valid pointer?
        {
            if (p->fKey == key)                 // hit?
                break;                          // found it!
            p = p->fNext;                       // nope, continue cycling the list
        }
    return p;                                   // return found (not found) ptr
}
 
 
// _________________________________________________________________________________________________________ //
 
 
/*  Change History (most recent last):
  No        Init.   Date        Comment
  1         khs     11/7/92     New file
  2         khs     11/28/92    Added TLinkedList
  3         khs     11/29/92    Added TQueue and TDeque
  4         khs     1/14/93     Cleanup
*/