/* * * File: darray.c * --------------- * David Wright * 10/8/98 * * See darray.h for function descriptions */ #include #include #include "darray.h" #ifdef _MFC_MEM_DEBUG #define _CRTDBG_MAP_ALLOC 1 #include #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; }