/**********************************************************************/ /** Microsoft LAN Manager **/ /** Copyright(c) Microsoft Corp., 1987-1990 **/ /**********************************************************************/ /* ARRAY.HXX LM 3.0 Generic array class module This module contains type generic implementations of static and dynamic arrays that provide bounds checking. FILE HISTORY: Johnl 02-Aug-90 Created Johnl 05-Dec-90 Changed Size to QuerySize, changed Cassert to UIASSERT RustanL 05-Dec-90 Added ARRAY_LIST RustanL 06-Dec-90 Split DECLARE_ARRAY_OF into itself and DEFINE_ARRAY_OF RustanL 07-Dec-90 Added several enhancements to ARRAY class RustanL 31-Dec-90 Removed 'inline' from ARRAY_LIST::Remove */ #ifndef _ARRAY_HXX_ #define _ARRAY_HXX_ /************************************************************************* NAME: ARRAY SYNOPSIS: Generic array implementation that provides bounds checking INTERFACE: DECLARE_ARRAY_OF( ) - Declares array package (interface) for type DEFINE_ARRAY_OF() - Defines array package (implementation) for type ARRAY_OF() - Declares an array object of type "type" ARRAY_type( ) - Constructor, takes a suggested size of array, and attempts to allocate the array of that size. If the allocation fails, the size is set to 0, but the array is still in a valid state. In fact, the array will always be in a valid state. Initial allocations of size 0 are always guaranteed to succeed. Hence, the client can always check if QueryCount returns a different value than was passed to the constructor in order to determine the space actually allocated. (Please note that checking QueryCount for being is, in general, not enough to determine if the allocation was successful.) An alternate version of the constructor accepts an existing vector of objects. This version can either use that vector as its own storage, resizing as needed - in which case the creator must surrender all rights to the presumably dynamically allocated vector - or else treats it as nonresizable. The default is never to resize such, since the vector might not come from freestore, or else may be shared with another object. ~ARRAY_type() - Deletes the array operator[] - Gives random access to array with bounds checking (asserts out if you go out of bounds) operator= - Copies one array to another (note, they must be of the same size). QueryCount() - Returns the count of elements of this array. Resize() - Resizes the array to the number of elements specified by size. (NOTE: the array only reallocates memory if the size changes by more than ARRAY_RESIZE_MEM bytes or when the new size exceeds the current.) Returns TRUE if successful. If FALSE is returned, then a memory error occurred, and the array remains untouched. Resize is guaranteed to work whenever the new size does not exceed the previous. CAVEATS: NOTES: CODEWORK. The Resize method could be made much more efficient if it used a proper buffer object that could be realloced, rather than having to allocate a brand new chunk of memory, call the constructor on each item in this array, copy items into the new array, call the destructor on every previously allocated item, and finally deallocate the previous chunk of memory. This should become a new class (BUFFER_ARRAY_OF or the like). HISTORY: Johnl 15-Jul-1990 Created RustanL 06-Dec-1990 Created DEFINE_ARRAY_OF from DECLARE_ARRAY_OF RustanL 07-Dec-1990 Added several enhancements beng 14-Aug-1991 Added vector-adopt mode; renamed QCount; op= relaxed; enhanced further Yi-HsinS 28-Oct-1992 Add a flag to Resize indicating not to downsize **************************************************************************/ #define ARRAY_OF(type) ARRAY_##type #define ARRAY_RESIZE_MEM 4096 // If the array loses more than 4096 bytes // on a resize, then go ahead and reallocate // memory etc., else just change _carray /************************************************************/ #define DECL_ARRAY_OF(type,dec) \ \ class dec ARRAY_OF(type) \ { \ private: \ type *_parray; \ UINT _carray; \ UINT _carrayAlloc; \ BOOL _fMayResize; \ \ BOOL WithinRange( UINT iIndex ) const; \ \ public: \ ARRAY_OF(type)( UINT cElem ); \ ARRAY_OF(type)( type* ptype, UINT ctype, \ BOOL fMayResize = FALSE); \ \ ~ARRAY_OF(type)(); \ \ type& operator[]( UINT iIndex ) const \ { \ ASSERT( WithinRange(iIndex) ); \ return _parray[ iIndex ]; \ } \ \ UINT QueryCount() const \ { \ return _carray; \ } \ \ ARRAY_OF(type) & operator=( ARRAY_OF(type)& a ); \ \ BOOL Resize( UINT cElemNew, BOOL fDownSize = TRUE ); \ }; /************************************************************************/ #define DEFINE_ARRAY_OF( type ) \ \ ARRAY_OF(type)::ARRAY_OF(type)( UINT cElem ) \ : _parray(NULL), \ _carray(0), \ _carrayAlloc(0), \ _fMayResize(TRUE) \ { \ if ( cElem > 0 ) \ { \ _parray = new type[ cElem ]; \ } \ \ if ( _parray != NULL ) \ { \ _carray = _carrayAlloc = cElem; \ } \ } \ \ \ ARRAY_OF(type)::ARRAY_OF(type)(type *px, UINT cx, BOOL fMayResize) \ : _parray(px), \ _carray(cx), \ _carrayAlloc(cx), \ _fMayResize(fMayResize) \ { \ /* nothing doing. */ \ } \ \ \ ARRAY_OF(type)::~ARRAY_OF(type)() \ { \ if ( _fMayResize && _carrayAlloc > 0 ) \ delete [ _carrayAlloc ] _parray; \ } \ \ \ ARRAY_OF(type) & ARRAY_OF(type)::operator=( ARRAY_OF(type)& a ) \ { \ ASSERT(_carrayAlloc <= a.QueryCount()); \ \ UINT iLim = ( a.QueryCount() <= _carrayAlloc) \ ? a.QueryCount() \ : _carrayAlloc; \ \ for ( UINT i = 0; i < iLim; i++ ) \ _parray[i] = a._parray[i]; \ \ _carray = iLim; \ return *this; \ } \ \ \ BOOL ARRAY_OF(type)::WithinRange( UINT iIndex ) const \ { \ return (iIndex < _carray); \ } \ \ \ BOOL ARRAY_OF(type)::Resize( UINT cElemNew, BOOL fDownSize ) \ { \ /* Prevent resizing owner-alloc arrays */ \ if (!_fMayResize) \ return FALSE; \ \ if ( ( cElemNew > _carrayAlloc ) \ || ( fDownSize && \ (( _carrayAlloc - cElemNew )*sizeof(type) > ARRAY_RESIZE_MEM))\ ) \ { \ type * parrayNew; \ if ( cElemNew > 0 ) \ { \ parrayNew = new type[ cElemNew ]; \ if ( parrayNew == NULL ) \ { \ /* Memory failure */ \ if ( cElemNew > _carrayAlloc ) \ { \ /* The array has been not been modified */ \ return FALSE; \ } \ else \ { \ /* Guarantee: resizing the array for */ \ /* new sizes not exceeding the current */ \ /* will always succeed. */ \ _carray = cElemNew; \ return TRUE; \ } \ } \ \ /* Copy each existing item */ \ /* that will fit into the new array */ \ for ( UINT i = 0 ; \ i < ( _carray < cElemNew ? _carray : cElemNew); \ i++ ) \ { \ parrayNew[i] = _parray[i]; \ } \ \ } \ else \ { \ parrayNew = NULL; \ } \ \ if ( _carrayAlloc > 0 ) \ { \ delete[ _carrayAlloc ] _parray; \ } \ else \ { \ ASSERT( _parray == NULL ); \ } \ _parray = parrayNew; \ _carray = _carrayAlloc = cElemNew; \ } \ else \ { \ /* (Array does not report errors.) New size is less */ \ /* than current size, but not so much less that we want */ \ /* to realloc to save memory. */ \ _carray = cElemNew; \ } \ \ return TRUE; \ } /************************************************************************* NAME: ARRAY_LIST SYNOPSIS: Generic unordered array collection implementation which provides bounds checking, easy ways to add/remove items, etc. In some respects, this collection can be thought of as an efficient singly linked list, for singly linked lists which don't change a lot dynamically. Hence the name "array list". INTERFACE: DECLARE_ARRAY_LIST_OF( ) Declares array list package for type 'type' DEFINE_ARRAY_LIST_OF() Defines array list package for type 'type', excluding the Sort and BinarySearch methods (see Notes below) DEFINE_EXT_ARRAY_LIST_OF() Defines the entire array list package for type 'type' WARNING: If you use EXT_ARRAY_LIST, the Compare() method for the parameter class must be _CRTAPI1. Otherwise the underlying qsort() or BinarySearch() will fail. ARRAY_LIST_OF() Declares an array list object of type 'type' ARRAY_LIST_type() Constructor. Takes an optional parameter, nInitialSize, which specifies the initial allocation size of the array list in number of array list items. Note, any call to Add or AddIdemp may cause the allocated size to be enlarged, and any call to Remove may reduce the allocated size. These allocations, however, are all kept away from the user, except for the nInitialSize parameter to the constructor. Add() Adds an item to the array list. Note, this will not sort the list. AddIdemp() Idempotently adds an item to the array list. Note, this will not sort the list. Remove() Removes an item from the array list. This may result in shrinking the allocated size of the array, and will not sort the list. Find() Finds the first occurrence of a given item in the array list. Since the list is not assumed to be sorted, a linear search is performed. Clear() Clears the array list of all of items items operator[]() Same as for the ARRAY class operator=() Same as for the ARRAY class QueryCount() Same as for the ARRAY class Sort() Sorts the array (see Notes below for Sort definition) BinarySearch() Works like Find, but performs a binary search rather than a linear. This method assumes that the list is sorted. (See Notes below for BinarySearch definition.) CAVEATS: Add and Remove will alter the ordering of the list in ways unexpected. See the implementation of Remove for details. The Sort routine calls qsort, which may swap the items. Although qsort is given a pointer to the Compare function, it does not have access to, or knowledge of, the assignment operator; rather, it performs a straight binary copy of each item. Hence, all types which require a custom assignment operation should not use the Sort method. CODEWORK. The debug version of BinarySearch could assert that the array list is sorted. This, however, would require adding an operator=( ARRAY ) and an operator=( ARRAY_LIST ) to this class, since these would function differently with an additional member keeping track of whether the array list is sorted. CODEWORK. We have a C++ heapsort, which we could emend to recognize type::Compare. NOTES: The array list declaration and definitions automatically take care of declaring and defining, respectively, the superclass ARRAY or the same type, since ARRAY_LIST inherits from ARRAY. The Find, Sort, and BinarySearch functions require the 'type' class to define a Compare method. The retail version of this class does not claim nor attempt to keep track of if the list is sorted. Thus, it is always up to the client to keep track of this. For the same reason, Remove and AddIdemp always call Find, rather than BinarySearch. The debug version could assert out if BinarySearch is called when the list is not sorted (see Caveats above). Since the Sort and BinarySearch definitions methods depend on definitions in stdlib.h and search.h, the definitions for these methods are only provided in the extended definition macro. This way, clients who do not wish to make use of Sort and BinarySearch, need not include stdlib.h and search.h. HISTORY: RustanL 5-Dec-1990 Created beng 14-Aug-1991 Remove inlines; changes in ARRAY Yi-HsinS 28-Oct-1992 Add a flag to Resize indicating not to downsize **************************************************************************/ #define ARRAY_LIST_OF(type) ARRAY_LIST_##type /************************************************************/ #define DECL_ARRAY_LIST_OF(type,dec) \ \ DECL_ARRAY_OF(type,dec); \ \ class dec ARRAY_LIST_OF( type ) : public ARRAY_OF( type ) \ { \ public: \ ARRAY_LIST_OF( type )( UINT cAlloc = 0 ); \ \ BOOL Add( const type & t ); \ BOOL AddIdemp( const type & t ); \ BOOL Remove( const type & t ); \ INT Find( const type & t ) const; \ VOID Clear(); \ \ VOID Sort(); \ INT BinarySearch( const type & t ) const; \ static int __cdecl SortFunc ( \ const void * p1, const void * p2 ) ; \ }; /********************************************************************/ #define DEFINE_ARRAY_LIST_OF( type ) \ \ DEFINE_ARRAY_OF( type ); \ \ ARRAY_LIST_OF( type )::ARRAY_LIST_OF( type )( UINT cAlloc ) \ : ARRAY_OF(type) ( cAlloc ) \ { \ Resize(0, FALSE); /* do not downsize */ \ } \ \ BOOL ARRAY_LIST_OF( type )::Add( const type & t ) \ { \ UINT n = QueryCount(); \ \ if ( ! Resize( n + 1 )) \ return FALSE; /* Out of memory */ \ \ (*this)[n] = t; \ return TRUE; \ } \ \ \ BOOL ARRAY_LIST_OF( type )::AddIdemp( const type & t ) \ { \ return (( Find( t ) >= 0 ) || Add( t )); \ } \ \ \ BOOL ARRAY_LIST_OF( type )::Remove( const type & t ) \ { \ INT i = Find( t ); \ if ( i < 0 ) \ return FALSE; /* item not found */ \ \ /* Since Find was able to find the item, the array list */ \ /* cannot be empty. */ \ ASSERT(QueryCount() > 0); \ UINT n = QueryCount() - 1; \ if ( (UINT)i < n ) \ (*this)[i] = (*this)[n]; \ \ /* Reducing the size of an array always succeeds */ \ REQUIRE( Resize( n )); \ \ return TRUE; \ } \ \ \ INT ARRAY_LIST_OF( type )::Find( const type & t ) const \ { \ for ( UINT i = 0; i < QueryCount(); i++ ) \ { \ if ( t.Compare( &(operator[]( i ))) == 0 ) \ return i; \ } \ \ return -1; /* not found */ \ } \ \ \ VOID ARRAY_LIST_OF( type )::Clear() \ { \ /* Resizing to size 0 is guaranteed always to work. */ \ REQUIRE( Resize( 0 )); \ } /********************************************************************/ #define DEFINE_EXT_ARRAY_LIST_OF( type ) \ \ DEFINE_ARRAY_LIST_OF( type ); \ \ int __cdecl ARRAY_LIST_OF( type )::SortFunc ( \ const void * p1, const void * p2 ) \ { \ const type * pt1 = (const type *) p1 ; \ const type * pt2 = (const type *) p2 ; \ return pt1->Compare( pt2 ) ; \ } \ \ VOID ARRAY_LIST_OF( type )::Sort() \ { \ qsort( &((*this)[0]), QueryCount(), sizeof( type ), \ ARRAY_LIST_OF( type )::SortFunc ); \ } \ \ \ INT ARRAY_LIST_OF( type )::BinarySearch( const type & t ) const \ { \ type * ptBase = &((*this)[0]); \ type * pt = (type *)bsearch( (VOID *)&t, \ (VOID *)ptBase, \ QueryCount(), \ sizeof( type ), \ ARRAY_LIST_OF( type)::SortFunc ); \ \ return (( pt == NULL ) ? -1 : (INT)(pt - ptBase) ); \ } // Helper macros for code preservation #define DECLARE_ARRAY_OF(type) \ DECL_ARRAY_OF(type,DLL_TEMPLATE) #define DECLARE_ARRAY_LIST_OF(type) \ DECL_ARRAY_LIST_OF(type,DLL_TEMPLATE) #endif //_ARRAY_HXX_