openmohaa/code/gamespy/darray.c
2023-02-04 21:00:01 +01:00

377 lines
7.6 KiB
C

/*
*
* File: darray.c
* ---------------
* David Wright
* 10/8/98
*
* See darray.h for function descriptions
*/
#include <stdlib.h>
#include <string.h>
#include "darray.h"
#ifdef _MFC_MEM_DEBUG
#define _CRTDBG_MAP_ALLOC 1
#include <crtdbg.h>
#endif
#define DEF_GROWBY 8
#ifdef _NO_NOPORT_H_
#define gsimalloc malloc
#define gsifree free
#define gsirealloc realloc
#include "common/gsAssert.h"
#else
#include "nonport.h" //for gsimalloc/realloc/free/GS_ASSERT
#endif
// STRUCTURES
struct DArrayImplementation
{
int count, capacity;
int elemsize;
int growby;
ArrayElementFreeFn elemfreefn;
void *list; //array of elements
};
// PROTOTYPES
static void *mylsearch(const void *key, void *base, int count, int size,
ArrayCompareFn comparator);
static void *mybsearch(const void *elem, void *base, int num, int elemsize,
ArrayCompareFn comparator, int *found);
// FUNCTIONS
/* FreeElement
* Frees the element at position N in the array
*/
static void FreeElement(DArray array, int n)
{
if (array->elemfreefn != NULL)
array->elemfreefn(ArrayNth(array,n));
}
/* ArrayGrow
* Reallocates the array to a new size, incresed by growby
*/
static void ArrayGrow(DArray array)
{
GS_ASSERT(array->elemsize) // sanity check -mj Oct 31st
array->capacity += array->growby;
array->list = gsirealloc(array->list, (size_t) array->capacity * array->elemsize);
GS_ASSERT(array->list);
}
/* SetElement
* Sets the element at pos to the contents of elem
*/
static void SetElement(DArray array, const void *elem, int pos)
{
GS_ASSERT(array) // safety check -mj Oct 31st
GS_ASSERT(elem)
GS_ASSERT(array->elemsize)
memcpy(ArrayNth(array,pos), elem, (size_t)array->elemsize);
}
DArray ArrayNew(int elemSize, int numElemsToAllocate,
ArrayElementFreeFn elemFreeFn)
{
DArray array;
array = (DArray) gsimalloc(sizeof(struct DArrayImplementation));
GS_ASSERT(array);
GS_ASSERT(elemSize);
if (numElemsToAllocate == 0)
numElemsToAllocate = DEF_GROWBY;
array->count = 0;
array->capacity = numElemsToAllocate;;
array->elemsize = elemSize;
array->growby = numElemsToAllocate;
array->elemfreefn = elemFreeFn;
if (array->capacity != 0)
{
array->list = gsimalloc((size_t)array->capacity * array->elemsize);
GS_ASSERT(array->list);
} else
array->list = NULL;
return array;
}
void ArrayFree(DArray array)
{
int i;
GS_ASSERT(array);
for (i = 0; i < array->count; i++)
{
FreeElement(array, i);
}
// mj to do: move these asserts into gsi_free. maybe, depends on whether user overloads them
GS_ASSERT(array->list)
GS_ASSERT(array)
gsifree(array->list);
gsifree(array);
}
void *ArrayGetDataPtr(DArray array)
{
GS_ASSERT(array);
return array->list;
}
void ArraySetDataPtr(DArray array, void *ptr, int count, int capacity)
{
int i;
GS_ASSERT(array);
if (array->list != NULL)
{
for (i = 0; i < array->count; i++)
{
FreeElement(array, i);
}
gsifree(array->list);
}
array->list = ptr;
array->count = count;
array->capacity = capacity;
}
int ArrayLength(const DArray array)
{
GS_ASSERT(array)
return array->count;
}
void *ArrayNth(DArray array, int n)
{
// 2004.Nov.16.JED - modified GS_ASSERT to include "if" to add robustness
GS_ASSERT( (n >= 0) && (n < array->count));
if( ! ((n >= 0) && (n < array->count)) )
return NULL;
return (char *)array->list + array->elemsize*n;
}
/* ArrayAppend
* Just do an Insert at the end of the array
*/
void ArrayAppend(DArray array, const void *newElem)
{
GS_ASSERT(array);
if(array)
ArrayInsertAt(array, newElem, array->count);
}
void ArrayInsertAt(DArray array, const void *newElem, int n)
{
GS_ASSERT (array)
GS_ASSERT ( (n >= 0) && (n <= array->count));
if (array->count == array->capacity)
ArrayGrow(array);
array->count++;
if (n < array->count - 1) //if we aren't appending
memmove(ArrayNth(array, n+1), ArrayNth(array,n),
(size_t)(array->count - 1 - n) * array->elemsize);
SetElement(array, newElem, n);
}
void ArrayInsertSorted(DArray array, const void *newElem, ArrayCompareFn comparator)
{
int n;
void *res;
int found;
GS_ASSERT (array)
GS_ASSERT (comparator);
res=mybsearch(newElem, array->list, array->count, array->elemsize, comparator, &found);
n = (((char *)res - (char *)array->list) / array->elemsize);
ArrayInsertAt(array, newElem, n);
}
void ArrayRemoveAt(DArray array, int n)
{
GS_ASSERT (array)
GS_ASSERT( (n >= 0) && (n < array->count));
if (n < array->count - 1) //if not last element
memmove(ArrayNth(array,n),ArrayNth(array,n+1),
(size_t)(array->count - 1 - n) * array->elemsize);
array->count--;
}
void ArrayDeleteAt(DArray array, int n)
{
GS_ASSERT (array)
GS_ASSERT ( (n >= 0) && (n < array->count));
FreeElement(array,n);
ArrayRemoveAt(array, n);
}
void ArrayReplaceAt(DArray array, const void *newElem, int n)
{
GS_ASSERT (array)
GS_ASSERT ( (n >= 0) && (n < array->count));
FreeElement(array, n);
SetElement(array, newElem,n);
}
void ArraySort(DArray array, ArrayCompareFn comparator)
{
GS_ASSERT (array)
qsort(array->list, (size_t)array->count, (size_t)array->elemsize, comparator);
}
//GS_ASSERT will be raised by ArrayNth if fromindex out of range
int ArraySearch(DArray array, const void *key, ArrayCompareFn comparator,
int fromIndex, int isSorted)
{
void *res;
int found = 1;
if (!array || array->count == 0)
return NOT_FOUND;
if (isSorted)
res=mybsearch(key, ArrayNth(array,fromIndex),
array->count - fromIndex, array->elemsize, comparator, &found);
else
res=mylsearch(key, ArrayNth(array, fromIndex),
array->count - fromIndex, array->elemsize, comparator);
if (res != NULL && found)
return (((char *)res - (char *)array->list) / array->elemsize);
else
return NOT_FOUND;
}
void ArrayMap(DArray array, ArrayMapFn fn, void *clientData)
{
int i;
GS_ASSERT (array)
GS_ASSERT(fn);
for (i = 0; i < array->count; i++)
fn(ArrayNth(array,i), clientData);
}
void ArrayMapBackwards(DArray array, ArrayMapFn fn, void *clientData)
{
int i;
GS_ASSERT(fn);
for (i = (array->count - 1) ; i >= 0 ; i--)
fn(ArrayNth(array,i), clientData);
}
void * ArrayMap2(DArray array, ArrayMapFn2 fn, void *clientData)
{
int i;
void * pcurr;
GS_ASSERT(fn);
GS_ASSERT(clientData);
for (i = 0; i < array->count; i++)
{
pcurr = ArrayNth(array,i);
if(!fn(pcurr, clientData))
return pcurr;
}
return NULL;
}
void * ArrayMapBackwards2(DArray array, ArrayMapFn2 fn, void *clientData)
{
int i;
void * pcurr;
GS_ASSERT(fn);
GS_ASSERT(clientData);
for (i = (array->count - 1) ; i >= 0 ; i--)
{
pcurr = ArrayNth(array,i);
if(!fn(pcurr, clientData))
return pcurr;
}
return NULL;
}
void ArrayClear(DArray array)
{
int i;
// This could be more optimal!
//////////////////////////////
for(i = (ArrayLength(array) - 1) ; i >= 0 ; i--)
ArrayDeleteAt(array, i);
}
/* mylsearch
* Implementation of a standard linear search on an array, since we
* couldn't use lfind
*/
static void *mylsearch(const void *key, void *base, int count, int size,
ArrayCompareFn comparator)
{
int i;
GS_ASSERT(key);
GS_ASSERT(base);
for (i = 0; i < count; i++)
{
if (comparator(key, (char *)base + size*i) == 0)
return (char *)base + size*i;
}
return NULL;
}
/* mybsearch
* Implementation of a bsearch, since its not available on all platforms
*/
static void *mybsearch(const void *elem, void *base, int num, int elemsize, ArrayCompareFn comparator, int *found)
{
int L, H, I, C;
GS_ASSERT(elem);
GS_ASSERT(base);
GS_ASSERT(found);
L = 0;
H = num - 1;
*found = 0;
while (L <= H)
{
I = (L + H) >> 1;
C = comparator(((char *)base) + I * elemsize,elem);
if (C == 0)
*found = 1;
if (C < 0)
L = I + 1;
else
{
H = I - 1;
}
}
return ((char *)base) + L * elemsize;
}