aboutsummaryrefslogtreecommitdiff
path: root/src/PtrList.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/PtrList.c')
-rw-r--r--src/PtrList.c656
1 files changed, 656 insertions, 0 deletions
diff --git a/src/PtrList.c b/src/PtrList.c
new file mode 100644
index 0000000..d15acc9
--- /dev/null
+++ b/src/PtrList.c
@@ -0,0 +1,656 @@
+#include "PtrList.h"
+#include <stdlib.h>
+#include <memory.h>
+
+#ifdef macintosh
+#include <string.h>
+#endif
+
+#define ITEMSONFULLPAGE 62
+#define TAKEDEFAULTSIZE 0
+#define MIN(a,b) (a<b?a:b)
+
+typedef struct ListPage_tag ListPage;
+
+typedef struct PtrList_tag
+{
+ ListPage *firstPage;
+ size_t itemsPerPage;
+} PtrList_PLACEHOLDER;
+
+/* ===========================================================================
+ * ____________________________ ListPage _____________________________________
+ * For efficiency, the pointers in the list are arranged into blocks,
+ * called ListPage's.
+ * ======================================================================== */
+
+typedef struct ListPage_tag
+{
+ size_t numberOfItems;
+ size_t maxItems;
+ ListPage * next;
+ void * *items;
+} ListPage_PLACEHOLDER;
+
+static ListPage *
+ListPage_New( size_t maxItems )
+{
+ ListPage *new = (ListPage *)calloc( 1, sizeof( ListPage ) );
+ if( new )
+ {
+ new->items = malloc( maxItems * sizeof( void * ) );
+ new->maxItems = maxItems;
+ }
+ return new;
+}
+
+#define ListPage_IsFull( page ) \
+ ( ((const ListPage *)page)->numberOfItems >= page->maxItems )
+
+
+static ListPage *
+ListPage_Dup( const ListPage * other );
+
+static void *
+ListPage_Append( ListPage * this, void * item );
+
+static void *
+ListPage_RemoveItem( ListPage * this, size_t index );
+
+static void *
+ListPage_InsertItem( ListPage * this, size_t index, void * item );
+
+ /* ListPage_Dup does not copy the next pointer */
+ListPage *
+ListPage_Dup( const ListPage * other )
+{
+ ListPage * this = ListPage_New( other->maxItems );
+ if( other->numberOfItems > 0 )
+ memcpy( this->items, other->items,
+ other->numberOfItems * sizeof( void * ) );
+
+ this->maxItems = other->maxItems;
+ this->numberOfItems = other->numberOfItems;
+
+ return this;
+}
+
+ /* Returns the input pointer if it doesn't fit on page
+ * (for similarity with InsertItem) */
+void *
+ListPage_Append( ListPage *this, void * item )
+{
+ if( this->numberOfItems < this->maxItems ) /* If page not full */
+ {
+ *( this->items + this->numberOfItems ) = item; /* item to end*/
+ this->numberOfItems++; /* Update number of items on page */
+ return NULL;
+ }
+ return item;
+}
+
+ /* Returns a pointer to the item removed, or NULL if no item found */
+void *
+ListPage_RemoveItem( ListPage *this, size_t index )
+{
+ if( index < this->numberOfItems )
+ {
+ const size_t lastItemToShift = this->numberOfItems - 1;
+ size_t i;
+ void *item = *( this->items + index );
+
+ this->numberOfItems--;
+
+ for( i = index; i < lastItemToShift; i++ )
+ *( this->items + i ) = *( this->items + i + 1 );
+
+ return item;
+ }
+ return NULL;
+}
+
+ /* Returns a pointer to any item bumped off the page due to
+ * the page being full. (could be the input item) */
+void *
+ListPage_InsertItem( ListPage *this, size_t index, void * item )
+{
+ void * remainder = NULL;
+
+ if( index < this->numberOfItems )
+ {
+ const size_t firstItemToShift = this->numberOfItems - 1;
+ size_t i;
+
+ if( this->numberOfItems >= this->maxItems )
+ remainder = *( this->items + firstItemToShift );
+
+ this->numberOfItems = MIN( this->numberOfItems + 1,
+ this->maxItems );
+
+ for( i = firstItemToShift; i > index; i-- )
+ *( this->items + i ) = *( this->items + i - 1 );
+
+ *( this->items + index ) = item;
+ }
+ else
+ remainder = ListPage_Append( this, item );
+
+ return remainder;
+}
+
+/* ===========================================================================
+ ______________________________ PtrList functions _________________________
+
+ ======================================================================== */
+
+static void
+List_HandleAddressingError()
+{
+ /* To taste */
+}
+
+static ListPage * List_Xerox( const PtrList *other);
+static void List_FindItemAddress( const PtrList *this, size_t itemNo,
+ ListPage ** thePage, size_t *thePageIndex );
+
+ /*_____________________________________________________________________
+ **Constructor & Destructor**__________________________________________
+ *_____________________________________________________________________
+ */
+
+PtrList *
+List_New( void )
+{
+ return List_NewWithPageSize( TAKEDEFAULTSIZE );
+}
+
+PtrList *
+List_NewWithPageSize( size_t itemsOnFullPage )
+{
+ PtrList *new = (PtrList *)calloc( 1, sizeof( PtrList ) );
+ if( new )
+ {
+ if( itemsOnFullPage == TAKEDEFAULTSIZE )
+ new->itemsPerPage = ITEMSONFULLPAGE;
+ else
+ new->itemsPerPage = itemsOnFullPage;
+ }
+ return new;
+}
+
+PtrList *
+List_MakeCopy( const PtrList *other )
+{
+ PtrList *this = (PtrList *)calloc( 1, sizeof( PtrList ) );
+ this->itemsPerPage = other->itemsPerPage;
+ this->firstPage = List_Xerox( other );
+ return this;
+}
+
+void
+List_Delete( PtrList *list )
+{
+ List_Empty( list );
+ free( list );
+}
+
+ /*_____________________________________________________________________
+ **Empty**____________________________________________________________
+ * Runs through the list of ListPages deleting the (previous) page.
+ *_____________________________________________________________________
+ */
+void
+List_Empty( PtrList * this )
+{
+ if( this->firstPage != NULL )
+ {
+ ListPage *prevPage = this->firstPage,
+ *startPage = this->firstPage->next;
+ ListPage *page;
+
+ this->firstPage = NULL; /* Prefer to do this first,
+ so list is consistent */
+ for( page = startPage; page != NULL; page = page->next )
+ {
+ free( prevPage );
+ prevPage = page;
+ }
+ free( prevPage );
+ }
+}
+ /*_____________________________________________________________________
+ **NumberOfItems**______________________________________________________
+ * Runs through the list of ListPages totalling the items on each page.
+ * -> This number could be cached.
+ *______________________________________________________________________
+ */
+size_t
+List_NumberOfItems( const PtrList *this )
+{
+ size_t number = 0;
+ ListPage *page;
+
+ for( page = this->firstPage; page != NULL; page = page->next )
+ number += page->numberOfItems;
+
+ return number;
+}
+ /*_____________________________________________________________________
+ **Append**_____________________________________________________________
+ * This and Insert the only PtrList method that allocates ListPages.
+ * If there are no pages with space on them, it allocates an new
+ * page and links the page the the previous last page.
+ * If there is space on the page, it adds the item to the end of
+ * the page and updates the number of items on the page.
+ *______________________________________________________________________
+ */
+void
+List_Append( PtrList *this, void * newItem )
+{
+ ListPage *page;
+
+ if( this->firstPage == NULL ) /* Add first page if necessary*/
+ this->firstPage = ListPage_New( this->itemsPerPage );
+
+ page = this->firstPage;
+
+ while( page != NULL ) /* Cycle through pages */
+ { /* Try to put on page*/
+ if( ListPage_Append( page, newItem ) == NULL )
+ return; /* Go home with a smile */
+ else if( page->next == NULL ) /* If next page doesn't exist*/
+ page->next = ListPage_New( this->itemsPerPage );
+ /* make new page and */
+ /* link to this*/
+
+ page = page->next; /* Try the next page*/
+ }
+ List_HandleAddressingError();
+}
+
+ /*_____________________________________________________________________
+ * Internal routine for List_Insert.
+ * Assumes check has already been done for item address and that
+ * it exists on the given page.
+ *
+ * It is inserted on the page. The question is, what to do if the
+ * page was almost empty, and an item had to be bumped off the page
+ * to accomodate it.
+ *
+ * Could shift all elemtents in the list up by one. Might be
+ * expensive
+ *
+ * Strategey here will be to add a new page after the present one
+ * if the next page is either full or non-existant. That way, only
+ * elements on the next page are affected, and if another item is
+ * added to the present page (as often happens) very little more work
+ * need be done.
+ *______________________________________________________________________
+ */
+static void
+List_InsertExistingItem( PtrList *list, ListPage *page, size_t pageIndex,
+ void *item )
+{
+ void * remnant = ListPage_InsertItem( page, pageIndex, item );
+ if( remnant ) /* An item got bumped off when inserting into page */
+ {
+ ListPage *next = page->next;
+ if( next == NULL || ListPage_IsFull( next ) )
+ {
+ ListPage *new = ListPage_New( list->itemsPerPage );
+ ListPage_Append( new, item ); /* Item on new */
+ new->next = next; /* link new page */
+ page->next = new;
+ }
+ else
+ ListPage_InsertItem( next, 0, remnant );
+ }
+}
+ /*_____________________________________________________________________
+ **List_Insert**
+ * Inserts the item at the specified index, shifting indices of
+ * existing list items as necessary. If index equals the number
+ * of items in the list less one, the item is appended to the list.
+ *______________________________________________________________________
+ */
+
+void
+List_Insert( PtrList * list, size_t index, void * item )
+{
+ if( index >= List_NumberOfItems( list ) )
+ List_Append( list, item );
+ else
+ {
+ ListPage *page = NULL;
+ size_t pageIndex;
+
+ List_FindItemAddress( list, index, &page, &pageIndex );
+
+ if( page != NULL )
+ List_InsertExistingItem( list, page, pageIndex, item );
+ else
+ List_HandleAddressingError();
+ /* probably should handle an addressing error */
+ }
+}
+
+ /*_____________________________________________________________________
+ **Item**_______________________________________________________________
+ * Returns the item of given index; returns NULL if no such index.
+ *______________________________________________________________________
+ */
+
+void *
+List_Item( const PtrList *this, size_t requestedIndex )
+{
+ ListPage *itsPage = NULL;
+ size_t itsPageIndex;
+
+ List_FindItemAddress( this, requestedIndex, &itsPage, &itsPageIndex );
+
+ if( itsPage != NULL )
+ return *( itsPage->items + itsPageIndex );
+
+ return NULL;
+}
+ /*_____________________________________________________________________
+ **SetItem**____________________________________________________________
+ * Sets the item of given index. Calls user-supplied addressing
+ * handler if no such item exists.
+ *______________________________________________________________________
+ */
+void
+List_SetItem( PtrList *this, size_t requestedIndex, void *value )
+{
+ ListPage *itsPage = NULL;
+ size_t itsPageIndex;
+
+ List_FindItemAddress( this, requestedIndex, &itsPage, &itsPageIndex );
+
+ if( itsPage != NULL )
+ *( itsPage->items + itsPageIndex ) = value;
+ else
+ List_HandleAddressingError();
+}
+
+ /*_____________________________________________________________________
+ **CopyList**_________________________________________________________
+ * Empties this list and puts items from other list in it.
+ *______________________________________________________________________
+ */
+void
+List_CopyList( PtrList * this, const PtrList * other )
+{
+ List_Empty( this );
+ this->itemsPerPage = other->itemsPerPage;
+ this->firstPage = List_Xerox( other );
+}
+ /*_____________________________________________________________________
+ **RemoveItem**_________________________________________________________
+ * Removes the item of given index from the list by shifting all the
+ * following items back by one and reducing itsNumberOfItems by one.
+ * -> It would kind of be nice if this also deleted empty pages
+ *______________________________________________________________________
+ */
+void *
+List_RemoveItem( PtrList *this, const size_t requestedIndex )
+{
+ ListPage *page = NULL;
+ size_t pageIndex = 0;
+
+ List_FindItemAddress( this, requestedIndex, &page, &pageIndex );
+
+ if( page != NULL )
+ return ListPage_RemoveItem( page, pageIndex );
+ else
+ List_HandleAddressingError();
+
+ return NULL;
+}
+ /*_____________________________________________________________________
+ **Remove**_____________________________________________________________
+ * Removes the first item in the list with matching pointer.
+ * Note: No check is done for duplicate pointers in the list.
+ *______________________________________________________________________
+ */
+void
+List_Remove( PtrList *this, void * item )
+{
+ size_t index = 0;
+
+ if( List_GetIndexOf( this, item, &index ) )
+ List_RemoveItem( this, index );
+}
+ /*_____________________________________________________________________
+ **GetIndexOf**_________________________________________________________
+ * Inverse of Item(), if pointers in list are unique.
+ * Note: No check is done for duplicate pointers in the list.
+ * Returns TRUE and sets the indexi argument to the first item in the
+ * list with matching pointer, if such an item exists; otherwise
+ * returns FALSE.
+ *______________________________________________________________________
+ */
+PLBOOL
+List_GetIndexOf( const PtrList *this, const void * item, size_t * index )
+{
+ size_t numItemsOnPreviousPages = 0;
+ ListPage *page = NULL;
+
+ for( page = this->firstPage; page != NULL; page = page->next )
+ {
+ const size_t numItemsOnPage = page->numberOfItems;
+ size_t i;
+
+ for( i = 0; i < numItemsOnPage; i++ )
+ if( *( page->items + i ) == item )
+ {
+ *index = numItemsOnPreviousPages + i;
+ return PLTRUE;
+ }
+ numItemsOnPreviousPages += numItemsOnPage;
+ }
+ return PLFALSE;
+}
+#if 0
+static void
+List_BubbleSort( PtrList * list, ListSortComparison comparison )
+{
+ const size_t n = List_NumberOfItems( list );
+ size_t i, j;
+
+ for( i = 0; i < n; i++ )
+ {
+ void *item_i = List_Item( list, i );
+
+ for( j = i + 1; j < n; j++ )
+ {
+ void *item_j = List_Item( list, j );
+
+ if( comparison( item_i, item_j ) > 0 )
+ {
+ List_SwapItems( list, i, j );
+ item_i = item_j;
+ }
+ }
+ }
+}
+#endif
+void
+List_SwapItems( PtrList * v, size_t a, size_t b )
+{
+ ListPage *aPage = NULL, *bPage = NULL;
+ size_t aPageInd, bPageInd;
+
+ List_FindItemAddress( v, a, &aPage, &aPageInd );
+ List_FindItemAddress( v, b, &bPage, &bPageInd );
+
+ if( aPage != NULL && bPage != NULL )
+ {
+ void *temp = *( aPage->items + aPageInd );
+ *( aPage->items + aPageInd ) = *( bPage->items + bPageInd );
+ *( bPage->items + bPageInd ) = temp;
+ }
+}
+ /* Adapted from Kernighan & Ritchie */
+static void
+List_Qsort( PtrList * v, size_t left, size_t right,
+ ListSortComparison comparison )
+{
+ size_t i, last;
+
+ if( left >= right ) /* do nothing if array contains */
+ return; /* fewer than two elements */
+ List_SwapItems( v, left, ( left + right ) / 2); /* move partition */
+ last = left; /* elem to v[0] */
+ for( i = left + 1; i <= right; i++ )
+ {/* SW this could be improved a lot */
+ void * item_i = List_Item( v, i );
+ void * item_left = List_Item( v, left );
+ if( comparison( item_i, item_left ) < 0 )
+ List_SwapItems( v, ++last, i );
+ }
+ List_SwapItems( v, left, last ); /* restore partition elem */
+/* SW NEED TO THINK ABOUT THIS */
+ if( last > 0 ) /* K&R use int, so this isn't an issue for them */
+ List_Qsort( v, left, last - 1, comparison );
+ List_Qsort( v, last + 1, right, comparison );
+}
+
+ /*_____________________________________________________________________
+ **SortAccordingTo**__________________________________________________
+ * Based on the comparison of any two items provided by the comparison
+ * function argument, sorts the list.
+ * Note here it uses a bubble sort, which is slow but easy to
+ * understand. Want something faster? Write your own!
+ *______________________________________________________________________
+ */
+void
+List_SortAccordingTo( PtrList * list, ListSortComparison comparison )
+{
+ size_t n = List_NumberOfItems( list );
+ if( n > 1 )
+ List_Qsort( list, 0, n - 1, comparison );
+}
+
+ /*_____________________________________________________________________
+ **FirstItemSuchThat**__________________________________________________
+ * Takes a ListCondition function, returns the pointer value that
+ * satisfies the condition, otherwise returns NULL,
+ *______________________________________________________________________
+ */
+void *
+List_FirstItemSuchThat( const PtrList *list, ListCondition condition )
+{
+ const size_t n = List_NumberOfItems( list );
+ size_t i;
+
+ for( i = 0; i < n; i++ )
+ if( condition( List_Item( list, i ) ) )
+ return List_Item( list, i );
+
+ return NULL;
+}
+
+ /*_____________________________________________________________________
+ **FindFirstIndexSuchThat**_____________________________________________
+ * Takes a ListCondition function, sets the index pointer to the index
+ * of the first pointer in the list that satisfies condition.
+ * Otherwise returns PLFALSE.
+ *______________________________________________________________________
+ */
+PLBOOL
+List_FindFirstIndexSuchThat( const PtrList *list, ListCondition condition,
+ size_t * index )
+{
+ const size_t n = List_NumberOfItems( list );
+ size_t i;
+
+ for( i = 0; i < n; i++ )
+ if( condition( List_Item( list, i ) ) )
+ {
+ *index = i;
+ return PLTRUE;
+ }
+
+ return PLFALSE;
+}
+
+ /*_____________________________________________________________________
+ **FreeItemsInListAndEmpty**____________________________________________
+ * Lists don't usually delete the things they refer to.
+ * This function takes the list and uses it to free the listed items.
+ *______________________________________________________________________
+ */
+void
+List_FreeItemsInListAndEmpty( PtrList * this )
+{
+ size_t i = List_NumberOfItems( this );
+ void *item = NULL;
+
+ while( i > 0 ) /* Remove items in reverse order for efficeincy */
+ {
+ i--;
+ item = List_Item( this, i );
+ List_RemoveItem( this, i ); /* Remove item before deleting */
+ /* (for consistency) */
+ free( item );
+ }
+}
+ /*_____________________________________________________________________
+ **FindItemAddress**____________________________________________________
+ * Private utility that gets the page and index relative to the page
+ * given the item index. Sets *thePage to NULL if no such index.
+ *______________________________________________________________________
+ */
+void
+List_FindItemAddress( const PtrList *this, size_t itemNo,
+ ListPage ** thePage, size_t *thePageIndex )
+{
+ ListPage *page = NULL;
+ size_t numItemsIncludingThisPage = 0,
+ numItemsOnPreviousPages = 0;
+
+ *thePage = NULL;
+ *thePageIndex = 0;
+
+ for( page = this->firstPage; page != NULL; page = page->next )
+ {
+ numItemsIncludingThisPage += page->numberOfItems;
+
+ if( numItemsIncludingThisPage > itemNo )
+ {
+ *thePage = page;
+ *thePageIndex = itemNo - numItemsOnPreviousPages;
+ return;
+ }
+ numItemsOnPreviousPages = numItemsIncludingThisPage;
+ }
+}
+
+ /*_____________________________________________________________________
+ **Xerox**______________________________________________________________
+ * Private utility that makes a duplicate of the pages in the list.
+ *______________________________________________________________________
+ */
+ListPage *
+List_Xerox( const PtrList *list)
+{
+ ListPage *copy = NULL, *newPage = NULL, *page = list->firstPage;
+
+ if( list->firstPage != NULL )
+ copy = newPage = ListPage_Dup( list->firstPage );
+
+ if( newPage != NULL )
+ do
+ {
+ if( page->next != NULL )
+ {
+ page = page->next;
+ newPage->next = ListPage_Dup( page );
+ }
+ newPage = newPage->next;
+ }
+ while( newPage != NULL );
+ return copy;
+}
+