This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tools/wlib/linkedlist.h

496 lines
11 KiB
C
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 3 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; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************\
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
******************************************************************************
Project Name:
File Name : linkedlist.h
Author : Neal Kettler
Start Date : June 19, 1997
Last Update : June 19, 1997
Linked list template. This is a fairly standard doubly linked list that
allows insertion and removal at any point in the list. A current pointer
is used to quickly access items when they are examined sequentially.
Copies of the data are stored instead of a pointer to the original.
If you want to store pointers then the template should be of a pointer type.
\****************************************************************************/
#ifndef LINKEDLIST_HEADER
#define LINKEDLIST_HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wstypes.h"
template <class T>
class LNode
{
public:
T Node;
LNode<T> *Next;
LNode<T> *Prev;
};
template <class T>
class LinkedList
{
public:
LinkedList();
LinkedList(LinkedList<T> &other);
~LinkedList();
// Remove all entries from the lsit
void clear(void);
// Add a node after the zero based 'pos'
bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL);
bit8 addTail(IN T &node, OUT T **newnodeptr=NULL);
bit8 addHead(IN T &node, OUT T **newnodeptr=NULL);
// Remove a node
bit8 remove(OUT T &node,sint32 pos);
bit8 remove(sint32 pos);
bit8 removeHead(OUT T &node);
bit8 removeTail(OUT T &node);
// Get a node without removing from the list
bit8 get(OUT T &node,sint32 pos);
bit8 getHead(OUT T &node);
bit8 getTail(OUT T &node);
// Get a pointer to the internally managed data (careful!)
bit8 getPointer(OUT T **node, sint32 pos);
// Get the number of entries in the list
sint32 length(void);
// Print information on the list
void print(IN FILE *out);
// assignment operator
LinkedList<T> &operator=(LinkedList<T> &other);
private:
sint32 Entries; // Number of entries
LNode<T> *Head; // Head of the list
LNode<T> *Tail; // Tail of the list
LNode<T> *Current; // Current pointer & index for speed only
sint32 CurIndex;
};
//Create the empty list
template <class T>
LinkedList<T>::LinkedList()
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
}
// copy constructor
template <class T>
LinkedList<T>::LinkedList(LinkedList<T> &other)
{
Entries=0;
Head=Tail=Current=NULL;
CurIndex=-1; // Not valid when 0 entries
(*this)=other;
}
//Free all the memory...
template <class T>
LinkedList<T>::~LinkedList()
{
clear(); // Remove the entries
}
// assignment operator
template <class T>
LinkedList<T> &LinkedList<T>::operator=(LinkedList<T> &other)
{
T node;
clear();
for (int i=0; i<other.length(); i++)
{
other.get(node,i);
addTail(node);
}
return(*this);
}
// Remove all the entries and free the memory
template <class T>
void LinkedList<T>::clear()
{
LNode<T> *temp,*del;
temp=Head;
while (temp) {
del=temp;
temp=temp->Next;
delete(del);
}
Entries=0;
CurIndex=-1;
Head=Tail=Current=NULL;
}
// When adding into a position, the new node goes at the zero based slot
// specified by pos. All other nodes get moved one slot down.
template <class T>
bit8 LinkedList<T>::add(IN T &node,sint32 pos, OUT T **newnodeptr)
{
LNode<T> *temp;
LNode<T> *item;
if (pos<0)
pos=0;
if (pos>Entries)
pos=Entries;
item=(LNode<T> *)new LNode<T>;
assert(item!=NULL);
item->Node=node; // copy the passed in object
item->Prev=NULL;
item->Next=NULL;
if (newnodeptr)
*newnodeptr=&(item->Node);
if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list!
if (pos==0) {
item->Next=Head;
if (Head)
Head->Prev=item;
Head=item;
}
if (pos==Entries) {
item->Prev=Tail;
if (Tail)
Tail->Next=item;
Tail=item;
}
Entries++;
Current=item;
CurIndex=pos;
return(TRUE);
}
// If control is here, we know the new node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item->Next=Current;
item->Prev=Current->Prev;
Current->Prev=item;
item->Prev->Next=item;
Current=item;
Entries++;
return(TRUE);
}
// Check the other possible speedup (adding after CurIndex)
if (pos==CurIndex+1) {
item->Next=Current->Next;
item->Prev=Current;
Current->Next=item;
item->Next->Prev=item;
Current=item;
CurIndex++;
Entries++;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
temp=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
temp=temp->Next;
assert(temp!=NULL);
}
item->Next=temp;
item->Prev=temp->Prev;
temp->Prev=item;
item->Prev->Next=item;
Current=item;
CurIndex=pos;
Entries++;
return(TRUE);
}
// Add to the first node, all others get shifted down one slot
template <class T>
bit8 LinkedList<T>::addHead(IN T &node, OUT T **newnodeptr)
{
return(add(node,0,newnodeptr));
}
// Append to the end of the list
template <class T>
bit8 LinkedList<T>::addTail(IN T &node, OUT T **newnodeptr)
{
return(add(node,length(),newnodeptr));
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(OUT T &node, sint32 pos)
{
////////LNode<T> *temp;
LNode<T> *item;
if (Entries==0)
return(FALSE);
if (pos<0)
pos=0;
if (pos>=Entries)
pos=Entries-1;
if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list
if (pos==0) {
item=Head;
if (item->Next)
item->Next->Prev=NULL;
Head=item->Next;
node=item->Node;
Current=Head;
CurIndex=0;
}
if (pos==Entries-1) {
item=Tail;
if (item->Prev)
item->Prev->Next=NULL;
Tail=item->Prev;
node=item->Node;
Current=Tail;
CurIndex=Entries-2;
}
delete(item);
Entries--;
if (Entries==0) { // Super paranoia check
assert(Current==NULL);
assert(CurIndex==-1);
assert(Head==NULL);
assert(Tail==NULL);
}
return(TRUE);
}
// If control is here, we know the target node is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
item=Current;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
// CurIndex stays the same
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Check the other possible speedup (removing after CurIndex)
if (pos==CurIndex+1) {
item=Current->Next;
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex++;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
item->Prev->Next=item->Next;
item->Next->Prev=item->Prev;
Current=item->Next;
CurIndex=pos;
node=item->Node;
delete(item);
Entries--;
return(TRUE);
}
// Remove at the zero based index specified by 'pos'. When removing from
// a slot, all others get shifted up by one.
template <class T>
bit8 LinkedList<T>::remove(sint32 pos)
{
T temp_node;
return(remove(temp_node,pos));
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::removeHead(OUT T &node)
{
return(remove(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::removeTail(OUT T &node)
{
return(remove(node,Entries-1));
}
template <class T>
bit8 LinkedList<T>::get(OUT T &node, sint32 pos)
{
T *objptr;
bool retval=getPointer(&objptr,pos);
if (retval && objptr)
node=*objptr;
return(retval);
}
template <class T>
bit8 LinkedList<T>::getPointer(OUT T **node,sint32 pos)
{
if ((node==0)||(Entries==0))
return(FALSE);
LNode<T> *item;
if (pos<0)
{
//pos=0;
return(FALSE);
}
if (pos>=Entries)
{
//pos=Entries-1;
return(FALSE);
}
if (pos==0) {
*node=&(Head->Node);
return(TRUE);
} else if (pos==Entries-1) {
*node=&(Tail->Node);
return(TRUE);
}
// If control reaches here, we know target is not an endpoint
// Check for possible speedup, so we don't have to scan the list
if (pos==CurIndex) {
*node=&(Current->Node);
return(TRUE);
} else if (pos==CurIndex+1) {
*node=&(Current->Next->Node);
CurIndex++;
Current=Current->Next;
return(TRUE);
} else if (pos==CurIndex-1) {
*node=&(Current->Prev->Node);
CurIndex--;
Current=Current->Prev;
return(TRUE);
}
// If control reaches here we have to scan the whole thing
item=Head->Next; // Can start at node '1' because head was special cased
for (int i=1; i<pos; i++) {
item=item->Next;
assert(item!=NULL);
}
*node=&(item->Node);
CurIndex=pos;
Current=item;
return(TRUE);
}
// Remove the first node of the list
template <class T>
bit8 LinkedList<T>::getHead(OUT T &node)
{
return(get(node,0));
}
// Remove the last node of the list
template <class T>
bit8 LinkedList<T>::getTail(OUT T &node)
{
return(get(node,Entries-1));
}
template <class T>
void LinkedList<T>::print(IN FILE *out)
{
LNode<T> *temp;
fprintf(out,"--------------------\n");
fprintf(out,"Entries = %d\n",length());
fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail);
temp=Head;
while (temp) {
fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next);
temp=temp->Next;
}
fprintf(out,"--------------------\n");
}
// Return the current length of the list
template <class T>
sint32 LinkedList<T>::length(void) {
return(Entries);
}
#endif