/****************************************************************************************/ /* array.c */ /* */ /* Author: Jim Mischel */ /* Description: Dynamic Array */ /* */ /* The contents of this file are subject to the Genesis3D Public License */ /* Version 1.01 (the "License"); you may not use this file except in */ /* compliance with the License. You may obtain a copy of the License at */ /* http://www.genesis3d.com */ /* */ /* Software distributed under the License is distributed on an "AS IS" */ /* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */ /* the License for the specific language governing rights and limitations */ /* under the License. */ /* */ /* The Original Code is Genesis3D, released March 25, 1999. */ /*Genesis3D Version 1.1 released November 15, 1999 */ /* Copyright (C) 1999 WildTangent, Inc. All Rights Reserved */ /* */ /****************************************************************************************/ #include "array.h" #include #include geBoolean Array_Init (Array *pArray, int InitialSize, int ItemSize) { assert (pArray != NULL); assert (InitialSize >= 0); assert (ItemSize > 0); pArray->ItemSize = ItemSize; pArray->ItemsAllocated = 0; // if user requested initial size, // then allocate memory for the items. if (InitialSize == 0) { pArray->Items = NULL; } else { pArray->Items = malloc (InitialSize * ItemSize); if (pArray->Items == NULL) { return GE_FALSE; } pArray->ItemsAllocated = InitialSize; } return GE_TRUE; } void Array_Uninit (Array *pArray) { assert (pArray != NULL); if (pArray->Items != NULL) { free (pArray->Items); pArray->Items = NULL; } pArray->ItemsAllocated = 0; } // Create an array object with given initial size (possibly 0). Array *Array_Create (int InitialSize, int ItemSize) { Array *pArray; assert (InitialSize >= 0); assert (ItemSize > 0); pArray = malloc (sizeof (Array)); if (pArray != NULL) { if (Array_Init (pArray, InitialSize, ItemSize) == GE_FALSE) { Array_Destroy (&pArray); } } return pArray; } // Destroy an array object void Array_Destroy (Array **ppArray) { assert (ppArray != NULL); assert (*ppArray != NULL); Array_Uninit (*ppArray); free (*ppArray); *ppArray = NULL; } // Resizes the array to contain NewSize elements. // Returns new size. int Array_Resize (Array *pArray, int NewSize) { void *NewItems; assert (pArray != NULL); assert (NewSize >= 0); NewItems = realloc (pArray->Items, (NewSize * pArray->ItemSize)); // realloc returns NULL in two cases: // 1) It was unable to allocate memory // 2) The size parameter was 0. if ((NewItems != NULL) || (NewSize == 0)) { pArray->Items = NewItems; pArray->ItemsAllocated = NewSize; } return pArray->ItemsAllocated; } // // Following code active only in debug mode. // Non-debug mode has all this stuff inline in the header. #ifdef _DEBUG // Returns the array's allocated size. int Array_GetSize (const Array *pArray) { assert (pArray != NULL); return pArray->ItemsAllocated; } int Array_GetItemSize (const Array *pArray) { assert (pArray != NULL); return pArray->ItemSize; } // Returns a pointer to the item's data. void *Array_ItemPtr (Array *pArray, int Index) { assert (pArray != NULL); assert (Index >= 0); assert (Index < pArray->ItemsAllocated); return (void *)(((long)(pArray->Items)) + (Index * pArray->ItemSize)); } // Copies DataSize bytes from pData to array[Index] void Array_PutAt (Array *pArray, int Index, void *pData, int DataSize) { void *pDest; assert (pArray != NULL); assert (pData != NULL); assert (Index < pArray->ItemsAllocated); assert (DataSize <= pArray->ItemSize); assert (DataSize >= 0); pDest = Array_ItemPtr (pArray, Index); memcpy (pDest, pData, DataSize); } // Inserts an item at the given position, moving all other items // down by 1. This causes the last item in the array to be lost. void Array_InsertAt (Array *pArray, int Index, void *pData, int DataSize) { int MoveSize; void *pSrc; void *pDest; assert (pArray != NULL); assert (pData != NULL); assert (Index < pArray->ItemsAllocated); assert (DataSize <= pArray->ItemSize); assert (DataSize >= 0); // move everything down by 1 MoveSize = (pArray->ItemsAllocated - Index - 1) * pArray->ItemSize; pSrc = Array_ItemPtr (pArray, Index); pDest = Array_ItemPtr (pArray, Index+1); memmove (pDest, pSrc, MoveSize); // and copy the new item into the spot memcpy (pSrc, pData, DataSize); } // Deletes the item at and moves all following items in the array up // to fill in the empty spot. void Array_DeleteAt (Array *pArray, int Index) { int MoveSize; void *pSrc; void *pDest; assert (pArray != NULL); assert (Index >= 0); assert (Index < pArray->ItemsAllocated); MoveSize = (pArray->ItemsAllocated - Index - 1) * pArray->ItemSize; pSrc = Array_ItemPtr (pArray, Index+1); pDest = Array_ItemPtr (pArray, Index); memcpy (pDest, pSrc, MoveSize); } #endif #ifdef ARRAY_TEST_CODE #include #include // atoi #include /* The test code reads array commands from an input file and applies those commands to arrays. The commands are in a sort of mini scripting language. The commands take the form , where is a 1- or 2-letter command (see below), and the argument is a string. Commands are stored in the file one per line, with being the first two characters, followed by a space, and then the argument which takes up the rest of the line. Blank lines and lines that start with a semicolon are considered comments and are ignored by the interpreter. For example: ;create a new array n ;append an item a Hello, world ;print the array from front to back pf ;destroy the array d The full list of commands is: c Create a new array d Destroy an array no argument i Insert an item p Put an item r Remove an item g Resize (grow)array pn Print #items no argument pi Print item ps Print item size no argument pr Print string */ static geBoolean ParseCommand ( char *InputLine, int LineNo, Array **ppArray ) { short intcmd; char *sarg; geBoolean rslt; char *c; int Index; int nItems, ItemSize; if ((InputLine[0] == '\0') || (InputLine[0] == ';')) { // it's a comment line return GE_TRUE; } if ((InputLine[1] == ' ') || (InputLine[1] == '\0') || (InputLine[1] == '\n')) { intcmd = InputLine[0]; } else if ((InputLine[2] == ' ') || (InputLine[2] == '\0') || (InputLine[2] == '\n')) { short temp; intcmd = *((short *)&InputLine[0]); // swap the bytes temp = (short)(intcmd & 0xff); intcmd = (short)((intcmd >> 8) | (temp << 8)); } else { printf ("Parse error on line %d\n", LineNo); return GE_FALSE; } rslt = GE_TRUE; switch (intcmd) { case 'c' : { // need to get size and item size from sarg sarg = &InputLine[2]; c = strchr (sarg, ' '); if (c == NULL) { puts ("Expected "); rslt = GE_FALSE; break; } *c = '\0'; ++c; nItems = atoi (sarg); ItemSize = atoi (c); if (*ppArray != NULL) { puts ("Warning: Deleting old array."); Array_Destroy (ppArray); } printf ("Creating array with %d items of size %d\n", nItems, ItemSize); *ppArray = Array_Create (nItems, ItemSize); if (*ppArray == NULL) { puts ("Error creating array"); rslt = GE_FALSE; } else { puts ("Created array"); } break; } case 'd' : if (*ppArray == NULL) { puts ("Warning: Trying to destroy NULL array."); } else { puts ("Destroy array"); Array_Destroy (ppArray); } break; case 'i' : sarg = &InputLine[2]; c = strchr (sarg, ' '); if (c == NULL) { puts ("Expected "); rslt = GE_FALSE; break; } *c = '\0'; ++c; Index = atoi (sarg); printf ("Insert (%s) at item %d\n", c, Index); Array_InsertAt (*ppArray, Index, c, strlen (c)+1); break; case 'p' : sarg = &InputLine[2]; c = strchr (sarg, ' '); if (c == NULL) { puts ("Expected "); rslt = GE_FALSE; break; } *c = '\0'; ++c; Index = atoi (sarg); printf ("Put (%s) at item %d\n", c, Index); Array_PutAt (*ppArray, Index, c, strlen (c)+1); break; case 'r' : sarg = &InputLine[2]; Index = atoi (sarg); c = Array_ItemPtr (*ppArray, Index); printf ("Remove item %d (%s)\n", Index, c); Array_DeleteAt (*ppArray, Index); break; case 'g' : sarg = &InputLine[2]; nItems = atoi (sarg); printf ("Resize to %d items\n", nItems); Array_Resize (*ppArray, nItems); break; case 'pn' : nItems = Array_GetSize (*ppArray); printf ("Number of items = %d\n", nItems); break; case 'ps' : ItemSize = Array_GetSize (*ppArray); printf ("Item size = %d\n", ItemSize); break; case 'pr' : puts (&InputLine[3]); break; case 'pi' : sarg = &InputLine[2]; Index = atoi (sarg); c = Array_ItemPtr (*ppArray, Index); printf ("Item #%d = (%s)\n", Index, c); break; default : printf ("Unknown command on line %d\n", LineNo); rslt = GE_FALSE; break; } if (!rslt) { printf ("Error on line %d\n", LineNo); printf ("Line = (%s)\n", InputLine); } return rslt; } int main ( int argc, char *argv[] ) { Array *pArray; char *InputFilename; FILE *infile; int LineNo; geBoolean rslt; puts ("Array test version 1.0"); puts ("---------------------"); if (argc != 2) { puts ("Usage is Array "); puts ("See source for scripting information."); return 0; } InputFilename = argv[1]; infile = fopen (InputFilename, "rt"); if (infile == NULL) { printf ("Can't open input file: '%s'\n", InputFilename); return 0; } LineNo = 0; pArray = NULL; rslt = GE_TRUE; while (rslt && !feof (infile)) { char InputLine[256]; char *c; if (fgets (InputLine, sizeof (InputLine), infile) == NULL) { // Why is this test necessary? // Why doesn't feof return TRUE above? if (!feof (infile)) { puts ("Input error"); } break; } // strip newline c = strchr (InputLine, '\n'); if (c != NULL) { *c = '\0'; } ++LineNo; rslt = ParseCommand (InputLine, LineNo, &pArray); } if (pArray != NULL) { Array_Destroy (&pArray); } fclose (infile); return 0; } #endif