/*****************************************************************************/ /* */ /* COLL.H */ /* */ /* (C) 1993-95 Ullrich von Bassewitz */ /* Zwehrenbuehlstrasse 33 */ /* D-72070 Tuebingen */ /* EMail: uz@ibb.schwaben.com */ /* */ /*****************************************************************************/ // $Id$ // // $Log$ // // #include #include #include #include "machine.h" #include "check.h" #include "object.h" #include "stream.h" #include "coll.h" // Pointer to error routine void (*CollectionError) (int) = NULL; /*****************************************************************************/ /* class _Collection */ /*****************************************************************************/ // This is a base class for the template class Collection that has full // functionality but uses void pointers instead of typed pointers. The // implementation of class Collection consists merely of inline functions // calling the derived member functions and casting when necessary. This will // hopefully help to decrease program size. _Collection::_Collection (StreamableInit): Items (0) { } _Collection::_Collection (int aLimit, int aDelta, int aShouldDelete): ShouldDelete (aShouldDelete) { // check parameters if (aLimit < 0 || aDelta < 0 || (aLimit + aDelta) == 0 || aLimit > MaxCollectionSize || aDelta > MaxCollectionSize) { FAIL ("_Collection::_Collection: Overflow error"); return; } // get values - one of is != 0 Limit = aLimit ? aLimit : aDelta; Delta = aDelta; Count = 0; // allocate memory Items = AllocItemSpace (Limit); } _Collection::~_Collection () { // Release allocated memory but do not try to call DeleteAll (this calls // FreeItem, but _Collection::FreeItem will call ABSTRACT) FreeItemSpace (Items); } void* _Collection::At (int Index) { // Check range if (Index < 0 || Index >= Count) { FAIL ("_Collection::At: Index out of bounds"); return NULL; } return Items [Index]; } const void* _Collection::At (int Index) const { // Check range if (Index < 0 || Index >= Count) { FAIL ("_Collection::At: Index out of bounds"); return NULL; } return Items [Index]; } void _Collection::AtDelete (int Index) { // Check range if (Index < 0 || Index >= Count) { FAIL ("_Collection::AtDelete: Index out of bounds"); return; } // Free entry if necessary if (ShouldDelete) { FreeItem (Items [Index]); } // Delete entry Count--; memmove (Items + Index, Items + Index + 1, (Count - Index) * sizeof (void*)); } void _Collection::AtInsert (int Index, void* Item) { // Check range, Index may be one more than the index of the last element if (Index < 0 || Index > Count) { FAIL ("_Collection::AtInsert: Index out of bounds"); return; } // Check if the collection must grow if (Count == Limit) { // Increase size, check if possible if (Delta == 0 || (Limit + Delta) > MaxCollectionSize) { // Cannot grow FAIL ("_Collection::AtInsert: Overflow error"); return; } // Change the size SetLimit (Limit + Delta); } // We can insert the element. If the item is not inserted at the end // of the collection, we must create a "hole" if (Count != Index) { memmove (Items + Index + 1, Items + Index, (Count - Index) * sizeof (void*)); } Count++; // store the new item Items [Index] = Item; } void _Collection::AtReplace (int Index, void* Item) { // check range if (Index < 0 || Index >= Count) { FAIL ("_Collection::AtReplace: Index out of bounds"); return; } // Free item if necessary if (ShouldDelete) { FreeItem (Items [Index]); } // Store item Items [Index] = Item; } void _Collection::Delete (void* Item) { int Index = IndexOf (Item); if (Index != -1) { if (ShouldDelete) { FreeItem (Items [Index]); } Items [Index] = NULL; } } void _Collection::DeleteAll () { if (ShouldDelete) { while (Count) { FreeItem (Items [--Count]); } } else { Count = 0; } } void* _Collection::Traverse (int Forward, int (*Func) (void*, void*), void* UserData) { int I; void** P; if (Forward) { for (I = 0, P = Items; I < Count; I++, P++) { if (Func (*P, UserData) != 0) { // stop and return current return *P; } } } else { for (I = Count-1, P = Items + I; I >= 0; I--, P--) { if (Func (*P, UserData) != 0) { // stop and return current return *P; } } } // Not found return NULL; } void _Collection::FreeItem (void*) { ABSTRACT (); } void* _Collection::GetItem (Stream& S) { // Assume it's some sort of object return S.Get (); } void** _Collection::AllocItemSpace (int ItemCount) // Allocate item space { return new void* [ItemCount]; } void _Collection::FreeItemSpace (void** Space) // Deallocate item space { delete [] Space; } int _Collection::IndexOf (const void* Item) { int C; void** I; // generic collection "features" a linear search for (C = 0, I = Items; C < Count; C++, I++) { if (*I == Item) { return C; } } // Not found return -1; } void _Collection::Insert (void* Item) { // Default is to add the item at the end AtInsert (Count, Item); } void _Collection::Pack () { int I, J; for (I = 0; I < Count; I++) { // Try to find a NULL pointer if (Items [I] == NULL) { // Count the number of NULL pointers in a row J = I+1; while (J < Count && Items [J] == NULL) { J++; } // Delete those NULL pointers memmove (Items + I, Items + J, (Count - J) * sizeof (void*)); // Update Count Count -= J - I; } } } void _Collection::PutItem (Stream& S, void* Item) const { // Assume it's a streamable object S.Put ((Streamable*) Item); } void _Collection::SetLimit (int aLimit) { // Limit the new limit if (aLimit < Count) { aLimit = Count; } else if (aLimit > MaxCollectionSize) { aLimit = MaxCollectionSize; } // allocate memory void** P = AllocItemSpace (aLimit); // Copy old data into new array memcpy (P, Items, Count * sizeof (void*)); // Release old memory FreeItemSpace (Items); // Remember new array and limit Items = P; Limit = aLimit; } void _Collection::Load (Stream& S) { // The build constructor sets Items to 0. So, if Items is *not* 0 now, // someone is calling Load (or operator <<) with existing data. Do the // best we can to imitate builtin data types in this case and delete the // existing stuff before reloading. if (Items) { // Delete all items, then delete the item space DeleteAll (); FreeItemSpace (Items); } // Read correct size u32 aCount, aDelta, aLimit; u16 aShouldDelete; S >> aCount; S >> aDelta; Delta = aDelta; S >> aLimit; Limit = aLimit; S >> aShouldDelete; ShouldDelete = aShouldDelete; // allocate memory Items = AllocItemSpace (Limit); // Read items. Do not insert them directly but use Insert. This causes // nearly no spead penalty on collections and only little on // SortedCollections, but does a re-sort if the sort order of a // SortedColl has changed (String ordering according to national // conventions for example). Count = 0; while (aCount--) { Insert (GetItem (S)); } } void _Collection::Store (Stream& S) const { // Write correct size u32 aCount = (u32) Count; u32 aDelta = (u32) Delta; u32 aLimit = (u32) Limit; u16 aShouldDelete = (u16) ShouldDelete; // Write data S << aCount << aDelta << aLimit << aShouldDelete; // Write items for (int I = 0; I < Count; I++) { PutItem (S, Items [I]); } }